Structured Logging Contract
All server-side code uses log/slog. Never log.Printf, fmt.Printf (for logs), or third-party loggers.
Log Levels
| Level | When to use |
|---|---|
slog.Debug | Detailed diagnostic info (request bodies, SQL queries, plugin calls) |
slog.Info | Normal operations (server started, realm created, plugin loaded) |
slog.Warn | Recoverable issues (plugin health check failed, deprecated API used) |
slog.Error | Unrecoverable issues (database unreachable, encryption failed, plugin crashed) |
log.Fatalf is reserved for fatal startup errors only (slog has no Fatal).
Standard Field Names
Every request-scoped log entry should include applicable fields from this list:
// Request context (set by middleware, present on all request-scoped logs)
"request_id" // string — from X-Request-Id header or generated
"method" // string — HTTP method (GET, POST, etc.)
"path" // string — URL path
"user_id" // string — authenticated user UUID
"realm" // string — realm slug (after resolution)
"status" // int — HTTP response status code
"duration_ms" // int64 — request duration in milliseconds
// Domain-specific (included when relevant)
"engine" // string — engine name
"plugin" // string — plugin name (if request touches a plugin)
"lease_id" // string — lease UUID
"policy_id" // string — policy/role UUID
"secret_key" // string — secret key name (NEVER the value)
// Error context
"error" // string — error message
"error_type" // string — category: "validation", "auth", "store", "plugin", "external", "internal"
// Plugin context
"plugin_name" // string — plugin binary name
"plugin_version" // string — plugin version
"host_function" // string — host function called (sql, http, store, audit)
Log Sanitization
NEVER log:
- Secret values, passwords, tokens, API keys
- Master key material or derived keys
- Full request/response bodies in production (Debug level only)
- User personal information beyond user_id
Handler Configuration
// Standalone: slog.TextHandler by default (human-readable to stderr)
// Multi-node: slog.JSONHandler (machine-parseable to stderr, queryable)
// Override with: ARCAN_LOG_FORMAT=json or ARCAN_LOG_FORMAT=text