Appearance
Activity Log
Who changed what and when: content, sign-ins, settings, users and roles.
kitloom/activity-log · admins: kitloom/activity-log-filament, kitloom/activity-log-nova
Video lessons
Recorded in the Filament admin; the Nova admin works the same way.
The site's activity log on spatie/laravel-activitylog: who changed which post, term, option or account, who signed in and who failed to, from which address and when. The admin is kitloom/activity-log-filament or kitloom/activity-log-nova.
What is logged
log_name | event | From |
|---|---|---|
content | created, updated, published, trashed, restored, deleted | Kitloom\WpSchema\Events\* — every post and taxonomy model, whatever wrote it |
content | created, approved, unapproved, spammed, trashed, restored | Kitloom\WpSchema\Events\CommentPosted, CommentStatusChanged — comments and product reviews, from the site's form, an admin or an agent |
auth | login, logout, failed | Laravel's auth events |
settings | created, updated, deleted | Kitloom\Settings\Events\SettingSaved, SettingDeleted — every write, through SettingWriter, a settings page or the model |
roles | created, updated, deleted | Kitloom\Permissions\Events\RoleSaved, RoleDeleted (the role editor), UserRoleChanged (a user given another role), PermissionsSynced (permissions that reached roles when a module or post type declared them) |
users | created, deleted | the Eloquent events of the model the default guard signs in with |
activity_log | cleared | the log cleared by hand |
- An update keeps the fields that changed (
changed), and the old and new values of the title, slug, status, parent, author, date, order and comment status. The content, the excerpt and a post's password are logged as changed, without values; for the body, the excerpt and a term's description the entry keeps what the edit did (diff). A save that changed nothing is not logged; publishing, trashing and restoring are entries of their own, not an update of the status. - Revisions, menu items (a saved menu rewrites every item), oEmbed caches, customizer changesets (
content.ignore_types) and auto-drafts are left out. - A comment is logged under its record —
comment by Ada on "Dune" approved,typethe comment type (comment,review),titlethe record's,status(approved,hold,spam,trash) andfromfor a move — by the user the writer acted for. What it said, the commenter's email and address are not kept. Order notes and WooCommerce's other internal rows of the comments table are left out, and so is a comment type named incontent.ignore_types. - A failed sign-in keeps the login that was tried — never the password — and the account it named, if there is one.
- An option whose name looks like a secret (
*pass*,*key*,*token*,*secret*,*dsn*… —settings.secret) is logged as changed without values. Transients,cronandrewrite_rulesare not logged (settings.ignore). Values over 500 characters are cut. - A role created or deleted keeps its permissions; a change keeps a new name and the permissions added and taken away, by name, not the whole list again. Permissions that reach roles on their own (
PermissionsSynced) are anupdatedentry per role withpermissions_added,source=sync, andlikewhen the grant followed a related capability — causer is the system. A user given another role — on their screen, as a new account's default role, or taken over from WordPress at sign-in — is anupdatedentry of typeuserwith the old and the new role. Here the causer is the actor the event carries, not whoever is signed in. - Nothing is logged while migrations run.
Every entry has type (the post type, the taxonomy, option, role, user) and title in its properties, via — web for a request a person made, console for the console, the queue and the scheduler — and, during a request, ip. The causer is whoever is signed in (the default guard); with nobody, the admin shows "System" for the console and "Visitor" for the web. by keeps the causer's login, so the log still names a deleted account. The email is never kept.
The old and new values are in attribute_changes, the way spatie's LogsActivity keeps them — {"old": {…}, "attributes": {…}}, old left out of a creation and attributes out of a deletion — so an entry written by spatie's activity() reads the same. The properties keep only kitloom's own data: the above, changed (every field that changed, those without values too) and diff.
Writing an entry never breaks what is being logged: a failure is reported (report()), not thrown.
php
use Kitloom\ActivityLog\Models\ActivityLog;
use Kitloom\WpSchema\WpSchema;
$changes = WpSchema::query(ActivityLog::KEY)->where('log_name', 'content')->ofType('book')->latest('id')->limit(20)->get();Where things are kept
| What | Where |
|---|---|
| Entries | spatie's table, activity_log (the table of the registry's model — spatie 5 has no activitylog.table_name) with the site's table prefix in front — migrated with the application's migrations, nothing to publish |
| Model | Kitloom\ActivityLog\Models\ActivityLog (extends spatie's Activity), key activity_log in the ModelRegistry; spatie's activity() and activitylog:clean are pointed at the same class |
| Retention | option activity_log_retention_days: 90 unless set, 0 keeps everything |
activity-log:prune deletes entries older than the retention (--days for one run); the module schedules it daily — the application's schedule:run has to run. It runs on one server of several, by a lock in the cache, so a site on several servers needs a shared cache store (Redis, the database) — with each server's own cache the lock is local and the task runs on every one. Clearing by hand (ActivityLogs::clear($actor, $olderThanDays)) takes manage_activity_log and leaves a cleared entry; reading the log in the admins takes view_activity_log. Both go to administrators.
AI agents
With kitloom/ai installed, an agent whose user has view_activity_log reads the log (activity_list): when, who, what, the record and the fields changed, filtered by log, event, user, record type, date or text. Clearing the log stays a person's. kitloom-activity-log.ai.register false — no log tool.
Personal data
With kitloom/privacy, Tools → Export Personal Data lists the entries about the person the address belongs to: what their account did, what was done to it (signed in, given a role), the sign-ins that failed with their address and what happened to their comments — when, the area, the event, what, and the IP address where it was theirs (an entry they made, a failed sign-in). The address of an administrator who changed their account is not exported.
Erase Personal Data keeps the entries and cuts the person out of them. The log is the site's audit: which post was published, which option changed, by whom — the owner still needs to read what happened, and deleting entries on request would let anyone wipe their tracks. What identifies the person goes: the account as causer or subject, the IP address of their entries and failed sign-ins, the login a failed sign-in tried, and their name in the entry's title, description and by, which say "Anonymous". Entries about their comments say "Anonymous" for the author; the comments themselves are kitloom/privacy's. The report counts the entries made anonymous and says that the log keeps them. The retention prunes them in time as usual; an owner who must keep the person's identity in the log for a security case turns the export and erasure off (privacy.register) and answers that request by hand.
Comments are matched by the address or the user they still have, so the log's eraser runs before the comments eraser of kitloom/privacy takes both away: providers register in the order Composer installs the packages, activity-log before privacy.
Configuration
config/kitloom-activity-log.php (php artisan vendor:publish --tag=kitloom-activity-log-config):
log.content,log.auth,log.settings,log.roles,log.users— each kind of logging on or off. spatie'sACTIVITY_LOGGER_ENABLEDturns everything off.ip.enabled,ip.anonymize— the address is kept in full by default: a security log has to tell where a run of failed sign-ins comes from, and the retention bounds how long it is kept.anonymizekeeps the network only (IPv4 without the last octet, IPv6 without the last 80 bits). Behind a proxy the application's TrustProxies decides whatRequest::ip()is.content.ignore_types,settings.ignore,settings.secret,settings.max_value_length.schedule— the daily pruning.privacy.register— the personal data export and erasure (kitloom/privacy).filament.register,nova.register— the admin sections.
What the application overrides
- The model:
ModelRegistry::use(ActivityLog::KEY, App\Models\ActivityLog::class)with a subclass of the package's; the admins and spatie's helpers read the class from the registry.
Tests
From the application that installs the package: php vendor/bin/phpunit --testsuite ActivityLog.