fluent-plugin-windows-exporter 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,882 @@
1
+ #
2
+ # Copyright 2021- Fujimoto Seiji, Fukuda Daijiro
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+ require "fluent/plugin/input"
17
+ require_relative "winffi"
18
+ require_relative "hkey_perf_data_reader"
19
+
20
+ module Fluent
21
+ module Plugin
22
+ module Constants
23
+ # https://github.com/prometheus-community/windows_exporter/blob/master/collector/collector.go
24
+ TICKS_TO_SECONDS_SCALE_FACTOR = 1 / 1e7
25
+ WINDOWS_EPOCH = 116444736000000000
26
+
27
+ # https://github.com/leoluk/perflib_exporter/blob/master/collector/mapper.go
28
+ # These flag values may be composed of the base flags in winperf.h.
29
+ # ref: https://github.com/Kochise/Picat-win32/blob/master/emu/windows/winperf.h
30
+ PERF_ELAPSED_TIME = 0x30240500
31
+ PERF_100NSEC_TIMER = 0x20510500
32
+ PERF_PRECISION_100NS_TIMER = 0x20570500
33
+ end
34
+
35
+ class WindowsExporterInput < Fluent::Plugin::Input
36
+ include Constants
37
+
38
+ Fluent::Plugin.register_input("windows_exporter", self)
39
+
40
+ helpers :timer
41
+
42
+ desc "Tag of the output events"
43
+ config_param :tag, :string, default: "windows.metrics"
44
+ desc "The interval time between data collection"
45
+ config_param :scrape_interval, :time, default: 60
46
+ desc "Enable cpu collector"
47
+ config_param :cpu, :bool, default: true
48
+ desc "Enable disk collector"
49
+ config_param :logical_disk, :bool, default: true
50
+ desc "Enable memory collector"
51
+ config_param :memory, :bool, default: true
52
+ desc "Enable network collector"
53
+ config_param :net, :bool, default: true
54
+ desc "Enable OS collector"
55
+ config_param :os, :bool, default: true
56
+
57
+ def configure(conf)
58
+ super
59
+ @cache_manager = CacheManager.new
60
+
61
+ @collectors = []
62
+ @collectors << method(:collect_cpu) if @cpu
63
+ @collectors << method(:collect_logical_disk) if @logical_disk
64
+ @collectors << method(:collect_memory) if @memory
65
+ @collectors << method(:collect_net) if @net
66
+ @collectors << method(:collect_os) if @os
67
+ end
68
+
69
+ def start
70
+ super
71
+ timer_execute(:in_windows_exporter, @scrape_interval, &method(:on_timer))
72
+ $log.info("Start in_windows_exporter (%i collectors, every %is)" % [@collectors.count, @scrape_interval])
73
+ end
74
+
75
+ def shutdown
76
+ super
77
+ end
78
+
79
+ def on_timer
80
+ now = Fluent::EventTime.now
81
+ update_cache()
82
+ $log.debug("Updated Windows counters (%.2fs)" % (Fluent::EventTime.now.to_f - now.to_f))
83
+
84
+ es = Fluent::MultiEventStream.new
85
+ for method in @collectors do
86
+ begin
87
+ for record in method.call() do
88
+ es.add(now, record)
89
+ end
90
+ rescue => e
91
+ $log.error(e.message)
92
+ $log.error_backtrace
93
+ end
94
+ end
95
+ router.emit_stream(@tag, es)
96
+ end
97
+
98
+ def update_cache
99
+ @cache_manager.update
100
+ end
101
+
102
+ def collect_cpu
103
+ hpd = @cache_manager.hkey_perf_data_cache
104
+ counterset_name = "Processor Information"
105
+ unless hpd.key?(counterset_name)
106
+ $log.warn("Could not get HKeyPerfData CounterSet: #{counterset_name}")
107
+ return []
108
+ end
109
+
110
+ records = []
111
+ for core in hpd[counterset_name].instances do
112
+ if core.name.downcase.include?("_total")
113
+ next
114
+ end
115
+ records += [
116
+ {
117
+ "type" => "counter",
118
+ "name" => "windows_cpu_cstate_seconds_total",
119
+ "desc" => "Time spent in low-power idle state",
120
+ "labels" => {"core" => core.name, "state" => "c1" },
121
+ "value" => core.counters["% C1 Time"].value
122
+ },
123
+ {
124
+ "type" => "counter",
125
+ "name" => "windows_cpu_cstate_seconds_total",
126
+ "desc" => "Time spent in low-power idle state",
127
+ "labels" => {"core" => core.name, "state" => "c2" },
128
+ "value" => core.counters["% C2 Time"].value
129
+ },
130
+ {
131
+ "type" => "counter",
132
+ "name" => "windows_cpu_cstate_seconds_total",
133
+ "desc" => "Time spent in low-power idle state",
134
+ "labels" => {"core" => core.name, "state" => "c3" },
135
+ "value" => core.counters["% C3 Time"].value
136
+ },
137
+ {
138
+ "type" => "counter",
139
+ "name" => "windows_cpu_time_total",
140
+ "desc" => "Time that processor spent in different modes (idle, user, system, ...)",
141
+ "labels" => {"core" => core.name, "mode" => "idle"},
142
+ "value" => core.counters["% Idle Time"].value
143
+ },
144
+ {
145
+ "type" => "counter",
146
+ "name" => "windows_cpu_time_total",
147
+ "desc" => "Time that processor spent in different modes (idle, user, system, ...)",
148
+ "labels" => {"core" => core.name, "mode" => "interrupt"},
149
+ "value" => core.counters["% Interrupt Time"].value
150
+ },
151
+ {
152
+ "type" => "counter",
153
+ "name" => "windows_cpu_time_total",
154
+ "desc" => "Time that processor spent in different modes (idle, user, system, ...)",
155
+ "labels" => {"core" => core.name, "mode" => "dpc"},
156
+ "value" => core.counters["% DPC Time"].value
157
+ },
158
+ {
159
+ "type" => "counter",
160
+ "name" => "windows_cpu_time_total",
161
+ "desc" => "Time that processor spent in different modes (idle, user, system, ...)",
162
+ "labels" => {"core" => core.name, "mode" => "privileged"},
163
+ "value" => core.counters["% Privileged Time"].value
164
+ },
165
+ {
166
+ "type" => "counter",
167
+ "name" => "windows_cpu_time_total",
168
+ "desc" => "Time that processor spent in different modes (idle, user, system, ...)",
169
+ "labels" => {"core" => core.name, "mode" => "user"},
170
+ "value" => core.counters["% User Time"].value
171
+ },
172
+ {
173
+ "type" => "counter",
174
+ "name" => "windows_cpu_interrupts_total",
175
+ "desc" => "Total number of received and serviced hardware interrupts",
176
+ "labels" => {"core" => core.name},
177
+ "value" => core.counters["Interrupts/sec"].value
178
+ },
179
+ {
180
+ "type" => "counter",
181
+ "name" => "windows_cpu_dpcs_total",
182
+ "desc" => "Total number of received and serviced deferred procedure calls (DPCs)",
183
+ "labels" => {"core" => core.name},
184
+ "value" => core.counters["DPCs Queued/sec"].value
185
+ },
186
+ {
187
+ "type" => "counter",
188
+ "name" => "windows_cpu_clock_interrupts_total",
189
+ "desc" => "Total number of received and serviced clock tick interrupts",
190
+ "labels" => {"core" => core.name},
191
+ "value" => core.counters["Clock Interrupts/sec"].value
192
+ },
193
+ {
194
+ "type" => "counter",
195
+ "name" => "windows_cpu_idle_break_events_total",
196
+ "desc" => "Total number of time processor was woken from idle",
197
+ "labels" => {"core" => core.name},
198
+ "value" => core.counters["Idle Break Events/sec"].value
199
+ },
200
+ {
201
+ "type" => "gauge",
202
+ "name" => "windows_cpu_parking_status",
203
+ "desc" => "Parking Status represents whether a processor is parked or not",
204
+ "labels" => {"core" => core.name},
205
+ "value" => core.counters["Parking Status"].value
206
+ },
207
+ {
208
+ "type" => "gauge",
209
+ "name" => "windows_cpu_core_frequency_mhz",
210
+ "desc" => "Core frequency in megahertz",
211
+ "labels" => {"core" => core.name},
212
+ "value" => core.counters["Processor Frequency"].value
213
+ },
214
+ {
215
+ "type" => "gauge",
216
+ "name" => "windows_cpu_processor_performance",
217
+ "desc" => "Processor Performance is the average performance of the processor while it is executing instructions, as a percentage of the nominal performance of the processor. On some processors, Processor Performance may exceed 100%",
218
+ "labels" => {"core" => core.name},
219
+ "value" => core.counters["% Processor Performance"].value
220
+ }
221
+ ]
222
+ end
223
+ return records
224
+ end
225
+
226
+ def collect_logical_disk
227
+ hpd = @cache_manager.hkey_perf_data_cache
228
+ counterset_name = "LogicalDisk"
229
+ unless hpd.key?(counterset_name)
230
+ $log.warn("Could not get HKeyPerfData CounterSet: #{counterset_name}")
231
+ return []
232
+ end
233
+
234
+ records = []
235
+ for volume in hpd[counterset_name].instances do
236
+ if volume.name.downcase.include?("_total")
237
+ next
238
+ end
239
+
240
+ records += [
241
+ {
242
+ "type" => "gauge",
243
+ "name" => "windows_logical_disk_requests_queued",
244
+ "desc" => "Number of requests outstanding on the disk at the time the performance data is collected",
245
+ "labels" => {"volume" => volume.name},
246
+ "value" => volume.counters["Current Disk Queue Length"].value
247
+ },
248
+ {
249
+ "type" => "counter",
250
+ "name" => "windows_logical_disk_read_bytes_total",
251
+ "desc" => "Rate at which bytes are transferred from the disk during read operations",
252
+ "labels" => {"volume" => volume.name},
253
+ "value" => volume.counters["Disk Read Bytes/sec"].value
254
+ },
255
+ {
256
+ "type" => "counter",
257
+ "name" => "windows_logical_disk_reads_total",
258
+ "desc" => "Rate of read operations on the disk",
259
+ "labels" => {"volume" => volume.name},
260
+ "value" => volume.counters["Disk Reads/sec"].value
261
+ },
262
+ {
263
+ "type" => "counter",
264
+ "name" => "windows_logical_disk_write_bytes_total",
265
+ "desc" => "Rate at which bytes are transferred to the disk during write operations",
266
+ "labels" => {"volume" => volume.name},
267
+ "value" => volume.counters["Disk Write Bytes/sec"].value
268
+ },
269
+ {
270
+ "type" => "counter",
271
+ "name" => "windows_logical_disk_writes_total",
272
+ "desc" => "Rate of write operations on the disk",
273
+ "labels" => {"volume" => volume.name},
274
+ "value" => volume.counters["Disk Writes/sec"].value
275
+ },
276
+ {
277
+ "type" => "counter",
278
+ "name" => "windows_logical_disk_read_seconds_total",
279
+ "desc" => "Seconds the disk was busy servicing read requests",
280
+ "labels" => {"volume" => volume.name},
281
+ "value" => volume.counters["% Disk Read Time"].value
282
+ },
283
+ {
284
+ "type" => "counter",
285
+ "name" => "windows_logical_disk_write_seconds_total",
286
+ "desc" => "Seconds the disk was busy servicing write requests",
287
+ "labels" => {"volume" => volume.name},
288
+ "value" => volume.counters["% Disk Write Time"].value
289
+ },
290
+ {
291
+ "type" => "gauge",
292
+ "name" => "windows_logical_disk_free_bytes",
293
+ "desc" => "Unused space of the disk in bytes (not real time, updates every 10-15 min)",
294
+ "labels" => {"volume" => volume.name},
295
+ "value" => volume.counters["Free Megabytes"].value * 1024 * 1024
296
+ },
297
+ {
298
+ "type" => "gauge",
299
+ "name" => "windows_logical_disk_size_bytes",
300
+ "desc" => "Total size of the disk in bytes (not real time, updates every 10-15 min)",
301
+ "labels" => {"volume" => volume.name},
302
+ "value" => volume.counters["% Free Space"].base_value * 1024 * 1024
303
+ },
304
+ {
305
+ "type" => "counter",
306
+ "name" => "windows_logical_disk_idle_seconds_total",
307
+ "desc" => "Seconds the disk was idle (not servicing read/write requests)",
308
+ "labels" => {"volume" => volume.name},
309
+ "value" => volume.counters["% Idle Time"].value
310
+ },
311
+ {
312
+ "type" => "counter",
313
+ "name" => "windows_logical_disk_split_ios_total",
314
+ "desc" => "Number of I/Os to the disk split into multiple I/Os",
315
+ "labels" => {"volume" => volume.name},
316
+ "value" => volume.counters["Split IO/Sec"].value
317
+ },
318
+ {
319
+ "type" => "counter",
320
+ "name" => "windows_logical_disk_read_latency_seconds_total",
321
+ "desc" => "Shows the average time, in seconds, of a read operation from the disk",
322
+ "labels" => {"volume" => volume.name},
323
+ "value" => volume.counters["Avg. Disk sec/Read"].value * TICKS_TO_SECONDS_SCALE_FACTOR
324
+ },
325
+ {
326
+ "type" => "counter",
327
+ "name" => "windows_logical_disk_write_latency_seconds_total",
328
+ "desc" => "Shows the average time, in seconds, of a write operation to the disk",
329
+ "labels" => {"volume" => volume.name},
330
+ "value" => volume.counters["Avg. Disk sec/Write"].value * TICKS_TO_SECONDS_SCALE_FACTOR
331
+ },
332
+ {
333
+ "type" => "counter",
334
+ "name" => "windows_logical_disk_read_write_latency_seconds_total",
335
+ "desc" => "Shows the time, in seconds, of the average disk transfer",
336
+ "labels" => {"volume" => volume.name},
337
+ "value" => volume.counters["Avg. Disk sec/Transfer"].value * TICKS_TO_SECONDS_SCALE_FACTOR
338
+ }
339
+ ]
340
+ end
341
+ return records
342
+ end
343
+
344
+ def collect_memory
345
+ hpd = @cache_manager.hkey_perf_data_cache
346
+ counterset_name = "Memory"
347
+ unless hpd.key?(counterset_name)
348
+ $log.warn("Could not get HKeyPerfData CounterSet: #{counterset_name}")
349
+ return []
350
+ end
351
+
352
+ return [
353
+ {
354
+ "type" => "gauge",
355
+ "name" => "windows_memory_available_bytes",
356
+ "desc" => "The amount of physical memory immediately available for allocation to a process or for system use. It is equal to the sum of memory assigned to the standby (cached), free and zero page lists",
357
+ "labels" => {},
358
+ "value" => hpd["Memory"].instances[0].counters["Available Bytes"].value
359
+ },
360
+ {
361
+ "type" => "gauge",
362
+ "name" => "windows_memory_cache_bytes",
363
+ "desc" => "Number of bytes currently being used by the file system cache",
364
+ "labels" => {},
365
+ "value" => hpd["Memory"].instances[0].counters["Cache Bytes"].value
366
+ },
367
+ {
368
+ "type" => "gauge",
369
+ "name" => "windows_memory_cache_bytes_peak",
370
+ "desc" => "Maximum number of CacheBytes after the system was last restarted",
371
+ "labels" => {},
372
+ "value" => hpd["Memory"].instances[0].counters["Cache Bytes Peak"].value
373
+ },
374
+ {
375
+ "type" => "gauge",
376
+ "name" => "windows_memory_cache_faults_total",
377
+ "desc" => "Number of faults which occur when a page sought in the file system cache is not found there and must be retrieved from elsewhere in memory (soft fault) or from disk (hard fault)",
378
+ "labels" => {},
379
+ "value" => hpd["Memory"].instances[0].counters["Cache Faults/sec"].value
380
+ },
381
+ {
382
+ "type" => "gauge",
383
+ "name" => "windows_memory_commit_limit",
384
+ "desc" => "Amount of virtual memory, in bytes, that can be committed without having to extend the paging file(s)",
385
+ "labels" => {},
386
+ "value" => hpd["Memory"].instances[0].counters["Commit Limit"].value
387
+ },
388
+ {
389
+ "type" => "gauge",
390
+ "name" => "windows_memory_committed_bytes",
391
+ "desc" => "Amount of committed virtual memory, in bytes",
392
+ "labels" => {},
393
+ "value" => hpd["Memory"].instances[0].counters["Committed Bytes"].value
394
+ },
395
+ {
396
+ "type" => "gauge",
397
+ "name" => "windows_memory_demand_zero_faults_total",
398
+ "desc" => "The number of zeroed pages required to satisfy faults. Zeroed pages, pages emptied of previously stored data and filled with zeros, are a security feature of Windows that prevent processes from seeing data stored by earlier processes that used the memory space",
399
+ "labels" => {},
400
+ "value" => hpd["Memory"].instances[0].counters["Demand Zero Faults/sec"].value
401
+ },
402
+ {
403
+ "type" => "gauge",
404
+ "name" => "windows_memory_free_and_zero_page_list_bytes",
405
+ "desc" => "(FreeAndZeroPageListBytes)",
406
+ "labels" => {},
407
+ "value" => hpd["Memory"].instances[0].counters["Free & Zero Page List Bytes"].value
408
+ },
409
+ {
410
+ "type" => "gauge",
411
+ "name" => "windows_memory_free_system_page_table_entries",
412
+ "desc" => "Number of page table entries not being used by the system",
413
+ "labels" => {},
414
+ "value" => hpd["Memory"].instances[0].counters["Free System Page Table Entries"].value
415
+ },
416
+ {
417
+ "type" => "gauge",
418
+ "name" => "windows_memory_modified_page_list_bytes",
419
+ "desc" => "(ModifiedPageListBytes)",
420
+ "labels" => {},
421
+ "value" => hpd["Memory"].instances[0].counters["Modified Page List Bytes"].value
422
+ },
423
+ {
424
+ "type" => "gauge",
425
+ "name" => "windows_memory_page_faults_total",
426
+ "desc" => "Overall rate at which faulted pages are handled by the processor",
427
+ "labels" => {},
428
+ "value" => hpd["Memory"].instances[0].counters["Page Faults/sec"].value
429
+ },
430
+ {
431
+ "type" => "gauge",
432
+ "name" => "windows_memory_swap_page_reads_total",
433
+ "desc" => "Number of disk page reads (a single read operation reading several pages is still only counted once)",
434
+ "labels" => {},
435
+ "value" => hpd["Memory"].instances[0].counters["Page Reads/sec"].value
436
+ },
437
+ {
438
+ "type" => "gauge",
439
+ "name" => "windows_memory_swap_pages_read_total",
440
+ "desc" => "Number of pages read across all page reads (ie counting all pages read even if they are read in a single operation)",
441
+ "labels" => {},
442
+ "value" => hpd["Memory"].instances[0].counters["Pages Input/sec"].value
443
+ },
444
+ {
445
+ "type" => "gauge",
446
+ "name" => "windows_memory_swap_pages_written_total",
447
+ "desc" => "Number of pages written across all page writes (ie counting all pages written even if they are written in a single operation)",
448
+ "labels" => {},
449
+ "value" => hpd["Memory"].instances[0].counters["Pages Output/sec"].value
450
+ },
451
+ {
452
+ "type" => "gauge",
453
+ "name" => "windows_memory_swap_page_operations_total",
454
+ "desc" => "Total number of swap page read and writes (PagesPersec)",
455
+ "labels" => {},
456
+ "value" => hpd["Memory"].instances[0].counters["Pages/sec"].value
457
+ },
458
+ {
459
+ "type" => "gauge",
460
+ "name" => "windows_memory_swap_page_writes_total",
461
+ "desc" => "Number of disk page writes (a single write operation writing several pages is still only counted once)",
462
+ "labels" => {},
463
+ "value" => hpd["Memory"].instances[0].counters["Page Writes/sec"].value
464
+ },
465
+ {
466
+ "type" => "gauge",
467
+ "name" => "windows_memory_pool_nonpaged_allocs_total",
468
+ "desc" => "The number of calls to allocate space in the nonpaged pool. The nonpaged pool is an area of system memory area for objects that cannot be written to disk, and must remain in physical memory as long as they are allocated",
469
+ "labels" => {},
470
+ "value" => hpd["Memory"].instances[0].counters["Pool Nonpaged Allocs"].value
471
+ },
472
+ {
473
+ "type" => "gauge",
474
+ "name" => "windows_memory_pool_nonpaged_bytes_total",
475
+ "desc" => "Number of bytes in the non-paged pool",
476
+ "labels" => {},
477
+ "value" => hpd["Memory"].instances[0].counters["Pool Nonpaged Bytes"].value
478
+ },
479
+ {
480
+ "type" => "gauge",
481
+ "name" => "windows_memory_pool_paged_allocs_total",
482
+ "desc" => "Number of calls to allocate space in the paged pool, regardless of the amount of space allocated in each call",
483
+ "labels" => {},
484
+ "value" => hpd["Memory"].instances[0].counters["Pool Paged Allocs"].value
485
+ },
486
+ {
487
+ "type" => "gauge",
488
+ "name" => "windows_memory_pool_paged_bytes",
489
+ "desc" => "Number of bytes in the paged pool",
490
+ "labels" => {},
491
+ "value" => hpd["Memory"].instances[0].counters["Pool Paged Bytes"].value
492
+ },
493
+ {
494
+ "type" => "gauge",
495
+ "name" => "windows_memory_pool_paged_resident_bytes",
496
+ "desc" => "(PoolPagedResidentBytes)",
497
+ "labels" => {},
498
+ "value" => hpd["Memory"].instances[0].counters["Pool Paged Resident Bytes"].value
499
+ },
500
+ {
501
+ "type" => "gauge",
502
+ "name" => "windows_memory_standby_cache_core_bytes",
503
+ "desc" => "(StandbyCacheCoreBytes)",
504
+ "labels" => {},
505
+ "value" => hpd["Memory"].instances[0].counters["Standby Cache Core Bytes"].value
506
+ },
507
+ {
508
+ "type" => "gauge",
509
+ "name" => "windows_memory_standby_cache_normal_priority_bytes",
510
+ "desc" => "(StandbyCacheNormalPriorityBytes)",
511
+ "labels" => {},
512
+ "value" => hpd["Memory"].instances[0].counters["Standby Cache Normal Priority Bytes"].value
513
+ },
514
+ {
515
+ "type" => "gauge",
516
+ "name" => "windows_memory_standby_cache_reserve_bytes",
517
+ "desc" => "(StandbyCacheReserveBytes)",
518
+ "labels" => {},
519
+ "value" => hpd["Memory"].instances[0].counters["Standby Cache Reserve Bytes"].value
520
+ },
521
+ {
522
+ "type" => "gauge",
523
+ "name" => "windows_memory_system_cache_resident_bytes",
524
+ "desc" => "(SystemCacheResidentBytes)",
525
+ "labels" => {},
526
+ "value" => hpd["Memory"].instances[0].counters["System Cache Resident Bytes"].value
527
+ },
528
+ {
529
+ "type" => "gauge",
530
+ "name" => "windows_memory_system_code_resident_bytes",
531
+ "desc" => "(SystemCodeResidentBytes)",
532
+ "labels" => {},
533
+ "value" => hpd["Memory"].instances[0].counters["System Code Resident Bytes"].value
534
+ },
535
+ {
536
+ "type" => "gauge",
537
+ "name" => "windows_memory_system_code_total_bytes",
538
+ "desc" => "(SystemCodeTotalBytes)",
539
+ "labels" => {},
540
+ "value" => hpd["Memory"].instances[0].counters["System Code Total Bytes"].value
541
+ },
542
+ {
543
+ "type" => "gauge",
544
+ "name" => "windows_memory_system_driver_resident_bytes",
545
+ "desc" => "(SystemDriverResidentBytes)",
546
+ "labels" => {},
547
+ "value" => hpd["Memory"].instances[0].counters["System Driver Resident Bytes"].value
548
+ },
549
+ {
550
+ "type" => "gauge",
551
+ "name" => "windows_memory_system_driver_total_bytes",
552
+ "desc" => "(SystemDriverTotalBytes)",
553
+ "labels" => {},
554
+ "value" => hpd["Memory"].instances[0].counters["System Driver Total Bytes"].value
555
+ },
556
+ {
557
+ "type" => "gauge",
558
+ "name" => "windows_memory_transition_faults_total",
559
+ "desc" => "(TransitionFaultsPersec)",
560
+ "labels" => {},
561
+ "value" => hpd["Memory"].instances[0].counters["Transition Faults/sec"].value
562
+ },
563
+ {
564
+ "type" => "gauge",
565
+ "name" => "windows_memory_transition_pages_repurposed_total",
566
+ "desc" => "(TransitionPagesRePurposedPersec)",
567
+ "labels" => {},
568
+ "value" => hpd["Memory"].instances[0].counters["Transition Pages RePurposed/sec"].value
569
+ },
570
+ {
571
+ "type" => "gauge",
572
+ "name" => "windows_memory_write_copies_total",
573
+ "desc" => "The number of page faults caused by attempting to write that were satisfied by copying the page from elsewhere in physical memory",
574
+ "labels" => {},
575
+ "value" => hpd["Memory"].instances[0].counters["Write Copies/sec"].value
576
+ }
577
+ ]
578
+ end
579
+
580
+ def collect_net
581
+ hpd = @cache_manager.hkey_perf_data_cache
582
+ counterset_name = "Network Interface"
583
+ unless hpd.key?(counterset_name)
584
+ $log.warn("Could not get HKeyPerfData CounterSet: #{counterset_name}")
585
+ return []
586
+ end
587
+
588
+ records = []
589
+ for nic in hpd[counterset_name].instances do
590
+ name = nic.name.gsub!(/[^a-zA-Z0-9]/, '_')
591
+ if name == ""
592
+ next
593
+ end
594
+
595
+ records += [
596
+ {
597
+ "type" => "counter",
598
+ "name" => "windows_net_bytes_received_total",
599
+ "desc" => "Total bytes received by interface",
600
+ "labels" => {"nic": name},
601
+ "value" => nic.counters["Bytes Received/sec"].value
602
+ },
603
+ {
604
+ "type" => "counter",
605
+ "name" => "windows_net_bytes_sent_total",
606
+ "desc" => "Total bytes transmitted by interface",
607
+ "labels" => {"nic": name},
608
+ "value" => nic.counters["Bytes Sent/sec"].value
609
+ },
610
+ {
611
+ "type" => "counter",
612
+ "name" => "windows_net_bytes_total",
613
+ "desc" => "Total bytes received and transmitted by interface",
614
+ "labels" => {"nic": name},
615
+ "value" => nic.counters["Bytes Total/sec"].value
616
+ },
617
+ {
618
+ "type" => "counter",
619
+ "name" => "windows_net_packets_outbound_discarded_total",
620
+ "desc" => "Total outbound packets that were chosen to be discarded even though no errors had been detected to prevent transmission",
621
+ "labels" => {"nic": name},
622
+ "value" => nic.counters["Packets Outbound Discarded"].value
623
+ },
624
+ {
625
+ "type" => "counter",
626
+ "name" => "windows_net_packets_outbound_errors_total",
627
+ "desc" => "Total packets that could not be transmitted due to errors",
628
+ "labels" => {"nic": name},
629
+ "value" => nic.counters["Packets Outbound Errors"].value
630
+ },
631
+ {
632
+ "type" => "counter",
633
+ "name" => "windows_net_packets_total",
634
+ "desc" => "Total packets received and transmitted by interface",
635
+ "labels" => {"nic": name},
636
+ "value" => nic.counters["Packets/sec"].value
637
+ },
638
+ {
639
+ "type" => "counter",
640
+ "name" => "windows_net_packets_received_discarded_total",
641
+ "desc" => "Total inbound packets that were chosen to be discarded even though no errors had been detected to prevent delivery",
642
+ "labels" => {"nic": name},
643
+ "value" => nic.counters["Packets Received Discarded"].value
644
+ },
645
+ {
646
+ "type" => "counter",
647
+ "name" => "windows_net_packets_received_errors_total",
648
+ "desc" => "Total packets that could not be received due to errors",
649
+ "labels" => {"nic": name},
650
+ "value" => nic.counters["Packets Received Errors"].value
651
+ },
652
+ {
653
+ "type" => "counter",
654
+ "name" => "windows_net_packets_received_total",
655
+ "desc" => "Total packets received by interface",
656
+ "labels" => {"nic": name},
657
+ "value" => nic.counters["Packets Received/sec"].value
658
+ },
659
+ {
660
+ "type" => "counter",
661
+ "name" => "windows_net_packets_received_unknown_total",
662
+ "desc" => "Total packets received by interface that were discarded because of an unknown or unsupported protocol",
663
+ "labels" => {"nic": name},
664
+ "value" => nic.counters["Packets Received Unknown"].value
665
+ },
666
+ {
667
+ "type" => "counter",
668
+ "name" => "windows_net_packets_sent_total",
669
+ "desc" => "Total packets transmitted by interface",
670
+ "labels" => {"nic": name},
671
+ "value" => nic.counters["Packets Sent/sec"].value
672
+ },
673
+ {
674
+ "type" => "gauge",
675
+ "name" => "windows_net_current_bandwidth_bytes",
676
+ "desc" => "Estimate of the interface's current bandwidth in bytes per second",
677
+ "labels" => {"nic": name},
678
+ "value" => nic.counters["Current Bandwidth"].value / 8
679
+ }
680
+ ]
681
+ end
682
+ return records
683
+ end
684
+
685
+ def collect_os
686
+ hpd = @cache_manager.hkey_perf_data_cache
687
+ mem = @cache_manager.memory_status_cache
688
+ work = @cache_manager.work_station_info_cache
689
+ perf = @cache_manager.performance_info_cache
690
+ reg = @cache_manager.registry_info_cache
691
+
692
+ records = [
693
+ {
694
+ "type" => "gauge",
695
+ "name" => "windows_os_info",
696
+ "desc" => "Contains full product name & version in labels",
697
+ "labels" => {
698
+ :product => "Microsoft #{reg[:ProductName]}",
699
+ :version => "#{work[:VersionMajor]}.#{work[:VersionMinor]}.#{reg[:CurrentBuildNumber]}"
700
+ },
701
+ "value" => 1.0
702
+ },
703
+ {
704
+ "type" => "gauge",
705
+ "name" => "windows_os_physical_memory_free_bytes",
706
+ "desc" => "Bytes of physical memory currently unused and available",
707
+ "labels" => {},
708
+ "value" => mem[:AvailPhys]
709
+ },
710
+ {
711
+ "type" => "gauge",
712
+ "name" => "windows_os_time",
713
+ "desc" => "Current time as reported by the operating system, in Unix time",
714
+ "labels" => {},
715
+ "value" => Fluent::EventTime.now.to_f
716
+ },
717
+ {
718
+ "type" => "gauge",
719
+ "name" => "windows_os_timezone",
720
+ "desc" => "Current timezone as reported by the operating system",
721
+ "labels" => {:timezone => Time.now.strftime("%z")},
722
+ "value" => 1.0
723
+ },
724
+ {
725
+ "type" => "gauge",
726
+ "name" => "windows_os_virtual_memory_free_bytes",
727
+ "desc" => "Bytes of virtual memory currently unused and available",
728
+ "labels" => {},
729
+ "value" => mem[:AvailPageFile]
730
+ },
731
+ {
732
+ "type" => "gauge",
733
+ "name" => "windows_os_processes_limit",
734
+ "desc" => "Maximum number of process contexts the operating system can support. The default value set by the provider is 4294967295 (0xFFFFFFFF)",
735
+ "labels" => {},
736
+ # prometheus-community/windows-exporter/collector/os.go#L275
737
+ "value" => 4294967295.0
738
+ },
739
+ {
740
+ "type" => "gauge",
741
+ "name" => "windows_os_process_memory_limit_bytes",
742
+ "desc" => "Maximum number of bytes of memory that can be allocated to a process",
743
+ "labels" => {},
744
+ "value" => mem[:TotalVirtual]
745
+ },
746
+ {
747
+ "type" => "gauge",
748
+ "name" => "windows_os_processes",
749
+ "desc" => "Number of process contexts currently loaded or running on the operating system",
750
+ "labels" => {},
751
+ "value" => perf[:ProcessCount]
752
+ },
753
+ {
754
+ "type" => "gauge",
755
+ "name" => "windows_os_users",
756
+ "desc" => "Number of user sessions for which the operating system is storing state information currently. For a list of current active logon sessions.",
757
+ "labels" => {},
758
+ "value" => work[:LoggedOnUsers]
759
+ },
760
+ {
761
+ "type" => "gauge",
762
+ "name" => "windows_os_paging_limit_bytes",
763
+ "desc" => "Total number of bytes that can be sotred in the operating system paging files. 0 (zero) indicates that there are no paging files",
764
+ "labels" => {},
765
+ "value" => reg[:PagingLimitBytes]
766
+ },
767
+ {
768
+ "type" => "gauge",
769
+ "name" => "windows_os_virtual_memory_bytes",
770
+ "desc" => "Bytes of virtual memory",
771
+ "labels" => {},
772
+ "value" => mem[:TotalPageFile],
773
+ },
774
+ {
775
+ "type" => "gauge",
776
+ "name" => "windows_os_visible_memory_bytes",
777
+ "desc" => "Total bytes of physical memory available to the operating system. This value does not necessarily indicate the true amount of physical memory, but what is reported to the operating system as available to it",
778
+ "labels" => {},
779
+ "value" => mem[:TotalPhys]
780
+ }
781
+ ]
782
+
783
+ counterset_name = "Paging File"
784
+ unless hpd.key?(counterset_name)
785
+ $log.warn("Could not get HKeyPerfData CounterSet: #{counterset_name}")
786
+ return records
787
+ end
788
+
789
+ pfusage = 0
790
+ for ins in hpd[counterset_name].instances do
791
+ unless ins.name.downcase.include?("_total")
792
+ pfusage += ins.counters["% Usage"].value
793
+ end
794
+ end
795
+
796
+ records += [
797
+ {
798
+ "type" => "gauge",
799
+ "name" => "windows_os_paging_free_bytes",
800
+ "desc" => "Number of bytes that can be mapped into the operating system paging files without causing any other pages to be swapped out",
801
+ "labels" => {},
802
+ "value" => reg[:PagingLimitBytes] - pfusage * perf[:PageSize]
803
+ }
804
+ ]
805
+
806
+ return records
807
+ end
808
+ end
809
+
810
+ module HKeyPerfDataWhiteList
811
+ NAMES = [
812
+ "Processor Information",
813
+ "LogicalDisk",
814
+ "Memory",
815
+ "Network Interface",
816
+ "Paging File",
817
+ ]
818
+ end
819
+
820
+ class CacheManager
821
+ include Constants
822
+
823
+ attr_reader :hkey_perf_data_cache
824
+ attr_reader :memory_status_cache
825
+ attr_reader :work_station_info_cache
826
+ attr_reader :performance_info_cache
827
+ attr_reader :registry_info_cache
828
+
829
+ def initialize
830
+ @hkey_perf_data_reader = HKeyPerfDataReader::Reader.new(
831
+ object_name_whitelist: HKeyPerfDataWhiteList::NAMES,
832
+ logger: $log
833
+ )
834
+
835
+ @hkey_perf_data_cache = nil
836
+ @memory_status_cache = nil
837
+ @work_station_info_cache = nil
838
+ @performance_info_cache = nil
839
+ @registry_info_cache = nil
840
+ end
841
+
842
+ def update
843
+ @hkey_perf_data_cache = get_hkey_perf_data()
844
+ @memory_status_cache = WinFFI.GetMemoryStatus()
845
+ @work_station_info_cache = WinFFI.GetWorkstationInfo()
846
+ @performance_info_cache = WinFFI.GetPerformanceInfo()
847
+ @registry_info_cache = WinFFI.GetRegistryInfo()
848
+ end
849
+
850
+ private
851
+
852
+ def get_hkey_perf_data
853
+ data = @hkey_perf_data_reader.read
854
+
855
+ data.each do |object_name, object|
856
+ object.instances.each do |instance|
857
+ instance.counters.each do |counter_name, counter|
858
+ counter.value = calc_hpd_counter_value(
859
+ object, counter.type, counter.value
860
+ )
861
+ end
862
+ end
863
+ end
864
+
865
+ data
866
+ end
867
+
868
+ def calc_hpd_counter_value(object, type, value)
869
+ # https://github.com/prometheus-community/windows_exporter/blob/master/collector/perflib.go
870
+
871
+ case type
872
+ when PERF_ELAPSED_TIME
873
+ return (value - WINDOWS_EPOCH) / object.perf_freq
874
+ when PERF_100NSEC_TIMER, PERF_PRECISION_100NS_TIMER
875
+ return value * TICKS_TO_SECONDS_SCALE_FACTOR
876
+ else
877
+ return value
878
+ end
879
+ end
880
+ end
881
+ end
882
+ end