Audit Logs
View and search the system audit trail for security monitoring and compliance.
Administration → Audit Logs (/admin/audit-logs), titled Audit Trail on screen and gated on ADMIN.AUDIT. Which tables are audited is set on Administration → Audit Configuration (/admin/audit-config, ADMIN.AUDIT_CONFIG).
Overview
The Audit Trail brings three independent change logs together in one list, newest first:
| Source | What it records |
|---|---|
| Database Change | Every insert, update and delete on a table that auditing is turned on for, written by a database trigger with the whole row before and after. Roles, permission grants, user role assignments, role network rules and email-sending settings are all recorded here |
| Permission | Impersonation starts and stops, and Permission Transfer imports |
| Grade Change | Edits to grades |
A table only appears once auditing is turned on for it in Audit Configuration. New tables are audited by default (migration 123). Bulk imports and sync tools intentionally bypass the log, so "no entries" does not always mean "no changes".
Reading the list
| Column | Description |
|---|---|
| When | Relative time, with the full timestamp beneath |
| Source | Database Change, Permission or Grade Change |
| Action | INSERT, UPDATE or DELETE (a permission grant shows as CREATE) |
| Table / Entity | The table or entity changed, with the record's identifier |
| Changed By | The person who made the change |
| Change Summary | For a database change, the fields that differ; otherwise the field and its old and new values |
Click a row, or its eye icon, for the full entry: the old and new values, the actor source, and — where they were recorded — the client's IP address, hostname and user agent.
Where a change came from
Every audited web request records the client's IP address and user agent on the row (migration 264). The address is the one the server settles on behind the reverse proxy, so it is the real client, not the proxy.
A successful sign-in appears as an UPDATE of core.auth_users (the last_login field), and it additionally carries the client's hostname, looked up by reverse DNS. The lookup is skipped if it takes longer than 300 ms, in which case the hostname is blank; it never delays or fails a sign-in. Hostnames are recorded for sign-ins and the other pre-login steps (access links, password setting) only.
Two limits are worth knowing:
- Failed sign-ins are not recorded. A failed attempt writes nothing to the database, so it has no audit row. Look in the web server logs for those.
- Off-campus users on ZPA appear as the ZPA App Connector's address, not their home address.
Rows written by background jobs and command-line tools have no address.
Actor source
Every row records which code path made the change:
| Origin | Meaning |
|---|---|
| Web (app) | A signed-in user, through the application |
| Auth (login/tokens) | Sign-in, access links and password setting, before a session exists |
| Public form | A form submitted without signing in |
| System | A scheduled job inside the application |
| Instructor grade entry, Registration | Those specific code paths |
| Colleague sync (nightly), Colleague legacy import, Initial migration | Data loaded from Colleague, and the original migration |
Impersonation
Changes made while impersonating are recorded against the administrator, with the impersonated person recorded alongside. An action taken while impersonating can never be mistaken for one the impersonated user took.
Filtering
| Filter | Description |
|---|---|
| Source | Database Change, Permission or Grade Change |
| Table / Entity | Only tables and entities that actually have entries are offered |
| Field | Rows where that field's value changed, such as email or status_id. Not available for grade changes |
| Action | Insert, Update or Delete |
| Origin | The actor source |
| Changed By | Search by the person's name |
| From / To | Date range |
The search box matches table or entity names and record identifiers. Filters combine: to find every sign-in in a week, choose Database Change, table core.auth_users, field last_login, and the date range.
Deleting entries
Entries can be deleted — the selected ones, or every entry matching the current filters. Both ask you to type DELETE to confirm, because this removes evidence of what changed and cannot be undone. The deletion itself is recorded — who deleted which entry, about which record, and when, in core.audit_trail_deletion_log, which nothing can delete from — but the original details are not.
WARNING
Audit logs may be required for regulatory compliance (FERPA, GLBA). Consult your compliance officer before deleting entries.
Data Retention
Entries are kept indefinitely unless deleted here. For a long-running system, coordinate any archival or purge schedule with your database administrator.
Best Practices
- Review regularly — especially changes to
core.role_permissions,core.user_rolesandcore.role_network_rules, and impersonation - Check where sign-ins come from — filter on
core.auth_users/last_loginand read the address and hostname of unexpected sign-ins - Keep auditing on for sensitive tables — Audit Configuration decides what is recorded at all
- Correlate with external logs — failed sign-ins and blocked requests are only in the web server and application logs