Skip to content

Commit

Permalink
Merge pull request #21 from 21TORR/log-cleaner
Browse files Browse the repository at this point in the history
  • Loading branch information
keichinger authored Jun 19, 2024
2 parents a2c02b0 + 47812fa commit 8c58e53
Show file tree
Hide file tree
Showing 11 changed files with 245 additions and 3 deletions.
4 changes: 3 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@
"21torr/bundle-helpers": "^2.1",
"21torr/cli": "^1.0",
"doctrine/orm": "^2.19",
"symfony/clock": "^7.0",
"dragonmantank/cron-expression": "^3.3",
"symfony/clock": "^7.1",
"symfony/config": "^7.0",
"symfony/console": "^7.0",
"symfony/dependency-injection": "^7.0",
"symfony/event-dispatcher-contracts": "^3.4",
"symfony/http-kernel": "^7.0",
"symfony/messenger": "^7.0",
"symfony/scheduler": "^7.1",
"symfony/string": "^7.0",
"symfony/uid": "^7.0"
},
Expand Down
4 changes: 4 additions & 0 deletions src/DependencyInjection/TaskManagerBundleConfiguration.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ public function getConfigTreeBuilder () : TreeBuilder
->info("The list of queues to inspect. This list should be sorted by descending priority.")
->scalarPrototype()->end()
->end()
->integerNode("log_ttl")
->info("The max age of log entries, before they are automatically cleaned.")
->defaultValue(28)
->end()
->end();

return $treeBuilder;
Expand Down
10 changes: 9 additions & 1 deletion src/Entity/TaskLog.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class TaskLog
private \DateTimeImmutable $timeQueued;

/** @var Collection<int, TaskRun> */
#[ORM\OneToMany(mappedBy: "taskLog", targetEntity: TaskRun::class)]
#[ORM\OneToMany(mappedBy: "taskLog", targetEntity: TaskRun::class, cascade: ["remove"], orphanRemoval: true)]
#[ORM\OrderBy(["timeStarted" => "asc"])]
private Collection $runs;

Expand Down Expand Up @@ -129,6 +129,14 @@ public function getLastUnfinishedRun () : ?TaskRun
return null;
}

/**
* Returns whether all runs for this task are finished
*/
public function isFinished () : bool
{
return null === $this->getLastUnfinishedRun();
}

/**
*
*/
Expand Down
16 changes: 16 additions & 0 deletions src/Listener/TaskIntegrationListener.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php declare(strict_types=1);

namespace Torr\TaskManager\Listener;

use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
use Torr\TaskManager\Event\RegisterTasksEvent;
use Torr\TaskManager\Log\Task\CleanOutdatedLogsTask;

final readonly class TaskIntegrationListener
{
#[AsEventListener]
public function onRegisterTasks (RegisterTasksEvent $event) : void
{
$event->register(new CleanOutdatedLogsTask());
}
}
44 changes: 44 additions & 0 deletions src/Log/LogCleaner.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php declare(strict_types=1);

namespace Torr\TaskManager\Log;

use Torr\TaskManager\Model\TaskLogModel;

final readonly class LogCleaner
{
public function __construct (
private int $logTtlInDays,
private TaskLogModel $model,
) {}

/**
* @return string[] labels for the removed tasks
*/
public function cleanLogEntries () : array
{
$deleted = [];

foreach ($this->model->fetchOutdatedTasks($this->logTtlInDays) as $logEntry)
{
$deleted[] = sprintf(
"<fg=yellow>%s</> (%s)",
$logEntry->getTaskLabel(),
$logEntry->getTimeQueued()->format("c"),
);

$this->model->remove($logEntry);
}

$this->model->flush();

return $deleted;
}

/**
* Returns the maximum age of log entries to keep (in days)
*/
public function getMaxLogEntryAge () : int
{
return $this->logTtlInDays;
}
}
29 changes: 29 additions & 0 deletions src/Log/Task/CleanOutdatedLogsTask.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php declare(strict_types=1);

namespace Torr\TaskManager\Log\Task;

use Torr\TaskManager\Task\Task;
use Torr\TaskManager\Task\TaskMetaData;

final readonly class CleanOutdatedLogsTask extends Task implements \Stringable
{
/**
* @inheritDoc
*/
public function getMetaData () : TaskMetaData
{
return new TaskMetaData(
label: "Clean log entries",
group: "Task Manager",
uniqueTaskId: "task-manager.clean-log",
);
}

/**
*
*/
public function __toString () : string
{
return $this->getMetaData()->label;
}
}
55 changes: 55 additions & 0 deletions src/Log/Task/CleanOutdatedLogsTaskHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php declare(strict_types=1);

namespace Torr\TaskManager\Log\Task;

use Symfony\Component\Messenger\Attribute\AsMessageHandler;
use Torr\TaskManager\Director\TaskDirector;
use Torr\TaskManager\Log\LogCleaner;

final readonly class CleanOutdatedLogsTaskHandler
{
/**
*/
public function __construct (
private LogCleaner $logCleaner,
private TaskDirector $taskDirector,
) {}

/**
*/
#[AsMessageHandler]
public function onCleanOutdatedLogs (CleanOutdatedLogsTask $task) : void
{
$run = $this->taskDirector->startRun($task);
$io = $run->getIo();

$io->title("Task Manager: Cleaning Outdated Log Entries");
$io->comment(sprintf(
"Cleaning log entries older than <fg=blue>%d days</>",
$this->logCleaner->getMaxLogEntryAge(),
));

$deletedEntries = $this->logCleaner->cleanLogEntries();

if ([] === $deletedEntries)
{
$io->success("No entries to remove found");
$run->finish(true);

return;
}

$io->writeln("Removed:");
$io->listing($deletedEntries);

$io->success(sprintf(
"Deleted <fg=yellow>%d</> %s:",
\count($deletedEntries),
1 !== \count($deletedEntries)
? "entries"
: "entry",
));

$run->finish(true);
}
}
7 changes: 7 additions & 0 deletions src/Manager/TaskManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Symfony\Component\Messenger\Stamp\StampInterface;
use Symfony\Component\Messenger\Transport\Receiver\ListableReceiverInterface;
use Symfony\Component\Messenger\Transport\Sync\SyncTransport;
use Symfony\Component\Scheduler\Messenger\SchedulerTransport;
use Torr\TaskManager\Exception\Transport\InvalidMessageTransportException;
use Torr\TaskManager\Task\Task;
use Torr\TaskManager\Transport\TransportsHelper;
Expand Down Expand Up @@ -83,6 +84,12 @@ public function fetchTasksInQueue (string $queueName) : iterable
{
$receiver = $this->transportsHelper->getTransport($queueName);

// ignore schedulers
if ($receiver instanceof SchedulerTransport)
{
return [];
}

// skip, as sync transports can't queue messages like regular transports
if ($receiver instanceof SyncTransport)
{
Expand Down
48 changes: 47 additions & 1 deletion src/Model/TaskLogModel.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Doctrine\ORM\EntityManagerInterface;
use Doctrine\ORM\EntityRepository;
use Psr\Clock\ClockInterface;
use Torr\TaskManager\Entity\TaskLog;
use Torr\TaskManager\Entity\TaskRun;
use Torr\TaskManager\Task\Task;
Expand All @@ -17,6 +18,7 @@ final class TaskLogModel
*/
public function __construct (
private readonly EntityManagerInterface $entityManager,
private readonly ClockInterface $clock,
)
{
$repository = $this->entityManager->getRepository(TaskLog::class);
Expand Down Expand Up @@ -57,9 +59,53 @@ public function createRunForTask (TaskLog $log) : TaskRun
}

/**
* @return list<TaskLog>
*/
public function flush () : void
public function fetchOutdatedTasks (int $maxAgeInDays) : array
{
$oldestTimeQueued = $this->clock->now()
->sub(new \DateInterval("P{$maxAgeInDays}D"));

/** @var TaskLog[] $entries */
$entries = $this->repository->createQueryBuilder("task")
->leftJoin("task.runs", "run")
->where("task.timeQueued <= :oldestTimestamp")
->setParameter("oldestTimestamp", $oldestTimeQueued)
->getQuery()
->getResult();

$filtered = [];

foreach ($entries as $entry)
{
if ($entry->isFinished())
{
$filtered[] = $entry;
}
}

return $filtered;
}

/**
* @return $this
*/
public function flush () : static
{
$this->entityManager->flush();

return $this;
}

/**
* Marks the log entry for removal
*
* @return $this
*/
public function remove (TaskLog $log) : static
{
$this->entityManager->remove($log);

return $this;
}
}
27 changes: 27 additions & 0 deletions src/Schedule/TaskManagerSchedule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php declare(strict_types=1);

namespace Torr\TaskManager\Schedule;

use Symfony\Component\Scheduler\Attribute\AsSchedule;
use Symfony\Component\Scheduler\RecurringMessage;
use Symfony\Component\Scheduler\Schedule;
use Symfony\Component\Scheduler\ScheduleProviderInterface;
use Torr\TaskManager\Log\Task\CleanOutdatedLogsTask;

#[AsSchedule("task_manager")]
final readonly class TaskManagerSchedule implements ScheduleProviderInterface
{
/**
*
*/
public function getSchedule () : Schedule
{
return (new Schedule())
->with(
RecurringMessage::cron(
"#daily",
new CleanOutdatedLogsTask(),
),
);
}
}
4 changes: 4 additions & 0 deletions src/TaskManagerBundle.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Torr\TaskManager\Config\BundleConfig;
use Torr\TaskManager\DependencyInjection\AutoDetectFailureTransportsCompilerInterface;
use Torr\TaskManager\DependencyInjection\TaskManagerBundleConfiguration;
use Torr\TaskManager\Log\LogCleaner;

final class TaskManagerBundle extends Bundle
{
Expand All @@ -24,6 +25,9 @@ static function (array $config, ContainerBuilder $container) : void
{
$container->getDefinition(BundleConfig::class)
->setArgument('$sortedQueues', $config["queues"]);

$container->getDefinition(LogCleaner::class)
->setArgument('$logTtlInDays', $config["log_ttl"]);
},
);
}
Expand Down

0 comments on commit 8c58e53

Please sign in to comment.