Skip to main content
Every tool call can be recorded to an append-only JSONL audit log — thread-safe for concurrent multi-agent writes.
The user enables audit logging; each tool invocation is appended to JSONL safely even when many agents run in parallel.

How It Works

Each tool call the agent makes is appended to the audit log before the result returns to the user.

Quick Start

1

Enable audit logging

enable_audit_log() registers on the process-global praisonaiagents.hooks registry — the audit sink applies to every Agent in the process. In a multi-tenant host (e.g. praisonai serve), one tenant enabling audit logging turns it on for all tenants sharing that process. Keep the returned hook ID and call praisonaiagents.hooks.remove_hook(hook_id) to disable it later.
2

Close on shutdown


What’s Logged

Each JSONL line records:
  • timestamp, session_id, agent_name
  • tool_name, tool_input, execution_time_ms
  • Optional tool_output (when include_output=True)
The hook registers on after_tool automatically when you call enable_audit_log().
When a tool call went through the gateway resolve boundary, the approver field now carries the resolving principal (e.g. operator:alice) rather than the constant "gateway", so audit reports can answer “who approved this exec?”. See how resolver identity is captured.

Log Rotation

Audit records keep landing on the live file after an external rotator moves or removes it — no user action, no descriptor leak (PR #4205). Each write compares the inode behind the open handle against the inode on the log path; when they differ (or the path is gone), the stale handle is closed and the path reopened at 0o600 before the line is appended.

Secret Redaction

Sensitive values in tool_input are replaced with ***REDACTED*** before the JSONL line is written — on by default, no configuration needed. Key matching is case-insensitive and recurses through nested dicts, lists, and tuples. The built-in denylist (_DEFAULT_SENSITIVE_KEYS in praisonai/security/audit.py):
Before / after on disk:
Redaction runs on tool_input only. When include_output=True, tool outputs are written verbatim (truncated at max_output_chars) with no redaction — keep secrets out of tool return values.
File permissions. The audit file is created with mode 0o600 (owner read/write only) regardless of umask. This is enforced on the first append even if the file already exists.

Configuration

enable_audit_log() and enable_security() accept only log_path and include_output — they do not yet forward redactor or sensitive_keys. To customise redaction, instantiate AuditLogHook directly and register it with add_hook("after_tool", ...) (see below).

Customising Redaction

Instantiate AuditLogHook directly for full control over redaction. Pick the option that fits your situation:
sensitive_keys only affects the built-in redactor. A redactor= callable you supply is used exactly as given — the class does not merge it with the default.

Thread Safety (PR #2062)

  • Uses threading.Lock for concurrent multi-agent writes
  • Rotation-safe: each write stats the log path and reopens when the on-disk inode differs from the open handle (recovers from logrotate, mv, or rm mid-run) — added in PR #4205
  • Each write calls fsync for crash durability
  • Call get_audit_log().close() on shutdown to flush and release the handle

Best Practices

Call enable_audit_log() before creating agents so every tool invocation is captured from the first turn — retrofitting mid-session misses earlier calls.
Call get_audit_log().close() in your shutdown handler to flush the file handle. Long-running daemons that skip this may lose the last buffered line on crash.
Leave include_output=False unless you need forensic replay. When enabled, tune max_output_chars to avoid bloating the JSONL with large tool payloads. Redaction covers tool_input only — outputs are written verbatim.
Grep your audit file for known-sensitive strings once, especially if your tools use exotic key names not in the default denylist. If you see leaks, extend sensitive_keys or supply a custom redactor on a directly-instantiated AuditLogHook.
Store logs outside web-served directories. The file is created 0o600 (owner-only) by default, and the audit path is protected — do not disable Protected Paths on production hosts. External rotators (logrotate, container log drivers, cron mv + HUP) are supported directly — the writer reopens the live file on the next write, so you never need to signal the process (PR #4205).
enable_audit_log() registers on the shared praisonaiagents.hooks registry, so every Agent in the same Python process is audited under a single sink and single configuration. This is fine for single-tenant deployments; for multi-tenant servers (e.g. praisonai serve), keep the returned hook ID and call praisonaiagents.hooks.remove_hook(hook_id) at teardown, or instantiate AuditLogHook and add_hook("after_tool", ...) per-request for per-tenant sinks.

Security Overview

Enable audit log with other security features

Protected Paths

Audit log file is itself protected