# Langfuse

> Export OpenTelemetry traces from Langfuse to Last9 for unified LLM observability and monitoring

Source: https://last9.io/docs/integrations/langfuse/

Monitor your LLM applications with Langfuse traces exported to Last9. Combine Langfuse's prompt management and evaluation capabilities with Last9's observability platform for comprehensive AI monitoring.

## What is Langfuse?

[Langfuse](https://langfuse.com) is an open-source LLM engineering platform that provides observability, prompt management, and evaluation tools for AI applications. It helps teams debug, analyze, and iterate on their LLM-powered features.

Key capabilities include:

- **Tracing** - Track LLM calls, chains, and agents with detailed span data
- **Prompt management** - Version and deploy prompts with built-in experimentation
- **Evaluation** - Score and analyze LLM outputs for quality monitoring
- **OpenTelemetry export** - Send trace data to external observability platforms

## Prerequisites

Before setting up the integration:

1. **Last9 Account** - Sign up at [app.last9.io](https://app.last9.io)
2. **Langfuse Account** - Create an account at [langfuse.com](https://langfuse.com) or self-host Langfuse
3. **LLM Application** - An existing application instrumented with Langfuse SDK

## Instrument Your Application

If you haven't already instrumented your application with Langfuse, here's how to get started:

**Python**

1. **Install the Langfuse SDK**

   ```bash
   pip install langfuse
   ```

2. **Configure Langfuse Credentials**

   Set your Langfuse credentials as environment variables:

   ```bash
   export LANGFUSE_PUBLIC_KEY="pk-..."
   export LANGFUSE_SECRET_KEY="sk-..."
   export LANGFUSE_HOST="https://cloud.langfuse.com"  # or your self-hosted URL
   ```

3. **Instrument Your LLM Calls**

   Use the `@observe` decorator to trace your LLM operations:

   ```python
   from langfuse import observe
   from openai import OpenAI

   client = OpenAI()

   @observe()
   def answer_question(question: str) -> str:
       response = client.chat.completions.create(
           model="gpt-4",
           messages=[
               {"role": "system", "content": "You are a helpful assistant."},
               {"role": "user", "content": question}
           ]
       )
       return response.choices[0].message.content

   # Usage
   answer = answer_question("What is observability?")
   ```

   For framework integrations:

   ```python
   # LangChain integration
   from langfuse.langchain import CallbackHandler

   langfuse_handler = CallbackHandler()

   # Pass to your LangChain calls
   chain.invoke({"input": "Hello"}, config={"callbacks": [langfuse_handler]})
   ```

**JavaScript / TypeScript**

1. **Install the Langfuse SDK**

   The Langfuse JS/TS SDK is OpenTelemetry-native. Install the tracing package:

   ```bash
   npm install @langfuse/tracing
   ```

2. **Configure Langfuse Credentials**

   Set your Langfuse credentials as environment variables:

   ```bash
   export LANGFUSE_PUBLIC_KEY="pk-..."
   export LANGFUSE_SECRET_KEY="sk-..."
   export LANGFUSE_HOST="https://cloud.langfuse.com"  # or your self-hosted URL
   ```

3. **Instrument Your LLM Calls**

   Use `startActiveObservation` to trace your LLM operations:

   ```typescript
   import { startActiveObservation } from "@langfuse/tracing";
   import OpenAI from "openai";

   const openai = new OpenAI();

   async function answerQuestion(question: string): Promise<string> {
     return startActiveObservation("answer-question", async (span) => {
       span.update({ input: question });

       const response = await openai.chat.completions.create({
         model: "gpt-4",
         messages: [
           { role: "system", content: "You are a helpful assistant." },
           { role: "user", content: question },
         ],
       });

       const answer = response.choices[0].message.content;
       span.update({ output: answer });

       return answer;
     });
   }
   ```

## Integration Setup

The Langfuse SDK (v3+) is OpenTelemetry-native, so add Last9 as a second span processor in your application's own OpenTelemetry setup. Langfuse has no deployment-level or dashboard toggle that forwards traces to a third-party OTLP backend. This works the same way for Langfuse Cloud and self-hosted Langfuse.

**Python**

1. **Get Your Last9 OTel Credentials**

   Navigate to [**Integrations** → **OpenTelemetry**](https://app.last9.io/integrations?integration=OpenTelemetry) in your Last9 dashboard. Copy the **OTel Endpoint** and **Auth Header** values.

2. **Install the OpenTelemetry Exporter**

   ```bash
   pip install opentelemetry-exporter-otlp-proto-http
   ```

3. **Add Last9 as a Second Span Processor**

   The Langfuse Python SDK doesn't expose a standalone `LangfuseSpanProcessor` — instead, pass your own `TracerProvider` to the `Langfuse` client, and attach Last9's exporter to that same provider. This requires the Langfuse credentials (`LANGFUSE_PUBLIC_KEY`, `LANGFUSE_SECRET_KEY`, `LANGFUSE_HOST`) from [Instrument Your Application](#instrument-your-application):

   ```python
   from langfuse import Langfuse
   from opentelemetry import trace
   from opentelemetry.sdk.resources import Resource
   from opentelemetry.sdk.trace import TracerProvider
   from opentelemetry.sdk.trace.export import BatchSpanProcessor
   from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter

   provider = TracerProvider(
       resource=Resource.create({"service.name": "my-llm-app"})
   )
   provider.add_span_processor(
       BatchSpanProcessor(
           OTLPSpanExporter(
               endpoint="https://otlp.last9.io/v1/traces",
               headers={"Authorization": "<your-auth-header>"},
           )
       )
   )
   trace.set_tracer_provider(provider)

   langfuse = Langfuse(tracer_provider=provider)
   ```

   Replace `<your-auth-header>` with the full Auth Header value from Step 1, including the `Basic ` prefix. Replace `my-llm-app` with your service name. Set this up once, before any `@observe`-decorated code runs — constructing `Langfuse(...)` registers it as the client `@observe` and `get_client()` use.

4. **Verify the Integration**

   Make some LLM calls through your Langfuse-instrumented application, then check the [Last9 Traces Explorer](https://app.last9.io/traces) to see your traces.

**JavaScript / TypeScript**

1. **Get Your Last9 OTel Credentials**

   Navigate to [**Integrations** → **OpenTelemetry**](https://app.last9.io/integrations?integration=OpenTelemetry) in your Last9 dashboard. Copy the **OTel Endpoint** and **Auth Header** values.

2. **Install the OpenTelemetry Packages**

   ```bash
   npm install @langfuse/otel @langfuse/tracing @opentelemetry/sdk-node @opentelemetry/exporter-trace-otlp-http @opentelemetry/sdk-trace-base
   ```

3. **Add Last9 as a Second Span Processor**

   In your OpenTelemetry setup (e.g. `instrumentation.ts`), add Last9's exporter alongside Langfuse's:

   ```typescript
   import { NodeSDK } from "@opentelemetry/sdk-node";
   import { LangfuseSpanProcessor } from "@langfuse/otel";
   import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
   import { SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base";

   const sdk = new NodeSDK({
     serviceName: "my-llm-app",
     spanProcessors: [
       new LangfuseSpanProcessor(),
       new SimpleSpanProcessor(
         new OTLPTraceExporter({
           url: "https://otlp.last9.io/v1/traces",
           headers: { Authorization: "<your-auth-header>" },
         }),
       ),
     ],
   });

   sdk.start();
   ```

   Replace `<your-auth-header>` with the full Auth Header value from Step 1, including the `Basic ` prefix. Replace `my-llm-app` with your service name. Import this file before any other application code runs.

4. **Verify the Integration**

   Make some LLM calls through your Langfuse-instrumented application, then check the [Last9 Traces Explorer](https://app.last9.io/traces) to see your traces.

## View Traces in Last9

After the integration is active, your Langfuse traces appear in Last9:

1. Navigate to [**Traces Explorer**](https://app.last9.io/traces)
2. Filter by the service name you set in [Integration Setup](#integration-setup), for example `my-llm-app`
3. Explore trace data including:
   - **LLM generations** - Model calls with prompts, completions, and token counts
   - **Trace hierarchy** - Parent-child relationships between operations
   - **Latency breakdown** - Time spent in each span
   - **Metadata** - Custom attributes and tags from Langfuse

## Correlate with Application Traces

Langfuse and Last9 share the same OpenTelemetry `TracerProvider`, which you set up in [Integration Setup](#integration-setup). As a result, Langfuse spans and your application's other spans already share the same trace ID, so you do not need to propagate metadata manually. A request that flows through your app and into an `@observe`-decorated LLM call shows up as a single trace in Last9's Traces Explorer.

## Advanced Configuration

### Filter Exported Traces

The `BatchSpanProcessor` / `SimpleSpanProcessor` you added for Last9 receives every span from the shared `TracerProvider`, independent of Langfuse's own export filtering. To reduce volume sent to Last9, add a [span processor filter](https://opentelemetry.io/docs/languages/python/instrumentation/#span-processor) or an OpenTelemetry Collector-based sampler in front of the exporter, rather than relying on Langfuse-side settings.

### Custom Attributes

Add custom attributes to your Langfuse traces that will appear in Last9. Wrap the operation in `propagate_attributes` rather than calling a trace-update method on the client:

```python
from langfuse import observe, propagate_attributes

@observe()
def process_document(doc_id: str, user_id: str):
    # Your logic here
    pass

# Usage
with propagate_attributes(
    user_id="user-123",
    metadata={
        "document_id": "doc-456",
        "environment": "production",
        "feature": "document-qa",
    },
    tags=["production", "document-processing"],
):
    process_document("doc-456", "user-123")
```

These attributes enable filtering and grouping in Last9's Traces Explorer.

## Use Cases

- **Unified Observability** - View LLM traces alongside your application's HTTP requests, database queries, and other operations in a single platform.
- **Production Monitoring** - Set up alerts on LLM latency, error rates, and token consumption using Last9's alerting capabilities.
- **Cost Analysis** - Correlate Langfuse's token tracking with Last9's analytics to understand LLM costs per feature or user segment.
- **Debugging** - Trace issues from Last9 alerts back to specific Langfuse generations, prompts, and model responses.
- **Compliance** - Maintain a complete audit trail of all LLM interactions in your observability infrastructure.

---

## Troubleshooting

- **Traces do not appear in Last9**

  - Verify the `OTLPSpanExporter` / `OTLPTraceExporter` endpoint is set to `https://otlp.last9.io/v1/traces`
  - Confirm the exporter's `Authorization` header is correctly configured (including the `Basic` prefix)
  - Ensure the Last9 span processor is registered on the same `TracerProvider` as `LangfuseSpanProcessor`, and that this setup runs before any `@observe`-decorated code or `startActiveObservation` calls
  - Ensure your Langfuse application is generating traces (check the Langfuse dashboard first)
  - In Node.js, confirm the process calls `sdk.shutdown()` (or otherwise flushes) before exit so batched spans aren't dropped

Please get in touch with us on [Discord](https://discord.com/invite/Q3p2EEucx9) or [Email](mailto:support@last9.io) if you have any questions.
