escarter/activity-log
A lightweight Laravel package for tracking user activity. Log model events and custom actions to the database, then query recent activity to build audit trails, dashboards, or admin feeds with minimal configuration. Works with Eloquent models and is simple to install and extend.
Architecture fit. The design is the standard Laravel audit-log shape: a polymorphic activity_log table (subject_type/subject_id, causer_type/causer_id, event, properties, batch_uuid, created_at), an opt-in trait that hooks Eloquent model events, and a fluent builder for manual/non-model events. It adds no infrastructure — no queue, no sidecar, no external service — so it inherits the app's existing DB, auth, backup, and access-control posture. For a monolith or a small number of Laravel services sharing one database, that is the correct fit and the cheapest defensible audit trail you can ship.
Integration feasibility. Mechanically low-effort: composer require, publish config + migration, php artisan migrate, add the trait to one model. The real work is not installation, it is the three product decisions the package does not make for you: which events are enabled per model, which attributes are captured (allow list, not deny list), and how the causer is resolved in unauthenticated contexts. Budget for those, not for the install.
Technical risk — and the dominant one is provenance, not code.
spatie/laravel-activitylog — same column names, same performedOn/causedBy/withProperties builder, same batch UUID concept. Most likely this is a fork or rename of it. That is simultaneously the biggest risk (you are one abandoned fork away from an unmaintained dependency) and the best mitigation (the migration cost to the upstream original is close to zero if the namespaces are compatible).Key questions to resolve before committing.
spatie/laravel-activitylog, and if so, what does it change and why not use upstream?afterCommit/queue?Stack fit. Laravel + Eloquent + relational DB, nothing exotic. It only fits cleanly if the audit trail is allowed to live in the primary database; if security or legal require tamper-evident or separately-resident storage, that is a compensating control you scope separately and this package does not provide it.
Migration path. Two entry points, both cheap:
composer.json swap plus a config/migration diff. Verify the exact class names rather than assuming — forks rename things.Compatibility. Pin the exact version (1.0.1) in composer.json. Do not float a caret constraint on a 1-star package with opaque release notes. Verify the Laravel/PHP matrix in a throwaway branch before it touches a shared codebase.
Sequencing.
Invoice, Order, or User), log a single update end-to-end, and confirm subject_type, non-null causer_id, and a renderable properties payload.subject_type (decide before you accumulate history; renaming an alias or moving a namespace orphans rows permanently).Explicitly out of scope for v1: cross-service audit unification, event streaming, and tamper-evidence. If those are requirements, stop and re-scope.
Maintenance. Assume bus factor of one. With 1 star and no release narrative, you are effectively adopting a package you may have to own. The good news: the surface is small (one table, one trait, one builder), and if it is a Spatie fork, the escape hatch is a namespace swap. Plan for the possibility of vendoring it and treating it as internal code — that is a legitimate outcome, but it should be a stated cost in the business case, not a discovery in month nine.
Support. No community to lean on, no Stack Overflow corpus under this name, no issue-tracker track record. Internal ramp-up comes from reading the published config, the migration, and the trait — three files — rather than from docs. Budget a day of one engineer's time to become the named owner and write a short internal runbook.
Scaling. The write path is the concern: one INSERT per logged mutation on the same database serving production traffic, plus index maintenance on subject_type/subject_id, causer_type/causer_id, and created_at. High-churn columns (last_seen_at, counters, updated_at) will generate a row per request if not excluded — a per-model configuration failure that shows up as a database growth problem, not a logging problem. Large properties JSON blobs slow both writes and scans; trim payloads rather than reaching for JSON indexes. The table grows monotonically — retention is a launch requirement, not a later optimization.
Failure modes to instrument against.
causer_id in background contexts: the single most common data-quality defect. Queued jobs, console commands, and scheduled tasks have no auth context. Attribute to the requesting user, not the worker, and assert non-null causers in tests for critical flows.afterCommit config. Verify explicitly under a rolled-back transaction if anything compliance-adjacent depends on it.vendor:publish --force over a live config; publish to a temp path, diff against the package stub, and hand-write a follow-up migration.Ramp-up. Low for any Laravel developer — trait plus builder is a familiar mental model. The skills that actually matter are the non-obvious ones: knowing that "no rows written" is a config/migration problem while "wrong rows written" is an attribute-list problem, and knowing to query the table directly in tinker filtered by subject ID and ordered by created_at as the first debugging step. Capture both in the runbook.
Recommendation. Conditional adopt, contingent on the provenance question. If this is a thin fork of spatie/laravel-activitylog, default to the upstream package — the integration cost is identical and the maintenance risk is strictly lower. If there is a specific reason to prefer this one, adopt it with the exact version pinned, a named internal owner, retention and redaction configured on day one, and a written exit path. In either case, correct the "mature, stable 1.0" framing in the existing PM assessment before it propagates into a funding or roadmap decision.
How can I help you explore Laravel packages today?