Observability and Tracing#
Dify's observability infrastructure runs on two complementary layers :
- OTel instrumentation — native span-level tracing wired into Flask, Celery, and key execution paths via decorators and auto-instrumentation (
api/extensions/otel/). - Ops trace pipeline — an event-driven system that ships structured run data to external providers (Langfuse, LangSmith, Phoenix, Aliyun, etc.) via a batched Celery queue.
The unified tracing framework (opt-in via OPS_TRACE_UNIFIED_ENABLED) bridges both layers with a provider-neutral canonical span model. The telemetry gateway is the single emit() entry point: SignalType.TRACE events go to TraceQueueManager → Celery; metric/log events go to enterprise telemetry (EE only); large payloads (>1 MB) are offloaded to object storage before enqueuing .
App invocation
→ TraceQueueManager.add_trace_task()
→ threading.Timer (batched flush every N seconds)
→ Celery: process_trace_tasks
→ provider adapter (Langfuse / LangSmith / Phoenix / Aliyun …)
OTel Instrumentation Layer#
Root: api/extensions/otel/
@trace_span() decorator#
@trace_span() is the primary way to create OTel spans around functions. It resolves a singleton SpanHandler and records exceptions on failure. Two custom handlers attach richer context :
AppGenerateHandler— attachesapp_id,tenant_id,user_type.WorkflowAppRunnerHandler— attaches workflow-level attributes.
Enabled by ENABLE_OTEL or ENABLE_OTEL_FOR_INSTRUMENT config flags.
Context propagation#
propagate_context() captures OTel and contextvars state at decoration time and re-attaches it on execution — essential for async tasks and Celery workers. setup_context_propagation() establishes a CompositePropagator combining W3C TraceContext and B3 Multi-Format for interoperability with Jaeger, Zipkin, etc.
Auto-instrumentation#
init_instruments(app) wires up automatic OTel instrumentation for Flask, SQLAlchemy (with SQL commenter), Redis, HTTPX, and Celery .
Semantic conventions#
| Module | Attributes |
|---|---|
semconv/dify.py | DifySpanAttributes: app_id, tenant_id, user_type, workflow_id, token usage |
semconv/gen_ai.py | GenAIAttributes: model, provider, span kind (LLM/TOOL/RETRIEVER/TASK), TTFT, prompts, completions |
Node-type span parsers#
api/extensions/otel/parser/ enriches spans with semantic attributes per node type :
| Parser | Coverage |
|---|---|
llm.py | Model, provider, token counts, prompts, completions, finish_reason |
retrieval.py | Query, documents with metadata/scores |
tool.py | Tool name/type/description, call arguments, results |
GenAI Span Conventions (v1.17+)#
PR #39339 established GenAI span conventions across providers and extended the Aliyun trace provider :
- TTFT:
gen_ai.response.time_to_first_token(nanoseconds) on LLM spans for both workflow and message traces. - Agent spans:
AGENTspan kind (replacingTASK) for agent-strategy nodes withgen_ai.operation.name=invoke_agent, agent name, and token usage. - ReAct decomposition: Per-round
STEPspans with childLLMandTOOLspans, anchored to monotonic node start time. Agent log entries are classified:Thought→LLM,CALL …→TOOL. - Tool attributes:
gen_ai.span.kind=TOOL,gen_ai.operation.name=execute_tool, plusgen_ai.tool.name/type/description/call.id/call.arguments/call.resultacross message tool traces, workflow tool nodes, and ReAct CALL spans. - Failed LLM fallback: When
process_datais empty on invocation failure, spans fall back to nodeinputs/errorsogen_ai.request.model,gen_ai.provider.name, andgen_ai.promptare still populated. gen_ai.response.modeladded to LLM spans in follow-on PR #40667 .
Unified Tracing Framework#
Root: api/core/ops/unified_trace/
Opt-in via OPS_TRACE_UNIFIED_ENABLED=true. Builds a single canonical span tree in Core once, then delegates only translation and transport to thin provider adapters .
Canonical entities (entities.py):
CanonicalSpanKind:CHAIN,LLM,RETRIEVER,TOOL,AGENTCanonicalSpan: one operation with explicitparent_id, timing,status,metadataCanonicalTrace: validated parent-before-child span tree with integrity checks enforced at construction
Provider routing: OpsTraceManager._get_dispatch_entry() routes per-provider at construction time. Currently registered in the unified registry: Phoenix and LangSmith (registry.py). Other providers (including Langfuse and Aliyun) use legacy provider-specific adapters .
Cross-node parent context: parent_context.py stores a ProviderParentContext envelope (trace_id, parent_id, provider-specific restoration state) in Redis across async Celery dispatch boundaries. TTL is OPS_TRACE_PARENT_CONTEXT_TTL_SECONDS (default 3900 s), sized to cover the max retry window of 780 retries × 5 s. If context is unavailable, the Celery task raises RetryableTraceDispatchError and retries .
Agent Observability Service#
api/services/agent/observability_service.py powers the agent monitoring dashboard. AgentObservabilityService aggregates daily token, cost, and latency stats for both webapp agents (querying Message) and workflow-embedded agent nodes (joining workflow_runs + workflow_node_executions via workflow_agent_node_bindings) . Key behaviors:
- Latency is merged with a weighted average to avoid double-counting across sources .
serialize_workflow_node_message()reads latency fromexecution_metadata.agent_log.agent_backend.usage.latency, falling back tonode_execution.elapsed_time.
Agent Trajectory Tracing#
Agent trajectory tracing is an independent, opt-in export pipeline specifically for agent execution traces, separate from system tracing. It uses the DIFY_AGENT_TRAJECTORY_* environment variables to export agent runs to a separate OTLP endpoint, typically for centralized monitoring in self-hosted deployments.
Key differences from system tracing:
- System tracing: General observability instrumentation across Flask, Celery, Redis, HTTPX, and workflow execution paths. Configured via standard
OTEL_*environment variables and exported through the OTel SDK. - Agent trajectory tracing: Dedicated pipeline for agent run spans only. Configured via
DIFY_AGENT_TRAJECTORY_*variables and exported to an independent OTLP endpoint.
Configuration#
Agent trajectory tracing is disabled by default. When enabled, it instruments agent runs with Dify-specific attributes (dify.trace_id, dify.tenant_id, dify.app_id, dify.agent_id, gen_ai.user.id, dify.invoke_from, dify.conversation.id, dify.workflow.id, dify.workflow.run_id, dify.node.id, dify.node.execution_id) and exports spans via OTLP.
| Variable | Default | Purpose |
|---|---|---|
DIFY_AGENT_TRAJECTORY_ENABLED | false | Enable agent trajectory export |
DIFY_AGENT_TRAJECTORY_OTLP_TRACES_ENDPOINT | — | OTLP endpoint URL for agent trajectory traces (required when enabled) |
DIFY_AGENT_TRAJECTORY_OTLP_HEADERS | {} | Optional headers for trajectory exporter (JSON object, e.g., {"Authorization":"Bearer token"}) |
DIFY_AGENT_TRAJECTORY_SERVICE_NAME | dify-agent-trajectory | Service name for trajectory traces |
DIFY_AGENT_TRAJECTORY_INCLUDE_CONTENT | false | Include prompts/completions in trajectory spans |
DIFY_AGENT_TRAJECTORY_TRACE_CONTEXT_MODE | isolated | Trace context mode: isolated (new trace per run) or shared (inherit parent context) |
DIFY_AGENT_TRAJECTORY_MAX_QUEUE_SIZE | 2048 | Max spans queued before export |
DIFY_AGENT_TRAJECTORY_MAX_EXPORT_BATCH_SIZE | 512 | Max spans per export batch (must not exceed queue size) |
DIFY_AGENT_TRAJECTORY_SCHEDULE_DELAY_MS | 5000 | Batch export interval (milliseconds) |
DIFY_AGENT_TRAJECTORY_EXPORT_TIMEOUT_MS | 5000 | Export timeout (milliseconds) |
The trajectory exporter is configured with environment isolation: inherited OTEL_* and LOGFIRE_* variables are temporarily stripped during agent pipeline setup to prevent platform endpoints, credentials, or sampling policies from leaking into the agent trajectory exporter.
Configuration Reference#
| Variable | Default | Purpose |
|---|---|---|
ENABLE_OTEL | false | Enable OTel span decoration and auto-instrumentation |
ENABLE_OTEL_FOR_INSTRUMENT | false | Enable OTel instrumentation independently |
OPS_TRACE_UNIFIED_ENABLED | false | Use provider-neutral unified tracing for Phoenix/LangSmith |
OPS_TRACE_PARENT_CONTEXT_TTL_SECONDS | 3900 | Redis TTL for cross-node parent context |
ENTERPRISE_INCLUDE_CONTENT | — | Gate whether prompts/completions appear in span attributes |
Related Topics#
- Trace Task Pipeline —
TraceQueueManagerbatching, Celery dispatch, and retry mechanics - Langfuse Integration — provider-specific trace adapter details
- Agent Log Event Pipeline — per-step SSE log events from agent strategy plugins to frontend UI