Skip to main content

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 FieldValue
sagaIdUUID of the saga in execution
sagaNameSaga name (@Saga("pix-payment"))
stepNameCurrent step name
attemptCurrent 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.

Correlation with other tools

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

MetricTypeTagsDescription
sagaweaw.saga.startedCountersagaTotal sagas started
sagaweaw.saga.completedCountersagaTotal sagas completed
sagaweaw.saga.failedCountersagaTotal sagas that failed
sagaweaw.saga.compensatedCountersagaTotal sagas compensated
sagaweaw.saga.durationTimersagaDuration distribution per saga
sagaweaw.step.durationTimersaga, stepDuration distribution per step
sagaweaw.step.attemptsDistributionSummarysaga, stepAttempt distribution
sagaweaw.deadletter.createdCountersaga, stepDead 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:

PanelTypeWhat it shows
Sagas StartedStatTotal sagas started in the interval
Success RateStatSuccess rate (last 5m), with green/orange/red thresholds
CompensatedStatSagas that triggered compensation
Outbox PendingStatMessages waiting for Kafka publication
Saga VolumeTime seriesRate of started/completed/failed/compensated per minute
Saga Duration P50/P95Time seriesMedian latency and 95th percentile in ms
Step Bottleneck P95Bar gaugeSlowest steps (P95 duration)
Step Failure RateBar gaugeSteps that fail most (failures/second)

Importing

  1. In Grafana: Dashboards → Import → Upload JSON file
  2. Upload the grafana-dashboard.json file from the repository
  3. Select your Prometheus datasource — done

The dashboard uses two template variables: $datasource (Prometheus) and $saga (filters by specific saga or shows all).

Built-in dashboard vs. Grafana

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/prometheus endpoint 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:

AttributeValue
saga.step.nameStep name
saga.step.typeCOMPENSABLE, PIVOT, or RETRIABLE
saga.idSaga UUID
saga.nameSaga name
saga.step.attemptAttempt 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
Two types of span per saga

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 →


Next Steps