Observability
Omni exports OpenTelemetry traces, metrics, and logs from the five core services: omni-web (Node/SvelteKit), omni-ai (Python/FastAPI), omni-searcher (Rust), omni-indexer (Rust), and omni-connector-manager (Rust). Connectors may capture telemetry individually but are not audited for signal completeness. This document describes the signal architecture, metric inventory, and how to configure an external collector or backend.Architecture & Signal Flow
OTEL_EXPORTER_OTLP_ENDPOINT. The default value is empty, which means
instrumentation is active but signals are dropped / not exported or stored.
When a non-empty endpoint is set, all three signals (traces, metrics, logs)
are sent to ${OTEL_EXPORTER_OTLP_ENDPOINT}/v1/{traces,metrics,logs}.
Point OTEL_EXPORTER_OTLP_ENDPOINT at any compatible OTLP/HTTP collector
or backend. See Production & Privacy Guidance
for production recommendations.
Base OTLP Endpoint Semantics
Resource Attributes
Every signal carries:service.name— the logical service name (omni-searcher,omni-indexer,omni-ai,omni-web,omni-connector-manager)service.version— fromSERVICE_VERSIONdeployment.environment— fromOTEL_DEPLOYMENT_ENVIRONMENTdeployment.id— fromOTEL_DEPLOYMENT_ID
Shutdown Behaviour
On graceful shutdown (SIGTERM/SIGINT), each of the five core services flushes pending spans, metrics, and logs before exiting. In Rust services the OTLP exporter timeout is hard-coded at 10 seconds in the opentelemetry-rust SDK;OTEL_EXPORTER_OTLP_TIMEOUT is not honoured. Busy shutdowns that exceed
the Rust SDK timeout may lose the final batch. Python (omni-ai) honours the
standard OTLP timeout. Node (omni-web) uses the SDK default.
Trace Continuity
HTTP Request Tracing
Rust services (searcher, indexer, connector-manager) use custom axum middleware. SERVER spans use boundedMETHOD /route-template names; custom
outbound CLIENT spans use HTTP METHOD. W3C traceparent / tracestate is
propagated in HTTP headers.
Node (omni-web) and Python (omni-ai) use auto-instrumentation provided
by the respective OpenTelemetry SDKs. Span names follow the SDK convention
(e.g. HTTP method + route pattern) rather than a unified scheme.
Outbound HTTP requests create a CLIENT span, producing parent/child
relationships within a single trace (not links) when the web service calls
internal APIs. The same traceId is shared across the web→searcher and
web→AI call chain.
Async Queue Tracing
The indexer and connector-manager use a Postgres-based queue.- Connector-manager enqueues connector events into
connector_events_queuewith a PRODUCER span, storingtraceparentandtracestatein the queue row. - Indexer consumes these events. Because the producer and consumer run in
different processes and transactions, the consumer creates a new root trace
(no parent). A native OTel link is added from the CONSUMER span back to
the PRODUCER span via the stored
traceparent/tracestatefrom the queue row. - Omni-ai batch processor consumes embedding events from
embedding_queueusing the same producer→consumer pattern withtraceparent/tracestatestored in queue rows and native OTel links.
services/ai/embeddings/batch_processor.py in Python.
Metric Inventory
All metric names use theomni.* namespace.
HTTP Server RED Metrics (cross-cutting, Rust services)
Recorded via axum middleware inshared/src/metrics.rs:
Indexer Queue Metrics
Recorded inshared/src/metrics.rs:
Searcher Metrics
Recorded inshared/src/metrics.rs:
Connector Sync Metrics
Recorded inshared/src/metrics.rs:
AI / Embedding Metrics
Recorded inservices/ai/embeddings/batch_processor.py:
No LLM metrics are currently recorded.
Metrics for Rust services are defined centrally in
shared/src/metrics.rs,
which all Rust services link. The AI service instruments embedding metrics
locally in batch_processor.py.
Log Correlation
Howtrace_id and span_id appear in log records depends on the runtime
and whether an active OTel span exists at the log call site.
Sanitisation
- Log body content is sent to the OTLP endpoint exactly as produced by the application. No global PII scrubber is applied.
- Node’s explicit Pino-to-OTel hook exports only allowlisted primitive log attributes. Its error serializer removes dynamic messages and stacks from structured error fields. Automatic Pino log sending is disabled to prevent duplicate export.
- Runtime logging call sites were audited to avoid sensitive and high-cardinality fields, but this is not a global scrubber. Future log sites must follow the same policy.
Troubleshooting
Production & Privacy Guidance
When using an external OTLP collector or backend in production:- Use TLS — enable TLS on the OTLP endpoint or use gRPC with TLS.
- Authenticate — use API keys or mTLS to secure the connection.
- Configure sampling — use a
probabilistic_samplerortail_samplingprocessor in your collector for high-throughput services. - Adjust batching — tune batch processor timeouts and sizes for your throughput.
- Set resource limits — configure memory limits and spike limits in the collector or backend.
- Never expose the OTLP endpoint publicly — it has no built-in authentication. Bind to loopback or use a private network.
- Filter sensitive data — no PII scrubbing is applied by Omni. Redact sensitive fields in the collector or at the application level before logging.