# How to Set Up Observability with Zipkin and Langfuse in Embabel Agent

> Easily set up observability with Zipkin and Langfuse in your Embabel Agent. Learn how to leverage OpenTelemetry and Spring Boot for automatic exporter configuration and enhanced tracing.

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

---

**Embabel Agent uses OpenTelemetry as its observability backbone, automatically configuring Zipkin or Langfuse exporters when you add the dependency and expose a `SpanExporter` bean via Spring Boot properties.**

The embabel/embabel-agent repository provides a comprehensive observability layer built on OpenTelemetry, allowing you to export distributed traces to any OpenTelemetry-compatible backend. Whether you need to monitor agent execution in Zipkin or analyze LLM interactions in Langfuse, the setup requires only dependency configuration and property files. This guide shows you exactly how to set up observability with Zipkin and Langfuse using the auto-configuration modules provided in the codebase.

## How Observability Works in Embabel Agent

Embabel Agent delegates all tracing to an OpenTelemetry `SdkTracerProvider` configured in [`OpenTelemetrySdkAutoConfiguration.java`](https://github.com/embabel/embabel-agent/blob/main/OpenTelemetrySdkAutoConfiguration.java). This auto-configuration activates only when it detects a `SpanExporter` bean on the classpath, ensuring zero overhead when observability is not needed.

The configuration lives in the `embabel-agent-observability-autoconfigure` module. When a valid exporter is present, the class builds the `SdkTracerProvider` and registers a global `OpenTelemetry` instance. If no exporter is found, tracing is disabled and a warning is logged to indicate that observability features are inactive.

## Configuring the Zipkin Exporter

Zipkin integrates seamlessly through the standard OpenTelemetry Zipkin exporter library.

### Add the Maven Dependency

Include the Zipkin exporter in your [`pom.xml`](https://github.com/embabel/embabel-agent/blob/main/pom.xml):

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

```

This dependency automatically registers a `SpanExporter` bean that `OpenTelemetrySdkAutoConfiguration` detects.

### Configure Application Properties

Add the following to your [`application.yml`](https://github.com/embabel/embabel-agent/blob/main/application.yml):

```yaml
embabel:
  agent:
    platform:
      observability:
        enabled: true
        tracingEnabled: true
        serviceName: embabel-agent
        disabledTraces:
          - "http.server.requests"

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

```

The `disabledTraces` list suppresses noisy spans such as internal HTTP requests, while `otel.exporter.zipkin.endpoint` directs traces to your Zipkin collector.

## Configuring the Langfuse Exporter

Langfuse provides specialized visualization for LLM agent traces and requires similar but distinct configuration.

### Add the Maven Dependency

Add the community-provided Langfuse exporter:

```xml
<dependency>
    <groupId>io.opentelemetry</groupId>
    <artifactId>opentelemetry-exporter-langfuse</artifactId>
    <version>1.35.0</version>
</dependency>

```

### Configure Application Properties

Configure your [`application.yml`](https://github.com/embabel/embabel-agent/blob/main/application.yml) with the Langfuse-specific endpoint and API key:

```yaml
embabel:
  agent:
    platform:
      observability:
        enabled: true
        tracingEnabled: true
        serviceName: embabel-agent

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

```

Store the `LANGFUSE_API_KEY` in environment variables or your secret management system—never commit this value to version control.

## Fine-Tuning Observability Settings

The [`ObservabilityProperties.java`](https://github.com/embabel/embabel-agent/blob/main/ObservabilityProperties.java) class defines the complete configuration surface for tracing behavior. Key properties include:

- **`embabel.agent.platform.observability.enabled`**: Master switch for all observability features.
- **`embabel.agent.platform.observability.tracingEnabled`**: Specifically controls span creation and export.
- **`embabel.agent.platform.observability.serviceName`**: Identifies your service in the trace backend.
- **`embabel.agent.platform.observability.disabledTraces`**: Array of observation names to suppress (e.g., `["http.server.requests"]`).
- **`embabel.agent.platform.observability.captureMessageContent`**: Toggles capture of LLM chat payloads; disable in production to avoid PII exposure.

These properties are mapped in [`ObservabilityProperties.java`](https://github.com/embabel/embabel-agent/blob/main/ObservabilityProperties.java) and consumed by both `OpenTelemetrySdkAutoConfiguration` and [`EmbabelSpanEventListener.java`](https://github.com/embabel/embabel-agent/blob/main/EmbabelSpanEventListener.java) to determine which events are recorded.

## Implementing Custom Traces in Code

Once the exporter is configured, you can inject the OpenTelemetry `Tracer` bean to create custom spans. The `Tracer` is provided by the `OpenTelemetrySdk` bean created in `OpenTelemetrySdkAutoConfiguration`.

```java
import io.opentelemetry.api.trace.Tracer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class MyService {

    private final Tracer tracer;

    @Autowired
    public MyService(Tracer tracer) {
        this.tracer = tracer;
    }

    public void doWork() {
        var span = tracer.spanBuilder("my.work")
                         .setAttribute("component", "service")
                         .startSpan();
        try (var ignored = span.makeCurrent()) {
            // business logic...
        } finally {
            span.end();
        }
    }
}

```

For declarative tracing, use the `@Tracked` annotation processed by [`TrackedAspect.java`](https://github.com/embabel/embabel-agent/blob/main/TrackedAspect.java), which automatically emits spans around annotated methods.

## Summary

- **Embabel Agent** uses OpenTelemetry as its core observability framework through the `embabel-agent-observability-autoconfigure` module.
- **Activation** requires only a `SpanExporter` bean on the classpath; no exporter means tracing is automatically disabled.
- **Zipkin** integration uses `opentelemetry-exporter-zipkin` configured via `otel.exporter.zipkin.endpoint`.
- **Langfuse** integration uses `opentelemetry-exporter-langfuse` with `otel.exporter.langfuse.endpoint` and `otel.exporter.langfuse.api-key`.
- **Configuration** is centralized in [`ObservabilityProperties.java`](https://github.com/embabel/embabel-agent/blob/main/ObservabilityProperties.java), allowing control over service names, span filtering, and content capture.
- **Custom instrumentation** is available via the injected `Tracer` bean or the `@Tracked` aspect-oriented approach.

## Frequently Asked Questions

### What happens if I don't add any exporter dependency?

If no `SpanExporter` bean is detected on the classpath, `OpenTelemetrySdkAutoConfiguration` skips tracer provider initialization and logs a warning. The application runs without exporting traces, effectively disabling observability features without throwing errors.

### Can I use both Zipkin and Langfuse simultaneously?

Yes. OpenTelemetry supports multiple exporters. If you include both dependencies in your build, both beans will be registered and traces will be exported to both backends simultaneously. Configure each exporter's endpoint independently in [`application.yml`](https://github.com/embabel/embabel-agent/blob/main/application.yml).

### How do I prevent sensitive data from appearing in my traces?

Set `embabel.agent.platform.observability.captureMessageContent` to `false` in your properties. This prevents [`EmbabelSpanEventListener.java`](https://github.com/embabel/embabel-agent/blob/main/EmbabelSpanEventListener.java) from attaching LLM chat payloads or RAG query content to spans, ensuring PII and proprietary data remain out of your observability backend.

### Where is the observability configuration logic located?

The auto-configuration logic resides in [`embabel-agent-autoconfigure/embabel-agent-observability-autoconfigure/src/main/java/com/embabel/agent/autoconfigure/observability/OpenTelemetrySdkAutoConfiguration.java`](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), while the property definitions are 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).