Instrumentation - Langfuse
Instrumentation
There are two main ways to instrument your application with the Langfuse SDKs:
- Using our native integrations for popular LLM and agent libraries such as OpenAI, LangChain, or the Vercel AI SDK. They automatically create observations and traces and capture prompts, responses, usage, and errors.
- Manually instrumenting your application with the Langfuse SDK. The SDKs provide 3 ways to create observations:
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:
- Record work that is self-contained or happens in parallel to the main execution flow but should still be part of the same overall trace (e.g., a background task initiated by a request).
- Manage the observation's lifecycle explicitly, perhaps because its start and end are determined by non-contiguous events.
- Obtain an observation object reference before it's tied to a specific context block.
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.