hedii/artisan-log-cleaner
A Laravel package that adds an Artisan command to clean up old log files. Set a retention period and schedule it to automatically delete stale logs, keeping storage tidy and preventing log directories from growing indefinitely.
Install the package via Composer:
composer require hedii/artisan-log-cleaner
The service provider is auto-discovered on Laravel 5.5+, so no manual registration is needed. Publish the config file so you can see exactly which files the command targets:
php artisan vendor:publish --provider="Hedii\ArtisanLogCleaner\ArtisanLogCleanerServiceProvider" --tag=config
This creates config/artisan-log-cleaner.php. Open it first — it defines the log_files array (paths relative to storage_path()) that the command will truncate. If you leave it empty, review the package source under vendor/hedii/artisan-log-cleaner/src to understand the default behavior before running the command in a shared environment.
Your first practical use case is trimming a runaway laravel.log during development:
php artisan log:clear
That's the whole daily workflow: one command, one config file, one log directory. Everything else is scheduling it or scoping it to specific files.
Manual cleanup during development. When storage/logs/laravel.log grows to gigabytes after a stack-trace loop, run php artisan log:clear instead of > storage/logs/laravel.log (which leaves the file handle open on some systems). Pair it with tail -f storage/logs/laravel.log in another terminal and clear on demand.
Scoped cleaning via config. Rather than nuking every log, enumerate specific files in config/artisan-log-cleaner.php:
'log_files' => [
'laravel.log',
'worker.log',
// leave 'audit.log' alone
],
Committing this file to version control gives the whole team a consistent, safe set of targets. Keep the audit/trail-sensitive logs out of the array.
Scheduling in Laravel 10 and earlier. Add to app/Console/Kernel.php:
protected function schedule(Schedule $schedule)
{
$schedule->command('log:clear')->dailyAt('03:00');
}
Scheduling in Laravel 11+. Same idea, but in routes/console.php or bootstrap/app.php:
use Illuminate\Support\Facades\Schedule;
Schedule::command('log:clear')->dailyAt('03:00');
Environment-aware deployment. Guard the schedule so it only fires in local/staging:
Schedule::command('log:clear')
->daily()
->environments(['local', 'staging']);
Production logs are usually shipped to CloudWatch/Datadog/Papertrail — clearing them locally destroys the only copy.
Wrapping in a deploy hook. After php artisan down, before php artisan up, running log:clear gives you a clean slate per deploy so post-deploy errors are unambiguous:
php artisan down
git pull && composer install --no-dev
php artisan log:clear
php artisan up
Complementing rather than replacing rotation. If you already use LOG_CHANNEL=daily, Laravel writes date-stamped files. Add those filenames (or a glob-friendly pattern supported by the package) to log_files so the cleaner handles the backlog, or leave rotation to Laravel and use this package only for single-file channels.
It truncates; it does not rotate or archive. Once run, the log content is gone. If you need history, ship logs to an external sink first (e.g. LOG_STACK=single,papertrail) or copy storage/logs to a backup path before invoking the command.
File permissions bite in Docker/Forge. The artisan process must have write access to the log files. If the web server user owns them, sudo -u www-data php artisan log:clear or a chown -R www-data:www-data storage/logs may be needed. Symptom: command exits 0 but files stay the same size.
Empty log_files is a footgun. Depending on version, an empty array may cause the command to no-op silently (safest) — but don't assume. Check the source or the command's --help output. If it defaults to "all files in storage/logs," you can accidentally wipe a log channel you were debugging.
Symlinked log directories. Some deployments symlink storage/logs to a mounted volume. The command follows the configured relative path under storage_path(), so make sure the symlink resolves and that you're targeting the file you think you are. realpath() the target once to confirm.
Scheduling needs a running scheduler. The command does nothing on a schedule unless * * * * * cd /path && php artisan schedule:run is in cron (or a schedule:work process is running). A common "the cleaner isn't working" bug is simply a missing cron entry — verify with php artisan schedule:list.
Don't schedule per-minute in production. Each invocation reads/truncates files. At everyMinute() on a busy fleet this adds unnecessary I/O. daily or hourly is plenty for a log cleaner.
Watch out for concurrent writes. Truncating a file another process has open for append can cause the file to immediately regrow, or on some filesystems reintroduce sparse blocks. It's harmless but make sure your "I cleared it" assumption isn't immediately invalidated by an active worker. Clearing during a quiet window avoids confusion.
Extension points are minimal — lean on the scheduler instead. The package is intentionally a single command with a config array. If you need retention windows (e.g. "delete files older than 14 days"), don't try to hack the config; write a small command that globs storage/logs/*.log and uses File::lastModified(), or just switch LOG_CHANNEL=daily and let Laravel's own rotation do the cleanup. Reach for hedii/artisan-log-cleaner when you specifically want a blunt "empty these files now" primitive.
Debugging: use --verbose and check the exit code. Run php artisan log:clear -v to surface the configured file list. Cross-check the reported paths against ls -la storage/logs — a mismatch almost always means a wrong log_files entry or a .env pointing at a different storage path in testing.
Version pin in CI. Last release was March 2025 and the star count is low (41), so this is a small, lightly maintained package. Pin the exact version in composer.json and cover it with a smoke test (assert the file is empty after invoking the command) so a future minor bump doesn't silently change default behavior.
How can I help you explore Laravel packages today?