Telemetry
The telemetry: block controls observability — what Coulisse logs to stderr, what it persists to SQLite for the studio UI, and whether it ships traces to your own OpenTelemetry backend.
Every field has a sensible default. Omit the block and you get stderr logs at info plus the studio's per-turn event tree, with no external traces.
Shape
telemetry:
fmt:
enabled: true # stderr logs; default on
sqlite:
enabled: true # mirrors spans into the studio's tables; default on
otlp: # absent = disabled (default)
endpoint: "http://localhost:4317"
protocol: grpc # or http_binary
service_name: coulisse
headers:
authorization: "Bearer ${OTEL_API_KEY}"
All three layers compose. Turn sqlite off if you don't need the studio. Add otlp to ship the same traces to Grafana, SigNoz, Jaeger, Honeycomb, or any OTLP-compatible backend.
telemetry.fmt
| Field | Type | Required | Notes |
|---|---|---|---|
enabled | bool | no | Default true. |
Writes structured logs to stderr. The level is controlled by the RUST_LOG environment variable; without it, the default is info,sqlx=warn (info from Coulisse, warnings only from the SQL driver). To see internal SQL traffic, run with RUST_LOG=debug. To silence everything, set RUST_LOG=error.
telemetry.sqlite
| Field | Type | Required | Notes |
|---|---|---|---|
enabled | bool | no | Default true. |
Mirrors turn and tool_call tracing spans into the events and tool_calls tables that the studio UI reads. Without this layer, the studio loses its per-turn event tree and tool-call panel.
The schema is part of the same SQLite file the rest of Coulisse persists into (controlled by memory.backend.path).
telemetry.otlp
Absent (the default) means Coulisse does not export traces externally. To plug Coulisse into your own observability stack, set the block:
| Field | Type | Required | Notes |
|---|---|---|---|
endpoint | string | yes | Collector URL. |
protocol | enum | no | grpc (default) or http_binary. |
service_name | string | no | OpenTelemetry resource attribute service.name. Default coulisse. |
headers | map | no | Static HTTP/gRPC headers attached to every export. |
Endpoint defaults
- gRPC (the default): port
4317, e.g.http://localhost:4317. - HTTP/protobuf: port
4318, e.g.http://localhost:4318/v1/traces.
The collector you point at decides the rest — Coulisse ships traces with service.name = coulisse and span names turn, tool_call, and llm_call. Span fields carry user_id, turn_id, agent, tool_name, kind, and the rest documented in the features chapter.
Headers
Useful for managed backends:
telemetry:
otlp:
endpoint: "https://ingest.us.signoz.cloud:443"
protocol: grpc
headers:
"signoz-access-token": "${SIGNOZ_TOKEN}"
YAML doesn't expand ${...} itself; substitute at deploy time (helm, envsubst, sops, etc.).
How the layers compose
The cli installs a single tracing_subscriber registry with the layers your config asked for, in order:
RUST_LOGenv filterfmt→ stderr (whenfmt.enabled)sqlite→events+tool_callstables (whensqlite.enabled)otlp→ external collector (whenotlpis set)
Every span emitted by the running server fans out to all enabled layers. There is no priority or fallback — the SQLite layer keeps full payloads (full prompts, args, results), the OTLP layer ships the same span attributes to your collector. If your backend chokes on multi-megabyte attributes, drop those fields in your collector pipeline rather than at the source.