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

Laravel Activitylog Laravel Package

spatie/laravel-activitylog

Easily log user activities in your Laravel app and automatically track model events. Store everything in the activity_log table, then retrieve and query activities with the Activity model and a fluent API.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit

  • This is a single-table, Eloquent-native audit log. It writes to one activity_log table with a polymorphic subject (the model acted upon) and causer (typically the authenticated user), plus a description, log_name, and a JSON properties payload.
  • Two integration modes matter for architecture: manual logging (activity()->performedOn($m)->causedBy($u)->log(...)) and automatic model-event logging via the LogsActivity trait, which records created/updated/deleted with an attribute_changes diff (attributes vs old).
  • Fit is strongest where the product need is "who did what, to which record, and what changed" inside the app itself — admin audit trails, change history panels, compliance-adjacent activity feeds, support/troubleshooting forensics.
  • Fit is weaker where the requirement is system-of-record audit for security/compliance (tamper-evidence, append-only guarantees, SIEM ingestion, non-repudiation). The package is an application-level write to the same database your app can write to; that is a fundamentally different trust model than an external immutable log store.
  • The package is a library, not a platform. There is no UI, no retention subsystem beyond a cleanup command, no alerting, and no query API beyond the Eloquent model. Those are all build-on-top work for the consuming team.

Integration feasibility

  • Low friction for a standard Laravel app: composer require, publish migration, php artisan migrate, optionally publish config. Auto-registration means no service provider wiring.
  • The main non-trivial step is identifier type alignment: the default migration assumes integer IDs. Any app using UUIDs/ULIDs must edit subject_id/causer_id column types before migrating. This is a one-time, cheap, but easy-to-miss gate — it is the single most common integration defect class for this package.
  • Config lets you set the table name (env-overridable) and default log name, which matters for multi-tenant or environment-segregated setups.
  • Adoption effort is genuinely small — days, not sprints, for a first vertical slice on 2–3 models. The effort concentrates in policy (what to log, retention, PII) and surface area (UI, export, access control), not in wiring.

Technical risk

  • Version-major risk: v5.0.0 requires PHP 8.4+ and Laravel 11+ and explicitly "modernized API." Any team on PHP 8.3 or Laravel 10 must pin v4.x, which means being on a maintenance branch of a package whose activity is concentrated on the v5 line. Conversely, going to v5 forces a PHP runtime upgrade project. This version gate should be resolved before commitment, not after.
  • PII and secret leakage: attribute_changes captures raw model attributes. Logging a User model with default logAll() will persist emails, phone numbers, and potentially password hashes or tokens into a JSON column that is often readable by far more staff than the source table. LogOptions (logOnly, logExcept, logOnlyDirty) exists precisely to mitigate this and should be a mandatory review gate, not an optional nicety.
  • Write amplification: the trait fires on every matching model event. Bulk imports, seeders, batch jobs, and event-sourced-ish flows can multiply write volume and table growth in ways that surprise teams post-launch. Volume forecasting is a real risk item.
  • Transactional coupling: log writes occur inline with model saves. A failure in the activity write can surface as a user-facing failure depending on transaction boundaries. Teams need a deliberate decision: fail the business operation, or swallow/defer the log write. The package does not choose for you.
  • Deletion semantics: deleting the subject leaves rows whose subject resolves to null; the subject_id and causer remain. Whether that satisfies an audit requirement is a product decision that should be made explicitly rather than discovered.
  • Data-quality caveats in the source metrics: "Dependents: 0" is almost certainly an undercount artifact rather than a real signal of adoption, and a "last release 2026-10-19" date should be verified against Packagist before it is used in any decision document. Do not let either number drive the recommendation.

Key questions

  1. Is the requirement product telemetry / troubleshooting or auditable security record? If the latter, what are the immutability, retention, and access-control obligations, and does a same-database JSON table satisfy them?
  2. What PHP and Laravel versions are in production, and is a PHP 8.4 upgrade already on the roadmap? This determines v4 vs. v5 and materially changes the timeline.
  3. Do we use integer, UUID, or ULID primary keys? Who owns the migration edit and the review to confirm it?
  4. What is the expected write volume at 12 months (model events/day × retention window), and what does that imply for table size, index strategy, and archival?
  5. Which models and which attributes are in scope — and which are explicitly out of scope for PII, credentials, and payment data?
  6. Does anyone need to read this data? If yes, we are committing to admin UI, filtering, export, and access-control work that is larger than the package integration itself.
  7. Is this expected to be per-tenant segregated? A shared activity_log table plus a tenant-scoped admin UI is a leak vector requiring deliberate query scoping.
  8. What is the rollback path if logging causes production write pressure — can it be disabled per-model via config without a deploy?

Integration Approach

Stack fit

  • Designed for conventional Laravel: Eloquent models, service container, migrations, config publishing, queues. No exotic dependencies, MIT licensed, no commercial gate.
  • Fits cleanly into an existing Laravel monolith or modular monolith. For multi-service architectures, each service either gets its own log table or logs into a shared store — decide this up front, because the polymorphic subject_type is a class-string and cross-service class names will not resolve.
  • Pairs naturally with an admin panel (Filament/Nova/custom) for the read side; the package deliberately provides none.

Migration path

  • Green-field or additive: essentially zero risk. Add the table, enable the trait on a pilot model, ship.
  • Brownfield with an existing bespoke audit/history table: do not attempt a data migration into the package's schema. Backfilling historical rows into activity_log buys little and creates schema-shape and semantics mismatches (your old rows have no old/attributes split, no causer polymorphic type). Run the two in parallel, cut over per-feature, then decommission the old table.
  • Existing custom history UIs must be re-pointed at Activity — treat this as a small, well-scoped frontend task, not free.

Compatibility

  • v4.x tracks Laravel 10–13 and PHP 8.x; v5.x requires PHP 8.4+ / Laravel 11+ with API changes. Pin the major explicitly in composer.json — a floating constraint on a package with aggressive major increments is an avoidable outage class.
  • subject_id/causer_id column types must match your key strategy; the config-driven table name supports environment separation.
  • Interaction with soft deletes, observers, and queued listeners is supported but has historically been the source of the subtle bugs (closure serialization in queued listeners, tapActivity firing twice, subject resolution on non-SoftDeletes models). Recent patch releases address several of these — another argument for staying current within your pinned major.

Sequencing

  1. Decide the requirement class (telemetry vs. audit-of-record) and lock retention/PII policy. Cheapest place to change your mind.
  2. Confirm runtime versions and pin v4 or v5 accordingly. Resolve the PHP 8.4 question now.
  3. Install and migrate with correct ID types, in a non-production environment, verified against a UUID-keyed model if applicable.
  4. Pilot on 2–3 high-value models with explicit LogOptions (logOnly/logExcept, dirty-only) rather than default logAll(). This is the PII control point.
  5. Define log-name conventions per domain so the read side is queryable without post-hoc cleanup.
  6. Build the read surface (scoped queries, access control, pagination) before enabling broad coverage — otherwise you accumulate rows nobody is allowed to see or able to use.
  7. Add retention/archival — schedule the cleanup command with an integer day boundary and an archive-before-delete step if audit obligations exist.
  8. Broaden coverage model-by-model, treating each addition as a small reviewed change with a volume estimate.
  9. Instrument: row counts, write latency on hot paths, and cleanup job success. Establish before/after baselines on the busiest write path.

Operational Impact

Maintenance

  • Spatie is a credible, long-running OSS steward with docs, changelog, releases, and a security contact. That is the good news.
  • The realistic maintenance burden is major-version chasing: the project ships majors that move the PHP/Laravel floor (v5 jumped to PHP 8.4+/Laravel 11+). Expect to be pulled into a runtime upgrade or to sit on a maintenance branch. Budget for this annually, not reactively.
  • The project's changelog is dominated by dependabot bumps and small correctness fixes. That profile indicates a stable core with steady housekeeping — low defect risk, but also low velocity on new capability. Do not plan on upstream delivering your missing features (UI, retention policy engine, multi-tenant scoping); those are yours.
  • Owned artifacts to maintain: the published migration (ID types), the config file (table name, defaults), the trait/LogOptions decisions per model, and the retention schedule.

Support

  • Support model is community: GitHub issues plus Spatie's docs site. Response times are best-effort. There is no SLA and no vendor to escalate to.
  • This is acceptable for a non-critical internal feature and not acceptable as the sole dependency of a compliance obligation without an internal owner who can read the package source. The codebase is small and readable, which materially softens this risk — a mid-level Laravel engineer can debug it.
  • Name an internal owner. "Everyone uses it, nobody owns it" is how audit tables end up with wrong ID types and uncontrolled PII.

Scaling

  • Growth is linear in model events and unbounded by default. A single JSON column plus polymorphic lookups means the table gets wide and the row count gets large; the standard mitigations apply and are well understood: composite indexes on (subject_type, subject_id) and (causer_type, causer_id), indexes on log_name and created_at, and time-based archival or partitioning.
  • Write cost is the real scaling constraint: every logged model event becomes an extra INSERT inside the request or job. On high-throughput write paths (bulk imports, queue workers doing thousands of updates), this can dominate. Mitigations: narrow the trait to the models that matter, prefer logOnlyDirty, and consider deferring writes via queued listeners — while accepting that queued logging introduces ordering and loss-on-failure tradeoffs.
  • Read scaling is usually trivial relative to write scaling, but unfiltered admin UI queries over a large activity_log table (especially JSON properties searches) will be the first thing to page. Design the read queries against indexes from day one.
  • Multi-tenant: the package has no native tenant scoping. A shared table requires every read path to be scoped by app code. Treat as a correctness requirement, not a nice-to-have.

Failure modes

  • ID-type mismatch (integer column, UUID keys): writes fail or silently truncate. Detect at migration review.
  • PII capture via defaults: sensitive attributes persisted to a broadly readable JSON column. Detect at LogOptions review; unrecoverable after the fact without a purge.
  • Write-path degradation: log inserts adding latency or lock contention on hot models. Detect with before/after latency baselines and row-growth dashboards.
  • Business-operation coupling: an activity-write failure bubbling into a user-facing error. Decide and encode the policy (fail-open vs. fail-closed) explicitly.
  • Subject drift: deleted or renamed model classes leave subject_type unresolvable and subject null. Plan for rendering "record deleted" states in the UI and for class renames during refactors.
  • Retention job failure: silent unbounded growth, often first noticed as a storage or slow-query incident. Alert on the cleanup job.
  • Queued-listener quirks: serialization and double-invocation bugs have appeared historically in the queue path. If you queue logging, test that path specifically.
  • Major-version upgrade breakage: an unpinned constraint pulling v5 into a PHP 8.3 environment. Pin the major.

Ramp-up

  • Engineer ramp-up is genuinely low — small, well-documented, idiomatic Laravel. A mid-level engineer is productive on the manual logging API within an hour and on the trait/config within a day.
  • The non-obvious learning is not the API but the policy: which models to instrument, which attributes to exclude, how to keep log names coherent, and how to keep the read side performant. That knowledge lives with the TPM and the owning engineer, not in the docs.
  • Recommended ramp: one pilot model end-to-end (log → read → display in admin) before broader rollout. That single slice surfaces the ID-type, PII, and query-performance issues while they are still cheap.
  • Documentation quality is a real asset here — the README plus Spatie's docs site plus the changelog give a new engineer enough to self-serve, which keeps the support burden on the internal owner low and predictable.
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.
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin
artisan-build/community-prompts
vleroy/smart
ayles-software/laravel-australian-public-holidays
cboxdk/laravel-id-spatie
cboxdk/laravel-id-billing
cboxdk/laravel-id-billing-bridge
simple-as-fuck/php-performance-log
hsluv/hsluv
seampact/codegen
seampact/hooks
seampact/ir
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table