Instrumentation - Langfuse

Instrumentation

There are two main ways to instrument your application with the Langfuse SDKs:

All approaches are interoperable. You can nest a decorator-created observation inside a context manager or mix manual spans with our native integrations.

Custom instrumentation

Instrument your application with the Langfuse SDK using the following methods:

Context manager

The context manager allows you to create a new span and set it as the currently active observation in the OTel context for its duration. All new observations created within this block will automatically be its children.

Python SDKJS/TS SDK

from langfuse import get_client, propagate_attributes

langfuse = get_client()

with langfuse.start_as_current_observation(
    as_type="span",
    name="user-request-pipeline",
    input={"user_query": "Tell me a joke"},
) as root_span:
    with propagate_attributes(user_id="user_123", session_id="session_abc"):
        with langfuse.start_as_current_observation(
            as_type="generation",
            name="joke-generation",
            model="gpt-4o",
        ) as generation:
            generation.update(output="Why did the span cross the road?")

root_span.update(output={"final_joke": "..."})

Observe wrapper

The observe decorator is an easy way to automatically capture inputs, outputs, timings, and errors of a wrapped function without modifying the function's internal logic.

Python SDKJS/TS SDK

Use observe() to decorate a function and automatically capture inputs, outputs, timings, and errors.

from langfuse import observe

@observe()
def my_data_processing_function(data, parameter):
    return {"processed_data": data, "status": "ok"}

@observe(name="llm-call", as_type="generation")
async def my_async_llm_call(prompt_text):
    return "LLM response"

Manual observations

You can also manually create observations. This is useful when you need to:

Python SDKJS/TS SDK

from langfuse import get_client

langfuse = get_client()

span = langfuse.start_observation(name="manual-span")
span.update(input="Data for side task")
child = span.start_observation(name="child-span", as_type="generation")
child.end()
span.end()

Nesting observations

The Langfuse SDKs methods automatically handle the nesting of observations.

Python SDKJS/TS SDK

Observe Decorator If you use the observe wrapper, the function call hierarchy is automatically captured and reflected in the trace.

from langfuse import observe

@observe
def my_data_processing_function(data, parameter):
    # ... processing logic ...
    return {"processed_data": data, "status": "ok"}

@observe
def main_function(data, parameter):
    return my_data_processing_function(data, parameter)

Python SDKJS/TS SDK

Use propagate_attributes() to add attributes to observations. In the Python SDK, environment is a first-class Langfuse environment and maps to langfuse.environment, not to trace metadata. Use it when the environment is request-scoped, for example when one shared proxy handles requests from multiple deployment environments.

from langfuse import get_client, propagate_attributes

langfuse = get_client()

with langfuse.start_as_current_observation(as_type="span", name="user-workflow"):
    with propagate_attributes(
        user_id="user_123",
        session_id="session_abc",
        metadata={"experiment": "variant_a"},
        version="1.0",
        environment="staging",
        trace_name="user-workflow",
    ):
        with langfuse.start_as_current_observation(as_type="generation", name="llm-call"):
            pass

Flush observations

Always flush() or shutdown() the client in short-lived processes (scripts, serverless functions, workers) to avoid losing data.