Observability Integrations
Sagaweaw automatically integrates with the observability stack you already use — no extra configuration beyond the starter.
MDC — Contextualized Logging
When executing any step, Sagaweaw enriches the MDC (Mapped Diagnostic Context) with the following fields:
| MDC Field | Value |
|---|---|
sagaId | UUID of the saga in execution |
sagaName | Saga name (@Saga("pix-payment")) |
stepName | Current step name |
attempt | Current attempt number (starts at 1) |
Enriched log example
With Logback (Spring Boot default), add %X{sagaId} to your pattern to automatically include context:
<!-- logback-spring.xml -->
<pattern>%d{HH:mm:ss} [%X{sagaName}/%X{stepName}] [attempt=%X{attempt}] %-5level %msg%n</pattern>
Output:
10:04:59 [pix-payment/validate-dict] [attempt=1] INFO Validating DICT key abc-123
10:05:00 [pix-payment/block-balance] [attempt=1] INFO Blocking balance R$ 150.00
10:05:01 [pix-payment/transmit-to-bacen] [attempt=3] WARN Timeout — retrying in 4s
When the saga ends (successfully or with failure), MDC fields are removed automatically — no leakage between threads in the pool.
The sagaId field can be propagated via OpenTelemetry, Datadog trace ID, or Elastic APM to correlate the saga with distributed tracing spans.
Micrometer — Automatic Metrics
Sagaweaw publishes metrics via Micrometer automatically when micrometer-core is on the classpath (included by default in Spring Boot Actuator).
Available metrics
| Metric | Type | Tags | Description |
|---|---|---|---|
sagaweaw.saga.started | Counter | saga | Total sagas started |
sagaweaw.saga.completed | Counter | saga | Total sagas completed |
sagaweaw.saga.failed | Counter | saga | Total sagas that failed |
sagaweaw.saga.compensated | Counter | saga | Total sagas compensated |
sagaweaw.saga.duration | Timer | saga | Duration distribution per saga |
sagaweaw.step.duration | Timer | saga, step | Duration distribution per step |
sagaweaw.step.attempts | DistributionSummary | saga, step | Attempt distribution |
sagaweaw.deadletter.created | Counter | saga, step | Dead letters created |
No configuration needed
If spring-boot-starter-actuator is on the classpath, metrics are published automatically. No bean to declare, no @EnableSagaMetrics.
<!-- This alone is sufficient -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
Prometheus
With micrometer-registry-prometheus on the classpath, metrics are exposed at /actuator/prometheus in Prometheus format.
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
PromQL Examples
# Saga failure rate per minute, by saga name
rate(sagaweaw_saga_failed_total[1m])
# 95th percentile duration of the "pix-payment" saga
histogram_quantile(0.95,
rate(sagaweaw_saga_duration_seconds_bucket{saga="pix-payment"}[5m])
)
# 99th percentile duration of the "transmit-to-bacen" step
histogram_quantile(0.99,
rate(sagaweaw_step_duration_seconds_bucket{step="transmit-to-bacen"}[5m])
)
# Dead letters created in the last hour
increase(sagaweaw_deadletter_created_total[1h])
# Success rate (last 5 minutes)
rate(sagaweaw_saga_completed_total[5m]) /
(rate(sagaweaw_saga_completed_total[5m]) + rate(sagaweaw_saga_failed_total[5m]))
Grafana — Ready-to-Use Dashboard
Sagaweaw provides a pre-configured Grafana dashboard with 8 panels:
| Panel | Type | What it shows |
|---|---|---|
| Sagas Started | Stat | Total sagas started in the interval |
| Success Rate | Stat | Success rate (last 5m), with green/orange/red thresholds |
| Compensated | Stat | Sagas that triggered compensation |
| Outbox Pending | Stat | Messages waiting for Kafka publication |
| Saga Volume | Time series | Rate of started/completed/failed/compensated per minute |
| Saga Duration P50/P95 | Time series | Median latency and 95th percentile in ms |
| Step Bottleneck P95 | Bar gauge | Slowest steps (P95 duration) |
| Step Failure Rate | Bar gauge | Steps that fail most (failures/second) |
Importing
- In Grafana: Dashboards → Import → Upload JSON file
- Upload the
grafana-dashboard.jsonfile from the repository - Select your Prometheus datasource — done
The dashboard uses two template variables: $datasource (Prometheus) and $saga (filters by specific saga or shows all).
The Sagaweaw Dashboard (built-in, port 8484) is a debug lens — inspect individual sagas, see step timelines, and manage dead letters in real time.
Grafana is for production-scale observability: alerts, historical retention, and correlation with other system metrics.
Use both.
Webhook Alerts
Configure a webhook URL to receive alerts for dead letters, stuck sagas, and failure rate spikes. Works with Slack, Discord, Teams, PagerDuty, or any HTTP endpoint.
sagaweaw.alerts.webhook-url=https://hooks.slack.com/services/...
sagaweaw.alerts.enabled=true
sagaweaw.alerts.dead-letter-threshold=5 # alert after 5 dead letters in a window
sagaweaw.alerts.stuck-saga-threshold-minutes=15
Payload (JSON POST):
{ "event": "DEAD_LETTER_THRESHOLD", "saga": "pix-payment", "count": 5, "timestamp": "..." }
Events: DEAD_LETTER_THRESHOLD, STUCK_SAGA_DETECTED, FAILURE_RATE_SPIKE
No UI needed — just one property.
Configurable Retention
sagaweaw.data.retention-days=30
sagaweaw.data.failed-retention-days=90 # keep failed sagas longer for investigation
A nightly @Scheduled job archives completed sagas older than N days to sagas_archive. Dead letters stay until manually reprocessed. Default: never delete.
Full Configuration
Minimum configuration to enable everything:
# application.yml
sagaweaw:
enabled: true
observability:
enabled: true
token: ${SAGAWEAW_TOKEN}
management:
endpoints:
web:
exposure:
include: health, prometheus, metrics
metrics:
tags:
application: ${spring.application.name}
env: ${spring.profiles.active:local}
With this you automatically get:
- MDC enriched in all step logs
- Micrometer metrics for all saga events
/actuator/prometheusendpoint for Prometheus scraping- Built-in dashboard via
sagaweaw.observability.token
OpenTelemetry — Distributed Tracing
Sagaweaw emits OTel spans automatically via the Micrometer Observation API. Each step execution and compensation generates a span with the following attributes:
| Attribute | Value |
|---|---|
saga.step.name | Step name |
saga.step.type | COMPENSABLE, PIVOT, or RETRIABLE |
saga.id | Saga UUID |
saga.name | Saga name |
saga.step.attempt | Attempt number |
Spans work with any OTel backend: Jaeger, Zipkin, Grafana Tempo, Datadog, Elastic APM.
Enabling
Add the OTel bridge to the classpath — no other configuration is needed:
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-otel</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry.instrumentation</groupId>
<artifactId>opentelemetry-spring-boot-starter</artifactId>
</dependency>
To disable spans without removing dependencies:
sagaweaw.tracing.enabled=false
sagaweaw.step.invoke — executed on the happy path, via interceptor chain.
sagaweaw.step.compensate — executed during compensation in reverse order.
Kafka — Outbox Relay (Optional)
Sagaweaw works 100% without Kafka. The outbox pattern is built in — each completed step writes a message to the sagaweaw_outbox_messages table. Kafka is just the optional delivery mechanism.
To enable, add spring-kafka to the classpath and configure the broker:
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
spring.kafka.bootstrap-servers=localhost:9092
Sagaweaw automatically detects KafkaTemplate and starts the relay. Topics follow the pattern sagaweaw.<saga-name>.<step-name>. Each message includes the idempotency-key header for consumer deduplication.
To explicitly disable even with spring-kafka on the classpath:
sagaweaw.kafka.enabled=false
See the full Kafka integration guide →