DocsObservabilitySDKsAdvanced Features

Advanced features

Use these patterns to harden your Langfuse instrumentation, protect sensitive data, and adapt the SDKs to complex environments. Each section shows the equivalent Python and JS/TS setup side-by-side.

Mask sensitive data

Route spans to different Langfuse projects (multi-tenant, staging vs. prod, etc.) by registering separate exporters. Note the limitations below.

Both SDKs expose helpers to attach scores to observations or entire traces, plus dataset utilities for repeatable experiments.

Provide a mask function when instantiating the client to scrub inputs, outputs, and metadata before they leave your infrastructure.

from langfuse import Langfuse
import re
 
def pii_masker(data: any, **kwargs) -> any:
    if isinstance(data, str):
        return re.sub(r"[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+", "[EMAIL_REDACTED]", data)
    elif isinstance(data, dict):
        return {k: pii_masker(data=v) for k, v in data.items()}
    elif isinstance(data, list):
        return [pii_masker(data=item) for item in data]
    return data
 
langfuse = Langfuse(mask=pii_masker)

Logging & debugging

The Langfuse SDK uses Python’s standard logging module. The main logger is named "langfuse". Enable detailed debug logging by either passing debug=True when instantiating Langfuse, setting the LANGFUSE_DEBUG="True" environment variable, or configuring the logger manually:

import logging
 
langfuse_logger = logging.getLogger("langfuse")
langfuse_logger.setLevel(logging.DEBUG)

The default log level for "langfuse" is logging.WARNING.

Sampling

Sample traces directly on the client via the sample_rate constructor argument or the LANGFUSE_SAMPLE_RATE environment variable. The value must be between 0.0 (drop everything) and 1.0 (send all traces). If a trace is not sampled, none of its observations or scores are exported.

from langfuse import Langfuse
 
langfuse = Langfuse(sample_rate=0.2)

Filter exported spans

Exclude spans from specific instrumentation scopes.

from langfuse import Langfuse
 
langfuse = Langfuse(blocked_instrumentation_scopes=["sqlalchemy", "psycopg"])

How it works: every OpenTelemetry span contains an instrumentation scope (visible in Langfuse under metadata.scope.name). Langfuse evaluates the scope against blocked_instrumentation_scopes when deciding whether to export. Be careful when filtering parents—child spans may become orphaned.

⚠️

Filtering parent spans may result in orphaned children in Langfuse.

Tracer provider isolation

Create a dedicated OTEL TracerProvider for Langfuse spans when you need isolation from other observability backends (Datadog, Jaeger, Zipkin, etc.).

Benefits

  • Prevent Langfuse spans from reaching other exporters.
  • Prevent third-party spans from appearing inside Langfuse.
  • Configure sampling/export policies independently.
from opentelemetry.sdk.trace import TracerProvider
from langfuse import Langfuse
 
langfuse_tracer_provider = TracerProvider()
langfuse = Langfuse(tracer_provider=langfuse_tracer_provider)
langfuse.start_span(name="isolated").end()
⚠️

TracerProviders still share the same context, so mixing providers may create orphaned spans.

Multi-project setups

Instantiate dedicated clients per project and pass the langfuse_public_key context where needed.

from langfuse import Langfuse, observe
 
project_a = Langfuse(public_key="pk-lf-project-a-...", secret_key="sk-lf-project-a-...")
project_b = Langfuse(public_key="pk-lf-project-b-...", secret_key="sk-lf-project-b-...")
 
@observe
def process_data_for_project_a(data, langfuse_public_key="pk-lf-project-a-..."):
    return {"processed": data}
 
@observe
def process_data_for_project_b(data, langfuse_public_key="pk-lf-project-b-..."):
    return {"processed": data}

You can also route OpenAI or LangChain integrations by passing langfuse_public_key on each call.

⚠️

Multi-project setups are experimental. Third-party OTEL spans (HTTP clients, databases, etc.) lack the Langfuse public key attribute and will therefore be exported to all projects.

How it works

  1. Langfuse spans carry a public-key attribute.
  2. You register one exporter per project.
  3. Each exporter filters spans by that attribute before sending them.

Important considerations

  • Always provide the correct langfuse_public_key to the top-most observed function or integration call.
  • Missing the key routes traces to the default project or drops them.
  • Third-party spans without the attribute go to every project.

Environment-specific considerations

Thread pools and multiprocessing

Use the OpenTelemetry threading instrumentor so context flows across worker threads.

from opentelemetry.instrumentation.threading import ThreadingInstrumentor
 
ThreadingInstrumentor().instrument()

For multiprocessing, follow the OpenTelemetry guidance. If you use Pydantic Logfire, enable distributed_tracing=True.

Distributed tracing

Prefer native OTEL propagation when linking services. The trace_context argument should be a last resort because it forces root-span semantics server-side.

Time to first token (TTFT)

from langfuse import get_client
import datetime, time
 
langfuse = get_client()
 
with langfuse.start_as_current_observation(as_type="generation", name="TTFT-Generation") as generation:
    time.sleep(3)
    generation.update(
        completion_start_time=datetime.datetime.now(),
        output="some response",
    )
 
langfuse.flush()

Self-signed TLS certificates

.env
OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE="/path/to/my-selfsigned-cert.crt"
import os, httpx
from langfuse import Langfuse
 
httpx_client = httpx.Client(verify=os.environ["OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE"])
langfuse = Langfuse(httpx_client=httpx_client)
⚠️

Understand the security implications before trusting self-signed certificates.

Was this page helpful?