Skip to main content

Observability

Logging​

Riverbase logs through tracing. RiverbaseApp::init installs the subscriber from config; workers and CLIs call it themselves:

riverbase_core::applog::init_logging("info", riverbase_core::LogFormat::Json);
SettingEnvValues
log_levelRIVERBASE_LOG_LEVELA tracing filter such as info or riverbase_core=debug,info. RUST_LOG overrides it.
log_formatRIVERBASE_LOG_FORMATcompact for local runs, pretty for multi-line, json for log shippers
log_http_requestsRIVERBASE_LOG_HTTP_REQUESTSOne line per request: method, URI, status, latency

Use json in production. Every line then carries structured fields that log pipelines can index.

Correlation​

Each request gets a correlation id that travels in EngineContext. It is written to every audit row the request produces and echoed in the response headers. Forward it on outbound calls, and the trail connects across services.

The audit log as an operational record​

The audit schema answers most "what happened?" questions without extra instrumentation:

QuestionLook at
Did the command run, and how did it end?The command log status (Created stuck means the process died mid-command)
What did it change?Events (event_log) keyed by action
What did the user see?The response log
Was a message published?Message log and outbox rows
Who read what?The query log, when audit_log.query = true

Bound its growth with purge_audit_logs and an AuditRetentionConfig on a schedule.

Errors​

Every failure is a RiverbaseError with a stable errcode. Alert on codes, not on message text:

  • DAT-* storage failures, especially DAT-090 conflicts under load;
  • AUT-* and CAS-* spikes, which point at an IdP or policy rollout;
  • CMD-027, a store that broke transactional guarantees.

In main, prefer riverbase_http::report_fatal(err) over returning the error. It logs through tracing, then exits with status 1.

OpenTelemetry​

The otel feature on riverbase_core enables OpenTelemetry hooks. The framework does not install exporters: configure the OTLP exporter and tracer provider in your binary, and Riverbase's spans flow into it. This keeps the choice of vendor and sampling with the service owner.