mirror of
https://github.com/prometheus-community/windows_exporter.git
synced 2026-10-17 10:09:05 +02:00
dashboard: add TCP/UDP and GPU metrics with panel descriptions (#2576)
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
This commit is contained in:
@@ -21,48 +21,3 @@ exclusions:
|
||||
entries:
|
||||
- dashboard: Windows Exporter
|
||||
panel: Automatic services not running
|
||||
panel-title-description-rule:
|
||||
reason: These existing panels have titles but no descriptions; new panels must include both.
|
||||
entries:
|
||||
- dashboard: Windows Exporter
|
||||
panel: Operating system
|
||||
- dashboard: Windows Exporter
|
||||
panel: CPUs
|
||||
- dashboard: Windows Exporter
|
||||
panel: RAM
|
||||
- dashboard: Windows Exporter
|
||||
panel: CPU busy
|
||||
- dashboard: Windows Exporter
|
||||
panel: Memory used
|
||||
- dashboard: Windows Exporter
|
||||
panel: CPU utilization
|
||||
- dashboard: Windows Exporter
|
||||
panel: Volumes
|
||||
- dashboard: Windows Exporter
|
||||
panel: Context switches and interrupts
|
||||
- dashboard: Windows Exporter
|
||||
panel: Processes and threads
|
||||
- dashboard: Windows Exporter
|
||||
panel: System calls and exceptions
|
||||
- dashboard: Windows Exporter
|
||||
panel: Volume usage
|
||||
- dashboard: Windows Exporter
|
||||
panel: Network errors and discards
|
||||
- dashboard: Windows Exporter
|
||||
panel: Running services
|
||||
- dashboard: Windows Exporter
|
||||
panel: Disabled services
|
||||
- dashboard: Windows Exporter
|
||||
panel: VMs in critical health
|
||||
- dashboard: Windows Exporter
|
||||
panel: Logical processors
|
||||
- dashboard: Windows Exporter
|
||||
panel: Virtual switch dropped packets
|
||||
- dashboard: Windows Exporter
|
||||
panel: Clock sync source
|
||||
- dashboard: Windows Exporter
|
||||
panel: Clock offset and NTP round-trip delay
|
||||
- dashboard: Windows Exporter
|
||||
panel: Samples per scrape
|
||||
- dashboard: Windows Exporter
|
||||
panel: Collectors
|
||||
|
||||
@@ -285,9 +285,9 @@ used locally.
|
||||
|
||||
Run `mise run lint-dashboards` to lint just the committed dashboard. `.dashboard-lint.yaml`
|
||||
documents exceptions for the sample dashboard's single-host selection, fleet
|
||||
queries, editable state, and existing panels without descriptions or numeric
|
||||
units. Other findings fail validation, including warnings. Remove panel
|
||||
exceptions when adding the missing descriptions.
|
||||
queries, editable state, and the service table without numeric units. All panels
|
||||
include descriptions; missing titles or descriptions fail validation, including
|
||||
warnings.
|
||||
|
||||
`.pint.hcl` requires a non-empty severity label, summary/description annotations,
|
||||
and an HTTP(S) runbook URL on alerts, in addition to pint's built-in checks.
|
||||
|
||||
@@ -68,6 +68,7 @@ b.tab(
|
||||
}),
|
||||
|
||||
b.panel.new(27, 'Context switches and interrupts', 'timeseries')
|
||||
+ b.panel.withDescription('Context switches, hardware interrupts and deferred procedure calls per second across the host. High rates can reflect scheduling or driver activity; correlate changes with CPU usage and workload rather than assuming they are faults.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('rate(windows_system_context_switches_total{job=~"$job", instance="$instance"}[$__rate_interval])', 'A')
|
||||
+ b.query.withLegendFormat('context switches'),
|
||||
@@ -82,6 +83,7 @@ b.tab(
|
||||
}),
|
||||
|
||||
b.panel.new(28, 'Processes and threads', 'timeseries')
|
||||
+ b.panel.withDescription('Current process and thread counts on the host. Sustained growth can help identify runaway process creation or thread leaks; thread counts use the right-hand axis.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('windows_system_processes{job=~"$job", instance="$instance"}', 'A')
|
||||
+ b.query.withLegendFormat('processes'),
|
||||
@@ -108,6 +110,7 @@ b.tab(
|
||||
]),
|
||||
|
||||
b.panel.new(29, 'System calls and exceptions', 'timeseries')
|
||||
+ b.panel.withDescription('System calls and exception dispatches per second. Exception dispatches include handled exceptions and do not directly count application crashes; compare with application logs. Exceptions use the right-hand axis.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('rate(windows_system_system_calls_total{job=~"$job", instance="$instance"}[$__rate_interval])', 'A')
|
||||
+ b.query.withLegendFormat('system calls'),
|
||||
|
||||
@@ -13,6 +13,7 @@ b.tab(
|
||||
]),
|
||||
[
|
||||
b.panel.new(37, 'Volumes', 'table')
|
||||
+ b.panel.withDescription('Size, available space and used percentage of each selected logical volume. Windows free-space counters can lag by 10 to 15 minutes; use trends rather than treating a single sample as immediate confirmation of reclaimed space.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('windows_logical_disk_size_bytes{job=~"$job", instance="$instance", volume=~"$volume"}', 'A')
|
||||
+ b.query.withInstant()
|
||||
@@ -161,6 +162,7 @@ b.tab(
|
||||
]),
|
||||
|
||||
b.panel.new(38, 'Volume usage', 'timeseries')
|
||||
+ b.panel.withDescription('Used space as a percentage of each selected logical volume. Sustained growth toward full capacity can prevent writes and disrupt applications; Windows free-space counters can lag by 10 to 15 minutes.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('100 * (1 - windows_logical_disk_free_bytes{job=~"$job", instance="$instance", volume=~"$volume"} / windows_logical_disk_size_bytes{job=~"$job", instance="$instance", volume=~"$volume"})', 'A')
|
||||
+ b.query.withLegendFormat('{{volume}}'),
|
||||
|
||||
@@ -159,6 +159,7 @@ b.tab(
|
||||
}),
|
||||
|
||||
b.panel.new(95, 'Samples per scrape', 'stat')
|
||||
+ b.panel.withDescription('Number of metric samples returned before Prometheus metric relabeling. Changes can reflect collector configuration or changing device/process counts; unexpected drops should be checked against scrape and collector success.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('scrape_samples_scraped{job=~"$job", instance="$instance"}', 'A'),
|
||||
])
|
||||
@@ -213,6 +214,7 @@ b.tab(
|
||||
]),
|
||||
|
||||
b.panel.new(99, 'Collectors', 'table')
|
||||
+ b.panel.withDescription('Latest success, timeout and duration for each enabled collector. Success is 1 for a completed collection and 0 for a failure; timeout is 1 when the scrape budget was exceeded. Disabled collectors are absent.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('windows_exporter_collector_success{job=~"$job", instance="$instance"}', 'A')
|
||||
+ b.query.withInstant()
|
||||
|
||||
@@ -8,6 +8,18 @@ b.tab(
|
||||
b.place(59, 12, 5, 12, 8),
|
||||
b.place(60, 0, 13, 12, 8),
|
||||
b.place(61, 12, 13, 12, 8),
|
||||
b.place(120, 0, 21, 12, 8),
|
||||
b.place(121, 12, 21, 12, 8),
|
||||
b.place(122, 0, 29, 12, 8),
|
||||
b.place(123, 12, 29, 12, 8),
|
||||
b.place(124, 0, 37, 12, 8),
|
||||
b.place(125, 12, 37, 12, 8),
|
||||
b.place(126, 0, 45, 12, 8),
|
||||
b.place(127, 12, 45, 12, 8),
|
||||
b.place(128, 0, 53, 12, 8),
|
||||
b.place(129, 12, 53, 12, 8),
|
||||
b.place(130, 0, 61, 12, 8),
|
||||
b.place(131, 12, 61, 12, 8),
|
||||
]),
|
||||
[
|
||||
b.panel.new(57, 'GPUs', 'table')
|
||||
@@ -275,5 +287,113 @@ b.tab(
|
||||
sortDesc: true,
|
||||
},
|
||||
}),
|
||||
|
||||
b.panel.new(120, 'GPU temperature', 'timeseries')
|
||||
+ b.panel.withDescription('Current GPU temperature, driver-reported throttling threshold and maximum temperature in degrees Celsius. Sustained readings near the warning threshold can indicate insufficient cooling. Requires the gpu collector, WDDM 2.4 or newer and driver support. Unsupported sensor values are absent, rather than zero.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('max by (luid, phys) (windows_gpu_temperature_celsius{job=~"$job", instance="$instance"}) * on (luid, phys) group_left (name) max by (luid, phys, name) (windows_gpu_info{job=~"$job", instance="$instance"})', 'A')
|
||||
+ b.query.withLegendFormat('{{name}} GPU {{phys}} temperature'),
|
||||
b.query.new('max by (luid, phys) (windows_gpu_temperature_warning_celsius{job=~"$job", instance="$instance"}) * on (luid, phys) group_left (name) max by (luid, phys, name) (windows_gpu_info{job=~"$job", instance="$instance"})', 'B')
|
||||
+ b.query.withLegendFormat('{{name}} GPU {{phys}} throttling threshold'),
|
||||
b.query.new('max by (luid, phys) (windows_gpu_temperature_max_celsius{job=~"$job", instance="$instance"}) * on (luid, phys) group_left (name) max by (luid, phys, name) (windows_gpu_info{job=~"$job", instance="$instance"})', 'C')
|
||||
+ b.query.withLegendFormat('{{name}} GPU {{phys}} maximum'),
|
||||
])
|
||||
+ b.panel.withDefaults({ min: 0, unit: 'celsius' }),
|
||||
|
||||
b.panel.new(121, 'GPU fan speed', 'timeseries')
|
||||
+ b.panel.withDescription('Current and maximum main-fan speed in revolutions per minute. A supported fan can report zero when stopped under light load. Requires the gpu collector, WDDM 2.4 or newer and driver support. Unsupported sensor values are absent, rather than zero.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('max by (luid, phys) (windows_gpu_fan_speed_rpm{job=~"$job", instance="$instance"}) * on (luid, phys) group_left (name) max by (luid, phys, name) (windows_gpu_info{job=~"$job", instance="$instance"})', 'A')
|
||||
+ b.query.withLegendFormat('{{name}} GPU {{phys}} fan'),
|
||||
b.query.new('max by (luid, phys) (windows_gpu_fan_speed_max_rpm{job=~"$job", instance="$instance"}) * on (luid, phys) group_left (name) max by (luid, phys, name) (windows_gpu_info{job=~"$job", instance="$instance"})', 'B')
|
||||
+ b.query.withLegendFormat('{{name}} GPU {{phys}} maximum'),
|
||||
])
|
||||
+ b.panel.withDefaults({ min: 0, unit: 'rotrpm' }),
|
||||
|
||||
b.panel.new(122, 'GPU power usage', 'timeseries')
|
||||
+ b.panel.withDescription('Current GPU power draw as a fraction of the driver-reported maximum power (TDP). This is a ratio, not an absolute measurement in watts. Requires the gpu collector, WDDM 2.4 or newer and driver support. Unsupported sensor values are absent, rather than zero.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('max by (luid, phys) (windows_gpu_power_usage_ratio{job=~"$job", instance="$instance"}) * on (luid, phys) group_left (name) max by (luid, phys, name) (windows_gpu_info{job=~"$job", instance="$instance"})', 'A')
|
||||
+ b.query.withLegendFormat('{{name}} GPU {{phys}} power'),
|
||||
])
|
||||
+ b.panel.withDefaults({ min: 0, unit: 'percentunit' }),
|
||||
|
||||
b.panel.new(123, 'GPU memory clock', 'timeseries')
|
||||
+ b.panel.withDescription('Current and maximum non-overclocked GPU memory clock in hertz. Clocks can fall at idle; correlate drops under load with temperature and power. Requires the gpu collector, WDDM 2.4 or newer and driver support. Unsupported sensor values are absent, rather than zero.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('max by (luid, phys) (windows_gpu_memory_frequency_hertz{job=~"$job", instance="$instance"}) * on (luid, phys) group_left (name) max by (luid, phys, name) (windows_gpu_info{job=~"$job", instance="$instance"})', 'A')
|
||||
+ b.query.withLegendFormat('{{name}} GPU {{phys}} memory clock'),
|
||||
b.query.new('max by (luid, phys) (windows_gpu_memory_frequency_max_hertz{job=~"$job", instance="$instance"}) * on (luid, phys) group_left (name) max by (luid, phys, name) (windows_gpu_info{job=~"$job", instance="$instance"})', 'B')
|
||||
+ b.query.withLegendFormat('{{name}} GPU {{phys}} maximum'),
|
||||
])
|
||||
+ b.panel.withDefaults({ min: 0, unit: 'hertz' }),
|
||||
|
||||
b.panel.new(124, 'GPU engine clock', 'timeseries')
|
||||
+ b.panel.withDescription('Current and maximum non-overclocked frequency per GPU engine in hertz. Only engines with a reported maximum clock are exposed, typically the 3D engine. Requires the gpu collector, WDDM 2.4 or newer and driver support. Unsupported sensor values are absent, rather than zero.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('max by (luid, phys, eng) (windows_gpu_engine_frequency_hertz{job=~"$job", instance="$instance"}) * on (luid, phys) group_left (name) max by (luid, phys, name) (windows_gpu_info{job=~"$job", instance="$instance"})', 'A')
|
||||
+ b.query.withLegendFormat('{{name}} GPU {{phys}} engine {{eng}} clock'),
|
||||
b.query.new('max by (luid, phys, eng) (windows_gpu_engine_frequency_max_hertz{job=~"$job", instance="$instance"}) * on (luid, phys) group_left (name) max by (luid, phys, name) (windows_gpu_info{job=~"$job", instance="$instance"})', 'B')
|
||||
+ b.query.withLegendFormat('{{name}} GPU {{phys}} engine {{eng}} maximum'),
|
||||
])
|
||||
+ b.panel.withDefaults({ min: 0, unit: 'hertz' }),
|
||||
|
||||
b.panel.new(125, 'GPU committed memory', 'timeseries')
|
||||
+ b.panel.withDescription('Total committed GPU memory per physical GPU in bytes. Commitment is a separate accounting measure from dedicated or shared memory residency; do not add these series together.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('max by (luid, phys) (windows_gpu_adapter_memory_committed_bytes{job=~"$job", instance="$instance"}) * on (luid, phys) group_left (name) max by (luid, phys, name) (windows_gpu_info{job=~"$job", instance="$instance"})', 'A')
|
||||
+ b.query.withLegendFormat('{{name}} GPU {{phys}} committed'),
|
||||
])
|
||||
+ b.panel.withDefaults({ min: 0, unit: 'bytes' }),
|
||||
|
||||
b.panel.new(126, 'GPU local and non-local memory', 'timeseries')
|
||||
+ b.panel.withDescription('Local and non-local adapter memory usage in bytes, summed across memory partitions for each physical GPU. These describe memory locality and overlap the dedicated/shared accounting views.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('sum by (luid, phys) (windows_gpu_local_adapter_memory_bytes{job=~"$job", instance="$instance"}) * on (luid, phys) group_left (name) max by (luid, phys, name) (windows_gpu_info{job=~"$job", instance="$instance"})', 'A')
|
||||
+ b.query.withLegendFormat('{{name}} GPU {{phys}} local'),
|
||||
b.query.new('sum by (luid, phys) (windows_gpu_non_local_adapter_memory_bytes{job=~"$job", instance="$instance"}) * on (luid, phys) group_left (name) max by (luid, phys, name) (windows_gpu_info{job=~"$job", instance="$instance"})', 'B')
|
||||
+ b.query.withLegendFormat('{{name}} GPU {{phys}} non-local'),
|
||||
])
|
||||
+ b.panel.withDefaults({ min: 0, unit: 'bytes' }),
|
||||
|
||||
b.panel.new(127, 'GPU dedicated system memory capacity', 'timeseries')
|
||||
+ b.panel.withDescription('System memory dedicated to each GPU in bytes. This capacity is distinct from dedicated video memory and the shared system memory pool; it can be zero on discrete adapters.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('max by (luid) (windows_gpu_dedicated_system_memory_size_bytes{job=~"$job", instance="$instance"}) * on (luid) group_left (name) max by (luid, name) (windows_gpu_info{job=~"$job", instance="$instance"})', 'A')
|
||||
+ b.query.withLegendFormat('{{name}} dedicated system capacity'),
|
||||
])
|
||||
+ b.panel.withDefaults({ min: 0, unit: 'bytes' }),
|
||||
|
||||
b.panel.new(128, 'Top 10 processes by shared GPU memory', 'timeseries')
|
||||
+ b.panel.withDescription('Top ten processes at each point in time by shared GPU memory in bytes, summed across GPUs. Membership can change over time. Process names require the process collector; otherwise the PID is shown. Memory accounting views overlap and should not be added together.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('topk(10, (sum by (process_id) (windows_gpu_process_memory_shared_bytes{job=~"$job", instance="$instance"}) * on (process_id) group_left (process) max by (process_id, process) (windows_process_info{job=~"$job", instance="$instance"})) or on (process_id) sum by (process_id) (windows_gpu_process_memory_shared_bytes{job=~"$job", instance="$instance"}))', 'A')
|
||||
+ b.query.withLegendFormat('{{process}} ({{process_id}})'),
|
||||
])
|
||||
+ b.panel.withDefaults({ min: 0, unit: 'bytes' }),
|
||||
|
||||
b.panel.new(129, 'Top 10 processes by committed GPU memory', 'timeseries')
|
||||
+ b.panel.withDescription('Top ten processes at each point in time by committed GPU memory in bytes, summed across GPUs. Membership can change over time. Process names require the process collector; otherwise the PID is shown. Memory accounting views overlap and should not be added together.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('topk(10, (sum by (process_id) (windows_gpu_process_memory_committed_bytes{job=~"$job", instance="$instance"}) * on (process_id) group_left (process) max by (process_id, process) (windows_process_info{job=~"$job", instance="$instance"})) or on (process_id) sum by (process_id) (windows_gpu_process_memory_committed_bytes{job=~"$job", instance="$instance"}))', 'A')
|
||||
+ b.query.withLegendFormat('{{process}} ({{process_id}})'),
|
||||
])
|
||||
+ b.panel.withDefaults({ min: 0, unit: 'bytes' }),
|
||||
|
||||
b.panel.new(130, 'Top 10 processes by local GPU memory', 'timeseries')
|
||||
+ b.panel.withDescription('Top ten processes at each point in time by local GPU memory in bytes, summed across GPUs. Membership can change over time. Process names require the process collector; otherwise the PID is shown. Memory accounting views overlap and should not be added together.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('topk(10, (sum by (process_id) (windows_gpu_process_memory_local_bytes{job=~"$job", instance="$instance"}) * on (process_id) group_left (process) max by (process_id, process) (windows_process_info{job=~"$job", instance="$instance"})) or on (process_id) sum by (process_id) (windows_gpu_process_memory_local_bytes{job=~"$job", instance="$instance"}))', 'A')
|
||||
+ b.query.withLegendFormat('{{process}} ({{process_id}})'),
|
||||
])
|
||||
+ b.panel.withDefaults({ min: 0, unit: 'bytes' }),
|
||||
|
||||
b.panel.new(131, 'Top 10 processes by non-local GPU memory', 'timeseries')
|
||||
+ b.panel.withDescription('Top ten processes at each point in time by non-local GPU memory in bytes, summed across GPUs. Membership can change over time. Process names require the process collector; otherwise the PID is shown. Memory accounting views overlap and should not be added together.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('topk(10, (sum by (process_id) (windows_gpu_process_memory_non_local_bytes{job=~"$job", instance="$instance"}) * on (process_id) group_left (process) max by (process_id, process) (windows_process_info{job=~"$job", instance="$instance"})) or on (process_id) sum by (process_id) (windows_gpu_process_memory_non_local_bytes{job=~"$job", instance="$instance"}))', 'A')
|
||||
+ b.query.withLegendFormat('{{process}} ({{process_id}})'),
|
||||
])
|
||||
+ b.panel.withDefaults({ min: 0, unit: 'bytes' }),
|
||||
]
|
||||
)
|
||||
|
||||
@@ -38,6 +38,7 @@ b.tab(
|
||||
]),
|
||||
|
||||
b.panel.new(64, 'VMs in critical health', 'stat')
|
||||
+ b.panel.withDescription('Number of Hyper-V virtual machines reporting critical health. Inspect the affected VM and Hyper-V event logs; this requires the optional hyperv collector.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('sum(windows_hyperv_virtual_machine_health_total_count{job=~"$job", instance="$instance", state="critical"})', 'A')
|
||||
+ b.query.withInstant(),
|
||||
@@ -95,6 +96,7 @@ b.tab(
|
||||
}),
|
||||
|
||||
b.panel.new(66, 'Logical processors', 'stat')
|
||||
+ b.panel.withDescription('Number of host logical processors reported by Hyper-V. Use this as capacity context for host and virtual processor load; this requires the optional hyperv collector.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('windows_hyperv_host_logical_processor_count{job=~"$job", instance="$instance"}', 'A')
|
||||
+ b.query.withInstant(),
|
||||
@@ -340,6 +342,7 @@ b.tab(
|
||||
]),
|
||||
|
||||
b.panel.new(81, 'Virtual switch dropped packets', 'timeseries')
|
||||
+ b.panel.withDescription('Incoming and outgoing packets dropped per second by each Hyper-V virtual switch. Correlate sustained drops with network load, resource pressure and switch policies; this requires the optional hyperv collector.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('rate(windows_hyperv_vswitch_dropped_packets_incoming_total{job=~"$job", instance="$instance"}[$__rate_interval])', 'A')
|
||||
+ b.query.withLegendFormat('{{vswitch}} incoming'),
|
||||
|
||||
@@ -2,11 +2,10 @@ local b = import 'builders.libsonnet';
|
||||
|
||||
b.tab(
|
||||
'Network',
|
||||
b.grid([
|
||||
b.place(45, 0, 0, 12, 8),
|
||||
b.place(46, 12, 0, 12, 8),
|
||||
b.place(47, 0, 8, 12, 8),
|
||||
b.place(48, 12, 8, 12, 8),
|
||||
b.rows([
|
||||
b.row('Interfaces', b.grid([b.place(45, 0, 0, 12, 8), b.place(46, 12, 0, 12, 8), b.place(47, 0, 8, 12, 8), b.place(48, 12, 8, 12, 8)])),
|
||||
b.row('TCP', b.grid([b.place(140, 0, 0, 12, 8), b.place(141, 12, 0, 12, 8), b.place(142, 0, 8, 12, 8), b.place(143, 12, 8, 12, 8)])),
|
||||
b.row('UDP', b.grid([b.place(144, 0, 0, 12, 8), b.place(145, 12, 0, 12, 8)])),
|
||||
]),
|
||||
[
|
||||
b.panel.new(45, 'Network throughput', 'timeseries')
|
||||
@@ -88,6 +87,7 @@ b.tab(
|
||||
]),
|
||||
|
||||
b.panel.new(48, 'Network errors and discards', 'timeseries')
|
||||
+ b.panel.withDescription('Receive and transmit packet errors, discarded packets and received packets with unknown protocols per second for each selected interface. Errors can indicate link or driver problems; discards can reflect buffer pressure or filtering.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('rate(windows_net_packets_received_errors_total{job=~"$job", instance="$instance", nic=~"$nic"}[$__rate_interval])', 'A')
|
||||
+ b.query.withLegendFormat('{{nic}} received errors'),
|
||||
@@ -104,5 +104,115 @@ b.tab(
|
||||
min: 0,
|
||||
unit: 'pps',
|
||||
}),
|
||||
|
||||
b.panel.new(140, 'TCP connections by state', 'timeseries')
|
||||
+ b.panel.withDescription('Needs the tcp collector, which is not enabled by default. A growing CLOSE_WAIT count means an application does not close its sockets. Many TIME_WAIT connections can use up the dynamic port range for outgoing connections.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('sum by (state) (windows_tcp_connections_state_count{job=~"$job", instance="$instance"})', 'A')
|
||||
+ b.query.withLegendFormat('{{state}}'),
|
||||
])
|
||||
+ b.panel.withDefaults({
|
||||
min: 0,
|
||||
unit: 'short',
|
||||
})
|
||||
+ b.panel.withOverrides([]),
|
||||
|
||||
b.panel.new(141, 'TCP connections opened, failed and reset', 'timeseries')
|
||||
+ b.panel.withDescription('Outgoing (active) and incoming (passive) connections opened per second, connection attempts that failed, and established connections that were reset.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('sum(rate(windows_tcp_connections_active_total{job=~"$job", instance="$instance"}[$__rate_interval]))', 'A')
|
||||
+ b.query.withLegendFormat('opened outgoing'),
|
||||
b.query.new('sum(rate(windows_tcp_connections_passive_total{job=~"$job", instance="$instance"}[$__rate_interval]))', 'B')
|
||||
+ b.query.withLegendFormat('accepted incoming'),
|
||||
b.query.new('sum(rate(windows_tcp_connection_failures_total{job=~"$job", instance="$instance"}[$__rate_interval]))', 'C')
|
||||
+ b.query.withLegendFormat('failed'),
|
||||
b.query.new('sum(rate(windows_tcp_connections_reset_total{job=~"$job", instance="$instance"}[$__rate_interval]))', 'D')
|
||||
+ b.query.withLegendFormat('reset'),
|
||||
])
|
||||
+ b.panel.withDefaults({
|
||||
min: 0,
|
||||
unit: 'ops',
|
||||
})
|
||||
+ b.panel.withOverrides([{ matcher: { id: 'byName', options: 'failed' }, properties: [{ id: 'color', value: { fixedColor: 'red', mode: 'fixed' } }] }, { matcher: { id: 'byName', options: 'reset' }, properties: [{ id: 'color', value: { fixedColor: 'orange', mode: 'fixed' } }] }]),
|
||||
|
||||
b.panel.new(142, 'TCP segments', 'timeseries')
|
||||
+ b.panel.withDescription('Received segments are drawn below the axis.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('sum(rate(windows_tcp_segments_sent_total{job=~"$job", instance="$instance"}[$__rate_interval]))', 'A')
|
||||
+ b.query.withLegendFormat('sent'),
|
||||
b.query.new('sum(rate(windows_tcp_segments_received_total{job=~"$job", instance="$instance"}[$__rate_interval]))', 'B')
|
||||
+ b.query.withLegendFormat('received'),
|
||||
b.query.new('sum(rate(windows_tcp_segments_retransmitted_total{job=~"$job", instance="$instance"}[$__rate_interval]))', 'C')
|
||||
+ b.query.withLegendFormat('retransmitted'),
|
||||
])
|
||||
+ b.panel.withDefaults({
|
||||
unit: 'pps',
|
||||
})
|
||||
+ b.panel.withOverrides([{ matcher: { id: 'byRegexp', options: 'received' }, properties: [{ id: 'custom.transform', value: 'negative-Y' }] }, { matcher: { id: 'byName', options: 'retransmitted' }, properties: [{ id: 'color', value: { fixedColor: 'red', mode: 'fixed' } }] }]),
|
||||
|
||||
b.panel.new(143, 'TCP retransmission rate', 'timeseries')
|
||||
+ b.panel.withDescription('Retransmitted segments as a share of sent segments. A rate that stays above a few percent points to packet loss on the network.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('sum(rate(windows_tcp_segments_retransmitted_total{job=~"$job", instance="$instance"}[$__rate_interval])) / sum(rate(windows_tcp_segments_sent_total{job=~"$job", instance="$instance"}[$__rate_interval]))', 'A')
|
||||
+ b.query.withLegendFormat('retransmitted'),
|
||||
])
|
||||
+ b.panel.withDefaults({
|
||||
custom: {
|
||||
thresholdsStyle: {
|
||||
mode: 'dashed',
|
||||
},
|
||||
},
|
||||
min: 0,
|
||||
thresholds: {
|
||||
steps: [
|
||||
{
|
||||
color: 'green',
|
||||
value: null,
|
||||
},
|
||||
{
|
||||
color: 'orange',
|
||||
value: 0.01,
|
||||
},
|
||||
{
|
||||
color: 'red',
|
||||
value: 0.05,
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
+ b.panel.withOptions({
|
||||
legend: {
|
||||
calcs: [],
|
||||
displayMode: 'list',
|
||||
},
|
||||
})
|
||||
+ b.panel.withOverrides([]),
|
||||
|
||||
b.panel.new(144, 'UDP datagrams', 'timeseries')
|
||||
+ b.panel.withDescription('Needs the udp collector, which is not enabled by default. Received datagrams are drawn below the axis.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('sum(rate(windows_udp_datagram_sent_total{job=~"$job", instance="$instance"}[$__rate_interval]))', 'A')
|
||||
+ b.query.withLegendFormat('sent'),
|
||||
b.query.new('sum(rate(windows_udp_datagram_received_total{job=~"$job", instance="$instance"}[$__rate_interval]))', 'B')
|
||||
+ b.query.withLegendFormat('received'),
|
||||
])
|
||||
+ b.panel.withDefaults({
|
||||
unit: 'pps',
|
||||
})
|
||||
+ b.panel.withOverrides([{ matcher: { id: 'byRegexp', options: 'received' }, properties: [{ id: 'custom.transform', value: 'negative-Y' }] }]),
|
||||
|
||||
b.panel.new(145, 'UDP errors', 'timeseries')
|
||||
+ b.panel.withDescription('Received datagrams that could not be delivered, and datagrams for a port that no application listens on.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('sum(rate(windows_udp_datagram_received_errors_total{job=~"$job", instance="$instance"}[$__rate_interval]))', 'A')
|
||||
+ b.query.withLegendFormat('received errors'),
|
||||
b.query.new('sum(rate(windows_udp_datagram_no_port_total{job=~"$job", instance="$instance"}[$__rate_interval]))', 'B')
|
||||
+ b.query.withLegendFormat('no listening port'),
|
||||
])
|
||||
+ b.panel.withDefaults({
|
||||
min: 0,
|
||||
unit: 'pps',
|
||||
})
|
||||
+ b.panel.withOverrides([{ matcher: { id: 'byName', options: 'received errors' }, properties: [{ id: 'color', value: { fixedColor: 'red', mode: 'fixed' } }] }]),
|
||||
]
|
||||
)
|
||||
|
||||
@@ -19,6 +19,7 @@ b.tab(
|
||||
]),
|
||||
[
|
||||
b.panel.new(10, 'Operating system', 'stat')
|
||||
+ b.panel.withDescription('Windows product name and version reported by the os collector. Use this to distinguish hosts when investigating version-specific behavior.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('windows_os_info{job=~"$job", instance="$instance"}', 'A')
|
||||
+ b.query.withInstant()
|
||||
@@ -58,12 +59,14 @@ b.tab(
|
||||
}),
|
||||
|
||||
b.panel.new(12, 'CPUs', 'stat')
|
||||
+ b.panel.withDescription('Number of logical processors available to Windows, including SMT threads. This is the logical CPU count rather than the number of physical sockets.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('windows_cpu_logical_processor{job=~"$job", instance="$instance"}', 'A')
|
||||
+ b.query.withInstant(),
|
||||
]),
|
||||
|
||||
b.panel.new(13, 'RAM', 'stat')
|
||||
+ b.panel.withDescription('Total physical memory visible to Windows in bytes. Compare with used memory and commit charge when investigating memory pressure.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('windows_memory_physical_total_bytes{job=~"$job", instance="$instance"}', 'A')
|
||||
+ b.query.withInstant(),
|
||||
@@ -74,6 +77,7 @@ b.tab(
|
||||
}),
|
||||
|
||||
b.panel.new(14, 'CPU busy', 'stat')
|
||||
+ b.panel.withDescription('Percentage of non-idle CPU time averaged across logical processors over the rate interval. Sustained high values together with a growing processor queue can indicate CPU contention.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('100 * (1 - avg by (instance) (clamp_max(rate(windows_cpu_time_total{job=~"$job", instance="$instance", mode="idle"}[$__rate_interval]), 1)))', 'A'),
|
||||
])
|
||||
@@ -104,6 +108,7 @@ b.tab(
|
||||
}),
|
||||
|
||||
b.panel.new(15, 'Memory used', 'stat')
|
||||
+ b.panel.withDescription('Percentage of physical memory in use, calculated as total minus available memory. This differs from commit charge, which measures promised virtual memory against the commit limit.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('100 * (1 - windows_memory_physical_free_bytes{job=~"$job", instance="$instance"} / windows_memory_physical_total_bytes{job=~"$job", instance="$instance"})', 'A'),
|
||||
])
|
||||
@@ -186,6 +191,7 @@ b.tab(
|
||||
}),
|
||||
|
||||
b.panel.new(18, 'CPU utilization', 'timeseries')
|
||||
+ b.panel.withDescription('Percentage of non-idle CPU time averaged across logical processors. This measures CPU time rather than the frequency-adjusted processor utility shown by Windows Task Manager.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('100 * (1 - avg by (instance) (clamp_max(rate(windows_cpu_time_total{job=~"$job", instance="$instance", mode="idle"}[$__rate_interval]), 1)))', 'A')
|
||||
+ b.query.withLegendFormat('CPU busy'),
|
||||
@@ -276,6 +282,7 @@ b.tab(
|
||||
]),
|
||||
|
||||
b.panel.new(20, 'Volumes', 'table')
|
||||
+ b.panel.withDescription('Size, available space and used percentage of each selected logical volume. Windows free-space counters can lag by 10 to 15 minutes; use trends rather than treating a single sample as immediate confirmation of reclaimed space.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('windows_logical_disk_size_bytes{job=~"$job", instance="$instance", volume=~"$volume"}', 'A')
|
||||
+ b.query.withInstant()
|
||||
|
||||
@@ -12,6 +12,7 @@ b.tab(
|
||||
]),
|
||||
[
|
||||
b.panel.new(50, 'Running services', 'stat')
|
||||
+ b.panel.withDescription('Number of services currently in the running state, reported by the service collector. Compare unexpected drops with the stopped automatic services table and service change history.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('sum(windows_service_state{job=~"$job", instance="$instance", state="running"})', 'A')
|
||||
+ b.query.withInstant(),
|
||||
@@ -60,6 +61,7 @@ b.tab(
|
||||
}),
|
||||
|
||||
b.panel.new(53, 'Disabled services', 'stat')
|
||||
+ b.panel.withDescription('Number of services configured with disabled startup. A disabled service cannot start until its startup mode changes; disabled services are not necessarily unhealthy.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('sum(windows_service_start_mode{job=~"$job", instance="$instance", start_mode="disabled"})', 'A')
|
||||
+ b.query.withInstant(),
|
||||
|
||||
@@ -29,6 +29,7 @@ b.tab(
|
||||
}),
|
||||
|
||||
b.panel.new(85, 'Clock sync source', 'stat')
|
||||
+ b.panel.withDescription('Active Windows Time synchronization provider. A non-NTP provider can be expected on virtual machines; this requires the optional time collector.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('windows_time_clock_sync_source{job=~"$job", instance="$instance"} == 1', 'A')
|
||||
+ b.query.withInstant()
|
||||
@@ -95,6 +96,7 @@ b.tab(
|
||||
}),
|
||||
|
||||
b.panel.new(88, 'Clock offset and NTP round-trip delay', 'timeseries')
|
||||
+ b.panel.withDescription('Signed offset from the selected time source and the NTP request round-trip delay, in seconds. Persistent offset or increased delay can affect time-sensitive applications; this compares the host to its chosen source and does not independently verify that source.')
|
||||
+ b.panel.withQueries([
|
||||
b.query.new('windows_time_computed_time_offset_seconds{job=~"$job", instance="$instance"}', 'A')
|
||||
+ b.query.withLegendFormat('clock offset'),
|
||||
|
||||
@@ -14,6 +14,9 @@ local tabs = [
|
||||
import 'exporter.libsonnet',
|
||||
];
|
||||
|
||||
local panelNames = std.flattenArrays([std.objectFields(tab.elements) for tab in tabs]);
|
||||
assert std.length(panelNames) == std.length(std.set(panelNames)) : 'Panel IDs must be unique across tabs';
|
||||
|
||||
dashboard.new('Kdaassddw', 'Windows Exporter')
|
||||
+ dashboard.spec.withCursorSync('Crosshair')
|
||||
+ dashboard.spec.withDescription('Fleet overview and per-host details for Windows hosts monitored by windows_exporter.')
|
||||
|
||||
+4
-3
@@ -8,8 +8,9 @@ and takes several ideas from Grafana's [windows-observ-lib](https://github.com/g
|
||||
|
||||
- Grafana 13 or later with a Prometheus data source. The file uses the v2 dashboard schema, which Grafana needs for tabs.
|
||||
- windows_exporter with the default collectors (`cpu`, `logical_disk`, `memory`, `net`, `os`, `physical_disk`, `service`, `system`).
|
||||
The GPU, Hyper-V and Time tabs need the `gpu`, `hyperv` and `time` collectors, which are not enabled by default.
|
||||
The GPU, Hyper-V and Time tabs and the TCP and UDP rows of the Network tab need the `gpu`, `hyperv`, `time`, `tcp` and `udp` collectors, which are not enabled by default.
|
||||
The process names in the GPU tab need the `process` collector.
|
||||
GPU temperature, fan, power and clock panels require WDDM 2.4 or newer and driver support; unsupported sensors are absent.
|
||||
|
||||
Import the JSON file through **Dashboards > New > Import** and pick the Prometheus data source in the **Data source** variable.
|
||||
|
||||
@@ -35,9 +36,9 @@ The **Reboots** annotation marks each reboot of the selected host on all graphs.
|
||||
| CPU | Utilization by mode and per core, processor queue length, context switches, interrupts, processes, threads, system calls and the effective CPU frequency. |
|
||||
| Memory | Used and available physical memory, commit charge, hard page faults, kernel pools and the system cache. |
|
||||
| Disk | Volume size and free space, usage, busy time, throughput, IOPS, latency and queue length. |
|
||||
| Network | Throughput, utilization against the link speed, packets, errors and discards per interface. |
|
||||
| Network | Throughput, utilization, packets, errors and discards per interface; TCP connection states and rates, segments and retransmissions; UDP datagrams and errors. |
|
||||
| Services | Running, pending and disabled services, automatic services that are not running, and services that started or stopped in the time range. |
|
||||
| GPU | Utilization by engine type, dedicated and shared memory, and the processes that use the GPU most. |
|
||||
| GPU | Utilization by engine type, memory capacity, commitment and locality, temperature, fans, power, clocks, and top processes by GPU utilization and memory. |
|
||||
| Hyper-V | VM count and health, Hyper-V WMI health, host CPU time, CPU and memory per VM, virtual disks, virtual switches and VM network adapters. |
|
||||
| Time | Time zone, clock sync source, NTP time sources, clock offset and NTP round-trip delay. |
|
||||
| Exporter | Scrape status and duration, exporter version and uptime, collector status, duration, failures and timeouts, and the exporter's CPU, memory, handles and Go runtime. |
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user