Reference

Logged Events

This page lists what the module records and how it is stored.

Action codes

Every entry has an action of the form <type>.<operation>.

Type Operations Source
item create, update, delete API events
media create, update, delete Entity events (see below)
item_set create, update, delete API events
user create, update, delete API events
user login, logout Login controller events
site create, update, delete API events
site_page create, update, delete API events
vocabulary create, update, delete API events
resource_template create, update, delete API events
property create, update, delete API events (off by default)
resource_class create, update, delete API events (off by default)
asset create, update, delete API events

API events are captured after the operation succeeds (api.create.post, api.update.post, api.delete.post), so a failed or rejected operation is not logged. Everything that goes through the Omeka S API manager is covered: the admin forms, the REST API, batch edits and modules that use the API.

Logins are recorded only when authentication succeeds. Failed login attempts are not recorded. Logins and logouts are always logged while the module is enabled, regardless of the Events to log checkboxes.

Read and search operations are never logged.

How media is captured

Saving an item in Omeka S writes its media as a side effect of the item save, without a separate media API call. To cover this, media is logged from Doctrine entity events (entity.persist.post, entity.update.post, entity.remove.post on Omeka\Entity\Media) instead of API events.

For media entries the label is the media source (usually the original filename) and the link points to the parent item's admin page. When a media is changed through a direct API call, both an entity event and an API event fire; the module records the entity event and skips the API event so the change appears once.

Stored fields

Entries live in the audit_trail table.

Column Type Content
id BIGINT Entry ID
timestamp DATETIME When the entry was written (server time)
actor_id INT User ID, or NULL when no user was authenticated
actor_display VARCHAR(255) User name, or email if the name is empty
action VARCHAR(128) Action code
resource_type VARCHAR(64) Type from the table above
resource_id VARCHAR(128) Resource ID
resource_label VARCHAR(512) Title, name, label or filename, depending on the resource
resource_url VARCHAR(1024) Admin URL for the resource
ip_address VARCHAR(45) Client address, possibly redacted
user_agent TEXT Browser user agent, up to 1,000 characters
extra JSON For API events: {"operation": "...", "resource_name": "..."} where resource_name is the API name (items, site_pages, and so on). NULL for other entries.

Indexes exist on timestamp, actor_id, action and (resource_type, resource_id).

Resource labels and links

The label is the first of these that the resource provides: display title, title, name, or label. The link is the resource's admin URL where available. Site pages link to /admin/site/s/<site-slug>/page/<page-slug>/edit. When no better URL can be derived, the module builds /admin/<type>/<id> for items, media, item sets, users, sites, vocabularies, resource templates and assets. Links are stored as recorded, so a resource that is later deleted or renamed keeps its original link, which may no longer resolve.

IP addresses

The address is read from the first value of the X-Forwarded-For header when present, otherwise from REMOTE_ADDR. Actions performed from the command line have no address.

With Redact IP addresses on:

  • IPv4: the last octet becomes 0 (203.0.113.42 becomes 203.0.113.0)
  • IPv6: the first three groups are kept and the rest replaced with **** (2001:db8:85a3:... becomes 2001:db8:85a3:****)

Redaction happens when the entry is written. Changing the setting does not alter existing entries.

Failure handling

All logging code catches its own exceptions and writes a message prefixed [AuditTrail] to the PHP error log. A logging failure never interrupts the action being performed, so a missing table or database error results in missing entries rather than a broken admin.

Querying the table directly

The table is plain SQL, so reports beyond the built-in filters can be run against it. For example, the most active users in the last 30 days:

SELECT actor_display, COUNT(*) AS events
FROM audit_trail
WHERE timestamp >= NOW() - INTERVAL 30 DAY
GROUP BY actor_id, actor_display
ORDER BY events DESC;
Log in for Support