mirror of
https://github.com/prometheus-community/windows_exporter.git
synced 2026-10-17 01:59:04 +02:00
netframework: add process_id to all metrics and process filters (#2767)
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
This commit is contained in:
@@ -2,6 +2,8 @@
|
||||
|
||||
The netframework collector exposes .NET Framework performance counters using PDH. Raw counter values preserve the existing metric types and units; counters named per second are published as cumulative values where specified below. The `_Global_` instance is excluded. Instance occurrence suffixes distinguish processes with the same name within a scrape and are not stable process identifiers.
|
||||
|
||||
Every metric has a `process_id` label with the ID of the process, so that the series can be joined with the metrics of the `process` collector, for example for IIS worker processes (`w3wp`). Only the `.NET CLR Memory` object has a `Process ID` counter. The collector reads it in the same sample as the other objects, also if `clrmemory` is disabled, and gives each instance of the other objects the process ID of the `.NET CLR Memory` instance with the same name and occurrence. If `.NET CLR Memory` has no valid process ID for an instance, its metrics have an empty `process_id`; the `process` label still keeps the series apart.
|
||||
|
||||
For compatibility, JIT and runtime security check time metrics retain their existing raw numerator divided by the performance-clock frequency. The GC time metric uses 100 times its raw numerator divided by the raw base; samples with a zero base omit that metric while retaining the other valid metrics. Missing performance counter objects return no data; initialization errors for missing individual counters are reported.
|
||||
|
||||
| | |
|
||||
@@ -16,96 +18,116 @@ For compatibility, JIT and runtime security check time metrics retain their exis
|
||||
|
||||
Comma-separated list of collectors to use. Defaults to all, if not specified. Supported values are: `clrexceptions`, `clrinterop`, `clrjit`, `clrloading`, `clrlocksandthreads`, `clrmemory`, `clrremoting`, `clrsecurity`.
|
||||
|
||||
### `--collector.netframework.process-include`
|
||||
|
||||
Regular expression of processes to include. The process name must both match `process-include` and not match `process-exclude` to be included. Default: `.+`
|
||||
|
||||
### `--collector.netframework.process-exclude`
|
||||
|
||||
Regular expression of processes to exclude. The process name must both match `process-include` and not match `process-exclude` to be included. Default: empty
|
||||
|
||||
The expressions must match the whole process name, which is the image name without `.exe`, as the `.NET CLR` objects report it. Like the filters of the `process` collector, they match the name without the occurrence suffix that the collector adds to processes with the same name, so `--collector.netframework.process-include="w3wp"` includes every IIS worker process. The expressions are case-sensitive.
|
||||
|
||||
If the `ProcessNameFormat` value of the registry key `HKLM\SYSTEM\CurrentControlSet\Services\.NETFramework\Performance` is `1`, the `.NET CLR` objects name instances after the image and the process ID, for example `w3wp_1234`. Then `--collector.netframework.process-include="w3wp"` matches no process; use `w3wp_.*`. The process ID in the instance name also makes the names unique, so the collector does not depend on the order of the instances to match the process IDs.
|
||||
|
||||
In the configuration file, the keys are `process-include` and `process-exclude`:
|
||||
|
||||
```yaml
|
||||
collector:
|
||||
netframework:
|
||||
process-include: "w3wp"
|
||||
```
|
||||
|
||||
## Metrics
|
||||
|
||||
### CLR Exceptions
|
||||
|
||||
| Name | Description | Type | Labels |
|
||||
|-----------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|-----------|
|
||||
| `windows_netframework_clrexceptions_exceptions_thrown_total` | Displays the total number of exceptions thrown since the application started. This includes both .NET exceptions and unmanaged exceptions that are converted into .NET exceptions. | counter | `process` |
|
||||
| `windows_netframework_clrexceptions_exceptions_filters_total` | Displays the total number of .NET exception filters executed. An exception filter evaluates regardless of whether an exception is handled. | counter | `process` |
|
||||
| `windows_netframework_clrexceptions_exceptions_finallys_total` | Displays the total number of finally blocks executed. Only the finally blocks executed for an exception are counted; finally blocks on normal code paths are not counted by this counter. | counter | `process` |
|
||||
| `windows_netframework_clrexceptions_throw_to_catch_depth_total` | Displays the total number of stack frames traversed, from the frame that threw the exception to the frame that handled the exception. | counter | `process` |
|
||||
| Name | Description | Type | Labels |
|
||||
|-----------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|-------------------------|
|
||||
| `windows_netframework_clrexceptions_exceptions_thrown_total` | Displays the total number of exceptions thrown since the application started. This includes both .NET exceptions and unmanaged exceptions that are converted into .NET exceptions. | counter | `process`, `process_id` |
|
||||
| `windows_netframework_clrexceptions_exceptions_filters_total` | Displays the total number of .NET exception filters executed. An exception filter evaluates regardless of whether an exception is handled. | counter | `process`, `process_id` |
|
||||
| `windows_netframework_clrexceptions_exceptions_finallys_total` | Displays the total number of finally blocks executed. Only the finally blocks executed for an exception are counted; finally blocks on normal code paths are not counted by this counter. | counter | `process`, `process_id` |
|
||||
| `windows_netframework_clrexceptions_throw_to_catch_depth_total` | Displays the total number of stack frames traversed, from the frame that threw the exception to the frame that handled the exception. | counter | `process`, `process_id` |
|
||||
|
||||
### CLR Interop
|
||||
|
||||
| Name | Description | Type | Labels |
|
||||
|---------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|-----------|
|
||||
| `windows_netframework_clrinterop_com_callable_wrappers_total` | Displays the current number of COM callable wrappers (CCWs). A CCW is a proxy for a managed object being referenced from an unmanaged COM client. | counter | `process` |
|
||||
| `windows_netframework_clrinterop_interop_marshalling_total` | Displays the total number of times arguments and return values have been marshaled from managed to unmanaged code, and vice versa, since the application started. | counter | `process` |
|
||||
| `windows_netframework_clrinterop_interop_stubs_created_total` | Displays the current number of stubs created by the common language runtime. Stubs are responsible for marshaling arguments and return values from managed to unmanaged code, and vice versa, during a COM interop call or a platform invoke call. | counter | `process` |
|
||||
| Name | Description | Type | Labels |
|
||||
|---------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|-------------------------|
|
||||
| `windows_netframework_clrinterop_com_callable_wrappers_total` | Displays the current number of COM callable wrappers (CCWs). A CCW is a proxy for a managed object being referenced from an unmanaged COM client. | counter | `process`, `process_id` |
|
||||
| `windows_netframework_clrinterop_interop_marshalling_total` | Displays the total number of times arguments and return values have been marshaled from managed to unmanaged code, and vice versa, since the application started. | counter | `process`, `process_id` |
|
||||
| `windows_netframework_clrinterop_interop_stubs_created_total` | Displays the current number of stubs created by the common language runtime. Stubs are responsible for marshaling arguments and return values from managed to unmanaged code, and vice versa, during a COM interop call or a platform invoke call. | counter | `process`, `process_id` |
|
||||
|
||||
### CLR JIT
|
||||
|
||||
| Name | Description | Type | Labels |
|
||||
|-----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|-----------|
|
||||
| `windows_netframework_clrjit_jit_methods_total` | Displays the total number of methods JIT-compiled since the application started. This counter does not include pre-JIT-compiled methods. | counter | `process` |
|
||||
| `windows_netframework_clrjit_jit_time_ratio` | Displays the ratio (0-1) of elapsed time spent in JIT compilation since the last JIT compilation phase. This counter is updated at the end of every JIT compilation phase. A JIT compilation phase occurs when a method and its dependencies are compiled. | gauge | `process` |
|
||||
| `windows_netframework_clrjit_jit_standard_failures_total` | Displays the peak number of methods the JIT compiler has failed to compile since the application started. This failure can occur if the MSIL cannot be verified or if there is an internal error in the JIT compiler. | counter | `process` |
|
||||
| `windows_netframework_clrjit_jit_il_bytes_total` | Displays the total number of Microsoft intermediate language (MSIL) bytes compiled by the just-in-time (JIT) compiler since the application started | counter | `process` |
|
||||
| Name | Description | Type | Labels |
|
||||
|-----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|-------------------------|
|
||||
| `windows_netframework_clrjit_jit_methods_total` | Displays the total number of methods JIT-compiled since the application started. This counter does not include pre-JIT-compiled methods. | counter | `process`, `process_id` |
|
||||
| `windows_netframework_clrjit_jit_time_ratio` | Displays the ratio (0-1) of elapsed time spent in JIT compilation since the last JIT compilation phase. This counter is updated at the end of every JIT compilation phase. A JIT compilation phase occurs when a method and its dependencies are compiled. | gauge | `process`, `process_id` |
|
||||
| `windows_netframework_clrjit_jit_standard_failures_total` | Displays the peak number of methods the JIT compiler has failed to compile since the application started. This failure can occur if the MSIL cannot be verified or if there is an internal error in the JIT compiler. | counter | `process`, `process_id` |
|
||||
| `windows_netframework_clrjit_jit_il_bytes_total` | Displays the total number of Microsoft intermediate language (MSIL) bytes compiled by the just-in-time (JIT) compiler since the application started | counter | `process`, `process_id` |
|
||||
|
||||
### CLR Loading
|
||||
|
||||
| Name | Description | Type | Labels |
|
||||
|-------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|-----------|
|
||||
| `windows_netframework_clrloading_loader_heap_size_bytes` | Displays the current size, in bytes, of the memory committed by the class loader across all application domains. Committed memory is the physical space reserved in the disk paging file. | gauge | `process` |
|
||||
| `windows_netframework_clrloading_appdomains_loaded_current` | Displays the current number of application domains loaded in this application. | gauge | `process` |
|
||||
| `windows_netframework_clrloading_assemblies_loaded_current` | Displays the current number of assemblies loaded across all application domains in the currently running application. If the assembly is loaded as domain-neutral from multiple application domains, this counter is incremented only once. | gauge | `process` |
|
||||
| `windows_netframework_clrloading_classes_loaded_current` | Displays the current number of classes loaded in all assemblies. | gauge | `process` |
|
||||
| `windows_netframework_clrloading_appdomains_loaded_total` | Displays the peak number of application domains loaded since the application started. | counter | `process` |
|
||||
| `windows_netframework_clrloading_appdomains_unloaded_total` | Displays the total number of application domains unloaded since the application started. If an application domain is loaded and unloaded multiple times, this counter increments each time the application domain is unloaded. | counter | `process` |
|
||||
| `windows_netframework_clrloading_assemblies_loaded_total` | Displays the total number of assemblies loaded since the application started. If the assembly is loaded as domain-neutral from multiple application domains, this counter is incremented only once. | counter | `process` |
|
||||
| `windows_netframework_clrloading_classes_loaded_total` | Displays the cumulative number of classes loaded in all assemblies since the application started. | counter | `process` |
|
||||
| `windows_netframework_clrloading_class_load_failures_total` | Displays the peak number of classes that have failed to load since the application started. | counter | `process` |
|
||||
| Name | Description | Type | Labels |
|
||||
|-------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|-------------------------|
|
||||
| `windows_netframework_clrloading_loader_heap_size_bytes` | Displays the current size, in bytes, of the memory committed by the class loader across all application domains. Committed memory is the physical space reserved in the disk paging file. | gauge | `process`, `process_id` |
|
||||
| `windows_netframework_clrloading_appdomains_loaded_current` | Displays the current number of application domains loaded in this application. | gauge | `process`, `process_id` |
|
||||
| `windows_netframework_clrloading_assemblies_loaded_current` | Displays the current number of assemblies loaded across all application domains in the currently running application. If the assembly is loaded as domain-neutral from multiple application domains, this counter is incremented only once. | gauge | `process`, `process_id` |
|
||||
| `windows_netframework_clrloading_classes_loaded_current` | Displays the current number of classes loaded in all assemblies. | gauge | `process`, `process_id` |
|
||||
| `windows_netframework_clrloading_appdomains_loaded_total` | Displays the peak number of application domains loaded since the application started. | counter | `process`, `process_id` |
|
||||
| `windows_netframework_clrloading_appdomains_unloaded_total` | Displays the total number of application domains unloaded since the application started. If an application domain is loaded and unloaded multiple times, this counter increments each time the application domain is unloaded. | counter | `process`, `process_id` |
|
||||
| `windows_netframework_clrloading_assemblies_loaded_total` | Displays the total number of assemblies loaded since the application started. If the assembly is loaded as domain-neutral from multiple application domains, this counter is incremented only once. | counter | `process`, `process_id` |
|
||||
| `windows_netframework_clrloading_classes_loaded_total` | Displays the cumulative number of classes loaded in all assemblies since the application started. | counter | `process`, `process_id` |
|
||||
| `windows_netframework_clrloading_class_load_failures_total` | Displays the peak number of classes that have failed to load since the application started. | counter | `process`, `process_id` |
|
||||
|
||||
### CLR Locks and Threads
|
||||
|
||||
| Name | Description | Type | Labels |
|
||||
|----------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|-----------|
|
||||
| `windows_netframework_clrlocksandthreads_current_queue_length` | Displays the total number of threads that are currently waiting to acquire a managed lock in the application. | gauge | `process` |
|
||||
| `windows_netframework_clrlocksandthreads_current_logical_threads` | Displays the number of current managed thread objects in the application. This counter maintains the count of both running and stopped threads. | gauge | `process` |
|
||||
| `windows_netframework_clrlocksandthreads_physical_threads_current` | Displays the number of native operating system threads created and owned by the common language runtime to act as underlying threads for managed thread objects. This counter's value does not include the threads used by the runtime in its internal operations; it is a subset of the threads in the operating system process. | gauge | `process` |
|
||||
| `windows_netframework_clrlocksandthreads_recognized_threads_current` | Displays the number of threads that are currently recognized by the runtime. These threads are associated with a corresponding managed thread object. The runtime does not create these threads, but they have run inside the runtime at least once. | gauge | `process` |
|
||||
| `windows_netframework_clrlocksandthreads_recognized_threads_total` | Displays the total number of threads that have been recognized by the runtime since the application started. These threads are associated with a corresponding managed thread object. The runtime does not create these threads, but they have run inside the runtime at least once. | counter | `process` |
|
||||
| `windows_netframework_clrlocksandthreads_queue_length_total` | Displays the total number of threads that waited to acquire a managed lock since the application started. | counter | `process` |
|
||||
| `windows_netframework_clrlocksandthreads_contentions_total` | Displays the total number of times that threads in the runtime have attempted to acquire a managed lock unsuccessfully. | counter | `process` |
|
||||
| Name | Description | Type | Labels |
|
||||
|----------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|-------------------------|
|
||||
| `windows_netframework_clrlocksandthreads_current_queue_length` | Displays the total number of threads that are currently waiting to acquire a managed lock in the application. | gauge | `process`, `process_id` |
|
||||
| `windows_netframework_clrlocksandthreads_current_logical_threads` | Displays the number of current managed thread objects in the application. This counter maintains the count of both running and stopped threads. | gauge | `process`, `process_id` |
|
||||
| `windows_netframework_clrlocksandthreads_physical_threads_current` | Displays the number of native operating system threads created and owned by the common language runtime to act as underlying threads for managed thread objects. This counter's value does not include the threads used by the runtime in its internal operations; it is a subset of the threads in the operating system process. | gauge | `process`, `process_id` |
|
||||
| `windows_netframework_clrlocksandthreads_recognized_threads_current` | Displays the number of threads that are currently recognized by the runtime. These threads are associated with a corresponding managed thread object. The runtime does not create these threads, but they have run inside the runtime at least once. | gauge | `process`, `process_id` |
|
||||
| `windows_netframework_clrlocksandthreads_recognized_threads_total` | Displays the total number of threads that have been recognized by the runtime since the application started. These threads are associated with a corresponding managed thread object. The runtime does not create these threads, but they have run inside the runtime at least once. | counter | `process`, `process_id` |
|
||||
| `windows_netframework_clrlocksandthreads_queue_length_total` | Displays the total number of threads that waited to acquire a managed lock since the application started. | counter | `process`, `process_id` |
|
||||
| `windows_netframework_clrlocksandthreads_contentions_total` | Displays the total number of times that threads in the runtime have attempted to acquire a managed lock unsuccessfully. | counter | `process`, `process_id` |
|
||||
|
||||
### CLR Memory
|
||||
|
||||
| Name | Description | Type | Labels |
|
||||
|----------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|-----------|
|
||||
| `windows_netframework_clrmemory_allocated_bytes_total` | Displays the total number of bytes allocated on the garbage collection heap. | counter | `process`, `process_id` |
|
||||
| `windows_netframework_clrmemory_finalization_survivors` | Displays the number of garbage-collected objects that survive a collection because they are waiting to be finalized. | gauge | `process`, `process_id` |
|
||||
| Name | Description | Type | Labels |
|
||||
|----------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|---------------------------------|
|
||||
| `windows_netframework_clrmemory_allocated_bytes_total` | Displays the total number of bytes allocated on the garbage collection heap. | counter | `process`, `process_id` |
|
||||
| `windows_netframework_clrmemory_finalization_survivors` | Displays the number of garbage-collected objects that survive a collection because they are waiting to be finalized. | gauge | `process`, `process_id` |
|
||||
| `windows_netframework_clrmemory_heap_size_bytes` | Displays the maximum bytes that can be allocated; it does not indicate the current number of bytes allocated. | gauge | `process`, `process_id`, `area` |
|
||||
| `windows_netframework_clrmemory_promoted_bytes` | Displays the bytes that were promoted from the generation to the next one during the last GC. Memory is promoted when it survives a garbage collection. | gauge | `process`, `process_id`, `area` |
|
||||
| `windows_netframework_clrmemory_number_gc_handles` | Displays the current number of garbage collection handles in use. Garbage collection handles are handles to resources external to the common language runtime and the managed environment. | gauge | `process`, `process_id` |
|
||||
| `windows_netframework_clrmemory_number_gc_handles` | Displays the current number of garbage collection handles in use. Garbage collection handles are handles to resources external to the common language runtime and the managed environment. | gauge | `process`, `process_id` |
|
||||
| `windows_netframework_clrmemory_collections_total` | Displays the number of times the generation objects are garbage collected since the application started. | counter | `process`, `process_id`, `area` |
|
||||
| `windows_netframework_clrmemory_induced_gc_total` | Displays the peak number of times garbage collection was performed because of an explicit call to GC.Collect. | counter | `process`, `process_id` |
|
||||
| `windows_netframework_clrmemory_number_pinned_objects` | Displays the number of pinned objects encountered in the last garbage collection. | gauge | `process`, `process_id` |
|
||||
| `windows_netframework_clrmemory_number_sink_blocksinuse` | Displays the current number of synchronization blocks in use. Synchronization blocks are per-object data structures allocated for storing synchronization information. They hold weak references to managed objects and must be scanned by the garbage collector. | gauge | `process`, `process_id` |
|
||||
| `windows_netframework_clrmemory_committed_bytes` | Displays the amount of virtual memory, in bytes, currently committed by the garbage collector. Committed memory is the physical memory for which space has been reserved in the disk paging file. | gauge | `process`, `process_id` |
|
||||
| `windows_netframework_clrmemory_reserved_bytes` | Displays the amount of virtual memory, in bytes, currently reserved by the garbage collector. Reserved memory is the virtual memory space reserved for the application when no disk or main memory pages have been used. | gauge | `process`, `process_id` |
|
||||
| `windows_netframework_clrmemory_gc_time_ratio` | Displays the ratio (0-1) of elapsed time spent performing a garbage collection since the end of the last garbage collection cycle. Left out when Windows reports no base value. | gauge | `process`, `process_id` |
|
||||
| `windows_netframework_clrmemory_induced_gc_total` | Displays the peak number of times garbage collection was performed because of an explicit call to GC.Collect. | counter | `process`, `process_id` |
|
||||
| `windows_netframework_clrmemory_number_pinned_objects` | Displays the number of pinned objects encountered in the last garbage collection. | gauge | `process`, `process_id` |
|
||||
| `windows_netframework_clrmemory_number_sink_blocksinuse` | Displays the current number of synchronization blocks in use. Synchronization blocks are per-object data structures allocated for storing synchronization information. They hold weak references to managed objects and must be scanned by the garbage collector. | gauge | `process`, `process_id` |
|
||||
| `windows_netframework_clrmemory_committed_bytes` | Displays the amount of virtual memory, in bytes, currently committed by the garbage collector. Committed memory is the physical memory for which space has been reserved in the disk paging file. | gauge | `process`, `process_id` |
|
||||
| `windows_netframework_clrmemory_reserved_bytes` | Displays the amount of virtual memory, in bytes, currently reserved by the garbage collector. Reserved memory is the virtual memory space reserved for the application when no disk or main memory pages have been used. | gauge | `process`, `process_id` |
|
||||
| `windows_netframework_clrmemory_gc_time_ratio` | Displays the ratio (0-1) of elapsed time spent performing a garbage collection since the end of the last garbage collection cycle. Left out when Windows reports no base value. | gauge | `process`, `process_id` |
|
||||
|
||||
### CLR Remoting
|
||||
|
||||
| Name | Description | Type | Labels |
|
||||
|-----------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------|---------|-----------|
|
||||
| `windows_netframework_clrremoting_channels_total` | Displays the total number of remoting channels registered across all application domains since application started. | counter | `process` |
|
||||
| `windows_netframework_clrremoting_context_bound_classes_loaded` | Displays the current number of context-bound classes that are loaded. | gauge | `process` |
|
||||
| `windows_netframework_clrremoting_context_bound_objects_total` | Displays the total number of context-bound objects allocated. | counter | `process` |
|
||||
| `windows_netframework_clrremoting_context_proxies_total` | Displays the total number of remoting proxy objects in this process since it started. | counter | `process` |
|
||||
| `windows_netframework_clrremoting_contexts` | Displays the current number of remoting contexts in the application. | gauge | `process` |
|
||||
| `windows_netframework_clrremoting_remote_calls_total` | Displays the total number of remote procedure calls invoked since the application started. | counter | `process` |
|
||||
| Name | Description | Type | Labels |
|
||||
|-----------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------|---------|-------------------------|
|
||||
| `windows_netframework_clrremoting_channels_total` | Displays the total number of remoting channels registered across all application domains since application started. | counter | `process`, `process_id` |
|
||||
| `windows_netframework_clrremoting_context_bound_classes_loaded` | Displays the current number of context-bound classes that are loaded. | gauge | `process`, `process_id` |
|
||||
| `windows_netframework_clrremoting_context_bound_objects_total` | Displays the total number of context-bound objects allocated. | counter | `process`, `process_id` |
|
||||
| `windows_netframework_clrremoting_context_proxies_total` | Displays the total number of remoting proxy objects in this process since it started. | counter | `process`, `process_id` |
|
||||
| `windows_netframework_clrremoting_contexts` | Displays the current number of remoting contexts in the application. | gauge | `process`, `process_id` |
|
||||
| `windows_netframework_clrremoting_remote_calls_total` | Displays the total number of remote procedure calls invoked since the application started. | counter | `process`, `process_id` |
|
||||
|
||||
### CLR Security
|
||||
|
||||
| Name | Description | Type | Labels |
|
||||
|-----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------|---------|-----------|
|
||||
| `windows_netframework_clrsecurity_link_time_checks_total` | Displays the total number of link-time code access security checks since the application started. | counter | `process` |
|
||||
| `windows_netframework_clrsecurity_rt_checks_time_ratio` | Displays the ratio (0-1) of time spent performing runtime code access security checks in the last sample. | gauge | `process` |
|
||||
| `windows_netframework_clrsecurity_stack_walk_depth` | Displays the depth of the stack during that last runtime code access security check. | gauge | `process` |
|
||||
| `windows_netframework_clrsecurity_runtime_checks_total` | Displays the total number of runtime code access security checks performed since the application started. | counter | `process` |
|
||||
| Name | Description | Type | Labels |
|
||||
|-----------------------------------------------------------|-----------------------------------------------------------------------------------------------------------|---------|-------------------------|
|
||||
| `windows_netframework_clrsecurity_link_time_checks_total` | Displays the total number of link-time code access security checks since the application started. | counter | `process`, `process_id` |
|
||||
| `windows_netframework_clrsecurity_rt_checks_time_ratio` | Displays the ratio (0-1) of time spent performing runtime code access security checks in the last sample. | gauge | `process`, `process_id` |
|
||||
| `windows_netframework_clrsecurity_stack_walk_depth` | Displays the depth of the stack during that last runtime code access security check. | gauge | `process`, `process_id` |
|
||||
| `windows_netframework_clrsecurity_runtime_checks_total` | Displays the total number of runtime code access security checks performed since the application started. | counter | `process`, `process_id` |
|
||||
|
||||
### Example metric
|
||||
_This collector does not yet have explained examples, we would appreciate your help adding them!_
|
||||
|
||||
@@ -901,7 +901,7 @@ windows_service_state{state="running"} == 0
|
||||
|
||||
- `windows_os_info` has an `installation_type` label, for example `Server` or `Client`.
|
||||
- The IIS HTTP request queue metrics (`windows_iis_http_requests_*`) have the label `app` instead of `site`. The value is unchanged: it always was the application pool that owns the HTTP.sys request queue, not a site. They are now filtered with `--collector.iis.app-include` and `--collector.iis.app-exclude` instead of the site filters.
|
||||
- The `windows_netframework_clrmemory_*` metrics have a `process_id` label.
|
||||
- All `windows_netframework_*` metrics have a `process_id` label, so processes with the same name, for example IIS worker processes (`w3wp`), can be joined with the `process` collector metrics. Only `.NET CLR Memory` reports the process ID; the other metrics take it from the `.NET CLR Memory` instance of the same sample and have an empty `process_id` if there is none. The new `--collector.netframework.process-include` and `--collector.netframework.process-exclude` flags filter the processes. The label changes the series identity: a restarted process, for example a recycled `w3wp`, starts a new series instead of continuing `process="w3wp"`. To keep the 0.31 series, aggregate with `without (process_id)` or `by (process)`.
|
||||
- In the `windows_hyperv_dynamic_memory_vm_*` metrics, unnamed VM instances have `vm="(unknown)"` (or `(unknown)#N`) instead of `vm="------"`.
|
||||
- VMs with the same name are published apart in the `windows_hyperv_dynamic_memory_vm_*`, `windows_hyperv_vid_*`, `windows_hyperv_hypervisor_virtual_processor_*` and `windows_hyperv_replica_vm_*` performance counter metrics: the first keeps its name, later ones have `vm="<name>#1"`, `vm="<name>#2"` and so on. Virtual switches with the same name get the same suffixes in the `windows_hyperv_vswitch_*` metrics. In 0.31 they overwrote each other. The 0.32 betas put the suffix of a duplicate virtual processor into the `core` label (`core="0#1"`); `core` is now always the processor number.
|
||||
- `windows_terminal_services_connection_broker_performance_total` no longer has a `connection="Pending"` series. It was the current number of pending connections, published as a counter. Use the gauge `windows_terminal_services_connection_broker_pending_connections`.
|
||||
@@ -922,7 +922,7 @@ These metrics keep their names, but 0.31 reported wrong values. Graphs show a st
|
||||
- `windows_remote_fx_gfx_frames_skipped_insufficient_resource_total` with `resource="client"` and `resource="server"` now count the matching frames. In 0.31 the two label values were swapped.
|
||||
- `windows_dns_zone_transfer_requests_sent_total{qtype="soa"}` now counts zone transfer SOA requests sent. In 0.31 it reported zone transfer requests received.
|
||||
- `windows_dns_zone_transfer_success_received_total{qtype="incremental",protocol="udp"}` now counts successful UDP incremental zone transfers. In 0.31 it repeated the TCP count.
|
||||
- `windows_netframework_clrmemory_gc_time_ratio` (renamed from `windows_netframework_clrmemory_gc_time_percent`) no longer overflows, and it is left out when Windows reports no base value. The `netframework` collector now reads the CLR counters through PDH instead of WMI; labels are unchanged, see [metric reference](#metric-reference) for the renamed metrics.
|
||||
- `windows_netframework_clrmemory_gc_time_ratio` (renamed from `windows_netframework_clrmemory_gc_time_percent`) no longer overflows, and it is left out when Windows reports no base value. The `netframework` collector now reads the CLR counters through PDH instead of WMI. Labels are unchanged except for `process_id` (see [label changes](#label-changes)); see [metric reference](#metric-reference) for the renamed metrics.
|
||||
- The IIS URI cache flush and kernel cache item metrics read the right Windows counters, and the metadata cache hit metrics are no longer always `0`.
|
||||
- `windows_pagefile_limit_bytes` and `windows_pagefile_free_bytes` read the paging file size from the `Paging File` counter instead of the file system. In 0.31 a paging file that could not be read reported a size of `0` and a negative free space; it is now left out.
|
||||
- `windows_logical_disk_readonly`, the FSRM quota template information, the SMTP general failure totals and the Hyper-V root partition interrupt mappings were documented but never exported. They are exported now.
|
||||
|
||||
@@ -33,6 +33,22 @@ below comes from a CI probe.
|
||||
- The `.NET CLR Memory` sizes and counts are `PERF_COUNTER_LARGE_RAWCOUNT`
|
||||
gauges in bytes or counts; `# Gen N Collections` and `# Induced GC` are
|
||||
cumulative. The object also has a `Process ID` counter.
|
||||
- Only `.NET CLR Memory` has `Process ID`. For the `process_id` label of the
|
||||
other objects, `Build` adds a second collector for that counter to the
|
||||
shared query whenever a sub-collector other than `clrmemory` is enabled,
|
||||
and `Collect` maps the Windows instance name and `Occurrence` (before
|
||||
`pdh.SuffixDuplicates`) to the process ID before the sub-collectors start.
|
||||
This relies on an undocumented assumption: the provider lists the
|
||||
processes in the same order in every object of one sample. Evidence:
|
||||
`PdhCollectQueryData` does one `RegQueryValueEx(HKEY_PERFORMANCE_DATA)`
|
||||
per collect for the whole query, and the CLR provider writes every
|
||||
object of that call from the same instance list. Separate queries per
|
||||
object, or a provider that orders objects differently, would break the
|
||||
join for duplicate names. With `ProcessNameFormat=1` (instance names like
|
||||
`w3wp_1234`) names are unique and the order does not matter. An instance without a valid `Process ID` gets
|
||||
`process_id=""`. Process filters match the name before the occurrence
|
||||
suffix and are applied before the suffixes are added, so all objects of a
|
||||
sample get the same suffixes.
|
||||
|
||||
## Counter reference
|
||||
|
||||
|
||||
@@ -22,7 +22,9 @@ import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"maps"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -31,6 +33,8 @@ import (
|
||||
"github.com/prometheus-community/windows_exporter/internal/mi"
|
||||
"github.com/prometheus-community/windows_exporter/internal/pdh"
|
||||
"github.com/prometheus-community/windows_exporter/internal/scrapecache"
|
||||
"github.com/prometheus-community/windows_exporter/internal/types"
|
||||
"github.com/prometheus-community/windows_exporter/internal/utils"
|
||||
"github.com/prometheus-community/windows_exporter/internal/utils/recovery"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
@@ -43,7 +47,9 @@ type perfCollector[T any] interface {
|
||||
const Name = "netframework"
|
||||
|
||||
type Config struct {
|
||||
CollectorsEnabled []string `yaml:"enabled"`
|
||||
CollectorsEnabled []string `yaml:"enabled"`
|
||||
ProcessInclude *regexp.Regexp `yaml:"process-include"`
|
||||
ProcessExclude *regexp.Regexp `yaml:"process-exclude"`
|
||||
}
|
||||
|
||||
//nolint:gochecknoglobals
|
||||
@@ -58,6 +64,8 @@ var ConfigDefaults = Config{
|
||||
collectorClrRemoting,
|
||||
collectorClrSecurity,
|
||||
},
|
||||
ProcessInclude: types.RegExpAny,
|
||||
ProcessExclude: types.RegExpEmpty,
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -92,6 +100,14 @@ type Collector struct {
|
||||
perfClrMemory perfCollector[perfDataClrMemory]
|
||||
perfClrRemoting perfCollector[perfDataClrRemoting]
|
||||
perfClrSecurity perfCollector[perfDataClrSecurity]
|
||||
// perfClrProcessID reads the Process ID counter of the .NET CLR Memory
|
||||
// object for the sub-collectors of the other objects, see processIDs.
|
||||
perfClrProcessID perfCollector[perfDataClrProcessID]
|
||||
processIDRows []perfDataClrProcessID
|
||||
// processIDs maps the instances of the current sample to their process
|
||||
// ID. Collect fills it before it starts the sub-collectors, which only
|
||||
// read it.
|
||||
processIDs map[processKey]string
|
||||
|
||||
collectorFns []func(ch chan<- prometheus.Metric, scrape *labelcache.Scrape, maxScrapeDuration time.Duration) error
|
||||
|
||||
@@ -170,6 +186,14 @@ func New(config *Config) *Collector {
|
||||
config.CollectorsEnabled = ConfigDefaults.CollectorsEnabled
|
||||
}
|
||||
|
||||
if config.ProcessInclude == nil {
|
||||
config.ProcessInclude = ConfigDefaults.ProcessInclude
|
||||
}
|
||||
|
||||
if config.ProcessExclude == nil {
|
||||
config.ProcessExclude = ConfigDefaults.ProcessExclude
|
||||
}
|
||||
|
||||
c := &Collector{
|
||||
config: *config,
|
||||
}
|
||||
@@ -190,6 +214,18 @@ func NewWithFlags(app *kingpin.Application) *Collector {
|
||||
"Comma-separated list of collectors to use. Defaults to all, if not specified.",
|
||||
).Default(strings.Join(ConfigDefaults.CollectorsEnabled, ",")).StringVar(&collectorsEnabled)
|
||||
|
||||
utils.RegexpFlag(app,
|
||||
"collector.netframework.process-exclude",
|
||||
"Regexp of processes to exclude. Process name must both match include and not match exclude to be included.",
|
||||
"", &c.config.ProcessExclude,
|
||||
)
|
||||
|
||||
utils.RegexpFlag(app,
|
||||
"collector.netframework.process-include",
|
||||
"Regexp of processes to include. Process name must both match include and not match exclude to be included.",
|
||||
".+", &c.config.ProcessInclude,
|
||||
)
|
||||
|
||||
app.Action(func(*kingpin.ParseContext) error {
|
||||
c.config.CollectorsEnabled = strings.Split(collectorsEnabled, ",")
|
||||
|
||||
@@ -216,6 +252,14 @@ func (c *Collector) Close() error {
|
||||
c.closeFns = nil
|
||||
c.collectorFns = nil
|
||||
|
||||
if c.perfClrProcessID != nil {
|
||||
c.perfClrProcessID.Close()
|
||||
c.perfClrProcessID = nil
|
||||
}
|
||||
|
||||
c.processIDRows = nil
|
||||
c.processIDs = nil
|
||||
|
||||
c.perfQuery.Close()
|
||||
c.perfQuery = nil
|
||||
|
||||
@@ -340,6 +384,19 @@ func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error {
|
||||
c.collectorFns = append(c.collectorFns, subCollectors[name].collect)
|
||||
}
|
||||
|
||||
// Only the .NET CLR Memory object has a Process ID counter. The
|
||||
// clrmemory rows carry their own, the other sub-collectors look it up.
|
||||
// With clrmemory enabled, Process ID is read twice on purpose: the
|
||||
// sub-collectors run concurrently, so they cannot share the clrmemory rows.
|
||||
if slices.ContainsFunc(collectorsEnabled, func(name string) bool { return name != collectorClrMemory }) {
|
||||
c.perfClrProcessID, err = newPerfCollector[perfDataClrProcessID](c.logger, c.perfQuery, ".NET CLR Memory")
|
||||
if err != nil {
|
||||
_ = c.Close()
|
||||
|
||||
return fmt.Errorf("failed to build the process ID lookup: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -354,6 +411,10 @@ func (c *Collector) Collect(ch chan<- prometheus.Metric, maxScrapeDuration time.
|
||||
}
|
||||
}
|
||||
|
||||
// The sub-collectors publish their metrics even if the process IDs are
|
||||
// unknown, with an empty process_id label.
|
||||
processIDErr := c.collectProcessIDs()
|
||||
|
||||
scrape := c.labels.Scrape()
|
||||
|
||||
var g recovery.Group
|
||||
@@ -364,7 +425,70 @@ func (c *Collector) Collect(ch chan<- prometheus.Metric, maxScrapeDuration time.
|
||||
})
|
||||
}
|
||||
|
||||
return g.Wait()
|
||||
return errors.Join(processIDErr, g.Wait())
|
||||
}
|
||||
|
||||
// processKey identifies an instance of a sample by the name Windows reported
|
||||
// and its occurrence, before suffixDuplicateProcesses changes the name.
|
||||
type processKey struct {
|
||||
name string
|
||||
occurrence int
|
||||
}
|
||||
|
||||
type perfDataClrProcessID struct {
|
||||
Name string
|
||||
Occurrence int
|
||||
ProcessID float64 `perfdata:"Process ID"`
|
||||
}
|
||||
|
||||
// collectProcessIDs maps the instances of the .NET CLR Memory object of the
|
||||
// current sample to their process IDs. The join assumes, without
|
||||
// documentation to back it, that the n-th instance with a name is the same
|
||||
// process in every .NET CLR object of a sample: PdhCollectQueryData does one
|
||||
// RegQueryValueEx(HKEY_PERFORMANCE_DATA) per collect for the whole query,
|
||||
// and the CLR provider writes every object from the same instance list.
|
||||
// Sampling the objects in separate queries would break the join for
|
||||
// duplicate names. An instance without a valid Process ID has no
|
||||
// entry. Without any .NET process, the map is empty and the sub-collectors
|
||||
// report that there is no data.
|
||||
func (c *Collector) collectProcessIDs() error {
|
||||
if c.processIDs == nil {
|
||||
c.processIDs = make(map[processKey]string)
|
||||
} else {
|
||||
clear(c.processIDs)
|
||||
}
|
||||
|
||||
if c.perfClrProcessID == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := c.perfClrProcessID.Collect(&c.processIDRows); err != nil {
|
||||
if errors.Is(err, pdh.ErrNoData) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("failed to collect the process IDs of .NET CLR Memory: %w", err)
|
||||
}
|
||||
|
||||
for _, row := range c.processIDRows {
|
||||
if row.Name == "_Global_" {
|
||||
continue
|
||||
}
|
||||
|
||||
c.processIDs[processKey{name: row.Name, occurrence: row.Occurrence}] = strconv.FormatUint(uint64(row.ProcessID), 10)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// isProcessIncluded reports whether name, the process name as Windows reports
|
||||
// it without an occurrence suffix, passes the process filters.
|
||||
func (c *Collector) isProcessIncluded(name string) bool {
|
||||
if c.config.ProcessInclude != nil && !c.config.ProcessInclude.MatchString(name) {
|
||||
return false
|
||||
}
|
||||
|
||||
return c.config.ProcessExclude == nil || !c.config.ProcessExclude.MatchString(name)
|
||||
}
|
||||
|
||||
// clrInstance is implemented by the rows of the .NET CLR objects.
|
||||
@@ -372,6 +496,46 @@ type clrInstance interface {
|
||||
instance() (name *string, occurrence int)
|
||||
}
|
||||
|
||||
// filterProcesses removes the _Global_ instance and the processes that do not
|
||||
// pass the process filters from rows. The filters match the name that Windows
|
||||
// reports, so they apply to every process with that name, like the filters
|
||||
// of the process collector. Filtering before suffixDuplicateProcesses keeps
|
||||
// the suffixes the same in every object of a sample.
|
||||
func filterProcesses[T any, P interface {
|
||||
*T
|
||||
clrInstance
|
||||
}](c *Collector, rows []T) []T {
|
||||
filter := !utils.IsMatchAll(c.config.ProcessInclude) || !utils.IsMatchNone(c.config.ProcessExclude)
|
||||
|
||||
return slices.DeleteFunc(rows, func(row T) bool {
|
||||
name, _ := P(&row).instance()
|
||||
|
||||
return *name == "_Global_" || (filter && !c.isProcessIncluded(*name))
|
||||
})
|
||||
}
|
||||
|
||||
// prepareProcesses filters rows, looks up the process ID of every row and
|
||||
// then adds occurrence suffixes to duplicate process names. The process ID
|
||||
// of rows[i] is processIDs[i]; it is empty if the .NET CLR Memory object has
|
||||
// no valid Process ID for the instance, e.g. if clrmemory is disabled and
|
||||
// the object is unavailable. The process label still keeps the series apart.
|
||||
func prepareProcesses[T any, P interface {
|
||||
*T
|
||||
clrInstance
|
||||
}](c *Collector, rows []T) ([]T, []string) {
|
||||
rows = filterProcesses[T, P](c, rows)
|
||||
processIDs := make([]string, len(rows))
|
||||
|
||||
for i := range rows {
|
||||
name, occurrence := P(&rows[i]).instance()
|
||||
processIDs[i] = c.processIDs[processKey{name: *name, occurrence: occurrence}]
|
||||
}
|
||||
|
||||
suffixDuplicateProcesses[T, P](rows)
|
||||
|
||||
return rows, processIDs
|
||||
}
|
||||
|
||||
// suffixDuplicateProcesses keeps processes with the same name apart. The .NET
|
||||
// CLR objects name instances after the process image and report a name once
|
||||
// per process, e.g. "powershell" twice for two PowerShell processes. Later
|
||||
|
||||
@@ -30,25 +30,25 @@ func (c *Collector) describeClrExceptions() {
|
||||
c.numberOfExceptionsThrown = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrExceptions+"_exceptions_thrown_total"),
|
||||
"Displays the total number of exceptions thrown since the application started. This includes both .NET exceptions and unmanaged exceptions that are converted into .NET exceptions.",
|
||||
[]string{"process"},
|
||||
[]string{"process", "process_id"},
|
||||
nil,
|
||||
)
|
||||
c.numberOfFilters = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrExceptions+"_exceptions_filters_total"),
|
||||
"Displays the total number of .NET exception filters executed. An exception filter evaluates regardless of whether an exception is handled.",
|
||||
[]string{"process"},
|
||||
[]string{"process", "process_id"},
|
||||
nil,
|
||||
)
|
||||
c.numberOfFinally = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrExceptions+"_exceptions_finallys_total"),
|
||||
"Displays the total number of finally blocks executed. Only the finally blocks executed for an exception are counted; finally blocks on normal code paths are not counted by this counter.",
|
||||
[]string{"process"},
|
||||
[]string{"process", "process_id"},
|
||||
nil,
|
||||
)
|
||||
c.throwToCatchDepth = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrExceptions+"_throw_to_catch_depth_total"),
|
||||
"Displays the total number of stack frames traversed, from the frame that threw the exception to the frame that handled the exception.",
|
||||
[]string{"process"},
|
||||
[]string{"process", "process_id"},
|
||||
nil,
|
||||
)
|
||||
}
|
||||
@@ -80,18 +80,15 @@ func (c *Collector) collectClrExceptions(ch chan<- prometheus.Metric, scrape *la
|
||||
return fmt.Errorf("failed to collect .NET CLR Exceptions: %w", err)
|
||||
}
|
||||
|
||||
suffixDuplicateProcesses(dst)
|
||||
|
||||
for _, process := range dst {
|
||||
if process.Name == "_Global_" {
|
||||
continue
|
||||
}
|
||||
dst, processIDs := prepareProcesses(c, dst)
|
||||
|
||||
for i, process := range dst {
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
c.numberOfExceptionsThrown,
|
||||
prometheus.CounterValue,
|
||||
float64(process.NumberofExcepsThrown),
|
||||
process.Name,
|
||||
processIDs[i],
|
||||
)
|
||||
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
@@ -99,6 +96,7 @@ func (c *Collector) collectClrExceptions(ch chan<- prometheus.Metric, scrape *la
|
||||
prometheus.CounterValue,
|
||||
float64(process.NumberofFiltersPersec),
|
||||
process.Name,
|
||||
processIDs[i],
|
||||
)
|
||||
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
@@ -106,6 +104,7 @@ func (c *Collector) collectClrExceptions(ch chan<- prometheus.Metric, scrape *la
|
||||
prometheus.CounterValue,
|
||||
float64(process.NumberofFinallysPersec),
|
||||
process.Name,
|
||||
processIDs[i],
|
||||
)
|
||||
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
@@ -113,6 +112,7 @@ func (c *Collector) collectClrExceptions(ch chan<- prometheus.Metric, scrape *la
|
||||
prometheus.CounterValue,
|
||||
float64(process.ThrowToCatchDepthPersec),
|
||||
process.Name,
|
||||
processIDs[i],
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -30,19 +30,19 @@ func (c *Collector) describeClrInterop() {
|
||||
c.numberOfCCWs = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrInterop+"_com_callable_wrappers_total"),
|
||||
"Displays the current number of COM callable wrappers (CCWs). A CCW is a proxy for a managed object being referenced from an unmanaged COM client.",
|
||||
[]string{"process"},
|
||||
[]string{"process", "process_id"},
|
||||
nil,
|
||||
)
|
||||
c.numberOfMarshalling = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrInterop+"_interop_marshalling_total"),
|
||||
"Displays the total number of times arguments and return values have been marshaled from managed to unmanaged code, and vice versa, since the application started.",
|
||||
[]string{"process"},
|
||||
[]string{"process", "process_id"},
|
||||
nil,
|
||||
)
|
||||
c.numberOfStubs = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrInterop+"_interop_stubs_created_total"),
|
||||
"Displays the current number of stubs created by the common language runtime. Stubs are responsible for marshaling arguments and return values from managed to unmanaged code, and vice versa, during a COM interop call or a platform invoke call.",
|
||||
[]string{"process"},
|
||||
[]string{"process", "process_id"},
|
||||
nil,
|
||||
)
|
||||
}
|
||||
@@ -73,18 +73,15 @@ func (c *Collector) collectClrInterop(ch chan<- prometheus.Metric, scrape *label
|
||||
return fmt.Errorf("failed to collect .NET CLR Interop: %w", err)
|
||||
}
|
||||
|
||||
suffixDuplicateProcesses(dst)
|
||||
|
||||
for _, process := range dst {
|
||||
if process.Name == "_Global_" {
|
||||
continue
|
||||
}
|
||||
dst, processIDs := prepareProcesses(c, dst)
|
||||
|
||||
for i, process := range dst {
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
c.numberOfCCWs,
|
||||
prometheus.CounterValue,
|
||||
float64(process.NumberofCCWs),
|
||||
process.Name,
|
||||
processIDs[i],
|
||||
)
|
||||
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
@@ -92,6 +89,7 @@ func (c *Collector) collectClrInterop(ch chan<- prometheus.Metric, scrape *label
|
||||
prometheus.CounterValue,
|
||||
float64(process.Numberofmarshalling),
|
||||
process.Name,
|
||||
processIDs[i],
|
||||
)
|
||||
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
@@ -99,6 +97,7 @@ func (c *Collector) collectClrInterop(ch chan<- prometheus.Metric, scrape *label
|
||||
prometheus.CounterValue,
|
||||
float64(process.NumberofStubs),
|
||||
process.Name,
|
||||
processIDs[i],
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -30,25 +30,25 @@ func (c *Collector) describeClrJIT() {
|
||||
c.numberOfMethodsJitted = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrJIT+"_jit_methods_total"),
|
||||
"Displays the total number of methods JIT-compiled since the application started. This counter does not include pre-JIT-compiled methods.",
|
||||
[]string{"process"},
|
||||
[]string{"process", "process_id"},
|
||||
nil,
|
||||
)
|
||||
c.timeInJit = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrJIT+"_jit_time_ratio"),
|
||||
"Displays the ratio (0-1) of elapsed time spent in JIT compilation since the last JIT compilation phase. This counter is updated at the end of every JIT compilation phase. A JIT compilation phase occurs when a method and its dependencies are compiled.",
|
||||
[]string{"process"},
|
||||
[]string{"process", "process_id"},
|
||||
nil,
|
||||
)
|
||||
c.standardJitFailures = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrJIT+"_jit_standard_failures_total"),
|
||||
"Displays the peak number of methods the JIT compiler has failed to compile since the application started. This failure can occur if the MSIL cannot be verified or if there is an internal error in the JIT compiler.",
|
||||
[]string{"process"},
|
||||
[]string{"process", "process_id"},
|
||||
nil,
|
||||
)
|
||||
c.totalNumberOfILBytesJitted = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrJIT+"_jit_il_bytes_total"),
|
||||
"Displays the total number of Microsoft intermediate language (MSIL) bytes compiled by the just-in-time (JIT) compiler since the application started",
|
||||
[]string{"process"},
|
||||
[]string{"process", "process_id"},
|
||||
nil,
|
||||
)
|
||||
}
|
||||
@@ -81,18 +81,15 @@ func (c *Collector) collectClrJIT(ch chan<- prometheus.Metric, scrape *labelcach
|
||||
return fmt.Errorf("failed to collect .NET CLR Jit: %w", err)
|
||||
}
|
||||
|
||||
suffixDuplicateProcesses(dst)
|
||||
|
||||
for _, process := range dst {
|
||||
if process.Name == "_Global_" {
|
||||
continue
|
||||
}
|
||||
dst, processIDs := prepareProcesses(c, dst)
|
||||
|
||||
for i, process := range dst {
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
c.numberOfMethodsJitted,
|
||||
prometheus.CounterValue,
|
||||
float64(process.NumberofMethodsJitted),
|
||||
process.Name,
|
||||
processIDs[i],
|
||||
)
|
||||
|
||||
if process.PercentTimeinJitBase > 0 {
|
||||
@@ -101,6 +98,7 @@ func (c *Collector) collectClrJIT(ch chan<- prometheus.Metric, scrape *labelcach
|
||||
prometheus.GaugeValue,
|
||||
process.PercentTimeinJit/process.PercentTimeinJitBase,
|
||||
process.Name,
|
||||
processIDs[i],
|
||||
)
|
||||
}
|
||||
|
||||
@@ -109,6 +107,7 @@ func (c *Collector) collectClrJIT(ch chan<- prometheus.Metric, scrape *labelcach
|
||||
prometheus.CounterValue,
|
||||
float64(process.StandardJitFailures),
|
||||
process.Name,
|
||||
processIDs[i],
|
||||
)
|
||||
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
@@ -116,6 +115,7 @@ func (c *Collector) collectClrJIT(ch chan<- prometheus.Metric, scrape *labelcach
|
||||
prometheus.CounterValue,
|
||||
float64(process.TotalNumberofILBytesJitted),
|
||||
process.Name,
|
||||
processIDs[i],
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -30,55 +30,55 @@ func (c *Collector) describeClrLoading() {
|
||||
c.bytesInLoaderHeap = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrLoading+"_loader_heap_size_bytes"),
|
||||
"Displays the current size, in bytes, of the memory committed by the class loader across all application domains. Committed memory is the physical space reserved in the disk paging file.",
|
||||
[]string{"process"},
|
||||
[]string{"process", "process_id"},
|
||||
nil,
|
||||
)
|
||||
c.currentAppDomains = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrLoading+"_appdomains_loaded_current"),
|
||||
"Displays the current number of application domains loaded in this application.",
|
||||
[]string{"process"},
|
||||
[]string{"process", "process_id"},
|
||||
nil,
|
||||
)
|
||||
c.currentAssemblies = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrLoading+"_assemblies_loaded_current"),
|
||||
"Displays the current number of assemblies loaded across all application domains in the currently running application. If the assembly is loaded as domain-neutral from multiple application domains, this counter is incremented only once.",
|
||||
[]string{"process"},
|
||||
[]string{"process", "process_id"},
|
||||
nil,
|
||||
)
|
||||
c.currentClassesLoaded = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrLoading+"_classes_loaded_current"),
|
||||
"Displays the current number of classes loaded in all assemblies.",
|
||||
[]string{"process"},
|
||||
[]string{"process", "process_id"},
|
||||
nil,
|
||||
)
|
||||
c.totalAppDomains = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrLoading+"_appdomains_loaded_total"),
|
||||
"Displays the peak number of application domains loaded since the application started.",
|
||||
[]string{"process"},
|
||||
[]string{"process", "process_id"},
|
||||
nil,
|
||||
)
|
||||
c.totalAppDomainsUnloaded = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrLoading+"_appdomains_unloaded_total"),
|
||||
"Displays the total number of application domains unloaded since the application started. If an application domain is loaded and unloaded multiple times, this counter increments each time the application domain is unloaded.",
|
||||
[]string{"process"},
|
||||
[]string{"process", "process_id"},
|
||||
nil,
|
||||
)
|
||||
c.totalAssemblies = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrLoading+"_assemblies_loaded_total"),
|
||||
"Displays the total number of assemblies loaded since the application started. If the assembly is loaded as domain-neutral from multiple application domains, this counter is incremented only once.",
|
||||
[]string{"process"},
|
||||
[]string{"process", "process_id"},
|
||||
nil,
|
||||
)
|
||||
c.totalClassesLoaded = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrLoading+"_classes_loaded_total"),
|
||||
"Displays the cumulative number of classes loaded in all assemblies since the application started.",
|
||||
[]string{"process"},
|
||||
[]string{"process", "process_id"},
|
||||
nil,
|
||||
)
|
||||
c.totalNumberOfLoadFailures = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrLoading+"_class_load_failures_total"),
|
||||
"Displays the peak number of classes that have failed to load since the application started.",
|
||||
[]string{"process"},
|
||||
[]string{"process", "process_id"},
|
||||
nil,
|
||||
)
|
||||
}
|
||||
@@ -115,18 +115,15 @@ func (c *Collector) collectClrLoading(ch chan<- prometheus.Metric, scrape *label
|
||||
return fmt.Errorf("failed to collect .NET CLR Loading: %w", err)
|
||||
}
|
||||
|
||||
suffixDuplicateProcesses(dst)
|
||||
|
||||
for _, process := range dst {
|
||||
if process.Name == "_Global_" {
|
||||
continue
|
||||
}
|
||||
dst, processIDs := prepareProcesses(c, dst)
|
||||
|
||||
for i, process := range dst {
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
c.bytesInLoaderHeap,
|
||||
prometheus.GaugeValue,
|
||||
float64(process.BytesinLoaderHeap),
|
||||
process.Name,
|
||||
processIDs[i],
|
||||
)
|
||||
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
@@ -134,6 +131,7 @@ func (c *Collector) collectClrLoading(ch chan<- prometheus.Metric, scrape *label
|
||||
prometheus.GaugeValue,
|
||||
float64(process.Currentappdomains),
|
||||
process.Name,
|
||||
processIDs[i],
|
||||
)
|
||||
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
@@ -141,6 +139,7 @@ func (c *Collector) collectClrLoading(ch chan<- prometheus.Metric, scrape *label
|
||||
prometheus.GaugeValue,
|
||||
float64(process.CurrentAssemblies),
|
||||
process.Name,
|
||||
processIDs[i],
|
||||
)
|
||||
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
@@ -148,6 +147,7 @@ func (c *Collector) collectClrLoading(ch chan<- prometheus.Metric, scrape *label
|
||||
prometheus.GaugeValue,
|
||||
float64(process.CurrentClassesLoaded),
|
||||
process.Name,
|
||||
processIDs[i],
|
||||
)
|
||||
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
@@ -155,6 +155,7 @@ func (c *Collector) collectClrLoading(ch chan<- prometheus.Metric, scrape *label
|
||||
prometheus.CounterValue,
|
||||
float64(process.TotalAppdomains),
|
||||
process.Name,
|
||||
processIDs[i],
|
||||
)
|
||||
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
@@ -162,6 +163,7 @@ func (c *Collector) collectClrLoading(ch chan<- prometheus.Metric, scrape *label
|
||||
prometheus.CounterValue,
|
||||
float64(process.Totalappdomainsunloaded),
|
||||
process.Name,
|
||||
processIDs[i],
|
||||
)
|
||||
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
@@ -169,6 +171,7 @@ func (c *Collector) collectClrLoading(ch chan<- prometheus.Metric, scrape *label
|
||||
prometheus.CounterValue,
|
||||
float64(process.TotalAssemblies),
|
||||
process.Name,
|
||||
processIDs[i],
|
||||
)
|
||||
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
@@ -176,6 +179,7 @@ func (c *Collector) collectClrLoading(ch chan<- prometheus.Metric, scrape *label
|
||||
prometheus.CounterValue,
|
||||
float64(process.TotalClassesLoaded),
|
||||
process.Name,
|
||||
processIDs[i],
|
||||
)
|
||||
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
@@ -183,6 +187,7 @@ func (c *Collector) collectClrLoading(ch chan<- prometheus.Metric, scrape *label
|
||||
prometheus.CounterValue,
|
||||
float64(process.TotalNumberofLoadFailures),
|
||||
process.Name,
|
||||
processIDs[i],
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -30,43 +30,43 @@ func (c *Collector) describeClrLocksAndThreads() {
|
||||
c.currentQueueLength = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrLocksAndThreads+"_current_queue_length"),
|
||||
"Displays the total number of threads that are currently waiting to acquire a managed lock in the application.",
|
||||
[]string{"process"},
|
||||
[]string{"process", "process_id"},
|
||||
nil,
|
||||
)
|
||||
c.numberOfCurrentLogicalThreads = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrLocksAndThreads+"_current_logical_threads"),
|
||||
"Displays the number of current managed thread objects in the application. This counter maintains the count of both running and stopped threads. ",
|
||||
[]string{"process"},
|
||||
[]string{"process", "process_id"},
|
||||
nil,
|
||||
)
|
||||
c.numberOfCurrentPhysicalThreads = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrLocksAndThreads+"_physical_threads_current"),
|
||||
"Displays the number of native operating system threads created and owned by the common language runtime to act as underlying threads for managed thread objects. This counter's value does not include the threads used by the runtime in its internal operations; it is a subset of the threads in the operating system process.",
|
||||
[]string{"process"},
|
||||
[]string{"process", "process_id"},
|
||||
nil,
|
||||
)
|
||||
c.numberOfCurrentRecognizedThreads = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrLocksAndThreads+"_recognized_threads_current"),
|
||||
"Displays the number of threads that are currently recognized by the runtime. These threads are associated with a corresponding managed thread object. The runtime does not create these threads, but they have run inside the runtime at least once.",
|
||||
[]string{"process"},
|
||||
[]string{"process", "process_id"},
|
||||
nil,
|
||||
)
|
||||
c.numberOfTotalRecognizedThreads = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrLocksAndThreads+"_recognized_threads_total"),
|
||||
"Displays the total number of threads that have been recognized by the runtime since the application started. These threads are associated with a corresponding managed thread object. The runtime does not create these threads, but they have run inside the runtime at least once.",
|
||||
[]string{"process"},
|
||||
[]string{"process", "process_id"},
|
||||
nil,
|
||||
)
|
||||
c.queueLengthPeak = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrLocksAndThreads+"_queue_length_total"),
|
||||
"Displays the total number of threads that waited to acquire a managed lock since the application started.",
|
||||
[]string{"process"},
|
||||
[]string{"process", "process_id"},
|
||||
nil,
|
||||
)
|
||||
c.totalNumberOfContentions = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrLocksAndThreads+"_contentions_total"),
|
||||
"Displays the total number of times that threads in the runtime have attempted to acquire a managed lock unsuccessfully.",
|
||||
[]string{"process"},
|
||||
[]string{"process", "process_id"},
|
||||
nil,
|
||||
)
|
||||
}
|
||||
@@ -101,18 +101,15 @@ func (c *Collector) collectClrLocksAndThreads(ch chan<- prometheus.Metric, scrap
|
||||
return fmt.Errorf("failed to collect .NET CLR LocksAndThreads: %w", err)
|
||||
}
|
||||
|
||||
suffixDuplicateProcesses(dst)
|
||||
|
||||
for _, process := range dst {
|
||||
if process.Name == "_Global_" {
|
||||
continue
|
||||
}
|
||||
dst, processIDs := prepareProcesses(c, dst)
|
||||
|
||||
for i, process := range dst {
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
c.currentQueueLength,
|
||||
prometheus.GaugeValue,
|
||||
float64(process.CurrentQueueLength),
|
||||
process.Name,
|
||||
processIDs[i],
|
||||
)
|
||||
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
@@ -120,6 +117,7 @@ func (c *Collector) collectClrLocksAndThreads(ch chan<- prometheus.Metric, scrap
|
||||
prometheus.GaugeValue,
|
||||
float64(process.NumberofcurrentlogicalThreads),
|
||||
process.Name,
|
||||
processIDs[i],
|
||||
)
|
||||
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
@@ -127,6 +125,7 @@ func (c *Collector) collectClrLocksAndThreads(ch chan<- prometheus.Metric, scrap
|
||||
prometheus.GaugeValue,
|
||||
float64(process.NumberofcurrentphysicalThreads),
|
||||
process.Name,
|
||||
processIDs[i],
|
||||
)
|
||||
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
@@ -134,6 +133,7 @@ func (c *Collector) collectClrLocksAndThreads(ch chan<- prometheus.Metric, scrap
|
||||
prometheus.GaugeValue,
|
||||
float64(process.Numberofcurrentrecognizedthreads),
|
||||
process.Name,
|
||||
processIDs[i],
|
||||
)
|
||||
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
@@ -141,6 +141,7 @@ func (c *Collector) collectClrLocksAndThreads(ch chan<- prometheus.Metric, scrap
|
||||
prometheus.CounterValue,
|
||||
float64(process.Numberoftotalrecognizedthreads),
|
||||
process.Name,
|
||||
processIDs[i],
|
||||
)
|
||||
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
@@ -148,6 +149,7 @@ func (c *Collector) collectClrLocksAndThreads(ch chan<- prometheus.Metric, scrap
|
||||
prometheus.CounterValue,
|
||||
float64(process.QueueLengthPeak),
|
||||
process.Name,
|
||||
processIDs[i],
|
||||
)
|
||||
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
@@ -155,6 +157,7 @@ func (c *Collector) collectClrLocksAndThreads(ch chan<- prometheus.Metric, scrap
|
||||
prometheus.CounterValue,
|
||||
float64(process.TotalNumberofContentions),
|
||||
process.Name,
|
||||
processIDs[i],
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -145,19 +145,18 @@ func (c *Collector) collectClrMemory(ch chan<- prometheus.Metric, scrape *labelc
|
||||
return fmt.Errorf("failed to collect .NET CLR Memory: %w", err)
|
||||
}
|
||||
|
||||
dst = filterProcesses(c, dst)
|
||||
suffixDuplicateProcesses(dst)
|
||||
|
||||
for _, process := range dst {
|
||||
if process.Name == "_Global_" {
|
||||
continue
|
||||
}
|
||||
processID := strconv.FormatUint(uint64(process.ProcessID), 10)
|
||||
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
c.allocatedBytes,
|
||||
prometheus.CounterValue,
|
||||
float64(process.AllocatedBytesPersec),
|
||||
process.Name,
|
||||
strconv.FormatUint(uint64(process.ProcessID), 10),
|
||||
processID,
|
||||
)
|
||||
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
@@ -165,7 +164,7 @@ func (c *Collector) collectClrMemory(ch chan<- prometheus.Metric, scrape *labelc
|
||||
prometheus.GaugeValue,
|
||||
float64(process.FinalizationSurvivors),
|
||||
process.Name,
|
||||
strconv.FormatUint(uint64(process.ProcessID), 10),
|
||||
processID,
|
||||
)
|
||||
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
@@ -173,7 +172,7 @@ func (c *Collector) collectClrMemory(ch chan<- prometheus.Metric, scrape *labelc
|
||||
prometheus.GaugeValue,
|
||||
float64(process.Gen0heapsize),
|
||||
process.Name,
|
||||
strconv.FormatUint(uint64(process.ProcessID), 10),
|
||||
processID,
|
||||
"Gen0",
|
||||
)
|
||||
|
||||
@@ -182,7 +181,7 @@ func (c *Collector) collectClrMemory(ch chan<- prometheus.Metric, scrape *labelc
|
||||
prometheus.GaugeValue,
|
||||
float64(process.Gen0PromotedBytesPerSec),
|
||||
process.Name,
|
||||
strconv.FormatUint(uint64(process.ProcessID), 10),
|
||||
processID,
|
||||
"Gen0",
|
||||
)
|
||||
|
||||
@@ -191,7 +190,7 @@ func (c *Collector) collectClrMemory(ch chan<- prometheus.Metric, scrape *labelc
|
||||
prometheus.GaugeValue,
|
||||
float64(process.Gen1heapsize),
|
||||
process.Name,
|
||||
strconv.FormatUint(uint64(process.ProcessID), 10),
|
||||
processID,
|
||||
"Gen1",
|
||||
)
|
||||
|
||||
@@ -200,7 +199,7 @@ func (c *Collector) collectClrMemory(ch chan<- prometheus.Metric, scrape *labelc
|
||||
prometheus.GaugeValue,
|
||||
float64(process.Gen1PromotedBytesPerSec),
|
||||
process.Name,
|
||||
strconv.FormatUint(uint64(process.ProcessID), 10),
|
||||
processID,
|
||||
"Gen1",
|
||||
)
|
||||
|
||||
@@ -209,7 +208,7 @@ func (c *Collector) collectClrMemory(ch chan<- prometheus.Metric, scrape *labelc
|
||||
prometheus.GaugeValue,
|
||||
float64(process.Gen2heapsize),
|
||||
process.Name,
|
||||
strconv.FormatUint(uint64(process.ProcessID), 10),
|
||||
processID,
|
||||
"Gen2",
|
||||
)
|
||||
|
||||
@@ -218,7 +217,7 @@ func (c *Collector) collectClrMemory(ch chan<- prometheus.Metric, scrape *labelc
|
||||
prometheus.GaugeValue,
|
||||
float64(process.LargeObjectHeapsize),
|
||||
process.Name,
|
||||
strconv.FormatUint(uint64(process.ProcessID), 10),
|
||||
processID,
|
||||
"LOH",
|
||||
)
|
||||
|
||||
@@ -227,7 +226,7 @@ func (c *Collector) collectClrMemory(ch chan<- prometheus.Metric, scrape *labelc
|
||||
prometheus.GaugeValue,
|
||||
float64(process.NumberGCHandles),
|
||||
process.Name,
|
||||
strconv.FormatUint(uint64(process.ProcessID), 10),
|
||||
processID,
|
||||
)
|
||||
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
@@ -235,7 +234,7 @@ func (c *Collector) collectClrMemory(ch chan<- prometheus.Metric, scrape *labelc
|
||||
prometheus.CounterValue,
|
||||
float64(process.NumberGen0Collections),
|
||||
process.Name,
|
||||
strconv.FormatUint(uint64(process.ProcessID), 10),
|
||||
processID,
|
||||
"Gen0",
|
||||
)
|
||||
|
||||
@@ -244,7 +243,7 @@ func (c *Collector) collectClrMemory(ch chan<- prometheus.Metric, scrape *labelc
|
||||
prometheus.CounterValue,
|
||||
float64(process.NumberGen1Collections),
|
||||
process.Name,
|
||||
strconv.FormatUint(uint64(process.ProcessID), 10),
|
||||
processID,
|
||||
"Gen1",
|
||||
)
|
||||
|
||||
@@ -253,7 +252,7 @@ func (c *Collector) collectClrMemory(ch chan<- prometheus.Metric, scrape *labelc
|
||||
prometheus.CounterValue,
|
||||
float64(process.NumberGen2Collections),
|
||||
process.Name,
|
||||
strconv.FormatUint(uint64(process.ProcessID), 10),
|
||||
processID,
|
||||
"Gen2",
|
||||
)
|
||||
|
||||
@@ -262,7 +261,7 @@ func (c *Collector) collectClrMemory(ch chan<- prometheus.Metric, scrape *labelc
|
||||
prometheus.CounterValue,
|
||||
float64(process.NumberInducedGC),
|
||||
process.Name,
|
||||
strconv.FormatUint(uint64(process.ProcessID), 10),
|
||||
processID,
|
||||
)
|
||||
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
@@ -270,7 +269,7 @@ func (c *Collector) collectClrMemory(ch chan<- prometheus.Metric, scrape *labelc
|
||||
prometheus.GaugeValue,
|
||||
float64(process.NumberofPinnedObjects),
|
||||
process.Name,
|
||||
strconv.FormatUint(uint64(process.ProcessID), 10),
|
||||
processID,
|
||||
)
|
||||
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
@@ -278,7 +277,7 @@ func (c *Collector) collectClrMemory(ch chan<- prometheus.Metric, scrape *labelc
|
||||
prometheus.GaugeValue,
|
||||
float64(process.NumberofSinkBlocksinuse),
|
||||
process.Name,
|
||||
strconv.FormatUint(uint64(process.ProcessID), 10),
|
||||
processID,
|
||||
)
|
||||
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
@@ -286,7 +285,7 @@ func (c *Collector) collectClrMemory(ch chan<- prometheus.Metric, scrape *labelc
|
||||
prometheus.GaugeValue,
|
||||
float64(process.NumberTotalcommittedBytes),
|
||||
process.Name,
|
||||
strconv.FormatUint(uint64(process.ProcessID), 10),
|
||||
processID,
|
||||
)
|
||||
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
@@ -294,7 +293,7 @@ func (c *Collector) collectClrMemory(ch chan<- prometheus.Metric, scrape *labelc
|
||||
prometheus.GaugeValue,
|
||||
float64(process.NumberTotalreservedBytes),
|
||||
process.Name,
|
||||
strconv.FormatUint(uint64(process.ProcessID), 10),
|
||||
processID,
|
||||
)
|
||||
|
||||
if process.PercentTimeinGC_base <= 0 {
|
||||
@@ -306,7 +305,7 @@ func (c *Collector) collectClrMemory(ch chan<- prometheus.Metric, scrape *labelc
|
||||
prometheus.GaugeValue,
|
||||
process.PercentTimeinGC/process.PercentTimeinGC_base,
|
||||
process.Name,
|
||||
strconv.FormatUint(uint64(process.ProcessID), 10),
|
||||
processID,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -30,37 +30,37 @@ func (c *Collector) describeClrRemoting() {
|
||||
c.channels = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrRemoting+"_channels_total"),
|
||||
"Displays the total number of remoting channels registered across all application domains since application started.",
|
||||
[]string{"process"},
|
||||
[]string{"process", "process_id"},
|
||||
nil,
|
||||
)
|
||||
c.contextBoundClassesLoaded = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrRemoting+"_context_bound_classes_loaded"),
|
||||
"Displays the current number of context-bound classes that are loaded.",
|
||||
[]string{"process"},
|
||||
[]string{"process", "process_id"},
|
||||
nil,
|
||||
)
|
||||
c.contextBoundObjects = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrRemoting+"_context_bound_objects_total"),
|
||||
"Displays the total number of context-bound objects allocated.",
|
||||
[]string{"process"},
|
||||
[]string{"process", "process_id"},
|
||||
nil,
|
||||
)
|
||||
c.contextProxies = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrRemoting+"_context_proxies_total"),
|
||||
"Displays the total number of remoting proxy objects in this process since it started.",
|
||||
[]string{"process"},
|
||||
[]string{"process", "process_id"},
|
||||
nil,
|
||||
)
|
||||
c.contexts = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrRemoting+"_contexts"),
|
||||
"Displays the current number of remoting contexts in the application.",
|
||||
[]string{"process"},
|
||||
[]string{"process", "process_id"},
|
||||
nil,
|
||||
)
|
||||
c.totalRemoteCalls = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrRemoting+"_remote_calls_total"),
|
||||
"Displays the total number of remote procedure calls invoked since the application started.",
|
||||
[]string{"process"},
|
||||
[]string{"process", "process_id"},
|
||||
nil,
|
||||
)
|
||||
}
|
||||
@@ -94,18 +94,15 @@ func (c *Collector) collectClrRemoting(ch chan<- prometheus.Metric, scrape *labe
|
||||
return fmt.Errorf("failed to collect .NET CLR Remoting: %w", err)
|
||||
}
|
||||
|
||||
suffixDuplicateProcesses(dst)
|
||||
|
||||
for _, process := range dst {
|
||||
if process.Name == "_Global_" {
|
||||
continue
|
||||
}
|
||||
dst, processIDs := prepareProcesses(c, dst)
|
||||
|
||||
for i, process := range dst {
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
c.channels,
|
||||
prometheus.CounterValue,
|
||||
float64(process.Channels),
|
||||
process.Name,
|
||||
processIDs[i],
|
||||
)
|
||||
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
@@ -113,6 +110,7 @@ func (c *Collector) collectClrRemoting(ch chan<- prometheus.Metric, scrape *labe
|
||||
prometheus.GaugeValue,
|
||||
float64(process.ContextBoundClassesLoaded),
|
||||
process.Name,
|
||||
processIDs[i],
|
||||
)
|
||||
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
@@ -120,6 +118,7 @@ func (c *Collector) collectClrRemoting(ch chan<- prometheus.Metric, scrape *labe
|
||||
prometheus.CounterValue,
|
||||
float64(process.ContextBoundObjectsAllocPersec),
|
||||
process.Name,
|
||||
processIDs[i],
|
||||
)
|
||||
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
@@ -127,6 +126,7 @@ func (c *Collector) collectClrRemoting(ch chan<- prometheus.Metric, scrape *labe
|
||||
prometheus.CounterValue,
|
||||
float64(process.ContextProxies),
|
||||
process.Name,
|
||||
processIDs[i],
|
||||
)
|
||||
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
@@ -134,6 +134,7 @@ func (c *Collector) collectClrRemoting(ch chan<- prometheus.Metric, scrape *labe
|
||||
prometheus.GaugeValue,
|
||||
float64(process.Contexts),
|
||||
process.Name,
|
||||
processIDs[i],
|
||||
)
|
||||
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
@@ -141,6 +142,7 @@ func (c *Collector) collectClrRemoting(ch chan<- prometheus.Metric, scrape *labe
|
||||
prometheus.CounterValue,
|
||||
float64(process.TotalRemoteCalls),
|
||||
process.Name,
|
||||
processIDs[i],
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -30,25 +30,25 @@ func (c *Collector) describeClrSecurity() {
|
||||
c.numberLinkTimeChecks = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrSecurity+"_link_time_checks_total"),
|
||||
"Displays the total number of link-time code access security checks since the application started.",
|
||||
[]string{"process"},
|
||||
[]string{"process", "process_id"},
|
||||
nil,
|
||||
)
|
||||
c.timeInRTChecks = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrSecurity+"_rt_checks_time_ratio"),
|
||||
"Displays the ratio (0-1) of time spent performing runtime code access security checks in the last sample.",
|
||||
[]string{"process"},
|
||||
[]string{"process", "process_id"},
|
||||
nil,
|
||||
)
|
||||
c.stackWalkDepth = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrSecurity+"_stack_walk_depth"),
|
||||
"Displays the depth of the stack during that last runtime code access security check.",
|
||||
[]string{"process"},
|
||||
[]string{"process", "process_id"},
|
||||
nil,
|
||||
)
|
||||
c.totalRuntimeChecks = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, collectorClrSecurity+"_runtime_checks_total"),
|
||||
"Displays the total number of runtime code access security checks performed since the application started.",
|
||||
[]string{"process"},
|
||||
[]string{"process", "process_id"},
|
||||
nil,
|
||||
)
|
||||
}
|
||||
@@ -81,18 +81,15 @@ func (c *Collector) collectClrSecurity(ch chan<- prometheus.Metric, scrape *labe
|
||||
return fmt.Errorf("failed to collect .NET CLR Security: %w", err)
|
||||
}
|
||||
|
||||
suffixDuplicateProcesses(dst)
|
||||
|
||||
for _, process := range dst {
|
||||
if process.Name == "_Global_" {
|
||||
continue
|
||||
}
|
||||
dst, processIDs := prepareProcesses(c, dst)
|
||||
|
||||
for i, process := range dst {
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
c.numberLinkTimeChecks,
|
||||
prometheus.CounterValue,
|
||||
float64(process.NumberLinkTimeChecks),
|
||||
process.Name,
|
||||
processIDs[i],
|
||||
)
|
||||
|
||||
if process.PercentTimeinRTchecksBase > 0 {
|
||||
@@ -101,6 +98,7 @@ func (c *Collector) collectClrSecurity(ch chan<- prometheus.Metric, scrape *labe
|
||||
prometheus.GaugeValue,
|
||||
process.PercentTimeinRTchecks/process.PercentTimeinRTchecksBase,
|
||||
process.Name,
|
||||
processIDs[i],
|
||||
)
|
||||
}
|
||||
|
||||
@@ -109,6 +107,7 @@ func (c *Collector) collectClrSecurity(ch chan<- prometheus.Metric, scrape *labe
|
||||
prometheus.GaugeValue,
|
||||
float64(process.StackWalkDepth),
|
||||
process.Name,
|
||||
processIDs[i],
|
||||
)
|
||||
|
||||
ch <- scrape.MustNewConstMetric(
|
||||
@@ -116,6 +115,7 @@ func (c *Collector) collectClrSecurity(ch chan<- prometheus.Metric, scrape *labe
|
||||
prometheus.CounterValue,
|
||||
float64(process.TotalRuntimeChecks),
|
||||
process.Name,
|
||||
processIDs[i],
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,19 @@ type expectedMetric struct {
|
||||
area string
|
||||
}
|
||||
|
||||
func assertFixtureMetrics(t *testing.T, collect func(chan<- prometheus.Metric, *labelcache.Scrape, time.Duration) error, expected []expectedMetric, withPID bool) {
|
||||
// fixtureProcessIDs are the process IDs of the fixture processes. The
|
||||
// .NET CLR Memory object has no valid Process ID for report_Total.
|
||||
func fixtureProcessIDs() map[processKey]string {
|
||||
return map[processKey]string{
|
||||
{name: "worker"}: "101",
|
||||
{name: "worker#1"}: "102",
|
||||
}
|
||||
}
|
||||
|
||||
// assertFixtureMetrics checks the metrics of the fixture processes worker,
|
||||
// worker#1 and report_Total. processIDs has the expected process_id label of
|
||||
// each process.
|
||||
func assertFixtureMetrics(t *testing.T, collect func(chan<- prometheus.Metric, *labelcache.Scrape, time.Duration) error, expected []expectedMetric, processIDs map[string]string) {
|
||||
t.Helper()
|
||||
|
||||
// A first scrape fills the label cache, so the checked scrape publishes
|
||||
@@ -112,13 +124,11 @@ func assertFixtureMetrics(t *testing.T, collect func(chan<- prometheus.Metric, *
|
||||
|
||||
require.InDelta(t, want.value, value, 1e-12, key)
|
||||
|
||||
labelCount := 1
|
||||
processID, ok := labels["process_id"]
|
||||
require.True(t, ok, "missing process_id label of %s", key)
|
||||
require.Equal(t, processIDs[labels["process"]], processID, key)
|
||||
|
||||
if withPID {
|
||||
require.Equal(t, "123", labels["process_id"])
|
||||
|
||||
labelCount++
|
||||
}
|
||||
labelCount := 2
|
||||
|
||||
if want.area != "" {
|
||||
labelCount++
|
||||
@@ -136,7 +146,7 @@ func assertFixtureMetrics(t *testing.T, collect func(chan<- prometheus.Metric, *
|
||||
}
|
||||
|
||||
func TestClrExceptionsMetricCompatibility(t *testing.T) {
|
||||
c := &Collector{}
|
||||
c := &Collector{processIDs: fixtureProcessIDs()}
|
||||
c.describeClrExceptions()
|
||||
|
||||
row := perfDataClrExceptions{
|
||||
@@ -158,11 +168,11 @@ func TestClrExceptionsMetricCompatibility(t *testing.T) {
|
||||
{"windows_netframework_clrexceptions_exceptions_filters_total", dto.MetricType_COUNTER, 12, ""},
|
||||
{"windows_netframework_clrexceptions_exceptions_finallys_total", dto.MetricType_COUNTER, 13, ""},
|
||||
{"windows_netframework_clrexceptions_throw_to_catch_depth_total", dto.MetricType_COUNTER, 14, ""},
|
||||
}, false)
|
||||
}, map[string]string{"worker": "101", "worker#1": "102", "report_Total": ""})
|
||||
}
|
||||
|
||||
func TestClrInteropMetricCompatibility(t *testing.T) {
|
||||
c := &Collector{}
|
||||
c := &Collector{processIDs: fixtureProcessIDs()}
|
||||
c.describeClrInterop()
|
||||
|
||||
row := perfDataClrInterop{
|
||||
@@ -182,11 +192,11 @@ func TestClrInteropMetricCompatibility(t *testing.T) {
|
||||
{"windows_netframework_clrinterop_com_callable_wrappers_total", dto.MetricType_COUNTER, 11, ""},
|
||||
{"windows_netframework_clrinterop_interop_marshalling_total", dto.MetricType_COUNTER, 13, ""},
|
||||
{"windows_netframework_clrinterop_interop_stubs_created_total", dto.MetricType_COUNTER, 12, ""},
|
||||
}, false)
|
||||
}, map[string]string{"worker": "101", "worker#1": "102", "report_Total": ""})
|
||||
}
|
||||
|
||||
func TestClrJITMetricCompatibility(t *testing.T) {
|
||||
c := &Collector{}
|
||||
c := &Collector{processIDs: fixtureProcessIDs()}
|
||||
c.describeClrJIT()
|
||||
|
||||
row := perfDataClrJIT{
|
||||
@@ -209,11 +219,11 @@ func TestClrJITMetricCompatibility(t *testing.T) {
|
||||
{"windows_netframework_clrjit_jit_time_ratio", dto.MetricType_GAUGE, 0.25, ""},
|
||||
{"windows_netframework_clrjit_jit_standard_failures_total", dto.MetricType_COUNTER, 13, ""},
|
||||
{"windows_netframework_clrjit_jit_il_bytes_total", dto.MetricType_COUNTER, 14, ""},
|
||||
}, false)
|
||||
}, map[string]string{"worker": "101", "worker#1": "102", "report_Total": ""})
|
||||
}
|
||||
|
||||
func TestClrLoadingMetricCompatibility(t *testing.T) {
|
||||
c := &Collector{}
|
||||
c := &Collector{processIDs: fixtureProcessIDs()}
|
||||
c.describeClrLoading()
|
||||
|
||||
row := perfDataClrLoading{
|
||||
@@ -245,11 +255,11 @@ func TestClrLoadingMetricCompatibility(t *testing.T) {
|
||||
{"windows_netframework_clrloading_assemblies_loaded_total", dto.MetricType_COUNTER, 16, ""},
|
||||
{"windows_netframework_clrloading_classes_loaded_total", dto.MetricType_COUNTER, 17, ""},
|
||||
{"windows_netframework_clrloading_class_load_failures_total", dto.MetricType_COUNTER, 18, ""},
|
||||
}, false)
|
||||
}, map[string]string{"worker": "101", "worker#1": "102", "report_Total": ""})
|
||||
}
|
||||
|
||||
func TestClrLocksAndThreadsMetricCompatibility(t *testing.T) {
|
||||
c := &Collector{}
|
||||
c := &Collector{processIDs: fixtureProcessIDs()}
|
||||
c.describeClrLocksAndThreads()
|
||||
|
||||
row := perfDataClrLocksAndThreads{
|
||||
@@ -277,7 +287,7 @@ func TestClrLocksAndThreadsMetricCompatibility(t *testing.T) {
|
||||
{"windows_netframework_clrlocksandthreads_recognized_threads_total", dto.MetricType_COUNTER, 15, ""},
|
||||
{"windows_netframework_clrlocksandthreads_queue_length_total", dto.MetricType_COUNTER, 16, ""},
|
||||
{"windows_netframework_clrlocksandthreads_contentions_total", dto.MetricType_COUNTER, 17, ""},
|
||||
}, false)
|
||||
}, map[string]string{"worker": "101", "worker#1": "102", "report_Total": ""})
|
||||
}
|
||||
|
||||
func TestClrMemoryMetricCompatibility(t *testing.T) {
|
||||
@@ -333,11 +343,11 @@ func TestClrMemoryMetricCompatibility(t *testing.T) {
|
||||
{"windows_netframework_clrmemory_committed_bytes", dto.MetricType_GAUGE, 24, ""},
|
||||
{"windows_netframework_clrmemory_reserved_bytes", dto.MetricType_GAUGE, 25, ""},
|
||||
{"windows_netframework_clrmemory_gc_time_ratio", dto.MetricType_GAUGE, 0.25, ""},
|
||||
}, true)
|
||||
}, map[string]string{"worker": "123", "worker#1": "123", "report_Total": "123"})
|
||||
}
|
||||
|
||||
func TestClrRemotingMetricCompatibility(t *testing.T) {
|
||||
c := &Collector{}
|
||||
c := &Collector{processIDs: fixtureProcessIDs()}
|
||||
c.describeClrRemoting()
|
||||
|
||||
row := perfDataClrRemoting{
|
||||
@@ -363,11 +373,11 @@ func TestClrRemotingMetricCompatibility(t *testing.T) {
|
||||
{"windows_netframework_clrremoting_context_proxies_total", dto.MetricType_COUNTER, 14, ""},
|
||||
{"windows_netframework_clrremoting_contexts", dto.MetricType_GAUGE, 15, ""},
|
||||
{"windows_netframework_clrremoting_remote_calls_total", dto.MetricType_COUNTER, 16, ""},
|
||||
}, false)
|
||||
}, map[string]string{"worker": "101", "worker#1": "102", "report_Total": ""})
|
||||
}
|
||||
|
||||
func TestClrSecurityMetricCompatibility(t *testing.T) {
|
||||
c := &Collector{}
|
||||
c := &Collector{processIDs: fixtureProcessIDs()}
|
||||
c.describeClrSecurity()
|
||||
|
||||
row := perfDataClrSecurity{
|
||||
@@ -390,7 +400,7 @@ func TestClrSecurityMetricCompatibility(t *testing.T) {
|
||||
{"windows_netframework_clrsecurity_rt_checks_time_ratio", dto.MetricType_GAUGE, 0.25, ""},
|
||||
{"windows_netframework_clrsecurity_stack_walk_depth", dto.MetricType_GAUGE, 13, ""},
|
||||
{"windows_netframework_clrsecurity_runtime_checks_total", dto.MetricType_COUNTER, 14, ""},
|
||||
}, false)
|
||||
}, map[string]string{"worker": "101", "worker#1": "102", "report_Total": ""})
|
||||
}
|
||||
|
||||
func TestGCZeroBaseDoesNotPublishInvalidRatio(t *testing.T) {
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
// Copyright The Prometheus Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build windows
|
||||
|
||||
package netframework
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus-community/windows_exporter/internal/labelcache"
|
||||
"github.com/prometheus-community/windows_exporter/internal/utils"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// gatherProcessIDs gathers the metrics of collect and returns the process_id
|
||||
// label of each process label of the metric family name.
|
||||
func gatherProcessIDs(t *testing.T, collect func(chan<- prometheus.Metric) error, name string) (map[string]string, error) {
|
||||
t.Helper()
|
||||
|
||||
var collectErr error
|
||||
|
||||
registry := prometheus.NewRegistry()
|
||||
registry.MustRegister(fixturePromCollector{collect: func(ch chan<- prometheus.Metric) { collectErr = collect(ch) }})
|
||||
families, err := registry.Gather()
|
||||
require.NoError(t, err)
|
||||
|
||||
got := map[string]string{}
|
||||
|
||||
for _, family := range families {
|
||||
if family.GetName() != name {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, metric := range family.GetMetric() {
|
||||
labels := make(map[string]string)
|
||||
for _, label := range metric.GetLabel() {
|
||||
labels[label.GetName()] = label.GetValue()
|
||||
}
|
||||
|
||||
require.Len(t, labels, 2)
|
||||
got[labels["process"]] = labels["process_id"]
|
||||
}
|
||||
}
|
||||
|
||||
return got, collectErr
|
||||
}
|
||||
|
||||
// gatherExceptionsThrown runs Collect with the clrexceptions sub-collector of
|
||||
// c and returns the process_id label of each process of
|
||||
// windows_netframework_clrexceptions_exceptions_thrown_total.
|
||||
func gatherExceptionsThrown(t *testing.T, c *Collector) (map[string]string, error) {
|
||||
t.Helper()
|
||||
|
||||
c.describeClrExceptions()
|
||||
c.collectorFns = []func(chan<- prometheus.Metric, *labelcache.Scrape, time.Duration) error{c.collectClrExceptions}
|
||||
|
||||
return gatherProcessIDs(t, func(ch chan<- prometheus.Metric) error {
|
||||
return c.Collect(ch, time.Second)
|
||||
}, "windows_netframework_clrexceptions_exceptions_thrown_total")
|
||||
}
|
||||
|
||||
// TestProcessIDLookup checks that the metrics of the other .NET CLR objects
|
||||
// get the process ID of the .NET CLR Memory instance with the same name and
|
||||
// occurrence, and an empty process_id if .NET CLR Memory has none.
|
||||
func TestProcessIDLookup(t *testing.T) {
|
||||
c := New(nil)
|
||||
c.perfClrProcessID = &fixturePerfCollector[perfDataClrProcessID]{rows: []perfDataClrProcessID{
|
||||
{Name: "_Global_", ProcessID: 0},
|
||||
{Name: "w3wp", ProcessID: 10},
|
||||
{Name: "w3wp#1", ProcessID: 30},
|
||||
{Name: "w3wp", Occurrence: 1, ProcessID: 20},
|
||||
}}
|
||||
c.perfClrExceptions = &fixturePerfCollector[perfDataClrExceptions]{rows: []perfDataClrExceptions{
|
||||
{Name: "_Global_"},
|
||||
{Name: "w3wp"},
|
||||
{Name: "w3wp#1"},
|
||||
{Name: "w3wp", Occurrence: 1},
|
||||
{Name: "powershell"},
|
||||
}}
|
||||
|
||||
got, err := gatherExceptionsThrown(t, c)
|
||||
require.NoError(t, err)
|
||||
|
||||
// "w3wp#1" is the name of a process, so the second w3wp is "w3wp#2".
|
||||
require.Equal(t, map[string]string{"w3wp": "10", "w3wp#1": "30", "w3wp#2": "20", "powershell": ""}, got)
|
||||
|
||||
// A scrape does not keep the process IDs of an earlier one.
|
||||
c.perfClrProcessID = &fixturePerfCollector[perfDataClrProcessID]{rows: []perfDataClrProcessID{{Name: "w3wp", ProcessID: 40}}}
|
||||
|
||||
got, err = gatherExceptionsThrown(t, c)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, map[string]string{"w3wp": "40", "w3wp#1": "", "w3wp#2": "", "powershell": ""}, got)
|
||||
}
|
||||
|
||||
// TestProcessIDLookupFailure checks that the metrics are published with an
|
||||
// empty process_id if the process IDs are unavailable, and that only a real
|
||||
// failure is reported.
|
||||
func TestProcessIDLookupFailure(t *testing.T) {
|
||||
failure := errors.New("provider failed")
|
||||
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
lookup perfCollector[perfDataClrProcessID]
|
||||
wantErr error
|
||||
}{
|
||||
{"missing object", unavailablePerfCollector[perfDataClrProcessID]{}, nil},
|
||||
{"failure", &fixturePerfCollector[perfDataClrProcessID]{err: failure}, failure},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
c := New(nil)
|
||||
c.perfClrProcessID = tc.lookup
|
||||
c.perfClrExceptions = &fixturePerfCollector[perfDataClrExceptions]{rows: []perfDataClrExceptions{{Name: "w3wp"}}}
|
||||
|
||||
got, err := gatherExceptionsThrown(t, c)
|
||||
if tc.wantErr == nil {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
require.ErrorIs(t, err, tc.wantErr)
|
||||
}
|
||||
|
||||
require.Equal(t, map[string]string{"w3wp": ""}, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestProcessFilter checks that the process filters match the name that
|
||||
// Windows reports, without the occurrence suffix, and that the process IDs
|
||||
// still match after filtering.
|
||||
func TestProcessFilter(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
include string
|
||||
exclude string
|
||||
expected map[string]string
|
||||
}{
|
||||
{"default", ".+", "", map[string]string{"w3wp": "10", "w3wp#1": "30", "w3wp#2": "20", "powershell": "40"}},
|
||||
// Without the process named "w3wp#1", the second w3wp is "w3wp#1".
|
||||
{"include", "w3wp", "", map[string]string{"w3wp": "10", "w3wp#1": "20"}},
|
||||
{"include name with #", "w3wp#1", "", map[string]string{"w3wp#1": "30"}},
|
||||
{"exclude", ".+", "w3wp.*", map[string]string{"powershell": "40"}},
|
||||
{"include and exclude", "w3wp.*", "w3wp", map[string]string{"w3wp#1": "30"}},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
include, err := utils.CompileAnchored(tc.include)
|
||||
require.NoError(t, err)
|
||||
|
||||
exclude, err := utils.CompileAnchored(tc.exclude)
|
||||
require.NoError(t, err)
|
||||
|
||||
c := New(&Config{ProcessInclude: include, ProcessExclude: exclude})
|
||||
c.perfClrProcessID = &fixturePerfCollector[perfDataClrProcessID]{rows: []perfDataClrProcessID{
|
||||
{Name: "w3wp", ProcessID: 10},
|
||||
{Name: "w3wp#1", ProcessID: 30},
|
||||
{Name: "powershell", ProcessID: 40},
|
||||
{Name: "w3wp", Occurrence: 1, ProcessID: 20},
|
||||
}}
|
||||
c.perfClrExceptions = &fixturePerfCollector[perfDataClrExceptions]{rows: []perfDataClrExceptions{
|
||||
{Name: "w3wp"},
|
||||
{Name: "w3wp#1"},
|
||||
{Name: "powershell"},
|
||||
{Name: "w3wp", Occurrence: 1},
|
||||
}}
|
||||
|
||||
got, err := gatherExceptionsThrown(t, c)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.expected, got)
|
||||
|
||||
// The clrmemory metrics have the same process labels.
|
||||
c.describeClrMemory()
|
||||
c.perfClrMemory = &fixturePerfCollector[perfDataClrMemory]{rows: []perfDataClrMemory{
|
||||
{Name: "w3wp", ProcessID: 10},
|
||||
{Name: "w3wp#1", ProcessID: 30},
|
||||
{Name: "powershell", ProcessID: 40},
|
||||
{Name: "w3wp", Occurrence: 1, ProcessID: 20},
|
||||
}}
|
||||
|
||||
got, err = gatherProcessIDs(t, func(ch chan<- prometheus.Metric) error {
|
||||
return c.collectClrMemory(ch, nil, time.Second)
|
||||
}, "windows_netframework_clrmemory_allocated_bytes_total")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.expected, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -570,6 +570,8 @@ func TestRegexpFlags(t *testing.T) {
|
||||
"collector.logical_disk.volume-include",
|
||||
"collector.net.nic-exclude",
|
||||
"collector.net.nic-include",
|
||||
"collector.netframework.process-exclude",
|
||||
"collector.netframework.process-include",
|
||||
"collector.physical_disk.disk-exclude",
|
||||
"collector.physical_disk.disk-include",
|
||||
"collector.printer.exclude",
|
||||
|
||||
Reference in New Issue
Block a user