Observability Guide

    Metrics, dashboards, alerts, distributed tracing, health checks, and troubleshooting.

    Table of Contents

    1. Quick Start (5 minutes)
    2. Exposed Metrics
    3. Grafana Dashboard
    4. Alerts
    5. Distributed Tracing (OpenTelemetry)
    6. Health Checks
    7. Agent Metrics
    8. Logs
    9. Troubleshooting

    1. Quick Start (5 minutes)

    Launch Prometheus + Grafana in 1 command

    bash
    1docker compose -f deploy/monitoring/docker-compose-monitoring.yml up -d

    Open:

    • 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)

    MetricTypeDescription
    streamflow_events_published_totalCounterTotal events published
    streamflow_events_consumed_totalCounterEvents consumed
    streamflow_batches_committed_totalCounterBatches written to disk
    streamflow_publish_latency_msHistogramPublish latency (p50/p95/p99)
    streamflow_fsync_latency_msHistogramDisk fsync latency
    streamflow_shard_countGaugeActive shard count
    streamflow_disk_usage_bytesGaugeDisk space used
    streamflow_disk_total_bytesGaugeTotal disk space

    Consumer

    MetricTypeDescription
    streamflow_consumer_lagGaugeConsumer lag per group/topic

    Raft Replication

    MetricTypeDescription
    streamflow_replication_lag_entriesGaugeReplication lag (entries)
    streamflow_backpressure_throttle_msGaugeActive back-pressure throttle
    streamflow_pool_overflow_totalCounterBatch pool overflows

    JVM

    MetricTypeDescription
    jvm_memory_used_bytes{area="heap"}GaugeHeap memory used
    jvm_memory_max_bytes{area="heap"}GaugeMax heap memory
    jvm_gc_pause_seconds_sumCounterTotal GC pause time

    Agents

    MetricTypeDescription
    agent.events.processedCounterEvents processed by agents
    agent.events.errorsCounterProcessing errors
    agent.llm.requestsCounterLLM calls (any provider)
    agent.llm.tokens.inputCounterTokens sent to LLM
    agent.llm.tokens.outputCounterTokens received from LLM
    agent.llm.latency.p99GaugeLLM latency p99
    agent.llm.circuit_breaker.openGaugeCircuit breaker open (0/1)
    agent.trigger.droppedCounterEvents dropped (queue full)

    MCP Tools

    MetricTypeDescription
    mcp_tool_calls_totalCounterMCP tool calls
    mcp_tool_call_failures_totalCounterTool failures
    mcp_tool_call_latency_msHistogramTool latency
    mcp_tool_denied_totalCounterTools denied (RBAC)
    mcp_tool_timeouts_totalCounterTool timeouts

    Agent Graphs

    MetricTypeDescription
    streamflow_graph_executions_started_totalCounterExecutions started
    streamflow_graph_executions_completed_totalCounterExecutions completed
    streamflow_graph_executions_failed_totalCounterExecutions failed
    streamflow_graph_execution_duration_msHistogramExecution duration
    streamflow_graph_node_duration_nsHistogramDuration 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

    PanelGreenOrangeRed
    Publish Latency p99< 10ms10–50ms> 50ms
    Fsync Latency p99< 10ms10–50ms> 50ms
    Backpressure0ms> 0ms> 1000ms
    Consumer Lag< 10K10K–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 grafana

    4. Alerts

    StreamFlow includes 16 alert rules in deploy/monitoring/streamflow-alerts.yml:

    Critical (immediate action)

    AlertConditionDuration
    StreamFlowThroughputZeroNo events published2 min
    StreamFlowFsyncLatencyHighfsync p99 > 100ms5 min
    StreamFlowLeaderLostRaft leadership lost30 sec
    StreamFlowHeapUsageHighHeap > 90%5 min

    Warnings (monitor)

    AlertConditionDuration
    StreamFlowThroughputDropIngestion < 100 evt/s5 min
    StreamFlowPublishLatencyHighp99 > 50ms5 min
    StreamFlowConsumerLagHighLag > 100K events5 min
    StreamFlowConsumerLagCriticalLag > 1M events5 min
    StreamFlowReplicationLagHighRaft lag > 10K entries2 min
    StreamFlowBackpressureActiveThrottle > 0ms2 min
    StreamFlowPoolOverflowBatch pool exhausted1 min
    StreamFlowDiskUsageHighDisk > 85%10 min
    StreamFlowSegmentCountHigh1000 open segments10 min
    StreamFlowGCPressureHighGC > 5% CPU5 min

    SLO

    AlertConditionDuration
    StreamFlowAvailabilitySLOBreachAvailability < 99.9%1 hour
    StreamFlowLatencySLOBreachp99 > 10ms1 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=5

    What 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µs

    Agent 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: 290ms

    View traces

    • Jaeger : http://localhost:16686
    • Grafana Tempo : integrated in the Grafana dashboard
    • Datadog : via Datadog Agent in OTLP

    6. Health Checks

    Endpoints

    EndpointPortUsageHealthy Response
    GET /health8080Liveness (process is running){"status":"UP"}
    GET /ready8080Readiness (ready to serve){"status":"UP"}
    gRPC Health9169Standard gRPC health protocolSERVING

    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: 5

    Docker 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: 30s

    7. 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

    LoggerLevelShows
    com.streamflow.core.storageDEBUGI/O details (pwrite, fsync)
    com.streamflow.connect.kafka.protocolDEBUGDetailed Kafka requests
    com.streamflow.core.replicationDEBUGRaft replication steps
    com.streamflow.agentDEBUGAgent reasoning trace
    com.streamflow.graphDEBUGGraph 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 up

    Symptom: 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 instances

    Symptom: 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

    PortProtocolSecurityUsage
    9092Kafka wireSASL + TLSProducers/Consumers
    8080HTTP RESTJWT/API Key + TLSAdmin API, Agents, Metrics
    9169gRPCmTLSInter-node Raft replication
    4317OTLP/gRPCTLSTrace export (Jaeger/Tempo)
    StreamFlow© 2026 StreamFlow, Built for real-time AI at scale.