Skip to main content
Lemma uses Langfuse as its instrumentation library. You write Langfuse spans; Langfuse exports them to Lemma over OpenTelemetry. This page wires up the exporter once. After this, every page in Instrumentation is just Langfuse code.

1. Install

npm install @langfuse/tracing @langfuse/otel @opentelemetry/sdk-trace-node @opentelemetry/exporter-trace-otlp-proto
Pin the Langfuse v4 SDK (@langfuse/tracing + @langfuse/otel). The examples in these docs use startActiveObservation and propagateAttributes from that version.

2. Set environment variables

Find your API key and project ID in Lemma project settings.
export LEMMA_BASE_URL="https://api.uselemma.ai/otel/v1/traces"
export LEMMA_API_KEY="lma_..."
export LEMMA_PROJECT_ID="proj_..."
Exporting only to Lemma does not require LANGFUSE_* credentials. You only need a Langfuse account if you also want traces in Langfuse — see dual export.

3. Register the exporter once at startup

Register this before any agent, provider client, or tool runs. Late initialization is the most common reason spans go missing.
// instrumentation.ts — imported first, before your app code
import { LangfuseSpanProcessor } from "@langfuse/otel";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";

export const lemmaProcessor = new LangfuseSpanProcessor({
  exporter: new OTLPTraceExporter({
    url: process.env.LEMMA_BASE_URL,
    headers: {
      Authorization: `Bearer ${process.env.LEMMA_API_KEY}`,
      "X-Lemma-Project-ID": process.env.LEMMA_PROJECT_ID,
    },
  }),
});

new NodeTracerProvider({ spanProcessors: [lemmaProcessor] }).register();
In Next.js, put this in the root instrumentation.ts and guard it to the Node.js runtime:
export async function register() {
  if (process.env.NEXT_RUNTIME === "nodejs") {
    await import("./instrumentation");
  }
}

4. Send your first trace

import { startActiveObservation } from "@langfuse/tracing";

await startActiveObservation("hello-agent", async (root) => {
  root.update({
    input: "What is the capital of France?",
    output: "Paris.",
  });
});
Open the Lemma dashboardTraces. Your trace appears within seconds.

Flushing

Span processors batch and export in the background. In short-lived or serverless runtimes, flush before the process exits so the whole trace ships in one batch.
import { lemmaProcessor } from "./instrumentation";

// at the end of a request / serverless handler
await lemmaProcessor.forceFlush();
Without a flush, a serverless function can exit before the root span is exported. If the root is dropped, the trace’s child spans can be orphaned and the trace discarded. Always flush at the end of short-lived handlers.

Next steps

Traces

Define the root span for one agent execution.

Generations

Capture LLM calls with model and token usage.

Tool calls

Record tool arguments and results as child spans.

Trace contract

The exact shape Lemma reads.