Observability Guide
Metrics, dashboards, alerts, distributed tracing, health checks, and troubleshooting.
Table of Contents
- Quick Start (5 minutes)
- Exposed Metrics
- Grafana Dashboard
- Alerts
- Distributed Tracing (OpenTelemetry)
- Health Checks
- Agent Metrics
- Logs
- Troubleshooting
1. Quick Start (5 minutes)
Launch Prometheus + Grafana in 1 command
bash
1docker compose -f deploy/monitoring/docker-compose-monitoring.yml up -dOpen:
- Grafana :
http://localhost:3000(admin / streamflow) - Prometheus :
http://localhost:9090
The StreamFlow dashboard is automatically provisioned.
Verify that metrics are flowing
bash
1# Raw metrics from StreamFlow
2curl http://localhost:8080/metrics
3
4# Verify in Prometheus
5curl 'http://localhost:9090/api/v1/query?query=streamflow_events_published_total'2. Exposed Metrics
StreamFlow exposes Prometheus text format metrics on GET /metrics (port 8080).
Engine (throughput, latency, disk)
| Metric | Type | Description |
|---|---|---|
| streamflow_events_published_total | Counter | Total events published |
| streamflow_events_consumed_total | Counter | Events consumed |
| streamflow_batches_committed_total | Counter | Batches written to disk |
| streamflow_publish_latency_ms | Histogram | Publish latency (p50/p95/p99) |
| streamflow_fsync_latency_ms | Histogram | Disk fsync latency |
| streamflow_shard_count | Gauge | Active shard count |
| streamflow_disk_usage_bytes | Gauge | Disk space used |
| streamflow_disk_total_bytes | Gauge | Total disk space |
Consumer
| Metric | Type | Description |
|---|---|---|
| streamflow_consumer_lag | Gauge | Consumer lag per group/topic |
Raft Replication
| Metric | Type | Description |
|---|---|---|
| streamflow_replication_lag_entries | Gauge | Replication lag (entries) |
| streamflow_backpressure_throttle_ms | Gauge | Active back-pressure throttle |
| streamflow_pool_overflow_total | Counter | Batch pool overflows |
JVM
| Metric | Type | Description |
|---|---|---|
| jvm_memory_used_bytes{area="heap"} | Gauge | Heap memory used |
| jvm_memory_max_bytes{area="heap"} | Gauge | Max heap memory |
| jvm_gc_pause_seconds_sum | Counter | Total GC pause time |
Agents
| Metric | Type | Description |
|---|---|---|
| agent.events.processed | Counter | Events processed by agents |
| agent.events.errors | Counter | Processing errors |
| agent.llm.requests | Counter | LLM calls (any provider) |
| agent.llm.tokens.input | Counter | Tokens sent to LLM |
| agent.llm.tokens.output | Counter | Tokens received from LLM |
| agent.llm.latency.p99 | Gauge | LLM latency p99 |
| agent.llm.circuit_breaker.open | Gauge | Circuit breaker open (0/1) |
| agent.trigger.dropped | Counter | Events dropped (queue full) |
MCP Tools
| Metric | Type | Description |
|---|---|---|
| mcp_tool_calls_total | Counter | MCP tool calls |
| mcp_tool_call_failures_total | Counter | Tool failures |
| mcp_tool_call_latency_ms | Histogram | Tool latency |
| mcp_tool_denied_total | Counter | Tools denied (RBAC) |
| mcp_tool_timeouts_total | Counter | Tool timeouts |
Agent Graphs
| Metric | Type | Description |
|---|---|---|
| streamflow_graph_executions_started_total | Counter | Executions started |
| streamflow_graph_executions_completed_total | Counter | Executions completed |
| streamflow_graph_executions_failed_total | Counter | Executions failed |
| streamflow_graph_execution_duration_ms | Histogram | Execution duration |
| streamflow_graph_node_duration_ns | Histogram | Duration per node |
3. Grafana Dashboard
The dashboard is automatically provisioned with 9 panels:
┌─────────────────────────────┬──────────────────────────────┐ │ Ingestion Throughput │ Publish Latency (p50/95/99) │ │ ████████████ 350K evt/s │ ▁▂▃▄ p99=5ms │ ├─────────────────────────────┼──────────────────────────────┤ │ Consumer Lag by Group │ Raft Replication │ │ my-app: 1,200 events │ lag: 42 entries │ ├─────────────────────────────┼──────────────────────────────┤ │ Backpressure (ms) │ Active Shards │ │ ▁▁▁▁▁ 0ms (healthy) │ 16 shards │ ├─────────────────────────────┼──────────────────────────────┤ │ Fsync Latency p99 │ Batches Committed/s │ │ ▁▂▁▁ 2ms │ ████ 1,200 batches/s │ ├─────────────────────────────┼──────────────────────────────┤ │ Pool Overflow Count │ │ │ 0 (healthy) │ │ └─────────────────────────────┴──────────────────────────────┘
Visual Thresholds
| Panel | Green | Orange | Red |
|---|---|---|---|
| Publish Latency p99 | < 10ms | 10–50ms | > 50ms |
| Fsync Latency p99 | < 10ms | 10–50ms | > 50ms |
| Backpressure | 0ms | > 0ms | > 1000ms |
| Consumer Lag | < 10K | 10K–100K | > 100K |
Add a custom dashboard
bash
1# Copy dashboard JSON into Grafana
2cp my-dashboard.json deploy/monitoring/
3# Add to provider, then restart Grafana
4docker compose -f deploy/monitoring/docker-compose-monitoring.yml restart grafana4. Alerts
StreamFlow includes 16 alert rules in deploy/monitoring/streamflow-alerts.yml:
Critical (immediate action)
| Alert | Condition | Duration |
|---|---|---|
| StreamFlowThroughputZero | No events published | 2 min |
| StreamFlowFsyncLatencyHigh | fsync p99 > 100ms | 5 min |
| StreamFlowLeaderLost | Raft leadership lost | 30 sec |
| StreamFlowHeapUsageHigh | Heap > 90% | 5 min |
Warnings (monitor)
| Alert | Condition | Duration |
|---|---|---|
| StreamFlowThroughputDrop | Ingestion < 100 evt/s | 5 min |
| StreamFlowPublishLatencyHigh | p99 > 50ms | 5 min |
| StreamFlowConsumerLagHigh | Lag > 100K events | 5 min |
| StreamFlowConsumerLagCritical | Lag > 1M events | 5 min |
| StreamFlowReplicationLagHigh | Raft lag > 10K entries | 2 min |
| StreamFlowBackpressureActive | Throttle > 0ms | 2 min |
| StreamFlowPoolOverflow | Batch pool exhausted | 1 min |
| StreamFlowDiskUsageHigh | Disk > 85% | 10 min |
| StreamFlowSegmentCountHigh | 1000 open segments | 10 min |
| StreamFlowGCPressureHigh | GC > 5% CPU | 5 min |
SLO
| Alert | Condition | Duration |
|---|---|---|
| StreamFlowAvailabilitySLOBreach | Availability < 99.9% | 1 hour |
| StreamFlowLatencySLOBreach | p99 > 10ms | 1 hour |
Integrate with Slack / PagerDuty
yaml
1# alertmanager.yml
2route:
3 receiver: 'slack-critical'
4 routes:
5 - match:
6 severity: critical
7 receiver: 'pagerduty'
8
9receivers:
10 - name: 'slack-critical'
11 slack_configs:
12 - api_url: 'https://hooks.slack.com/services/xxx/yyy/zzz'
13 channel: '#streamflow-alerts'
14 title: '{{ .GroupLabels.alertname }}'
15 text: '{{ .Annotations.description }}'
16
17 - name: 'pagerduty'
18 pagerduty_configs:
19 - service_key: 'xxx-yyy-zzz'5. Distributed Tracing (OpenTelemetry)
Configure OpenTelemetry (OTLP)
Properties
1# streamflow.properties
2otel.enabled=true
3otel.endpoint=http://jaeger:4317 # or Tempo, Datadog Agent
4otel.service.name=streamflow-node-1
5otel.metrics.interval.seconds=30
6otel.traces.delay.seconds=5What is traced
Trace: publish("orders", "order-123", payload)
├── Span: KafkaProtocolServer.handleConnection
│ ├── Span: KafkaRequestHandler.handleProduceFastPath
│ │ ├── Span: parseRecordSetIntoFlatBatch (69µs)
│ │ ├── Span: ShardedEventEngine.ingestFlatBatch (250µs)
│ │ │ ├── Span: DirectIOSegment.appendFlatBatch (160µs)
│ │ │ └── Span: writeOrDefer (0.1µs, deferred)
│ │ └── Span: PartitionFetchBuffer.appendBatch (56µs)
│ └── Span: TCP write response (7µs)
└── Total: 390µsAgent Graph Traces
Trace: GraphRunner.execute("order-processing")
├── Span: Node "validate", AgentNode (120ms)
│ └── Span: LLM call, cloud provider (100ms)
├── Span: Node "score-risk", AgentNode (80ms)
│ └── Span: LLM call, cloud provider (60ms)
├── Span: Node "risk-route", ConditionalRouter (0.1ms)
└── Span: Node "auto-approve", AgentNode (90ms)
├── Span: LLM call, cloud provider (70ms)
└── Span: Tool call "database-query" (15ms)
Total: 290msView traces
- Jaeger :
http://localhost:16686 - Grafana Tempo : integrated in the Grafana dashboard
- Datadog : via Datadog Agent in OTLP
6. Health Checks
Endpoints
| Endpoint | Port | Usage | Healthy Response |
|---|---|---|---|
| GET /health | 8080 | Liveness (process is running) | {"status":"UP"} |
| GET /ready | 8080 | Readiness (ready to serve) | {"status":"UP"} |
| gRPC Health | 9169 | Standard gRPC health protocol | SERVING |
Kubernetes Probes
yaml
1# Already configured in deploy/kubernetes/streamflow-statefulset.yaml
2livenessProbe:
3 httpGet:
4 path: /health
5 port: 8080
6 initialDelaySeconds: 30
7 periodSeconds: 10
8
9readinessProbe:
10 httpGet:
11 path: /ready
12 port: 8080
13 initialDelaySeconds: 15
14 periodSeconds: 5Docker Health Check
yaml
1# docker-compose.yml
2healthcheck:
3 test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
4 interval: 10s
5 timeout: 5s
6 retries: 12
7 start_period: 30s7. Agent Metrics
Query via REST API
bash
1# List agents with live metrics
2curl http://localhost:8080/api/v1/agents | jq '.[].metrics'JSON
1{
2 "name": "fraud-detector",
3 "status": "RUNNING",
4 "instances": 5,
5 "metrics": {
6 "eventsPerSec": 1250.0,
7 "totalDecisions": 847293,
8 "errorRate": 0.02,
9 "avgLatencyMs": 45.3,
10 "p99LatencyMs": 120.0,
11 "queueDepth": 42,
12 "tokensUsedToday": 152847,
13 "costToday": 2.13
14 }
15}Per-agent metrics in Grafana
bash
1# Throughput per agent
2rate(agent_events_processed{agent="fraud-detector"}[5m])
3
4# LLM latency p99
5agent_llm_latency_p99{agent="fraud-detector"}
6
7# Circuit breaker open?
8agent_llm_circuit_breaker_open{agent="fraud-detector"} == 1
9
10# Tokens consumed per day
11increase(agent_llm_tokens_input[24h]) + increase(agent_llm_tokens_output[24h])8. Logs
Configuration
XML
1<!-- logback.xml (streamflow-app/src/main/resources/) -->
2<configuration>
3 <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
4 <encoder>
5 <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
6 </encoder>
7 </appender>
8
9 <root level="WARN"/>
10 <logger name="com.streamflow" level="INFO"/>
11 <!-- Debug for a specific module -->
12 <logger name="com.streamflow.connect.kafka" level="DEBUG"/>
13</configuration>Structured Logs (JSON)
To send logs to Elasticsearch/Loki, configure JSON output:
XML
1<appender name="JSON" class="ch.qos.logback.core.ConsoleAppender">
2 <encoder class="net.logstash.logback.encoder.LogstashEncoder"/>
3</appender>Useful Log Levels
| Logger | Level | Shows |
|---|---|---|
| com.streamflow.core.storage | DEBUG | I/O details (pwrite, fsync) |
| com.streamflow.connect.kafka.protocol | DEBUG | Detailed Kafka requests |
| com.streamflow.core.replication | DEBUG | Raft replication steps |
| com.streamflow.agent | DEBUG | Agent reasoning trace |
| com.streamflow.graph | DEBUG | Graph execution trace |
9. Troubleshooting
Symptom: Throughput drop
bash
1# 1. Check disk latency
2curl -s http://localhost:8080/metrics | grep fsync_latency
3# If p99 > 50ms → disk issue (saturated pd-ssd, IOPS throttled)
4
5# 2. Check GC
6curl -s http://localhost:8080/metrics | grep gc_pause
7# If GC > 5% CPU → increase -Xmx or reduce batch.size
8
9# 3. Check backpressure
10curl -s http://localhost:8080/metrics | grep backpressure
11# If throttle > 0 → consumers can't keep upSymptom: Growing consumer lag
bash
1# 1. Check lag
2curl -s http://localhost:8080/metrics | grep consumer_lag
3# lag > 100K → scale consumers
4
5# 2. Check replication
6curl -s http://localhost:8080/metrics | grep replication_lag
7# If raft lag > 0 → a follower is slow
8
9# 3. Check agents
10curl http://localhost:8080/api/v1/agents | jq '.[].metrics.queueDepth'
11# If queue > 5000 → scale agent instancesSymptom: Agent unresponsive
bash
1# 1. Check circuit breaker
2curl -s http://localhost:8080/metrics | grep circuit_breaker
3# If open=1 → LLM is down or rate-limited
4
5# 2. Check errors
6curl -s http://localhost:8080/metrics | grep agent_events_errors
7# Error rate > 5% → check agent logs
8
9# 3. View recent traces
10curl http://localhost:8080/api/v1/agents/{id} | jq '.recentTraces[-3:]'Symptom: Isolated Raft node
bash
1# 1. Check leadership
2curl -s http://localhost:8080/metrics | grep leader
3# If leader_lost → check network connectivity between nodes
4
5# 2. Check gRPC ports
6nc -z streamflow-2 9169 && echo "OK" || echo "BLOCKED"
7
8# 3. Verify TLS certificates
9openssl s_client -connect streamflow-2:9169 < /dev/null 2>&1 | grep "Verify return code"Monitoring Architecture
┌──────────────┐ /metrics ┌────────────┐ query ┌─────────┐
│ StreamFlow │────────────────→│ Prometheus │←───────────│ Grafana │
│ Node 1-3 │ scrape 15s │ (retention │ │ :3000 │
│ :8080 │ │ 30 days) │ │ │
└──────┬───────┘ └──────┬──────┘ └─────────┘
│ │
│ OTLP/gRPC │ alerts
↓ ↓
┌──────────────┐ ┌──────────────┐
│ Jaeger/Tempo │ │ AlertManager │
│ (traces) │ │ → Slack │
│ :4317 │ │ → PagerDuty │
└──────────────┘ └──────────────┘Ports & Protocols Summary
| Port | Protocol | Security | Usage |
|---|---|---|---|
| 9092 | Kafka wire | SASL + TLS | Producers/Consumers |
| 8080 | HTTP REST | JWT/API Key + TLS | Admin API, Agents, Metrics |
| 9169 | gRPC | mTLS | Inter-node Raft replication |
| 4317 | OTLP/gRPC | TLS | Trace export (Jaeger/Tempo) |