Logging
Set the log level and destinations, keep per-session protocol logs for troubleshooting, and bound the audit log. Metrics and traces are covered in Monitoring.
| Key | Type | Allowed values | Notes |
|---|---|---|---|
LOG_LEVEL | string | trace, debug, info, warn, error, fatal | Application log level [optional] (default: debug) |
LOG_ENABLE_HTTP_LOGGER | boolean | - | Enable HTTP request logging [optional] (default: false) |
LOG_HTTP_RESPONSE_BODY | boolean | - | Capture and log HTTP response bodies (buffered up to LOG_HTTP_RESPONSE_BODY_MAX_LENGTH bytes). Disabled by default because response bodies may contain access tokens, credentials, or other sensitive data. [optional] (default: false) |
LOG_HTTP_RESPONSE_BODY_MAX_LENGTH | number | - | Maximum number of bytes to capture for HTTP response bodies. Set to 0 to disable truncation. [optional] (default: 4096) |
LOG_REDACT_SENSITIVE_DATA | boolean | - | Redact sensitive request/response fields from logs. Disable only for debugging. [optional] (default: true) |
LOG_OID4VP_DECRYPTED_RESPONSE | boolean | - | Log decrypted OID4VP authorization responses. Disable by default because responses may contain personal data and credentials. [optional] (default: false) |
LOG_ENABLE_SESSION_LOGGER | boolean | - | Enable session flow logging [optional] (default: false) |
LOG_SESSION_STORE | string | off, errors, all, verbose | Controls whether session log entries are persisted to the database. 'off' disables storage, 'errors' stores only warn/error entries, 'all' stores everything, 'verbose' stores everything including full request/response bodies and error stacks. [optional] (default: off) |
LOG_TO_FILE | boolean | - | Enable logging to file in addition to console [optional] (default: false) |
LOG_FILE_PATH | string | - | File path for log output when LOG_TO_FILE is enabled [optional] (default: ./logs/session.log) |
AUDIT_LOG_RETENTION_DAYS | number | - | Delete tenant activity audit log entries older than N days. Set to 0 to disable time-based pruning. [optional] (default: 0) |
OTEL_SDK_DISABLED | boolean | - | Disable OpenTelemetry SDK (and OTel log forwarding) [optional] (default: false) |
AUDIT_LOG_MAX_ENTRIES_PER_TENANT | number | - | Keep only the newest N tenant activity audit log entries per tenant. Set to 0 to disable count-based pruning. [optional] (default: 0) |
Log level and destinations
LOG_LEVEL accepts trace, debug, info, warn, error and fatal. Its
default is debug, but warn when NODE_ENV=production, which the container
image sets. Set LOG_LEVEL=info to see startup and flow messages in a container.
The backend writes logs to up to three destinations at the same level:
| Destination | Format | Enabled by |
|---|---|---|
| Console (stdout) | Human-readable (pino-pretty) | always |
| File | JSON, one object per line | LOG_TO_FILE=true, path LOG_FILE_PATH |
| OpenTelemetry (Loki) | OTLP log records with trace IDs | always, unless OTEL_SDK_DISABLED=true (Monitoring) |
For structured logs in a log platform, use the OpenTelemetry export or the JSON
file. The file is not rotated; use logrotate or similar.
HTTP request logs
LOG_ENABLE_HTTP_LOGGER=true logs requests and responses of the wallet-facing
endpoints. Management API calls (/api/...) and /health are never logged this
way. With LOG_REDACT_SENSITIVE_DATA=true (default), these values are replaced
by [redacted]: the Authorization, Cookie, DPoP,
OAuth-Client-Attestation and OAuth-Client-Attestation-PoP request headers,
Set-Cookie, and the response fields access_token, refresh_token,
id_token, c_nonce, credential, credentials and attestation_challenge.
LOG_HTTP_RESPONSE_BODY=true adds response bodies (up to
LOG_HTTP_RESPONSE_BODY_MAX_LENGTH bytes). Bodies can contain credentials and
personal data; enable it only while debugging.
To inspect decrypted wallet responses during local debugging, set both
LOG_LEVEL=trace and LOG_OID4VP_DECRYPTED_RESPONSE=true. These logs contain
personal data; never enable this in shared or production environments.
Session logs
Session logs record the protocol steps of one issuance or presentation session, for example authorization, token exchange, credential issuance or presentation verification, with errors. Enable them and choose what is stored in the database:
LOG_ENABLE_SESSION_LOGGER=true
LOG_SESSION_STORE=errors
LOG_SESSION_STORE | Stored |
|---|---|
off (default) | Nothing; events go only to the log destinations |
errors | Warnings and errors |
all | All events |
verbose | All events with full request and response bodies and error stacks |
LOG_SESSION_STORE has no effect without LOG_ENABLE_SESSION_LOGGER=true.
Read the entries with GET /api/session/:id/logs (role issuance:offer for
issuance sessions, presentation:request for presentation sessions) or in the
Logs tab of a session in the web client.
They are deleted with their session (session retention).
verbose stores personal data and much more volume; use it for debugging only.
Correlate logs with sessions
Once a request has resolved its session (by offer ID, walletNonce, issuer_state, code or access token), every following log line of that request, including the HTTP request log, carries sessionId and tenantId. This works with or without OpenTelemetry; with it, the request span and the span handling the session also get session.id.
- The OID4VP routes (
/presentations/{walletNonce}/oid4vp/...) contain the wallet nonce, not the session ID, so the HTTP request log reports it asreq.walletNonce. - The value of the session search parameter
qis always replaced by[redacted]in logged URLs and in theurl.queryspan attribute, because it can contain a pre-authorized code.
To go from a log line or a pasted offer link to the session, search the session list with GET /api/session?q=... (Finding sessions).
Audit log
Changes to a tenant and to its credential, issuance, presentation and
status-list configurations, webhook endpoints and attribute providers are
recorded with actor, time and changed fields, as are bundle imports, exports,
detach actions and generated client secrets. Key chain and trust list changes
are not audited. Read them with
GET /api/admin/audit-logs (role clients:manage) or in the web client. The
audit log is kept forever unless you limit it; a daily job at 03:00 applies:
AUDIT_LOG_RETENTION_DAYS=365 # delete entries older than this, 0 = keep
AUDIT_LOG_MAX_ENTRIES_PER_TENANT=10000 # keep only the newest N per tenant, 0 = all
Use session logs to answer "what happened in this flow?" and the audit log for "who changed the configuration, and when?".