Langfuse.OpenTelemetry (Langfuse v0.1.0)

View Source

OpenTelemetry integration for Langfuse.

This module provides comprehensive OpenTelemetry support including:

  • Span Processor - Intercept OTEL spans and convert to Langfuse observations
  • Attribute Mapping - Map GenAI semantic conventions to Langfuse fields
  • W3C Trace Context - Distributed tracing with traceparent headers
  • Setup Helpers - Easy configuration for OTEL export

Quick Start

Option 1: Native OTEL Export (Simplest)

Configure OpenTelemetry to export directly to Langfuse:

# config/runtime.exs
config :opentelemetry_exporter,
  otlp_protocol: :http_protobuf,
  otlp_endpoint: System.get_env("LANGFUSE_HOST", "https://cloud.langfuse.com") <>
                 "/api/public/otel/v1/traces",
  otlp_headers: [
    {"Authorization", "Basic " <> Base.encode64(
      System.get_env("LANGFUSE_PUBLIC_KEY") <> ":" <>
      System.get_env("LANGFUSE_SECRET_KEY")
    )}
  ]

Or programmatically:

Langfuse.OpenTelemetry.Setup.configure_exporter()

Option 2: Span Processor (More Control)

Use Langfuse's span processor for fine-grained control:

# In your application supervisor
Langfuse.OpenTelemetry.Setup.register_processor()

# Or with filtering
Langfuse.OpenTelemetry.Setup.register_processor(
  filter_fn: fn span ->
    attrs = elem(span, 7)
    Map.has_key?(attrs, "gen_ai.request.model")
  end
)

Attribute Mapping

The integration automatically maps OpenTelemetry semantic conventions to Langfuse fields. See Langfuse.OpenTelemetry.AttributeMapper for the complete mapping reference.

GenAI Conventions

OTEL AttributeLangfuse Field
gen_ai.request.modelmodel
gen_ai.usage.input_tokensusage.input
gen_ai.usage.output_tokensusage.output
gen_ai.promptinput
gen_ai.completionoutput
gen_ai.request.temperaturemodelParameters.temperature

Langfuse Namespace

OTEL AttributeLangfuse Field
langfuse.user.iduserId
langfuse.session.idsessionId
langfuse.trace.tagstags
langfuse.observation.levellevel

Distributed Tracing

Propagate trace context across service boundaries using W3C Trace Context:

# Extract from incoming request
context = Langfuse.OpenTelemetry.TraceContext.extract!(conn.req_headers)
trace = Langfuse.trace(id: context.trace_id, name: "api-request")

# Inject into outgoing request
headers = Langfuse.OpenTelemetry.TraceContext.inject(trace.id, span.id)
Req.post(downstream_url, headers: headers, json: payload)

Example: Tracing LLM Calls

defmodule MyApp.LLM do
  require OpenTelemetry.Tracer, as: Tracer

  def generate(prompt, opts \\ []) do
    model = Keyword.get(opts, :model, "gpt-4")

    Tracer.with_span "llm.generate", kind: :client do
      Tracer.set_attributes([
        {"gen_ai.system", "openai"},
        {"gen_ai.request.model", model},
        {"gen_ai.prompt", prompt},
        {"langfuse.user.id", opts[:user_id]}
      ])

      {response, usage} = call_openai(prompt, model)

      Tracer.set_attributes([
        {"gen_ai.completion", response},
        {"gen_ai.usage.input_tokens", usage.input},
        {"gen_ai.usage.output_tokens", usage.output}
      ])

      response
    end
  end
end

Submodules

Summary

Functions

Configures the OpenTelemetry exporter to send to Langfuse.

Returns the OTEL exporter configuration for sending to Langfuse.

Extracts trace and span IDs from an OpenTelemetry span context.

Extracts W3C Trace Context from HTTP headers.

Generates W3C Trace Context headers for outgoing requests.

Maps OpenTelemetry semantic convention attributes to Langfuse fields.

Returns the span processor configuration for OpenTelemetry.

Creates a Langfuse trace correlated with an OpenTelemetry span context.

Functions

configure_exporter(opts \\ [])

@spec configure_exporter(keyword()) :: :ok

Configures the OpenTelemetry exporter to send to Langfuse.

Delegates to Langfuse.OpenTelemetry.Setup.configure_exporter/1.

Examples

Langfuse.OpenTelemetry.configure_exporter()

exporter_config(opts \\ [])

@spec exporter_config(keyword()) :: keyword()

Returns the OTEL exporter configuration for sending to Langfuse.

Delegates to Langfuse.OpenTelemetry.Setup.exporter_config/1.

Examples

config = Langfuse.OpenTelemetry.exporter_config()

extract_ids(span_ctx)

@spec extract_ids(term()) :: {String.t(), String.t()} | nil

Extracts trace and span IDs from an OpenTelemetry span context.

Returns a tuple of {trace_id, span_id} as hex strings suitable for use with Langfuse's trace correlation.

Examples

ctx = OpenTelemetry.Ctx.get_current()
span_ctx = OpenTelemetry.Tracer.current_span_ctx(ctx)
{trace_id, span_id} = Langfuse.OpenTelemetry.extract_ids(span_ctx)

extract_trace_context(headers)

@spec extract_trace_context(list() | map()) ::
  {:ok, Langfuse.OpenTelemetry.TraceContext.t()} | {:error, atom()}

Extracts W3C Trace Context from HTTP headers.

Delegates to Langfuse.OpenTelemetry.TraceContext.extract/1.

Examples

headers = [{"traceparent", "00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01"}]
{:ok, context} = Langfuse.OpenTelemetry.extract_trace_context(headers)

inject_trace_context(trace_id, span_id, opts \\ [])

@spec inject_trace_context(String.t(), String.t(), keyword()) :: [
  {String.t(), String.t()}
]

Generates W3C Trace Context headers for outgoing requests.

Delegates to Langfuse.OpenTelemetry.TraceContext.inject/3.

Examples

headers = Langfuse.OpenTelemetry.inject_trace_context(trace_id, span_id)

map_attributes(attrs)

@spec map_attributes(map()) :: map()

Maps OpenTelemetry semantic convention attributes to Langfuse fields.

Delegates to Langfuse.OpenTelemetry.AttributeMapper.map_attributes/1.

Examples

iex> attrs = %{"gen_ai.request.model" => "gpt-4", "gen_ai.usage.input_tokens" => 100}
iex> Langfuse.OpenTelemetry.map_attributes(attrs)
%{model: "gpt-4", usage: %{input: 100}}

processor_config(opts \\ [])

@spec processor_config(keyword()) :: {module(), map()}

Returns the span processor configuration for OpenTelemetry.

Delegates to Langfuse.OpenTelemetry.Setup.processor_config/1.

Examples

config :opentelemetry,
  processors: [
    Langfuse.OpenTelemetry.processor_config()
  ]

trace_from_context(span_ctx, opts \\ [])

@spec trace_from_context(
  term(),
  keyword()
) :: {:ok, Langfuse.Trace.t()} | {:error, :invalid_context}

Creates a Langfuse trace correlated with an OpenTelemetry span context.

The trace will use the OTEL trace ID for correlation, enabling unified tracing across OTEL and Langfuse instrumented code.

Options

All standard Langfuse.trace/1 options are supported.

Examples

span_ctx = OpenTelemetry.Tracer.current_span_ctx()
{:ok, trace} = Langfuse.OpenTelemetry.trace_from_context(
  span_ctx,
  name: "my-operation",
  user_id: "user-123"
)