Audit Logging¶
Import: from selectools.audit import AuditLogger Stability: stable
audit_quick.py
import tempfile
from selectools import Agent, AgentConfig, LocalProvider, tool
from selectools.audit import AuditLogger, PrivacyLevel
@tool()
def greet(name: str) -> str:
"""Say hello."""
return f"Hello, {name}!"
with tempfile.TemporaryDirectory() as tmpdir:
audit = AuditLogger(
log_dir=tmpdir,
privacy=PrivacyLevel.KEYS_ONLY,
daily_rotation=True,
)
agent = Agent(
tools=[greet],
provider=LocalProvider(),
config=AgentConfig(observers=[audit]),
)
result = agent.run("Greet Alice")
print(result.content)
# Check tmpdir for the generated JSONL audit file
See Also
- Guardrails - Input/output validation pipeline
- Security - Tool output screening and coherence checking
Added in: v0.15.0
AuditLogger provides a JSONL append-only audit trail for every agent action. It records tool calls, LLM responses, policy decisions, and errors — all with configurable privacy controls and daily file rotation.
Quick Start¶
from selectools import Agent, AgentConfig, OpenAIProvider, tool
from selectools.audit import AuditLogger, PrivacyLevel
@tool(description="Search the knowledge base")
def search(query: str) -> str:
return f"Results for: {query}"
audit = AuditLogger(
log_dir="./audit",
privacy=PrivacyLevel.KEYS_ONLY, # redact argument values
daily_rotation=True, # audit-2026-03-12.jsonl
)
agent = Agent(
tools=[search],
provider=OpenAIProvider(),
config=AgentConfig(observers=[audit]),
)
result = agent.ask("Find articles about Python")
# Check ./audit/audit-2026-03-12.jsonl
Every event is one JSON line:
{"event":"run_start","run_id":"abc123","message_count":1,"ts":"2026-03-12T18:30:00.000000+00:00"}
{"event":"tool_start","run_id":"abc123","call_id":"xyz","tool_name":"search","tool_args":{"query":"<redacted>"},"ts":"..."}
{"event":"tool_end","run_id":"abc123","call_id":"xyz","tool_name":"search","duration_ms":42.5,"success":true,"ts":"..."}
{"event":"llm_end","run_id":"abc123","model":"gpt-4o","prompt_tokens":150,"completion_tokens":50,"cost_usd":0.001,"ts":"..."}
{"event":"run_end","run_id":"abc123","iterations":2,"tool_name":"search","ts":"..."}
Privacy Levels¶
Control how sensitive data appears in audit logs:
| Level | Behaviour | Example {"query": "secret data"} |
|---|---|---|
PrivacyLevel.FULL | Log everything verbatim | {"query": "secret data"} |
PrivacyLevel.KEYS_ONLY | Redact values | {"query": "<redacted>"} |
PrivacyLevel.HASHED | SHA-256 hash (truncated) | {"query": "2bb80d537b1da3..."} |
PrivacyLevel.NONE | Omit arguments entirely | {} |
# Full logging (development)
AuditLogger(privacy=PrivacyLevel.FULL)
# Keys only (production default)
AuditLogger(privacy=PrivacyLevel.KEYS_ONLY)
# Hashed (compliance — can verify without exposing)
AuditLogger(privacy=PrivacyLevel.HASHED)
# No args (strictest privacy)
AuditLogger(privacy=PrivacyLevel.NONE)
File Rotation¶
# Daily rotation (default) — audit-2026-03-12.jsonl, audit-2026-03-13.jsonl, ...
AuditLogger(log_dir="./audit", daily_rotation=True)
# Single file — audit.jsonl
AuditLogger(log_dir="./audit", daily_rotation=False)
Recorded Events¶
| Event | When | Key Fields |
|---|---|---|
run_start | Agent starts processing | run_id, message_count |
run_end | Agent finishes | run_id, iterations, tool_name, total_cost_usd |
tool_start | Before tool execution | run_id, call_id, tool_name, tool_args |
tool_end | After successful tool | run_id, call_id, tool_name, duration_ms, success |
tool_error | Tool raised exception | run_id, tool_name, error, error_type, duration_ms |
llm_end | After LLM response | run_id, model, prompt_tokens, cost_usd |
policy_decision | Policy evaluated tool | run_id, tool_name, decision, reason |
error | Unrecoverable error | run_id, error, error_type |
Include LLM Response Content¶
By default, response content is not logged (privacy). Opt in:
AuditLogger(include_content=True)
# llm_end events will include "response_length": 250
# tool_end events will include "result_length": 100
Combining with Other Observers¶
AuditLogger is just an AgentObserver — combine it with others:
from selectools.observer import LoggingObserver
agent = Agent(
tools=[...],
provider=provider,
config=AgentConfig(
observers=[
AuditLogger(log_dir="./audit"), # JSONL file
LoggingObserver(), # Python logging
],
),
)
Thread Safety¶
AuditLogger uses a threading.Lock for file writes, making it safe for concurrent batch() usage.
API Reference¶
| Class / Enum | Description |
|---|---|
AuditLogger(log_dir, privacy, daily_rotation, include_content) | JSONL audit logger (implements AgentObserver) |
PrivacyLevel.FULL | Log all values |
PrivacyLevel.KEYS_ONLY | Redact values to "<redacted>" |
PrivacyLevel.HASHED | SHA-256 hash of values |
PrivacyLevel.NONE | Omit tool_args entirely |
Related Examples¶
| # | Script | Description |
|---|---|---|
| 30 | 30_audit_logging.py | JSONL audit logging with privacy levels |
| 20 | 20_customer_support_bot.py | Production bot with audit and security |
| 31 | 31_tool_output_screening.py | Tool output screening (security companion) |