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
@Trackedannotation triggers automatic span creation viaTrackedAspectfor any annotated method. - Enable observability by setting
embabel.observability.tracing.enabledandobservability.tracking.enabledtotrue. - Add the
opentelemetry-exporter-zipkinoropentelemetry-exporter-langfusedependency to route spans to your chosen backend. - Configure exporter endpoints under the
otel.exporternamespace in yourapplication.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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →