From c0c7622aad291548c8d4e99c8dce1f0d818bc5a3 Mon Sep 17 00:00:00 2001 From: Ben-Orlando Lampert Date: Tue, 16 Sep 2025 11:28:53 +0200 Subject: [PATCH 1/4] Add SysLog Cleanup Command and update README with usage instructions --- .../Command/Cleanup/SysLogCleanupCommand.php | 107 ++++++++++++++++++ README.md | 13 +++ 2 files changed, 120 insertions(+) create mode 100644 Classes/Command/Cleanup/SysLogCleanupCommand.php diff --git a/Classes/Command/Cleanup/SysLogCleanupCommand.php b/Classes/Command/Cleanup/SysLogCleanupCommand.php new file mode 100644 index 0000000..46f7fba --- /dev/null +++ b/Classes/Command/Cleanup/SysLogCleanupCommand.php @@ -0,0 +1,107 @@ +setDescription('Cleanup TYPO3 sys_log table from outdated entries.'); + $this->setHelp('Deletes entries from sys_log matching: + - details LIKE "%has cleared the cache%" + - details LIKE "[scheduler%" + - details LIKE "User %s logged in from%" + - details LIKE "%was deleted unrecoverable%" + - error > 0 + - tstamp older than --days (default 360) + +Use --dry-run to see row count without deleting.'); + + + $this->addCommonOptions(); + $this->addOption( + 'days', + 'd', + InputOption::VALUE_OPTIONAL, + 'Delete rows older than this many days (by tstamp).', + '360' + ); + } + + public function execute(InputInterface $input, OutputInterface $output): int + { + try { + $this->initializeCommand($input, $output); + } catch (\Throwable $e) { + return $this->handleException($e); + } + + $days = (int)($input->getOption('days') ?? 360); + $oldestEntryTstamp = time() - ($days * 86400); + + $this->io->note(($this->dryRun ? '[DRY-RUN] ' : '') . 'Cleaning sys_log with --days=' . $days); + + $totalAffected = 0; + + $qb = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_log'); + + if ($this->dryRun) { + $count = (int)$qb + ->selectLiteral('COUNT(*)') + ->from('sys_log') + ->where( + $qb->expr()->or( + $qb->expr()->like('details', $qb->createNamedParameter('%has cleared the cache%')), + $qb->expr()->like('details', $qb->createNamedParameter('[scheduler%')), + $qb->expr()->like('details', $qb->createNamedParameter('User %s logged in from%')), + $qb->expr()->lt('tstamp', $qb->createNamedParameter($oldestEntryTstamp, ParameterType::INTEGER)), + $qb->expr()->like('details', $qb->createNamedParameter('%was deleted unrecoverable%')), + $qb->expr()->gt('error', $qb->createNamedParameter(0, ParameterType::INTEGER)), + ) + ) + ->executeQuery() + ->fetchOne(); + $this->io->writeln(sprintf('Would delete %d rows', $count)); + } else { + $affected = (int)$qb + ->delete('sys_log') + ->where( + $qb->expr()->or( + $qb->expr()->like('details', $qb->createNamedParameter('%has cleared the cache%')), + $qb->expr()->like('details', $qb->createNamedParameter('[scheduler%')), + $qb->expr()->like('details', $qb->createNamedParameter('User %s logged in from%')), + $qb->expr()->lt('tstamp', $qb->createNamedParameter($oldestEntryTstamp, ParameterType::INTEGER)), + $qb->expr()->like('details', $qb->createNamedParameter('%was deleted unrecoverable%')), + $qb->expr()->gt('error', $qb->createNamedParameter(0, ParameterType::INTEGER)), + ) + ) + ->executeStatement(); + } + + if ($this->dryRun) { + $this->io->success('Dry-run completed. No rows were deleted.'); + } else { + $this->io->success('Cleanup completed. Total deleted: ' . $affected); + } + + return Command::SUCCESS; + } +} diff --git a/README.md b/README.md index b9377bc..4bc29aa 100644 --- a/README.md +++ b/README.md @@ -123,6 +123,19 @@ typo3 housekeeper:cleanup-missing [options] > Files that failed to be deleted will be written to a log file: `var/log/housekeeper:cleanup-missing_failed_DATE.log`. +### SysLog Cleanup Command +Cleanup old sys_log entries. You can define the age of the entries to be deleted. + +``` +typo3 housekeeper:cleanup-syslog [options] +``` +#### Options + +| Option | Short | Description | Default | +|---------------------|-------|-------------------------------------------|---------------| +| `--days` | `-d` | Delete rows older than this many days | 360 | +| `--dry-run` | - | Only pretend deletion | false | + ### Consolidate External URLs Command This command searches for external URLs in the database and converts them to From f61ea1c47974ba099bb7db307bf83a817742ff9f Mon Sep 17 00:00:00 2001 From: Ben-Orlando Lampert Date: Tue, 16 Sep 2025 11:46:24 +0200 Subject: [PATCH 2/4] Add SysLogCleanupCommand to services configuration --- Configuration/Services.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml index 84ddcd1..5f653cc 100644 --- a/Configuration/Services.yaml +++ b/Configuration/Services.yaml @@ -34,3 +34,10 @@ services: command: 'housekeeper:consolidate-external-urls' description: 'Find external urls in RTEs and replace them with the internal link.' schedulable: true + + Elementareteilchen\Housekeeper\Command\Cleanup\SysLogCleanupCommand: + tags: + - name: 'console.command' + command: 'housekeeper:cleanup-syslog' + description: 'Cleanup TYPO3 sys_log table from outdated entries.' + schedulable: true From 87339fe0a05d8c8d3b9069e9a7eaee9409be943a Mon Sep 17 00:00:00 2001 From: Ben-Orlando Lampert Date: Wed, 17 Sep 2025 12:26:05 +0200 Subject: [PATCH 3/4] Add minDays option to allow keeping rows younger than a specified number of days --- .../Command/Cleanup/SysLogCleanupCommand.php | 44 ++++++++++++------- README.md | 3 +- 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/Classes/Command/Cleanup/SysLogCleanupCommand.php b/Classes/Command/Cleanup/SysLogCleanupCommand.php index 46f7fba..9bdde3d 100644 --- a/Classes/Command/Cleanup/SysLogCleanupCommand.php +++ b/Classes/Command/Cleanup/SysLogCleanupCommand.php @@ -14,9 +14,9 @@ use TYPO3\CMS\Core\Utility\GeneralUtility; /** - * Cleanup sys_log table from noisy or outdated entries + * Cleanup sys_log table from noisy or outdated rows * - * Deletes entries matching specific patterns and entries older than a + * Deletes rows matching specific patterns and rows older than a * configurable number of days. Supports dry-run to preview deletions. */ class SysLogCleanupCommand extends AbstractCommand @@ -24,26 +24,33 @@ class SysLogCleanupCommand extends AbstractCommand public function configure() { - $this->setDescription('Cleanup TYPO3 sys_log table from outdated entries.'); - $this->setHelp('Deletes entries from sys_log matching: + $this->setDescription('Cleanup TYPO3 sys_log table from outdated rows.'); + $this->setHelp('Deletes rows from sys_log matching: - details LIKE "%has cleared the cache%" - details LIKE "[scheduler%" - details LIKE "User %s logged in from%" - details LIKE "%was deleted unrecoverable%" - error > 0 - - tstamp older than --days (default 360) + - tstamp older than --maxDays (default 360) +Keeps rows younger than --minDays (default 10). Use --dry-run to see row count without deleting.'); - $this->addCommonOptions(); $this->addOption( - 'days', - 'd', + 'maxDays', + 'D', InputOption::VALUE_OPTIONAL, 'Delete rows older than this many days (by tstamp).', '360' ); + $this->addOption( + 'minDays', + 'd', + InputOption::VALUE_OPTIONAL, + 'Keep rows younger than this many days (by tstamp).', + '10' + ); } public function execute(InputInterface $input, OutputInterface $output): int @@ -54,12 +61,17 @@ public function execute(InputInterface $input, OutputInterface $output): int return $this->handleException($e); } - $days = (int)($input->getOption('days') ?? 360); - $oldestEntryTstamp = time() - ($days * 86400); + $maxDays = (int)($input->getOption('maxDays') ?? 360); + $minDays = (int)($input->getOption('minDays') ?? 10); + $maxDaysTstamp = time() - ($maxDays * 86400); + $minDaysTstamp = time() - ($minDays * 86400); - $this->io->note(($this->dryRun ? '[DRY-RUN] ' : '') . 'Cleaning sys_log with --days=' . $days); + if ($minDays >= $maxDays) { + $this->io->error('Invalid options: --minDays must be less than --maxDays'); + return Command::INVALID; + } - $totalAffected = 0; + $this->io->note(($this->dryRun ? '[DRY-RUN] ' : '') . 'Cleaning sys_log with --maxDays=' . $maxDays . ' --minDays=' . $minDays); $qb = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_log'); @@ -68,27 +80,29 @@ public function execute(InputInterface $input, OutputInterface $output): int ->selectLiteral('COUNT(*)') ->from('sys_log') ->where( + $qb->expr()->lt('tstamp', $qb->createNamedParameter($minDaysTstamp, ParameterType::INTEGER)), $qb->expr()->or( $qb->expr()->like('details', $qb->createNamedParameter('%has cleared the cache%')), $qb->expr()->like('details', $qb->createNamedParameter('[scheduler%')), $qb->expr()->like('details', $qb->createNamedParameter('User %s logged in from%')), - $qb->expr()->lt('tstamp', $qb->createNamedParameter($oldestEntryTstamp, ParameterType::INTEGER)), + $qb->expr()->lt('tstamp', $qb->createNamedParameter($maxDaysTstamp, ParameterType::INTEGER)), $qb->expr()->like('details', $qb->createNamedParameter('%was deleted unrecoverable%')), $qb->expr()->gt('error', $qb->createNamedParameter(0, ParameterType::INTEGER)), ) ) ->executeQuery() ->fetchOne(); - $this->io->writeln(sprintf('Would delete %d rows', $count)); + $this->io->writeln(sprintf('Would delete %d row(s)', $count)); } else { $affected = (int)$qb ->delete('sys_log') ->where( + $qb->expr()->lt('tstamp', $qb->createNamedParameter($minDaysTstamp, ParameterType::INTEGER)), $qb->expr()->or( $qb->expr()->like('details', $qb->createNamedParameter('%has cleared the cache%')), $qb->expr()->like('details', $qb->createNamedParameter('[scheduler%')), $qb->expr()->like('details', $qb->createNamedParameter('User %s logged in from%')), - $qb->expr()->lt('tstamp', $qb->createNamedParameter($oldestEntryTstamp, ParameterType::INTEGER)), + $qb->expr()->lt('tstamp', $qb->createNamedParameter($maxDaysTstamp, ParameterType::INTEGER)), $qb->expr()->like('details', $qb->createNamedParameter('%was deleted unrecoverable%')), $qb->expr()->gt('error', $qb->createNamedParameter(0, ParameterType::INTEGER)), ) diff --git a/README.md b/README.md index 4bc29aa..655f727 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,8 @@ typo3 housekeeper:cleanup-syslog [options] | Option | Short | Description | Default | |---------------------|-------|-------------------------------------------|---------------| -| `--days` | `-d` | Delete rows older than this many days | 360 | +| `--maxDays` | `-D` | Delete rows older than this many days | 360 | +| `--minDays` | `-d` | Keep rows younger than this many days | 10 | | `--dry-run` | - | Only pretend deletion | false | ### Consolidate External URLs Command From 4559abe249662694ba8c9eae9e63e0ce3e289224 Mon Sep 17 00:00:00 2001 From: Ben-Orlando Lampert Date: Fri, 19 Sep 2025 09:20:34 +0200 Subject: [PATCH 4/4] Refactor SysLogCleanupCommand options and update README for clarity --- .../Command/Cleanup/SysLogCleanupCommand.php | 89 ++++++++----------- README.md | 11 ++- 2 files changed, 47 insertions(+), 53 deletions(-) diff --git a/Classes/Command/Cleanup/SysLogCleanupCommand.php b/Classes/Command/Cleanup/SysLogCleanupCommand.php index 9bdde3d..2af2caa 100644 --- a/Classes/Command/Cleanup/SysLogCleanupCommand.php +++ b/Classes/Command/Cleanup/SysLogCleanupCommand.php @@ -14,9 +14,9 @@ use TYPO3\CMS\Core\Utility\GeneralUtility; /** - * Cleanup sys_log table from noisy or outdated rows + * Cleanup sys_log table from noisy or outdated entries * - * Deletes rows matching specific patterns and rows older than a + * Deletes entries matching specific patterns and entries older than a * configurable number of days. Supports dry-run to preview deletions. */ class SysLogCleanupCommand extends AbstractCommand @@ -24,31 +24,31 @@ class SysLogCleanupCommand extends AbstractCommand public function configure() { - $this->setDescription('Cleanup TYPO3 sys_log table from outdated rows.'); - $this->setHelp('Deletes rows from sys_log matching: + $this->setDescription('Cleanup TYPO3 sys_log table from outdated entries.'); + $this->setHelp(' +This command deletes entries from sys_log if they are older than --retentionPeriod and match one of the following criteria: - details LIKE "%has cleared the cache%" - details LIKE "[scheduler%" - details LIKE "User %s logged in from%" - details LIKE "%was deleted unrecoverable%" - error > 0 - - tstamp older than --maxDays (default 360) + - tstamp older than --cutoffPeriod (default 360) -Keeps rows younger than --minDays (default 10). Use --dry-run to see row count without deleting.'); $this->addCommonOptions(); $this->addOption( - 'maxDays', - 'D', + 'cutoffPeriod', + 'c', InputOption::VALUE_OPTIONAL, - 'Delete rows older than this many days (by tstamp).', + 'Any row older than this value will be deleted (in days).', '360' ); $this->addOption( - 'minDays', - 'd', + 'retentionPeriod', + 'r', InputOption::VALUE_OPTIONAL, - 'Keep rows younger than this many days (by tstamp).', + 'Any row younger than this value will not be deleted (in days).', '10' ); } @@ -61,61 +61,50 @@ public function execute(InputInterface $input, OutputInterface $output): int return $this->handleException($e); } - $maxDays = (int)($input->getOption('maxDays') ?? 360); - $minDays = (int)($input->getOption('minDays') ?? 10); - $maxDaysTstamp = time() - ($maxDays * 86400); - $minDaysTstamp = time() - ($minDays * 86400); + $cutoffPeriod = (int)($input->getOption('cutoffPeriod') ?? 360); + $retentionPeriod = (int)($input->getOption('retentionPeriod') ?? 10); + $cutoffTstamp = time() - ($cutoffPeriod * 86400); + $retentionTstamp = time() - ($retentionPeriod * 86400); - if ($minDays >= $maxDays) { - $this->io->error('Invalid options: --minDays must be less than --maxDays'); + if ($retentionPeriod >= $cutoffPeriod) { + $this->io->error('Invalid options: --retentionPeriod must be less than --cutoffPeriod'); return Command::INVALID; } - $this->io->note(($this->dryRun ? '[DRY-RUN] ' : '') . 'Cleaning sys_log with --maxDays=' . $maxDays . ' --minDays=' . $minDays); + $this->io->note(($this->dryRun ? '[DRY-RUN] ' : '') . 'Cleaning sys_log with --cutoffPeriod=' . $cutoffPeriod . ' --retentionPeriod=' . $retentionPeriod); $qb = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_log'); + $expressions = [ + $qb->expr()->lt('tstamp', $qb->createNamedParameter($retentionTstamp, ParameterType::INTEGER)), + $qb->expr()->or( + $qb->expr()->like('details', $qb->createNamedParameter('%has cleared the cache%')), + $qb->expr()->like('details', $qb->createNamedParameter('[scheduler%')), + $qb->expr()->like('details', $qb->createNamedParameter('User %s logged in from%')), + $qb->expr()->lt('tstamp', $qb->createNamedParameter($cutoffTstamp, ParameterType::INTEGER)), + $qb->expr()->like('details', $qb->createNamedParameter('%was deleted unrecoverable%')), + $qb->expr()->gt('error', $qb->createNamedParameter(0, ParameterType::INTEGER)), + ) + ]; + if ($this->dryRun) { $count = (int)$qb ->selectLiteral('COUNT(*)') ->from('sys_log') - ->where( - $qb->expr()->lt('tstamp', $qb->createNamedParameter($minDaysTstamp, ParameterType::INTEGER)), - $qb->expr()->or( - $qb->expr()->like('details', $qb->createNamedParameter('%has cleared the cache%')), - $qb->expr()->like('details', $qb->createNamedParameter('[scheduler%')), - $qb->expr()->like('details', $qb->createNamedParameter('User %s logged in from%')), - $qb->expr()->lt('tstamp', $qb->createNamedParameter($maxDaysTstamp, ParameterType::INTEGER)), - $qb->expr()->like('details', $qb->createNamedParameter('%was deleted unrecoverable%')), - $qb->expr()->gt('error', $qb->createNamedParameter(0, ParameterType::INTEGER)), - ) - ) + ->where(...$expressions) ->executeQuery() ->fetchOne(); $this->io->writeln(sprintf('Would delete %d row(s)', $count)); - } else { - $affected = (int)$qb - ->delete('sys_log') - ->where( - $qb->expr()->lt('tstamp', $qb->createNamedParameter($minDaysTstamp, ParameterType::INTEGER)), - $qb->expr()->or( - $qb->expr()->like('details', $qb->createNamedParameter('%has cleared the cache%')), - $qb->expr()->like('details', $qb->createNamedParameter('[scheduler%')), - $qb->expr()->like('details', $qb->createNamedParameter('User %s logged in from%')), - $qb->expr()->lt('tstamp', $qb->createNamedParameter($maxDaysTstamp, ParameterType::INTEGER)), - $qb->expr()->like('details', $qb->createNamedParameter('%was deleted unrecoverable%')), - $qb->expr()->gt('error', $qb->createNamedParameter(0, ParameterType::INTEGER)), - ) - ) - ->executeStatement(); + $this->io->success('Dry-run completed. No rows were deleted.'); + return Command::SUCCESS; } - if ($this->dryRun) { - $this->io->success('Dry-run completed. No rows were deleted.'); - } else { - $this->io->success('Cleanup completed. Total deleted: ' . $affected); - } + $affected = (int)$qb + ->delete('sys_log') + ->where(...$expressions) + ->executeStatement(); + $this->io->success('Cleanup completed. Total deleted: ' . $affected); return Command::SUCCESS; } } diff --git a/README.md b/README.md index 655f727..faff022 100644 --- a/README.md +++ b/README.md @@ -124,7 +124,12 @@ typo3 housekeeper:cleanup-missing [options] `var/log/housekeeper:cleanup-missing_failed_DATE.log`. ### SysLog Cleanup Command -Cleanup old sys_log entries. You can define the age of the entries to be deleted. +This command deletes entries from sys_log if they are older than --retentionPeriod and match one of the following criteria: + - details LIKE "%has cleared the cache%" + - details LIKE "[scheduler%" + - details LIKE "User %s logged in from%" + - error > 0 + - tstamp older than --cutoffPeriod (default 360) ``` typo3 housekeeper:cleanup-syslog [options] @@ -133,8 +138,8 @@ typo3 housekeeper:cleanup-syslog [options] | Option | Short | Description | Default | |---------------------|-------|-------------------------------------------|---------------| -| `--maxDays` | `-D` | Delete rows older than this many days | 360 | -| `--minDays` | `-d` | Keep rows younger than this many days | 10 | +| `--cutoffPeriod` | `-c` | Delete rows older than this (in days) | 360 | +| `--retentionPeriod` | `-r` | Keep rows younger than this (in days) | 10 | | `--dry-run` | - | Only pretend deletion | false | ### Consolidate External URLs Command