Setup
Call setup_tracing() once at process startup, before any executor or
framework initializes. It promotes Langfuse's TracerProvider to the global
OTel provider and configures all filtering, sampling, and capture behaviour for
the lifetime of the process.
See 00-tracing-overview.md for why this call matters.
Minimal setup
from semantic_tracing import setup_tracing
setup_tracing(service_name="my_workflow")
service_name becomes the Langfuse service tag attached to every trace.
Production vs. local-development
By default, setup_tracing() is strict: missing Langfuse credentials or a
failed auth_check() raises RuntimeError. Pass require_langfuse=False to
degrade gracefully instead — Agent Framework spans still emit through OTel, but
nothing is exported to Langfuse. Use this in local dev and CI environments where
Langfuse keys are not always present.
setup_tracing(
service_name="my_workflow",
# require_langfuse defaults to True — missing creds raise RuntimeError
)
setup_tracing(
service_name="my_workflow",
require_langfuse=False, # degrade gracefully when Langfuse keys are absent
)
Environments
The environment parameter tags all traces from this process with a Langfuse
environment label. Environments separate traces from different deployment
contexts (production, development, staging, CI) while keeping prompts, datasets,
and annotation queues shared within the same Langfuse project.
setup_tracing(
service_name="my_workflow",
environment="production",
)
Environments are created automatically when the first trace with that label arrives — no pre-configuration in the Langfuse UI is needed.
The environment parameter sets LANGFUSE_TRACING_ENVIRONMENT before the
Langfuse client is initialized. If the env var is already present in the process
environment (e.g. set by a Kubernetes secret or CI pipeline variable), it takes
precedence. This means infrastructure can control the environment without code
changes:
LANGFUSE_TRACING_ENVIRONMENT=production
If nothing is set, Langfuse uses the label "default".
Releases
A release is the identifier of what produced a trace: the build, and the
instructions and knowledge it served. Release carries it:
from semantic_tracing import Release, set_release, setup_tracing
setup_tracing(
service_name="my_assistant",
release=Release("2026.11.3", instructions_version="12", knowledge_version="40"),
)
# A knowledge promotion switches a version pointer without a redeploy.
# The process keeps running, so the release changes at runtime.
set_release(Release("2026.11.3", instructions_version="12", knowledge_version="41"))
id is required. instructions_version and knowledge_version are optional.
An empty string for any of them raises ValueError. set_release swaps the
whole value; set_release(None) clears it.
What shows in Langfuse
| Where | What |
|---|---|
| Langfuse's trace release field | id. The trace list filters on it. |
| Trace metadata | release_id, instructions_version, knowledge_version. A version that is None is left out. |
The three metadata keys are the names semantic_evaluation reserves in
run_config for a release service. A production trace and an offline
evaluation run of the same release therefore carry the same keys, and you never
type the key strings yourself.
One release per trace
A trace takes its release when its root observation starts, and every other
observation copies its parent's. set_release during a running trace changes
only the traces that start afterwards, so one trace never splits across two
releases. Langfuse reads a trace's release from its root observation.
Precedence
| Source | Applies to |
|---|---|
A reserved key in propagate(metadata={...}) |
That request, per key. release_id also sets the release field. |
set_release(), or setup_tracing(release=) |
Every trace whose root starts after the call |
LANGFUSE_RELEASE and the CI commit variables Langfuse reads (BUILD_SOURCEVERSION, GITHUB_SHA, GIT_COMMIT, ...) |
Only while no release is set |
propagate(version=...) |
Nothing above: it sets Langfuse's separate version field |
Limits
- A trace that spans several requests (see 13-tracing-across-requests.md) has no running parent to copy from. Each recorded observation takes the release current when it is recorded, and the trace's release is its root's.
additional_exporters(for example App Insights) and local capture receive spans before the SDK finalizes the release. They may show Langfuse's own release value and a privatesemantic_tracing.releaseattribute.- Platform agents that run none of this code carry no release.
Sampling
sample_rate controls what fraction of traces are exported to Langfuse. The
default is 1.0 (export everything). Set it lower to reduce Langfuse unit
consumption on high-traffic services.
The sampling decision is deterministic per session: when propagate() is
called with a session_id, all turns of that conversation hash to the same
keep/drop decision. Without session_id, each trace_id produces an independent
decision. Either way, all spans within one trace are always kept or dropped
together — Langfuse never receives a partial trace.
always_send_errors=True bypasses sample_rate for any trace that contains an
ERROR-status span, ensuring failures are always visible in Langfuse regardless
of the configured rate.
setup_tracing(
service_name="my_workflow",
sample_rate=1.0, # default — export everything
)
setup_tracing(
service_name="my_workflow",
sample_rate=0.2, # export 20% of traces
always_send_errors=True, # but always export error traces
)
Dropping empty observations
drop_empty_observations=True discards spans where both input and output are
absent or empty before they reach Langfuse. This targets framework-internal
bookkeeping spans that carry no diagnostic content.
It suits leaf noise only. Descendants of a span dropped this way are not
reattached, because whether a span is empty is only known once it has ended, by
which point its children may already have been exported. Name a span in
drop_span_names instead when it has children.
setup_tracing(
service_name="my_workflow",
drop_empty_observations=True,
)
Dropping noisy framework spans
Frameworks emit internal spans that clutter the Langfuse trace view without providing diagnostic value. A built-in default drop list covers common sources: Microsoft Kiota (msgraph SDK) dispatch internals, and HTTP spans from pods that do not use the dual-provider architecture.
Extend the defaults with pod-specific noise via drop_span_names and
drop_span_name_prefixes:
setup_tracing(
service_name="my_workflow",
drop_span_names=["edge_group.process SingleEdgeGroup"],
drop_span_name_prefixes=["send_async"],
)
A named span that has children is safe to drop: its descendants are reattached to the nearest ancestor that survives, so removing a framework's mid-tree bookkeeping span does not detach the work nested inside it. Matching spans are also kept out of the local trace cache, so a pod does not have to filter the same noise twice.
A dropped span can hand attributes to its children. Each key named in
carry_dropped_span_attributes that the dropped span holds is copied onto every
child as the child starts, unless the child set that key itself, and it chains
through consecutive drops:
setup_tracing(
service_name="my_workflow",
drop_span_names=["model_round"],
carry_dropped_span_attributes=["langfuse.observation.metadata.round"],
)
Only what the dropped span holds when a child starts is copied, so set the
attribute as the wrapper starts, in a span processor's on_start or before it
opens its first child. The copied attributes reach the local trace cache too.
Pods that use the dual-provider architecture (FastAPI/httpx explicitly routed to App Insights) already keep HTTP spans out of Langfuse and rarely need to extend the default list.
To disable the built-in defaults entirely and rely solely on pod-supplied lists:
setup_tracing(
service_name="my_workflow",
clear_default_drop_prefixes=True,
drop_span_name_prefixes=["my.custom.prefix"],
)
See 05-tracing-frameworks.md for the full list of Microsoft Agent Framework spans typically filtered in production.
Capturing prompt and tool bodies
Agent Framework's telemetry layers attach prompt and completion bodies, and tool arguments and results, to spans only when sensitive data capture is enabled. Evaluators that score against the actual model response require this:
setup_tracing(
service_name="my_workflow",
capture_sensitive_data=True,
)
Default is False. The flag is forwarded to
agent_framework.observability.enable_instrumentation.
Local trace capture
capture_local_traces=True mirrors every span into an in-process store as it
ends, so a predictor for local offline evaluation can read a complete trace
synchronously instead of polling Langfuse. Typically enabled only in eval/dev
configurations:
setup_tracing(
service_name="my_workflow",
capture_local_traces=True,
)
Read-side API and interactions
See 12-tracing-local-capture.md for the full
read-side API (get_local_trace, fetch_local_trace_via_http) and how local
capture interacts with sampling, capture_sensitive_data, and export suppression.
Global input filtering
@step serializes all function arguments into the observation input by default.
Framework-internal objects (such as WorkflowContext) produce noisy, unreadable
input fields. Pass an InputFilter to strip specific argument names or types
globally:
from semantic_tracing import InputFilter, setup_tracing
setup_tracing(
service_name="my_workflow",
input_filter=InputFilter(
exclude_arg_names=["ctx", "runner_context"],
exclude_arg_types=["WorkflowContext"],
),
)
See 04-tracing-input-capture.md for per-span filtering and full details on the two-layer model.
Full parameter reference
| Parameter | Type | Default | Description |
|---|---|---|---|
service_name |
str |
"eval_workflow" |
Tag applied to every trace from this process |
environment |
str \| None |
None |
Langfuse environment label; sets LANGFUSE_TRACING_ENVIRONMENT unless already present |
release |
Release \| None |
None |
Release stamped on every trace whose root starts afterwards; change it at runtime with set_release(). See Releases |
require_langfuse |
bool |
True |
Raise on missing creds; pass False to degrade to OTel-only |
sample_rate |
float |
1.0 |
Fraction of traces to export (0.0–1.0); deterministic per session.id or trace_id |
always_send_errors |
bool |
False |
Export traces containing ERROR-status spans regardless of sample_rate |
drop_empty_observations |
bool |
False |
Discard spans with no input and no output before Langfuse export; does not reattach descendants, so use it for leaf noise |
drop_span_names |
list[str] |
[] |
Exact span names to discard (merged with built-in defaults); descendants are reattached to the nearest surviving ancestor |
drop_span_name_prefixes |
list[str] |
[] |
Prefix patterns to discard (merged with built-in defaults); same reattachment behaviour as drop_span_names |
clear_default_drop_prefixes |
bool |
False |
Disable the built-in default drop lists; use only the pod-supplied lists |
carry_dropped_span_attributes |
list[str] \| None |
None |
Attribute keys copied from a span dropped by name or prefix onto each of its children as they start; a child's own value wins |
capture_sensitive_data |
bool |
False |
Forward to Agent Framework's sensitive data setting |
input_filter |
InputFilter \| None |
None |
Global argument filter for all @step observations |
additional_exporters |
list[SpanExporter] \| None |
None |
Extra OTLP-compatible exporters that receive the same spans as Langfuse |
suppress_usage_on_spans |
frozenset[str] \| set[str] \| None |
None |
Observation names for which all token-count data is suppressed. Prevents wrapper GENERATION observations from inflating Langfuse's flat-sum per-trace cost attribution when the same model call is also recorded by a leaf span |
capture_local_traces |
bool |
False |
Mirror every span into an in-process store for synchronous reads via get_local_trace() — see 12-tracing-local-capture.md |
local_trace_cache_size |
int |
500 |
Maximum number of traces held in the local capture store at once (LRU eviction) |