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
- Langfuse spans carry a public-key attribute.
- You register one exporter per project.
- Each exporter filters spans by that attribute before sending them.
Important considerations
- Always provide the correct
langfuse_public_keyto 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
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.