How to Configure Zipkin or Langfuse Observability with the @Tracked Annotation in Embabel

Embabel-Agent's built-in observability module automatically creates OpenTelemetry spans for any method annotated with @Tracked, enabling seamless export to Zipkin or Langfuse through classpath detection and simple property configuration.

The embabel-agent repository provides a comprehensive observability solution that integrates OpenTelemetry tracing directly into your Spring Boot application through aspect-oriented programming. By using the @Tracked annotation, you can generate detailed spans for custom business operations without writing boilerplate instrumentation code, then route those spans to Zipkin or Langfuse for visualization and analysis.

Architecture of the @Tracked Observability Pipeline

Understanding how the pipeline works helps troubleshoot configuration issues and optimize span generation.

Aspect-Oriented Span Creation

When a method annotated with @Tracked is invoked, the TrackedAspect (a Spring AOP aspect defined in embabel-agent-observability/src/main/java/com/embabel/agent/observability/tracing/TrackedAspect.java) intercepts the call. It starts an OpenTelemetry span whose name, type, and description are extracted directly from the annotation attributes. The aspect wraps the method execution, ensuring the span ends when the method returns or throws an exception.

Configuration Property Resolution

The ObservabilityProperties class (embabel-agent-observability/src/main/java/com/embabel/agent/observability/ObservabilityProperties.java) supplies the global flags that control the pipeline. Specifically, observability.tracing.enabled activates the OpenTelemetry SDK, while observability.tracking.enabled enables the TrackedAspect processing. Both must be true for @Tracked annotations to generate spans.

SDK Auto-Configuration

The OpenTelemetrySdkAutoConfiguration class (embabel-agent-autoconfigure/embabel-agent-observability-autoconfigure/src/main/java/com/embabel/agent/autoconfigure/observability/OpenTelemetrySdkAutoConfiguration.java) builds the SdkTracerProvider. It performs classpath detection: if the Zipkin or Langfuse exporter dependency is present, it auto-configures the appropriate span exporter. If no exporter is found, it falls back to a no-op provider to prevent runtime errors.

Span Export to Collectors

Once the SDK creates a span, the configured exporter sends it to the remote collector. The TrackedAspectAutoConfiguration (embabel-agent-autoconfigure/embabel-agent-observability-autoconfigure/src/main/java/com/embabel/agent/autoconfigure/observability/TrackedAspectAutoConfiguration.java) ensures the aspect is only active when tracking is explicitly enabled via properties.

Step-by-Step Configuration for Zipkin or Langfuse

Configuring an exporter requires adding the correct dependency and setting the endpoint properties.

Add the Exporter Dependency

Add the OpenTelemetry exporter dependency matching your target backend to your Maven or Gradle build. Only the exporter block corresponding to the present dependency will be active at runtime.

<!-- Zipkin Exporter -->
<dependency>
    <groupId>io.opentelemetry</groupId>
    <artifactId>opentelemetry-exporter-zipkin</artifactId>
</dependency>

<!-- OR Langfuse Exporter -->
<dependency>
    <groupId>io.opentelemetry</groupId>
    <artifactId>opentelemetry-exporter-langfuse</artifactId>
</dependency>

Enable Tracing in application.yml

Configure the global flags and exporter-specific settings in application.yml or application.properties. The embabel.observability namespace controls the Embabel-specific features, while the otel.exporter namespace configures the OpenTelemetry SDK.

embabel:
  observability:
    tracing:
      enabled: true          # Activates OpenTelemetry SDK

      tracked:
        enabled: true        # Enables @Tracked aspect processing

# Zipkin Configuration

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

# OR Langfuse Configuration

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

Annotating Methods with @Tracked

The @Tracked annotation (embabel-agent-observability/src/main/java/com/embabel/agent/observability/annotation/Tracked.java) supports several attributes to enrich your telemetry data.

Annotation Attributes Reference

Attribute Type Description
value (alias name) String Human-readable operation name; defaults to the method name if omitted.
type TrackType Enum classification (e.g., PROCESSING, IO) stored as the embabel.tracked.type attribute.
description String Free-form text stored as the embabel.tracked.description attribute.
event String Optional event name emitted when the method returns successfully.

The TrackType enum (embabel-agent-observability/src/main/java/com/embabel/agent/observability/annotation/TrackType.java) provides standardized categorization for your operations.

Practical Examples

Apply the annotation to service methods to capture business logic execution. The generated spans automatically include the annotation attributes as OpenTelemetry span attributes.

import com.embabel.agent.observability.annotation.Tracked
import com.embabel.agent.observability.annotation.TrackType

class OrderService {

    @Tracked("createOrder")
    fun create(order: Order) {
        // Business logic
    }

    @Tracked(
        value = "cancelOrder",
        type = TrackType.PROCESSING,
        description = "Cancels an existing order and triggers refund"
    )
    fun cancel(orderId: String) {
        // Cancellation logic
    }

    @Tracked(
        value = "fetchInventory",
        type = TrackType.IO,
        event = "inventory.loaded"
    )
    fun loadInventory(): Inventory {
        // External API call
        return inventoryClient.fetch()
    }
}

When these methods execute, the spans appear in your Zipkin or Langfuse dashboard with attributes like embabel.tracked.name, embabel.tracked.type, and embabel.tracked.description.

Summary

  • The @Tracked annotation triggers automatic span creation via TrackedAspect for any annotated method.
  • Enable observability by setting embabel.observability.tracing.enabled and observability.tracking.enabled to true.
  • Add the opentelemetry-exporter-zipkin or opentelemetry-exporter-langfuse dependency to route spans to your chosen backend.
  • Configure exporter endpoints under the otel.exporter namespace in your application.yml.
  • Use annotation attributes (type, description, event) to add semantic context to your traces.

Frequently Asked Questions

What is the difference between observability.tracing.enabled and observability.tracking.enabled?

observability.tracing.enabled activates the OpenTelemetry SDK and global span recording, while observability.tracking.enabled specifically enables the Spring AOP aspect that processes @Tracked annotations. You must enable both properties for the annotation to generate spans.

Can I export spans to both Zipkin and Langfuse simultaneously?

Yes. The OpenTelemetrySdkAutoConfiguration supports multiple exporters on the classpath. If you include both the Zipkin and Langfuse exporter dependencies and configure both endpoints in application.yml, the SDK will export spans to both collectors concurrently.

How do I access the custom attributes added by @Tracked in the observability UI?

The attributes appear as standard OpenTelemetry span attributes. In Zipkin, look for tags prefixed with embabel.tracked. (e.g., embabel.tracked.name, embabel.tracked.type). In Langfuse, these attributes map to the trace or span metadata fields depending on the exporter mapping configuration.

Does @Tracked work with private methods or only public methods?

The TrackedAspect uses Spring AOP, which typically proxies public methods of Spring beans. Private methods, static methods, or internal calls within the same class bypass the proxy and will not trigger span creation. Annotate public service methods to ensure reliable instrumentation.

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 →