Skip to content

Prometheus and Grafana

Medusa can expose internal metrics (battery states, track counts, engagement rates, Pk values, timing data) in Prometheus format. When enabled, it writes a metrics file to your DCS Saved Games directory. A Docker stack reads that file into Prometheus, which feeds Grafana dashboards and the tactical display.

  • Docker Desktop (or Docker Engine + Docker Compose)
  • DCS World desanitization. Medusa needs io and lfs to write the metrics file. In your DCS installation directory, edit Scripts/MissionScripting.lua and uncomment or add the io and lfs lines so they are not set to nil.

Add PrometheusEnabled = true to your config before loading Medusa:

MEDUSA_CONFIG = {
PrometheusEnabled = true,
Networks = {
{ name = "RED", coalition = "red", prefix = "iads" },
},
}

Medusa writes Logs/medusa_metrics.prom in your DCS Saved Games directory every 10 seconds. This file contains all metrics in Prometheus text format.

For per-battery and per-track detail (positions, ammo, Pk values, engagement ranges), enable extended metrics:

MEDUSA_CONFIG = {
PrometheusEnabled = true,
PrometheusExtendEnabled = true, -- writes every 2 seconds instead of 10
}

Extended metrics add more DCS API calls per write cycle. On large missions with many batteries, this can add a few milliseconds of overhead per write.

If you have Task installed (recommended):

Terminal window
task prometheus

Or with Docker Compose directly:

Terminal window
cd tools/prometheus
docker compose up -d

Other Task commands: task prometheus:stop to shut down, task prometheus:restart to restart, task prometheus:reset to wipe all stored data.

This starts four containers:

ContainerPortWhat it does
medusa-node-exporter9100Reads the .prom file from your DCS Logs directory and serves it to Prometheus
medusa-prometheus9090Scrapes the node exporter every 3 seconds and stores 30 days of data
medusa-grafana3000Dashboards. Login: admin / medusa. Anonymous viewing enabled.
medusa-tactical8080Tactical display web app. Requires PrometheusExtendEnabled = true. See Tactical Display

The stack reads the DCS Logs path from tools/prometheus/.env. Edit this file to match your setup:

DCS_LOGS_PATH=C:\Users\YourName\Saved Games\DCS.openbeta\Logs

Four dashboards are pre-configured:

Medusa Overview. Network-wide status: battery states, track counts by identification, engagement rates, shots/kills, Rolling Pk, HARM response activity, ammo remaining.

Medusa Performance. Per-phase timing: tick duration, sensor polling, track classification, HARM evaluation, target assignment, handoff, EMCON. Shows p50, p90, p99 for each.

Medusa Battery Details. Per-battery drill-down: ammo, engagement range, weapon range, HARM defense status, last-chance salvo status.

Medusa Track Details. Per-track analysis: track age at expiry, update count distribution, best/second Pk values.

All dashboards filter by network using a $network dropdown at the top. When the mission is not running, panels go blank (they gate on the medusa:live recording rule which checks if the heartbeat is fresh).

Medusa writes a medusa_heartbeat_epoch gauge on every metrics export. A Prometheus recording rule called medusa:live checks if the heartbeat is less than 30 seconds old. All dashboard panels and the tactical display use this to hide stale data when the mission stops.

Metrics are prefixed with medusa_. Counters end in _total. Summaries include _count and _sum suffixes plus quantile labels.

These are always available when PrometheusEnabled = true.

MetricTypeDescription
medusa_mission_time_secondsgaugeSeconds since mission start
medusa_mission_infoinfoCurrent mission name
medusa_batteries_totalgaugeTotal batteries in network
medusa_sensors_totalgaugeTotal sensors in network
medusa_tracks_activegaugeActive tracks
medusa_batteries_hotgaugeBatteries in HOT state
medusa_batteries_warmgaugeBatteries in WARM state
medusa_batteries_coldgaugeBatteries in COLD state
medusa_engagements_activegaugeBatteries currently engaging
medusa_tracks_hostilegaugeHOSTILE tracks
medusa_tracks_harmgaugeTracks assessed as HARM
medusa_tracks_banditgaugeBANDIT tracks
medusa_tracks_bogeygaugeBOGEY tracks
medusa_tracks_unknowngaugeUNKNOWN tracks
medusa_tracks_sprt_evaluatinggaugeTracks under HARM evaluation
medusa_ammo_remaininggaugeTotal missiles remaining
medusa_batteries_rearminggaugeBatteries out of ammo
medusa_batteries_damagedgaugeBatteries with degraded status
medusa_batteries_shutdowngaugeBatteries in HARM shutdown
medusa_batteries_suppressedgaugeBatteries suppressed by HARMs
medusa_batteries_self_defendinggaugeBatteries self-defending against HARMs
medusa_batteries_pd_protectedgaugeBatteries protected by point defense
medusa_rolling_pkgaugeNetwork rolling kill rate
medusa_effective_pk_floorgaugeEffective PkFloor after rolling adjustment
medusa_ticks_totalcounterTotal ticks processed
medusa_battery_go_hot_totalcounterHOT transitions
medusa_battery_go_warm_totalcounterWARM transitions
medusa_battery_go_cold_totalcounterCOLD transitions
medusa_battery_destroyed_totalcounterBatteries destroyed
medusa_shots_fired_totalcounterShots fired
medusa_kills_totalcounterKills scored
medusa_tracks_created_totalcounterTracks created
medusa_tracks_expired_totalcounterTracks expired
medusa_engagements_assigned_totalcounterEngagements assigned
medusa_harm_confirmed_totalcounterTracks confirmed as HARM
medusa_harm_shutdowns_totalcounterHARM shutdowns
medusa_last_chance_activated_totalcounterLast-chance salvo windows opened
medusa_last_chance_fired_totalcounterShots during last-chance
medusa_roe_changes_totalcounterRuntime ROE changes via API
medusa_track_promotions_totalcounterTrack identification promotions
medusa_assignment_pairs_evaluatedcounterBattery-track pairs evaluated
medusa_detections_totalcounterSensor detections
medusa_goHot_blocked_totalcountergoHot calls blocked
medusa_sensor_empty_polls_totalcounterEmpty sensor polls
medusa_tick_duration_secondssummaryTick processing time (p50/p90/p99)
medusa_poll_sensors_duration_secondssummarySensor polling time
medusa_prune_stale_duration_secondssummaryTrack pruning time
medusa_classification_duration_secondssummaryTrack classification time
medusa_harm_eval_duration_secondssummaryHARM detection + response time
medusa_assignment_duration_secondssummaryTarget assignment time
medusa_handoff_duration_secondssummaryHandoff + deactivation time
medusa_emcon_duration_secondssummaryEMCON policy time
medusa_chunk_processedgaugeItems processed in last chunk invocation (labeled by phase)
medusa_chunk_queuedgaugeItems remaining in chunk queue (labeled by phase)
medusa_serialize_duration_secondssummaryMetrics serialization time
medusa_track_age_at_expiry_secondshistogramTrack age when expired
medusa_track_updates_at_expiryhistogramUpdate count when expired

Requires PrometheusExtendEnabled = true. These are per-battery and per-track metrics. They add DCS API calls per write cycle and increase the metrics file size.

MetricTypeLabelsDescription
medusa_battery_pos_xgaugenetwork, batteryBattery position X
medusa_battery_pos_zgaugenetwork, batteryBattery position Z
medusa_battery_latgaugenetwork, batteryBattery latitude (WGS84)
medusa_battery_longaugenetwork, batteryBattery longitude (WGS84)
medusa_battery_infogaugenetwork, battery, role, status, state, target, systemBattery metadata
medusa_battery_weapon_range_mgaugenetwork, battery, weaponWeapon max range
medusa_battery_engagement_range_mgaugenetwork, battery, systemCurrent engagement range
medusa_battery_shots_firedgaugenetwork, batteryPer-battery shots fired
medusa_battery_ammogaugenetwork, batteryPer-battery ammo remaining
medusa_battery_cluster_latgaugenetwork, battery, clusterLauncher cluster latitude (only for distributed groups)
medusa_battery_cluster_longaugenetwork, battery, clusterLauncher cluster longitude (only for distributed groups)
medusa_battery_cluster_range_mgaugenetwork, battery, clusterLauncher cluster max weapon range (only for distributed groups)
medusa_battery_harm_defensegaugenetwork, battery, stateHARM defense state
medusa_battery_harm_threatsgaugenetwork, batteryInbound HARMs threatening battery
medusa_battery_harm_defendersgaugenetwork, batteryHARM-capable units defending
medusa_battery_harm_ratiogaugenetwork, batteryDefender to threat ratio
medusa_battery_last_chancegaugenetwork, battery, trackLast-chance salvo active
medusa_battery_last_chance_shots_remaininggaugenetwork, batteryLast-chance missiles remaining
medusa_track_pos_xgaugenetwork, trackTrack position X
medusa_track_pos_ygaugenetwork, trackTrack altitude
medusa_track_pos_zgaugenetwork, trackTrack position Z
medusa_track_latgaugenetwork, trackTrack latitude (WGS84)
medusa_track_longaugenetwork, trackTrack longitude (WGS84)
medusa_track_vel_xgaugenetwork, trackTrack velocity X
medusa_track_vel_ygaugenetwork, trackTrack velocity Y
medusa_track_vel_zgaugenetwork, trackTrack velocity Z
medusa_track_speedgaugenetwork, trackTrack ground speed (m/s)
medusa_track_updatesgaugenetwork, trackTrack update count
medusa_track_agegaugenetwork, trackTrack age (seconds)
medusa_track_harm_scoregaugenetwork, trackHARM likelihood score
medusa_track_infogaugenetwork, track, type, unit, identification, aircraft_type, maneuverTrack classification
medusa_track_sprt_llrgaugenetwork, trackSPRT log-likelihood ratio
medusa_track_sprt_scansgaugenetwork, trackSPRT scan count
medusa_track_sprt_infogaugenetwork, track, labelSPRT evaluation label
medusa_track_best_pkgaugenetwork, track, batteryBest Pk for track
medusa_track_second_pkgaugenetwork, track, batterySecond best Pk for track