audit
The stream has two sinks and they are not equals. The Logging destination below is the archive: one JSON line per event, unconditionally, for an external log system to ship and retain. The database table is a bounded cache that exists only to serve the web UI’s recent-history views, which is why it is pruned and deliberately not searchable beyond the two indexed subject columns.
| Key | Type | Default |
|---|---|---|
audit.retention |
duration | 1440h |
audit.max_rows |
int | 1000000 |
audit.sweep_interval |
duration | 1h |
audit.logging.* |
||
audit.logging.level |
string | empty |
audit.logging.add_source |
bool | false |
audit.logging.log_json |
bool | true |
retention
Section titled “retention”duration, default 1440h
How long an event stays in the database table. The scheduled sweep deletes rows older than this. It bounds the UI’s history, not the deployment’s: the shipped log is the archive, so a short window here loses nothing that matters. Zero disables age-based pruning, leaving max_rows as the only bound.
One thing does read the table back: the directory sync decides who to email when it disables an admin from the logins recorded here over the last 60 days. A retention below that, or a max_rows that trims those logins, shortens that window, and the sync logs the window it actually had.
audit: retention: 1440hmax_rows
Section titled “max_rows”int, default 1000000
Caps the table as a safety valve behind Retention, so a burst of events cannot grow it without bound inside the retention window. The same sweep deletes oldest-first past the cap. The default is deliberately high: this exists to bound pathology, not to be the operative limit. Zero disables the cap.
audit: max_rows: 1000000sweep_interval
Section titled “sweep_interval”duration, default 1h
How often the retention sweep runs. Pruning is not urgent — the window is measured in weeks — so this is measured in hours rather than minutes.
audit: sweep_interval: 1hlogging
Section titled “logging”The durable export: a dedicated destination that receives one JSON line per event, routed by a type=audit attribute like the LDAP and queue logs. Set its filename to split it into its own rotating file; leave it unset and the events still reach the general log. A deployment that configures nothing here loses the archive, not the audit trail’s correctness.
Unconditional means unconditional: audit events are emitted at INFO and logging.level ships at WARN, so this destination is held at an INFO floor whenever it has neither a filename nor a level of its own. Setting a level here overrides that floor, which is the point — turning the trail down is a deliberate choice, not one to arrive at by leaving this block empty.
logging.*
Section titled “logging.*”Log-file rotation for this destination, via the embedded timberjack logger, with the same keys and meanings as logging.* above. This destination is only split out of the main log once its filename is set; until then its records go to the general log.
logging.level
Section titled “logging.level”string, default empty
The minimum log level for this destination, in the same form as logging.level. Empty means this destination has no threshold of its own and is filtered at logging.level with everything else.
audit: logging: level: "info"logging.add_source
Section titled “logging.add_source”bool, default false
Includes the source file and line on each record.
audit: logging: add_source: falselogging.log_json
Section titled “logging.log_json”bool, default true
Writes JSON instead of text. Text is meant to be read, not parsed: the message is written as bare prose so quoted values in it are not escaped, which is not a machine-readable format. Set this wherever a log collector has to pull fields back out.
audit: logging: log_json: false