Skip to content

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 private semantic_tracing.release attribute.
  • 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)