A military data pipeline that delivers the wrong picture is worse than no pipeline at all. Track data arrives stale, sensor feeds go silent, message queues back up — and without systematic observability, none of these faults surface until an operator notices something is wrong on the COP. By then the window for effective action may have closed.

Instrumenting these systems for observability is more constrained than in commercial engineering. Classification boundaries prohibit sending telemetry to cloud platforms. Network bandwidth is often scarce. The tooling available on a classified network reflects what was approved and transferred months or years ago, not what is current in the open-source ecosystem. And when something breaks, the engineer who built the system may not be in the room. This article covers how to close those gaps systematically — with an air-gapped observability stack, well-chosen metrics, correlation-ID-based tracing, and alerting that reaches the right people in the right time.

The principles apply to any pipeline that moves sensor data into a common operational picture: systems built around military data fusion workflows, streaming track processing stateful architectures, or message queue defense data pipeline designs.

Why observability is harder in defense data pipelines

The core constraint is data sovereignty. Commercial observability stacks — Datadog, New Relic, Honeycomb, Google Cloud Monitoring — are SaaS platforms that receive telemetry from your systems over the internet. None of these are available in a classified enclave. Every component of the observability stack must be deployed, operated, and maintained entirely on-premises, within the classification boundary.

This creates a second-order problem: tooling currency. The Prometheus version approved for your classified network may be 18 months behind the current release. Grafana plugins that make dashboarding easier may not have passed the software approval process. Teams must either work with what is available or invest in the approval process — which takes time and organizational effort that operational programmes rarely budget for.

Bandwidth is a third constraint that has no commercial analogue. On a tactical network operating over degraded radio links, the overhead of emitting detailed telemetry from every component can noticeably consume capacity that is needed for actual data. Observability instrumentation must be designed to be frugal: scrape intervals of 15–30 seconds rather than 5, structured logs that are compact and machine-parseable rather than verbose, and sampling strategies for traces that capture the diagnostically relevant subset without logging every message.

Operational tempo adds urgency. A commercial engineering team investigating a latency regression has the luxury of time — they can query historical data, run experiments, and iterate over hours or days. An operations center responding to a degraded pipeline during a live event may have minutes. Dashboard designs must surface the most actionable signal immediately, without requiring the responder to understand the internal architecture of the pipeline. Alert annotations must link directly to runbooks written for operators, not for engineers.

The three pillars in classified environments: metrics, traces, logs

The three-pillar model — metrics, traces, logs — organizes observability tooling in a way that avoids the trap of collecting everything and understanding nothing. Each pillar answers a different question: metrics reveal that something changed, traces identify where, logs explain why.

Metrics are numeric time-series measurements aggregated across all instances of a component. Message throughput, end-to-end latency percentiles, error rates, and queue depth are all metrics. In a classified environment, Prometheus is the natural choice: it is a single binary with no external dependencies, runs on-premises, and has broad compatibility with the open-source ecosystem. Alertmanager pairs with Prometheus to evaluate alert rules and route notifications.

Traces are correlated records of a single message flowing through multiple components, annotated with per-hop timing and metadata. A trace answers: where did this specific message spend its time, and at which hop did it fail? Local Jaeger or Zipkin deployments fulfill this role without external connectivity. Both ship as self-contained services and require only a local Elasticsearch or Cassandra instance for trace storage.

Logs are per-component event records. In a classified pipeline, structured logs in JSON with a consistent schema — including timestamp, component name, message ID, trace_id, and event type — make it possible to correlate log entries across services using a local log aggregation tool such as Loki (Grafana's log store, designed to pair with Prometheus and run on-premises).

The toolchain that emerges from these constraints is: Prometheus + Alertmanager for metrics and alerting, Jaeger for traces, Loki for logs, and Grafana as the unified query and visualization front-end. All four can be deployed on a single VM for small installations or distributed across a cluster for high-availability. None require internet access during operation.

Latency metrics for sensor-to-picture pipelines

End-to-end latency — the time from a sensor observation to a track update appearing on the COP — is the metric that most directly reflects operational value. Everything else is a leading indicator for this number. Instrument it at both the pipeline level and at each stage, so you can isolate which hop is contributing to latency violations.

Use histogram metrics, not gauges, for latency. A histogram captures the full distribution — P50, P95, P99 — which a gauge cannot. A pipeline that processes 95% of messages under 2 seconds but has a P99 of 30 seconds is operationally problematic even if the average looks acceptable. Prometheus histograms use configurable bucket boundaries; set them to bracket your SLO thresholds: for a 5-second end-to-end SLO, include buckets at 1s, 2s, 3s, 5s, 8s, 15s.

Appropriate SLO targets by track type:

Track type P95 SLO target Alert threshold (warn) Alert threshold (critical)
Air track (air defense) < 1 s 0.8 s 2 s
Ground track (tactical COP) < 5 s 4 s 10 s
Maritime track < 15 s 12 s 30 s
HUMINT report < 60 s 45 s 120 s

Degrade-headroom planning is essential. If a tactical radio link normally contributes 300 ms to end-to-end latency but degrades to 2 seconds under jamming conditions, the non-radio pipeline stages must complete in under 1 second even at P99 to stay within the 3-second degraded-mode budget. Measure each stage independently under load so you know how much headroom you have and where optimizations would be most effective.

Distributed tracing with correlation IDs

Latency metrics tell you that a pipeline stage is slow. They do not tell you which specific message was slow, what path it took, or what state the system was in when it passed through. Distributed tracing fills this gap by attaching a unique identifier to each message at ingestion and propagating it through every downstream component, so the complete processing history of any individual message can be reconstructed.

The implementation pattern is consistent regardless of the underlying message format. At the ingestion adapter — where a CoT feed, an NFFI stream, or a MIP transaction enters the pipeline — generate a UUID (version 4) and write it into a header or envelope field before any processing occurs. For CoT messages, a detail sub-element is the natural location:

<detail>
  <_pipeline_trace_id>a3f7c2e1-8b4d-4e9a-b1c6-2d5f0e3a7b8c</_pipeline_trace_id>
  <_pipeline_ingest_ts>1750809600450</_pipeline_ingest_ts>
</detail>

For NFFI and MIP messages that cross a format-translation boundary, the correlation ID must survive the translation. Map it into the equivalent free-text or extension field in the target format, and document this mapping explicitly in the pipeline's data dictionary. Without this documentation, the next engineer to touch the translation layer will not know the field is load-bearing.

Every pipeline component should emit a span record when it processes a message: the component name, the trace ID, the start and end timestamps, and any error details. These spans are collected by the local Jaeger agent and assembled into a complete trace. Querying by trace ID then reconstructs the full per-hop latency breakdown for any specific message.

Sampling strategy matters at scale. A pipeline processing 5,000 track updates per minute cannot store full traces for every message. The recommended approach is: 100% sampling for any message that results in an error or triggers a fallback path; 100% sampling for any message whose observed end-to-end latency exceeds the critical alert threshold; and 1–5% head-based sampling for the healthy baseline. This concentrates trace storage on the events that actually require investigation while preserving statistical latency signal for routine monitoring.

Track loss detection and gap alerts

A sensor going silent is one of the most operationally significant failure modes and one of the easiest to miss without specific instrumentation. Without track loss detection, a sensor that stops transmitting produces no errors, no exceptions, and no obvious signal in throughput metrics — the throughput just drops to zero, which looks exactly like a quiet period. An operator consulting the COP sees the last known position of every track from that sensor, with no indication that those positions may be minutes or hours stale.

The correct instrument is a timestamp gauge: for each source, record the Unix timestamp of the most recently received message as a metric. A Prometheus alerting rule then computes the elapsed time since the last message and fires an alert when it exceeds the source-specific silence threshold:

groups:
  - name: track_loss
    rules:
      - alert: SensorFeedSilent
        expr: |
          (time() - pipeline_ingest_last_message_timestamp_seconds)
            > on(source_id) group_left
          pipeline_source_silence_threshold_seconds
        for: 0m
        labels:
          severity: critical
        annotations:
          summary: "Sensor {{ $labels.source_id }} silent for {{ $value | humanizeDuration }}"
          runbook_url: "https://ops.internal/runbooks/sensor-silence"

The silence threshold must be parameterized per source. A radar updating at 2 Hz should alert after 10 seconds of silence; a HUMINT relay updating hourly should alert after 90 minutes. Store these thresholds as a Prometheus recording rule or as pipeline configuration that is exported as a metric, so the alert rule can read them dynamically rather than being hardcoded per source.

Expected-versus-received rate analysis extends this to rate-based anomalies. Even if a source is not fully silent, a radar normally sending 120 tracks per minute sending only 30 may indicate a hardware fault, a denial-of-service condition, or a reconfiguration. Alert on: observed rate < 50% of the rolling 15-minute baseline for that source for more than 2 minutes. This catches partial degradation that the silence threshold would miss entirely.

When a sensor goes silent, the appropriate system response depends on the track type. For tracked objects where dead-reckoning is applicable — vehicles and vessels with known kinematics — trigger a dead-reckoning fallback that extrapolates position from the last known state vector. Mark all dead-reckoned tracks with a visual indicator on the COP (typically a dashed symbol outline) and a freshness timestamp so operators know they are seeing a model estimate, not a live observation. Stop the dead-reckoning extrapolation after a configured maximum time — typically 30–60 seconds for fast-moving platforms — at which point the track should be marked as lost.

Alerting architecture for classified environments

Alertmanager's routing model is well-suited to classified operations centers. Alerts arrive from Prometheus via a local HTTP call, are grouped and deduplicated by Alertmanager, and are routed to receivers defined in the configuration. The key design decisions are routing hierarchy, receiver implementation, and escalation timing.

A practical routing hierarchy for a defense pipeline has three levels:

  • Informational — metric drifting toward a threshold, queue depth elevated, throughput below baseline. Route to a local Mattermost channel or syslog. No immediate human action required.
  • Warning — P95 latency above 80% of SLO, a non-critical sensor silent for over threshold. Route to the on-call operator's terminal via a local webhook that posts to the operations console. Acknowledgement required within 5 minutes.
  • Critical — end-to-end latency above SLO, a critical sensor silent, pipeline component down. Route simultaneously to the operations console, the on-call operator's terminal, and — for systems that support it — an audible annunciator in the operations center. Escalate to tier-2 if not acknowledged within 3 minutes.

For the receiver implementation without SaaS platforms: a small Python or Go webhook server that Alertmanager calls is sufficient. It receives the JSON alert payload, formats a human-readable notification, and delivers it via whatever local channel is available — a Unix socket, a syslog facility, a local email relay, or a POST to an on-premises Mattermost or Rocket.Chat instance. This webhook should itself be monitored: Alertmanager has a Watchdog alert that fires continuously when healthy; configure a receiver for this alert and ensure your operations center knows to escalate if the Watchdog goes silent.

Alert fatigue is a serious operational risk. An operations center that receives 50 alerts per shift learns to ignore them. Keep alert rules tightly targeted: alert on the SLO violation, not on every underlying metric. Group related alerts — multiple sensors silent simultaneously — into a single notification so the operator receives one actionable item rather than five identical ones. Set appropriate group_wait and repeat_interval values in Alertmanager so a persistent condition generates one alert, not one per minute.

Operational dashboards for data pipeline health

Grafana deployed against a local Prometheus datasource provides the visualization layer. Dashboard design for an operations center differs from developer-facing dashboards in two important ways: the audience may not know the system internals, and the dashboard will be consulted under time pressure. Every panel should answer a specific operational question, and the answer should be visible without scrolling, zooming, or hovering.

A practical pipeline health overview contains five panels:

  1. End-to-end latency stat panel — current P95 latency in seconds, color-coded by threshold: green below SLO, amber at 80–100% of SLO, red above SLO. Answers: is the pipeline meeting its commitment right now?
  2. Per-source throughput graph — a multi-line time-series showing messages received per minute from each source over the last 60 minutes. Gaps appear as flat lines at zero. Answers: which sources are delivering data?
  3. Latency distribution heatmap — end-to-end latency as a heatmap over time, with one row per latency bucket. Answers: are latency spikes isolated events or a pattern?
  4. Queue depth gauges — one gauge per message queue, showing current depth with a threshold line at the backpressure warning level. Answers: is any stage falling behind its input rate?
  5. Active alerts table — current firing alerts with severity, age, and a direct link to the runbook. Answers: what action is required right now?

Dashboard annotations are a powerful but underused feature. Every time an alert fires and resolves, Alertmanager can post an annotation to Grafana that marks the event on the time axis of every panel. Over weeks, this builds a visual history: latency spikes, sensor outages, and queue buildups appear alongside their alert events, making it easy to recognize recurring patterns and correlate pipeline behavior with external events such as radio link degradation or sensor maintenance windows.

Runbooks deserve the same engineering discipline as code. Each alert should have a runbook that covers: what the alert means in plain language, the immediate steps to assess severity, the most common root causes with diagnostic queries, and the escalation path if the operator cannot resolve the issue within 15 minutes. Store runbooks on a local intranet page that is accessible from the operations network without internet access, and link each Alertmanager alert directly to its runbook via the annotations.runbook_url field. A Grafana panel with an active-alerts table that renders those URLs as clickable links turns the dashboard into the entry point for incident response rather than just a status board.

Finally, validate the observability stack regularly. Run a quarterly fault-injection exercise: stop a simulated sensor feed and confirm the track loss alert fires within the expected window. Inject artificial latency and confirm the latency metric and the trace both reflect it. The observability system that has never been tested under failure conditions will fail to alert when it is most needed. Treat a missed alert during a fault-injection exercise the same way you would treat a missed alert during a live event: as a P1 bug that blocks the next deployment.