Tracing backendsΒΆ
Flock keeps its traces locally for the dashboard's trace viewer (FLOCK_TRACE_FILE=true) and exports them over OTLP to any OpenTelemetry backend. This guide shows the settings for five of them and what each shows of a Flock run. Every setting below was checked with a two-agent run (a bug report goes to a diagnostician, its diagnosis to a fixer) against a local instance.
What Flock exports:
- every span of the run, with its inputs and outputs (see auto-tracing);
- one
LM.chatspan per model call in the OpenTelemetry GenAI conventions: model, provider, tokens and the agent (see model calls); - the run's correlation ID as
session.idandgen_ai.conversation.id, so tools that group by session or thread put a run's traces together.
The variablesΒΆ
Flock reads the standard OpenTelemetry variables:
| Variable | Meaning |
|---|---|
OTEL_EXPORTER_OTLP_ENDPOINT | Base endpoint; setting it switches export on |
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT | Full traces endpoint, used as given (wins over the base endpoint) |
OTEL_EXPORTER_OTLP_PROTOCOL | grpc (default) or http/protobuf; over HTTP, Flock appends /v1/traces to the base endpoint |
OTEL_EXPORTER_OTLP_HEADERS | Headers as key=value pairs, comma-separated and URL-encoded (a space is %20) |
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT | true adds the messages of each model call to its LM.chat span (off by default) |
FLOCK_TRACE_LEVEL, FLOCK_TRACE_SERVICES and FLOCK_TRACE_IGNORE apply to exported spans too: a filtered operation creates no span at all.
What each tool showsΒΆ
Model calls are the LM.chat spans. Flock exports each call's tokens, not a cost: the Cost column is the tool's own estimate from its price list. Flock's own cost is per agent run (flock.usage.cost_usd, see auto-tracing).
| Tool | Model calls | Tokens | Cost | A run grouped as | Checked with |
|---|---|---|---|---|---|
| Langfuse | Generations, with model and parameters | Per call and per session | When the model is in Langfuse's price list | Session | Langfuse v4, self-hosted |
| Opik | LLM spans, with model and provider | Per call and per trace | Estimated per call | Thread | Opik (2026-10), self-hosted |
| Arize Phoenix | LLM spans, with model and provider | Per call | Not checked | session.id set | Phoenix 20.26 |
| Jaeger | Spans, the gen_ai.* attributes as tags | As tags | No | No | Jaeger 2.22 |
| MLflow | Spans of the trace | Per trace | Not checked | No | MLflow 3.17 |
LangfuseΒΆ
Langfuse takes OTLP over HTTP at /api/public/otel and authenticates with the project's key pair as HTTP basic auth:
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:3000/api/public/otel # your Langfuse host
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic%20$(printf '%s' 'pk-lf-...:sk-lf-...' | base64 | tr -d '\n')"
What you see:
- each model call as a generation with its model, temperature, max tokens and tokens;
- the cost of a call when Langfuse knows the model's price; for a model it does not know, add a model definition in Langfuse's settings;
- a run as one session, with its traces and their tokens;
- with
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true, the messages of each call as a chat in the session view.
Local setup: Langfuse's docker-compose.yml starts the web app on port 3000. Replace the values marked CHANGEME with secrets of your own; LANGFUSE_INIT_* creates an organisation, a project, its keys and a user at the first start.
OpikΒΆ
Opik takes OTLP over HTTP at /api/v1/private/otel. The projectName header chooses the project:
# Self-hosted
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:5173/api/v1/private/otel
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
export OTEL_EXPORTER_OTLP_HEADERS="projectName=my-flock-app"
# Opik Cloud (from Opik's documentation)
export OTEL_EXPORTER_OTLP_ENDPOINT=https://www.comet.com/opik/api/v1/private/otel
export OTEL_EXPORTER_OTLP_HEADERS="Authorization=<api key>,Comet-Workspace=<workspace>,projectName=my-flock-app"
What you see:
- each model call as an LLM span with its model, provider, tokens and an estimated cost, and the totals per trace;
- a run as one thread (Opik groups by
gen_ai.conversation.id).
Local setup: in Opik's repository, deployment/docker-compose starts it with the profile opik (or ./opik.sh); the frontend serves on port 5173. The docker-compose.override.yaml next to it publishes the database ports (MySQL, Redis, ClickHouse, MinIO); leave it out when other stacks on the machine use those ports. OPIK_USAGE_REPORT_ENABLED=false switches off usage reporting.
Arize PhoenixΒΆ
Phoenix takes OTLP over HTTP on its web port:
docker run -p 6006:6006 arizephoenix/phoenix
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:6006
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
What you see: each model call as an LLM span with its model, provider and tokens (Phoenix reads the GenAI attributes), the other spans as general spans, and session.id on the spans of a run. The traces land in Phoenix's default project.
JaegerΒΆ
Jaeger takes OTLP over gRPC on port 4317, which is Flock's default protocol:
docker run -p 16686:16686 -p 4317:4317 jaegertracing/jaeger
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317
What you see: every span of the run under the service flock-auto-trace, with all attributes as tags, LM.chat and its gen_ai.* attributes included. Jaeger has no view of its own for model calls; it is the general-purpose timeline. The UI is on port 16686.
MLflowΒΆ
MLflow's tracking server (3.x) takes OTLP over HTTP at /v1/traces and needs the experiment the traces belong to as a header:
mlflow server --port 5001 --backend-store-uri sqlite:///mlflow.db
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:5001
export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
export OTEL_EXPORTER_OTLP_HEADERS="x-mlflow-experiment-id=<experiment id>"
What you see: each trace in the experiment's traces, with the token usage of the trace.
Several backends at onceΒΆ
The variables configure one OTLP exporter. To send to several backends, run an OpenTelemetry Collector that fans out, or add an exporter per backend to the tracer provider in your code, after Flock has set it up:
from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from flock import Flock
flock = Flock() # auto-tracing has set up the tracer provider
provider = trace.get_tracer_provider()
provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter(
endpoint="http://localhost:5173/api/v1/private/otel/v1/traces",
headers={"projectName": "my-flock-app"},
)))
An endpoint passed in code is used as given: write the full traces path.