How to Implement Observability in Embabel Using Zipkin and Langfuse

Embabel implements observability using OpenTelemetry with auto-configuration support for Zipkin and Langfuse exporters, automatically wiring SpanExporter beans into a BatchSpanProcessor without requiring manual instrumentation code.

The embabel/embabel-agent repository provides a production-ready observability layer built on the OpenTelemetry standard. This architecture allows developers to route distributed traces to any OpenTelemetry-compatible backend, with specific support for Zipkin and Langfuse through Spring Boot's auto-configuration mechanism.

OpenTelemetry Foundation and Auto-Configuration

The core of Embabel's observability implementation resides in the embabel-agent-observability-autoconfigure module. The class OpenTelemetrySdkAutoConfiguration serves as the central integration point, registering an SdkTracerProvider and managing the lifecycle of span exporters.

According to the source code in OpenTelemetrySdkAutoConfiguration.java, the configuration deliberately executes after Spring Boot's own exporter auto-configurations to prevent double-wrapping of exporters. This ensures that a single SpanExporter bean—whether for Zipkin, Langfuse, or OTLP—is processed exactly once (see lines 49-52).

The auto-configuration detects any SpanExporter bean present on the classpath and, if no custom SpanProcessor is defined, automatically wraps each exporter in a BatchSpanProcessor (lines 75-78). This batching mechanism optimizes network usage when transmitting spans to Zipkin or Langfuse endpoints.

Configuring Zipkin and Langfuse Exporters

Maven Dependencies

To enable Zipkin support, add the OpenTelemetry Zipkin exporter to your project dependencies:

<dependency>
    <groupId>io.opentelemetry</groupId>
    <artifactId>opentelemetry-exporter-zipkin</artifactId>
    <version>1.29.0</version>
</dependency>

For Langfuse integration, include the corresponding exporter dependency:

<dependency>
    <groupId>io.opentelemetry</groupId>
    <artifactId>opentelemetry-exporter-langfuse</artifactId>
    <version>1.29.0</version>
</dependency>

Application Properties

Once the dependency is present, configure the exporter endpoints in your application.yml. For Zipkin:

otel:
  exporter:
    zipkin:
      endpoint: http://localhost:9411/api/v2/spans

For Langfuse, specify the API endpoint and authentication token:

otel:
  exporter:
    langfuse:
      endpoint: https://api.langfuse.com/api/spans
      api-key: ${LANGFUSE_API_KEY}

Spring Boot creates the appropriate SpanExporter bean automatically when these properties are detected. The OpenTelemetrySdkAutoConfiguration then picks up these beans through its exporter customizer logic (lines 99-102).

Granular Control via ObservabilityProperties

The ObservabilityProperties class in embabel-agent-observability/src/main/java/com/embabel/agent/observability/ObservabilityProperties.java provides hierarchical flags to control which spans are generated and exported. The Javadoc explicitly confirms compatibility with "any OpenTelemetry-compatible exporter (Zipkin, OTLP, Langfuse, etc.)".

Key configuration options include:

  • enabled – Global master switch for all observability features
  • tracing-enabled – Controls whether spans are created at all
  • trace-llm-calls – Specifically enables tracing of LLM invocation spans
  • trace-tool-loop – Enables tracing of tool execution loops
  • disabled-traces – A list of observation names to suppress (useful for filtering out infrastructure noise)

Example configuration to enable full tracing for Zipkin or Langfuse:

embabel:
  agent:
    platform:
      observability:
        enabled: true
        tracing-enabled: true
        trace-llm-calls: true
        trace-tool-loop: true
        disabled-traces:
          - "health-check"
          - "internal-refresh"

Complete Implementation Workflow

Implementing observability in Embabel using Zipkin and Langfuse requires three steps:

  1. Add the exporter dependency to your pom.xml for either Zipkin or Langfuse
  2. Configure the endpoint via Spring Boot properties (otel.exporter.*)
  3. Enable Embabel tracing via embabel.agent.platform.observability.tracing-enabled=true

Once configured, every Embabel-generated span—including agent runs, action executions, tool loops, LLM calls, and RAG operations—flows automatically to your chosen backend. The BatchSpanProcessor handles queueing and batch transmission, ensuring minimal performance overhead on the agent execution path.

Summary

  • Embabel uses OpenTelemetry as its observability foundation, compatible with any OTel-compliant exporter including Zipkin and Langfuse.
  • The OpenTelemetrySdkAutoConfiguration class automatically wires SpanExporter beans into BatchSpanProcessor instances without manual code changes.
  • Configuration is controlled through ObservabilityProperties, offering granular switches for different trace types and a suppression list for unwanted spans.
  • Setup requires only adding the appropriate Maven dependency and setting the exporter endpoint in application.yml.
  • The architecture prevents double-wrapping of exporters by executing after Spring Boot's native auto-configurations.

Frequently Asked Questions

Does Embabel require manual instrumentation code to send traces to Zipkin?

No. Once you add the opentelemetry-exporter-zipkin dependency and configure the endpoint, Spring Boot creates the SpanExporter bean automatically. The OpenTelemetrySdkAutoConfiguration detects this bean and wires it into the span processing pipeline. All Embabel operations generate spans automatically through the framework's internal instrumentation.

Can I use both Zipkin and Langfuse simultaneously?

Yes. The auto-configuration supports multiple SpanExporter beans on the classpath. If you include both the Zipkin and Langfuse dependencies and configure both endpoints, Embabel will route spans to both backends simultaneously. Each exporter gets its own BatchSpanProcessor instance to isolate transmission failures between systems.

How do I prevent specific spans from being exported to Zipkin or Langfuse?

Use the disabled-traces property in ObservabilityProperties to suppress specific observation names. Alternatively, set specific tier flags like trace-llm-calls: false to disable entire categories of spans. These filters apply regardless of which exporter (Zipkin, Langfuse, or OTLP) you have configured.

What OpenTelemetry components does Embabel use internally?

Embabel uses the SdkTracerProvider for span creation and BatchSpanProcessor for asynchronous export. The implementation resides in the embabel-agent-observability module and relies on standard OpenTelemetry Java SDK components (version 1.29.0+) rather than custom instrumentation agents.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →