# How to Implement Observability in Embabel Using Zipkin and Langfuse

> Implement observability in Embabel using Zipkin and Langfuse. Learn how Embabel automates Zipkin and Langfuse exporter configuration with OpenTelemetry for effortless tracing.

- Repository: [Embabel/embabel-agent](https://github.com/embabel/embabel-agent)
- Tags: how-to-guide
- Published: 2026-08-09

---

**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`](https://github.com/embabel/embabel-agent/blob/main/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](https://github.com/embabel/embabel-agent/blob/main/embabel-agent-autoconfigure/embabel-agent-observability-autoconfigure/src/main/java/com/embabel/agent/autoconfigure/observability/OpenTelemetrySdkAutoConfiguration.java#L49-L52)).

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](https://github.com/embabel/embabel-agent/blob/main/embabel-agent-autoconfigure/embabel-agent-observability-autoconfigure/src/main/java/com/embabel/agent/autoconfigure/observability/OpenTelemetrySdkAutoConfiguration.java#L75-L78)). 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:

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

```

For **Langfuse** integration, include the corresponding exporter dependency:

```xml
<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`](https://github.com/embabel/embabel-agent/blob/main/application.yml). For Zipkin:

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

```

For Langfuse, specify the API endpoint and authentication token:

```yaml
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](https://github.com/embabel/embabel-agent/blob/main/embabel-agent-autoconfigure/embabel-agent-observability-autoconfigure/src/main/java/com/embabel/agent/autoconfigure/observability/OpenTelemetrySdkAutoConfiguration.java#L99-L102)).

## Granular Control via ObservabilityProperties

The `ObservabilityProperties` class in [`embabel-agent-observability/src/main/java/com/embabel/agent/observability/ObservabilityProperties.java`](https://github.com/embabel/embabel-agent/blob/main/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:

```yaml
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`](https://github.com/embabel/embabel-agent/blob/main/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`](https://github.com/embabel/embabel-agent/blob/main/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.