cesargb/laravel-logs-rotate
Automatically rotate Laravel log files. This package compresses and archives daily logs, removes old files by age or count, and integrates with the scheduler. Simple config keeps storage tidy without manual cleanup.
Install and publish
composer require cesargb/laravel-logs-rotate
php artisan vendor:publish --provider="Cesargb\Log\LaravelLogsRotateServiceProvider" --tag=config
The service provider is auto-discovered and schedules the rotation command for you — no Kernel.php/routes/console.php edit required for the default setup.
Where to look first
config/logrotate.php — the only file you normally touch. Each entry describes one log file and its rotation policy.php artisan list | grep -i log to confirm the exact signature in your installed version) — use it to trigger a rotation by hand instead of waiting for the scheduler.storage/logs/ — the rotated artefacts (.1, .2, .gz) are the ground truth that it worked.First use case
The canonical win is the single channel: storage/logs/laravel.log grows unbounded until the disk fills. Add one config entry with a size threshold, a keep count, and compression:
'logs' => [
'laravel' => [
'path' => storage_path('logs/laravel.log'),
'size_threshold' => 10 * 1024 * 1024, // rotate at ~10 MB
'keep' => 7,
'compress' => true,
'min_size' => 64 * 1024, // new in 2.9.0
],
],
Then verify with php artisan list, run the command once manually, and ls -lh storage/logs to confirm the rotated file appears.
Config-as-inventory. Treat config/logrotate.php as a declarative inventory: one entry per log you own — laravel.log, a queue worker log, a custom payments.log written via Log::build(). Entries are independent, so each file gets its own threshold/retention without code.
Size vs. retention, chosen per environment. In config/logrotate.php use env()/config() guards so local keeps 2 files uncompressed (fast to grep) while production keeps 14 and compresses. Compression plus a longer keep is cheap; uncompressed retention is what eats the disk.
Pair with the scheduler you already run. If you prefer explicit control (e.g. ->onOneServer()->withoutOverlapping() on multi-node setups), disable the package's built-in schedule and register the command yourself in routes/console.php — either way, make sure schedule:run is actually cron'd.
Exploit the new min_size in 2.9.0. Use it to gate rotation on both sides: size_threshold says "big enough to rotate", min_size says "too small to be worth rotating". This is most valuable for low-traffic services and for short-lived files created between rotations — without it, a file that just got created after a rotation can be immediately rolled over into a .1 with 3 lines in it. Pair a min_size of a few tens of KB with a size_threshold in the MBs and the noise disappears.
Write tests against a temp path. Point the config at storage_path('framework/testing/x.log'), write N bytes, invoke the command ($this->artisan(...) or Artisan::call), assert the rotated file and the .gz exist. This is the cheapest way to prove retention/compression behaviour before it matters in production.
Combine with log content tooling. Rotation is orthogonal to daily channels: if you use daily, Laravel already rotates by date — this package is for single and for custom paths. Don't run both policies against the same file.
www-data, forge), but the scheduler typically runs as another user. Renaming/compressing a file the scheduler can't write produces silent no-ops or Permission denied in the scheduler log. Run the scheduler as the same user or put both in a group with g write on storage/logs.keep with the compressed and uncompressed files in mind; a keep of 3 can feel very short once .gz siblings are in the same directory.withoutOverlapping() / a cache lock).size_threshold instead of a small threshold alone.path (absolute vs. relative bites here)? Is compress enabled but zlib missing from the PHP build? Did the command actually run — check the scheduler's output log, not just storage/logs.cesargb/php-log-rotate. If you need per-run custom logic (e.g. rotating only files matching a date pattern), call that library directly from an artisan command instead of bending the config.laravel.log to nothing. Keep at least one previous file so a post-incident investigation isn't looking at a 200-byte file that was created five minutes ago.How can I help you explore Laravel packages today?