Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Php Log Rotation Laravel Package

cesargb/php-log-rotation

A framework-agnostic PHP library for rotating log files by size or time, with automatic gzip/bzip2 compression, retention limits, and cleanup of old logs. Works standalone or with Laravel, PSR-3 loggers, and custom handlers.

Deep Wiki
Context7

Getting Started

Install with Composer:

composer require cesargb/php-log-rotation

The package is a single, dependency-free class. Your first stop should be the README example and src/LogRotate.php — the public fluent API (files(), size(), compress(), dateFolder(), truncate(), rotate()) is the whole surface area, so you can read it end-to-end in a few minutes.

Minimal working rotation:

use CesarGB\LogRotation\LogRotate;

$rotator = (new LogRotate('/var/log/app/app.log'))
    ->files(7)        // keep the 7 most recent rotations
    ->size(10240)     // rotate when the active file exceeds this size (KB)
    ->compress()      // gzip rotated files
    ->rotate();       // bool: did this run actually rotate?

The intended first use case is a scheduled job (cron, systemd timer, or a Laravel scheduled command) that calls rotate() once per hour/day. The class does not run a daemon and does not self-schedule — it is a one-shot imperative call that you invoke from your own scheduler.

Implementation Patterns

Cron / Laravel scheduler wrapper. Keep the paths in config, not in the scheduler closure:

// app/Console/Kernel.php
$schedule->call(function () {
    foreach (config('logging.rotate') as $path => $opts) {
        (new LogRotate(storage_path("logs/{$path}")))
            ->files($opts['keep'])
            ->size($opts['size'])
            ->compress($opts['gzip'])
            ->rotate();
    }
})->hourly();

Size + retention together. size() is the trigger, files() is the cap. Use both: without size() you either rotate every run (too many tiny files) or only on a manual call; without files() you accumulate rotations forever. Add minSize() where available so idle logs don't produce empty rotations on every scheduled tick.

Long-running writers — use truncate(). If a process (PHP-FPM, a daemon, supervisor-managed worker) holds the log file open, a normal rename-based rotation means the process keeps writing to the renamed file and the new path stays empty. ->truncate() truncates in place, preserving the inode, so the open handle keeps pointing at the same file. This is the correct mode for anything that isn't reopened after rotation.

Custom naming / dated folders. ->dateFolder('Y-m-d') groups rotations into subdirectories, which is convenient for per-day retention and for humans grepping old logs. Combine with compress() to keep the tree small. If you need a specific rotated filename, use the custom-filename option rather than post-processing.

Multi-log rotation. The class is per-file, so loop over a list. Wrap that loop in a small service (LogRotationService::rotateAll()) so you can unit-test the globbing/path logic without touching the real class.

Integration with Monolog. This is not a Monolog handler; it lives one level below. The clean integration is a small Monolog HandlerInterface wrapper that calls rotate() on close(), or simply a scheduled command if you use StreamHandler. Don't try to force it into the logging hot path — rotation is an out-of-band concern.

Gotchas and Tips

  • size() units. Historically this is KB, not bytes. A value of 1024 means ~1 MB, not 1 KB. Verify against the version you install — this is the single most common "my logs got rotated at the wrong time" bug.
  • Open file handles. As above: rename-based rotation + an open handle = writes go to the rotated file and the live file stays empty. Diagnose with lsof /path/to.log. Fix with truncate() or by restarting the writer after rotation.
  • No locking. The class does not coordinate with concurrent rotate() calls or with the writer. Two overlapping cron runs (or a slow compression step overlapping the next tick) can race. Guard with a lock file or withoutOverlapping() in the scheduler.
  • Compression needs zlib. compress() uses gzopen/gzwrite; on stripped-down PHP builds or Docker images without zlib you'll get errors or silent skips. Test in the same image you deploy.
  • Permissions. Cron typically runs as a different user than the web server. Rotated files inherit the umask of the rotating user; if cron can't read/rename a log owned by www-data, rotation silently fails. Run the job as the owner or set group ACLs.
  • rotate() returns bool. Use it to log/metric whether rotation actually happened — this is your best signal that size thresholds are tuned correctly.
  • Timestamp collisions. Rotation filenames are timestamp-based; two rotations within the same second (e.g. a manual call right after a scheduled one) can collide. Don't call rotate() in a tight loop for the same file.
  • Retention with date folders. Confirm that files() cleanup traverses dated subdirectories in your version — retention counting semantics differ between flat and folder modes, and it's easy to end up with old dated folders that are never pruned.
  • Version drift. The API moved between major versions (constructor options vs. fluent chain). Pin the version in composer.json and check the CHANGELOG before upgrading; examples online frequently mix 2.x and 3.x syntax.
  • Test against a temp directory. Point the constructor at sys_get_temp_dir() in tests and assert on the returned bool plus the files spawned — much cheaper than asserting on real rotated logs.
  • Not atomic, not line-aware. Rotation can drop the bytes written between your size check and the rename. For audit-grade logs, accept the small gap or use truncate() and a writer that flushes per line.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource
noith/settings
kda/laravel-package