• Start

Slow-query logging

The Enterprise slow-query log pipeline: how a query qualifies, record shape, rotation, hash chaining, redaction and pipeline self-metrics.

Enterprise
Available since: v3.1.0

The slow-query log captures the full statement text and execution context for queries that cross a configurable duration threshold. It is part of SurrealDB Enterprise and is off by default — set SURREAL_SLOW_QUERY_SINK=file, SURREAL_SLOW_QUERY_FILE_PATH and SURREAL_SLOW_QUERY_THRESHOLD_MS to enable it.

The slow-query pipeline is architecturally identical to the audit log pipeline — same observer, same queue, same worker, same NDJSON file format, same optional OTel logs export, same hash chaining, same three-pass redaction. The intentional symmetry keeps operator runbooks short. The two pipelines differ in defaults and intent:

  • Audit records are compliance data. Default overflow policy is block (preserve at the cost of latency); typical configurations also enable hash chaining.

  • Slow-query records are triage data. Default overflow policy is drop (prefer dropping over busy-yielding the executor on a hot path). Hash chaining is rarely needed.

The full set of configuration variables lives on the configuration reference.

Two related instruments cover slow queries:

  • The surrealdb_slow_query_total counter (Community edition; see metrics reference) increments for every statement above SURREAL_SLOW_QUERY_METRIC_THRESHOLD_MS (default 1000 ms). It is cheap and always-on when metrics are enabled.

  • The slow-query log records on this page capture the full statement text and context for queries above SURREAL_SLOW_QUERY_THRESHOLD_MS (no default — required when the sink is enabled). This is opt-in and writes to a file sink.

The two thresholds are independent. Most deployments set the metric threshold at a high value (the counter is a sanity-check signal) and the log threshold lower (to capture the long tail of triage candidates).

A statement is captured when its wall-clock duration is ≥ SURREAL_SLOW_QUERY_THRESHOLD_MS. The pipeline only registers an observer when the sink is enabled, so the cost of slow-query capture is zero when SURREAL_SLOW_QUERY_SINK=none (the default).

Slow-query records share the audit envelope (ts, event_type, outcome, duration_ms, identity context, optional sql, optional hash chain fields) and additionally carry:

  • statement_type — the statement category (select, update, create, …).

  • threshold_ms — the threshold the query crossed, for forensic context.

  • result_rows — number of rows produced (when applicable).

The event_type field is always slow_query. A captured record looks like:

{
"ts": "2026-03-04T10:23:11.482Z",
"event_type": "slow_query",
"outcome": "success",
"statement_type": "select",
"duration_ms": 512,
"threshold_ms": 250,
"result_rows": 1842,
"namespace": "acme",
"database": "prod",
"user": "svc_orders",
"sql": "SELECT * FROM orders WHERE status = 'shipped' FETCH customer"
}

The sql field is included by default (SURREAL_SLOW_QUERY_INCLUDE_SQL=true). Throughput-sensitive deployments can set it to false to skip the per-statement to_sql() rendering, at the cost of losing SQL context on captures.

When SURREAL_SLOW_QUERY_OTEL_EXPORT=true each captured record is also emitted as an OTel LogRecord on the SDK logger provider:

  • Event name: surrealdb.slow_query.statement.

  • Severity: WARN (fixed — slow path is degraded by definition).

  • Body: a short human-readable string, for example "slow select 500ms (threshold 100ms)".

  • Attributes: the structured fields from the record envelope (db.namespace, db.name, db.user, db.statement, surrealdb.statement_type, surrealdb.outcome, surrealdb.duration_ms, surrealdb.threshold_ms, surrealdb.result_rows).

Like the audit pipeline, OTel export is off by default. The file sink is the primary path.

Same knobs as the audit pipeline, prefixed SURREAL_SLOW_QUERY_*:

  • File mode 0600 on Unix. The parent directory must exist or startup fails.

  • Size-based rotation at SURREAL_SLOW_QUERY_FILE_ROTATE_BYTES (default 256 MiB).

  • SURREAL_SLOW_QUERY_FILE_ROTATE_KEEP rotation generations retained (default 8).

  • Mid-stream fsync cadence from SURREAL_SLOW_QUERY_FSYNC_EVERY (default 0). Rotation and graceful shutdown always fsync regardless.

Identical to the audit pipeline. SURREAL_SLOW_QUERY_HASH_CHAIN=true adds prev_hash / hash SHA-256 fields and requires SURREAL_SLOW_QUERY_FSYNC_EVERY=1. Most operators leave hash chaining off for slow-query data — the pipeline is triage tooling, not a compliance record, and the per-record fsync cost is rarely worth paying for a drop-default pipeline. See the audit log Hash chaining section for the full semantics.

Identical to the audit pipeline, under SURREAL_SLOW_QUERY_REDACT_LITERALS, SURREAL_SLOW_QUERY_REDACT_TABLES and SURREAL_SLOW_QUERY_REDACT_REGEX. Three layered passes run synchronously on the executor thread before the record reaches the queue. See Redaction on the audit log page for the full mechanics.

The slow-query pipeline defaults to drop — a record that cannot be enqueued is discarded and the surrealdb_slow_query_dropped gauge increments. The reasoning is that the executor is on a hot path during a slow query (by definition), and busy-yielding it to preserve a triage record is a poor trade-off.

Operators who need lossless slow-query capture can set SURREAL_SLOW_QUERY_OVERFLOW=block. The trade-off is the same as on the audit pipeline — the policy uses a bounded busy-yield loop, not a wall-clock time-bound, and offers no lossless guarantee on a current_thread runtime.

Whichever policy is configured, alert on rate(surrealdb_slow_query_dropped[5m]) > 0 and rate(surrealdb_slow_query_append_errors[5m]) > 0 — both indicate records were lost.

Five observable gauges expose the live state of the pipeline, mirroring the audit equivalents:

MetricNotes
surrealdb_slow_query_recordsCumulative records successfully enqueued.
surrealdb_slow_query_droppedCumulative records dropped. Alert on any non-zero rate.
surrealdb_slow_query_queue_depthRecords currently buffered between observer and worker.
surrealdb_slow_query_appendedCumulative records the worker wrote to the sink.
surrealdb_slow_query_append_errorsCumulative sink-write failures. Alert on any non-zero rate.

Was this page helpful?