Skip to main content
TurnCall instruments every call with two complementary mechanisms, both on by default:
  • Observers — log per-call signals (user→bot latency, turn timing, LLM, transcription, startup). No external infrastructure; they just log.
  • OpenTelemetry tracing — emits a span tree per call (conversation → turn → STT/LLM/TTS) with TTFB and token counts, exported to any OTLP backend (Jaeger, Grafana Tempo, Datadog, Honeycomb, …).
Both cover cascade and S2S calls. See ADR-0010.

Tracing

Each call becomes one trace whose conversation_id is the call_id, so a trace joins straight back to the call record. The span tree:
conversation (call_id)
└── turn
    ├── stt   (metrics.ttfb, gen_ai.system, model)
    ├── llm   (metrics.ttfb, gen_ai.usage.input/output_tokens, model)
    └── tts   (metrics.ttfb, character_count)
Span attributes also carry turncall.project_id, turncall.agent_id, turncall.direction, and turncall.transport — plus turncall.from_number / turncall.to_number unless you turn PII off (below).

Enable it

Point TurnCall at an OTLP collector:
OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf      # or grpc
OTEL_EXPORTER_OTLP_HEADERS=authorization=Bearer xxx   # if your backend needs auth
Then make a call and the spans appear in your backend.
Production requires an endpoint. Tracing never falls back to console output in production — synchronous stdout writes would stall the realtime audio path. With no OTEL_EXPORTER_OTLP_ENDPOINT set in production, tracing self-disables with a warning. In development, tracing prints spans to the console when no endpoint is configured.

Observers

The five observers log to the application logger on every call:
ObserverLogs
User↔bot latencyresponse latency (caller stops → bot starts)
Turn trackingturn boundaries and timing
Startup timingper-processor startup + transport readiness
LLMLLM activity and responses
Transcriptionspeech-to-text events
They supplement the server events / webhook transcript stream (which is your product data) — observers are operational logging for debugging “why did that call feel slow?”.

Configuration

VariableDefaultDescription
PIPECAT_ENABLE_OBSERVERStrueToggle the five observers
PIPECAT_ENABLE_TRACINGtrueToggle OTel tracing
PIPECAT_TRACE_INCLUDE_PIItruePut caller phone numbers (from/to) on spans. Set false for compliance-sensitive deployments — non-PII attributes stay.
PIPECAT_OTEL_SERVICE_NAMEturncallservice.name in traces
OTEL_EXPORTER_OTLP_ENDPOINT(unset)OTLP collector URL; required for tracing in production

Local quick start

Run a collector with a traces UI, then point TurnCall at it:
docker run -d --name jaeger -p 16686:16686 -p 4318:4318 jaegertracing/all-in-one
# TurnCall: OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
# make a call, then open http://localhost:16686