Compare commits

..

1 Commits

Author SHA1 Message Date
Jan-Otto Kröpke
1e381f860d service: [0.27.x] fix panic with NewWithConfig code path (#1646)
Co-authored-by: Erik Baranowski <erik.r.baranowski@gmail.com>
2024-09-28 10:40:55 +02:00
151 changed files with 5511 additions and 8302 deletions

View File

@@ -1,16 +1,6 @@
root = true
[*]
charset = utf-8
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
tab_width = 4
[*.wxs]
[*.{json,wxs,xml}]
indent_style = space
indent_size = 4
[*.{yml,yaml}]
indent_style = space
indent_size = 2

View File

@@ -6,27 +6,6 @@ body:
attributes:
value: Thanks for taking the time to fill out this bug report!
- type: markdown
attributes:
value: |-
> [!NOTE]
> If you encounter "Counter not found" issues, try to re-build the performance counter first.
```
PS C:\WINDOWS\system32> cd c:\windows\system32
PS C:\windows\system32> lodctr /R
Error: Unable to rebuild performance counter setting from system backup store, error code is 2
PS C:\windows\system32> cd ..
PS C:\windows> cd syswow64
PS C:\windows\syswow64> lodctr /R
Info: Successfully rebuilt performance counter setting from system backup store
PS C:\windows\syswow64> winmgmt.exe /RESYNCPERF
```
----
- type: textarea
attributes:
label: Current Behavior

View File

@@ -12,8 +12,6 @@ on:
- "tools/e2e-output.txt"
branches:
- master
- next
- main
pull_request:
paths:
- "go.mod"
@@ -23,11 +21,9 @@ on:
- "tools/e2e-output.txt"
branches:
- master
- next
- main
env:
VERSION_PROMU: '0.14.0'
PROMU_VER: '0.14.0'
PROMTOOL_VER: '2.43.0'
jobs:
@@ -44,9 +40,9 @@ jobs:
- name: Install e2e deps
run: |
Invoke-WebRequest -Uri https://github.com/prometheus/promu/releases/download/v$($Env:VERSION_PROMU)/promu-$($Env:VERSION_PROMU).windows-amd64.zip -OutFile promu-$($Env:VERSION_PROMU).windows-amd64.zip
Expand-Archive -Path promu-$($Env:VERSION_PROMU).windows-amd64.zip -DestinationPath .
Copy-Item -Path promu-$($Env:VERSION_PROMU).windows-amd64\promu.exe -Destination "$(go env GOPATH)\bin"
Invoke-WebRequest -Uri https://github.com/prometheus/promu/releases/download/v$($Env:PROMU_VER)/promu-$($Env:PROMU_VER).windows-amd64.zip -OutFile promu-$($Env:PROMU_VER).windows-amd64.zip
Expand-Archive -Path promu-$($Env:PROMU_VER).windows-amd64.zip -DestinationPath .
Copy-Item -Path promu-$($Env:PROMU_VER).windows-amd64\promu.exe -Destination "$(go env GOPATH)\bin"
# GOPATH\bin dir must be appended to PATH else the `promu` command won't be found
echo "$(go env GOPATH)\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
@@ -68,9 +64,9 @@ jobs:
Expand-Archive -Path prometheus-$($Env:PROMTOOL_VER).windows-amd64.zip -DestinationPath .
Copy-Item -Path prometheus-$($Env:PROMTOOL_VER).windows-amd64\promtool.exe -Destination "$(go env GOPATH)\bin"
Invoke-WebRequest -Uri https://github.com/prometheus/promu/releases/download/v$($Env:VERSION_PROMU)/promu-$($Env:VERSION_PROMU).windows-amd64.zip -OutFile promu-$($Env:VERSION_PROMU).windows-amd64.zip
Expand-Archive -Path promu-$($Env:VERSION_PROMU).windows-amd64.zip -DestinationPath .
Copy-Item -Path promu-$($Env:VERSION_PROMU).windows-amd64\promu.exe -Destination "$(go env GOPATH)\bin"
Invoke-WebRequest -Uri https://github.com/prometheus/promu/releases/download/v$($Env:PROMU_VER)/promu-$($Env:PROMU_VER).windows-amd64.zip -OutFile promu-$($Env:PROMU_VER).windows-amd64.zip
Expand-Archive -Path promu-$($Env:PROMU_VER).windows-amd64.zip -DestinationPath .
Copy-Item -Path promu-$($Env:PROMU_VER).windows-amd64\promu.exe -Destination "$(go env GOPATH)\bin"
# GOPATH\bin dir must be appended to PATH else the `promu` command won't be found
echo "$(go env GOPATH)\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
@@ -104,5 +100,5 @@ jobs:
- name: golangci-lint
uses: golangci/golangci-lint-action@v6
with:
version: v1.60
args: "--max-same-issues=0"
version: v1.59
args: "--timeout=5m"

View File

@@ -37,7 +37,7 @@ jobs:
- name: check
run: |
PR_TITLE_PREFIX=$(echo "$PR_TITLE" | cut -d':' -f1)
if [[ -d "pkg/collector/$PR_TITLE_PREFIX" ]] ||[[ -d "pkg/$PR_TITLE_PREFIX" ]] || [[ -d "$PR_TITLE_PREFIX" ]] || [[ "$PR_TITLE_PREFIX" == "chore" ]] || [[ "$PR_TITLE_PREFIX" == "chore(docs)" ]] || [[ "$PR_TITLE_PREFIX" == "chore(deps)" ]] || [[ "$PR_TITLE_PREFIX" == "*" ]] || [[ "$PR_TITLE_PREFIX" == "Synchronize common files from prometheus/prometheus" ]]; then
if [[ -d "pkg/collector/$PR_TITLE_PREFIX" ]] || [[ -d "$PR_TITLE_PREFIX" ]] || [[ "$PR_TITLE_PREFIX" == "chore" ]] || [[ "$PR_TITLE_PREFIX" == "chore(deps)" ]] || [[ "$PR_TITLE_PREFIX" == "*" ]] || [[ "$PR_TITLE_PREFIX" == "Synchronize common files from prometheus/prometheus" ]]; then
exit 0
fi

View File

@@ -16,16 +16,17 @@ permissions:
packages: write
env:
VERSION_PROMU: '0.14.0'
VERSION_CONTAINERD: '1.7.21'
VERSION_BUILDKIT: '0.15.2'
VERSION_BUILDX: '0.16.2'
PROMU_VER: '0.14.0'
jobs:
build:
runs-on: windows-2022
steps:
- uses: actions/checkout@v4
with:
# fetch-depth required for gitversion in `Build` step
fetch-depth: 0
- uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
@@ -33,38 +34,41 @@ jobs:
# https://github.com/pl4nty/Windows-Containers/blob/Main/helpful_tools/Install-BuildKit-GitHubActions/workflow.yaml
- name: Setup containerd
run: |
curl.exe -L https://github.com/containerd/containerd/releases/download/v${{ env.VERSION_CONTAINERD }}/containerd-${{ env.VERSION_CONTAINERD }}-windows-amd64.tar.gz -o containerd.tar.gz
$version = "1.7.20"
curl.exe -L https://github.com/containerd/containerd/releases/download/v$version/containerd-$version-windows-amd64.tar.gz -o containerd.tar.gz
tar.exe xvf containerd.tar.gz
.\bin\containerd.exe --register-service
Start-Service containerd
- name: Setup BuildKit
run: |
curl.exe -L https://github.com/moby/buildkit/releases/download/v${{ env.VERSION_BUILDKIT }}/buildkit-v${{ env.VERSION_BUILDKIT }}.windows-amd64.tar.gz -o buildkit.tar.gz
$version = "v0.15.0"
curl.exe -L https://github.com/moby/buildkit/releases/download/$version/buildkit-$version.windows-amd64.tar.gz -o buildkit.tar.gz
tar.exe xvf buildkit.tar.gz
.\bin\buildkitd.exe --register-service
Start-Service buildkitd
- name: Setup Docker Buildx
run: |
curl.exe -L https://github.com/docker/buildx/releases/download/v${{ env.VERSION_BUILDX }}/buildx-v${{ env.VERSION_BUILDX }}.windows-amd64.exe -o $env:ProgramData\Docker\cli-plugins\docker-buildx.exe
$version = "v0.16.1"
curl.exe -L https://github.com/docker/buildx/releases/download/$version/buildx-$version.windows-amd64.exe -o $env:ProgramData\Docker\cli-plugins\docker-buildx.exe
- uses: docker/setup-buildx-action@v3
with:
driver: remote
endpoint: npipe:////./pipe/buildkitd
- name: Install WiX
run: dotnet tool install --global wix
- name: Install WiX extensions
run: |
wix extension add -g WixToolset.Util.wixext
wix extension add -g WixToolset.Ui.wixext
wix extension add -g WixToolset.Firewall.wixext
- name: Install Build deps
run: |
Invoke-WebRequest -Uri https://github.com/prometheus/promu/releases/download/v$($Env:VERSION_PROMU)/promu-$($Env:VERSION_PROMU).windows-amd64.zip -OutFile promu-$($Env:VERSION_PROMU).windows-amd64.zip
Expand-Archive -Path promu-$($Env:VERSION_PROMU).windows-amd64.zip -DestinationPath .
Copy-Item -Path promu-$($Env:VERSION_PROMU).windows-amd64\promu.exe -Destination "$(go env GOPATH)\bin"
Invoke-WebRequest -Uri https://github.com/prometheus/promu/releases/download/v$($Env:PROMU_VER)/promu-$($Env:PROMU_VER).windows-amd64.zip -OutFile promu-$($Env:PROMU_VER).windows-amd64.zip
Expand-Archive -Path promu-$($Env:PROMU_VER).windows-amd64.zip -DestinationPath .
Copy-Item -Path promu-$($Env:PROMU_VER).windows-amd64\promu.exe -Destination "$(go env GOPATH)\bin"
# GOPATH\bin dir must be added to PATH else the `promu` commands won't be found
echo "$(go env GOPATH)\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append

View File

@@ -10,7 +10,7 @@ on:
- master
env:
VERSION_PROMU: 'v0.14.0'
PROMU_VER: 'v0.14.0'
jobs:
codespell:

View File

@@ -1,29 +0,0 @@
name: 'Close stale issues and PRs'
on:
workflow_dispatch: {}
schedule:
- cron: '30 1 * * *'
permissions:
issues: write
pull-requests: write
jobs:
stale:
if: github.repository_owner == 'prometheus' || github.repository_owner == 'prometheus-community' # Don't run this workflow on forks.
runs-on: ubuntu-latest
steps:
- uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
# opt out of defaults to avoid marking issues as stale and closing them
# https://github.com/actions/stale#days-before-close
# https://github.com/actions/stale#days-before-stale
days-before-stale: -1
days-before-close: -1
stale-pr-message: ''
stale-issue-message: 'This issue has been marked as stale because it has been open for 90 days with no activity. This thread will be automatically closed in 30 days if no further activity occurs.'
operations-per-run: 30
# override days-before-stale, for only marking the pull requests as stale
days-before-issue-stale: 90
days-before-issue-close: 30
stale-pr-label: stale
exempt-pr-labels: keepalive

View File

@@ -1,31 +1,21 @@
name: Stale Check
name: 'Close stale issues and PRs'
on:
workflow_dispatch: {}
schedule:
- cron: '16 22 * * *'
- cron: '30 1 * * *'
permissions:
issues: write
pull-requests: write
jobs:
stale:
if: github.repository_owner == 'prometheus' || github.repository_owner == 'prometheus-community' # Don't run this workflow on forks.
runs-on: ubuntu-latest
steps:
- uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0
- uses: actions/stale@v9
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
# opt out of defaults to avoid marking issues as stale and closing them
# https://github.com/actions/stale#days-before-close
# https://github.com/actions/stale#days-before-stale
days-before-stale: -1
days-before-close: -1
# Setting it to empty string to skip comments.
# https://github.com/actions/stale#stale-pr-message
# https://github.com/actions/stale#stale-issue-message
stale-pr-message: ''
stale-issue-message: ''
operations-per-run: 30
# override days-before-stale, for only marking the pull requests as stale
days-before-pr-stale: 60
stale-pr-label: stale
exempt-pr-labels: keepalive
stale-issue-message: 'This issue has been marked as stale because it has been open for 90 days with no activity. This thread will be automatically closed in 30 days if no further activity occurs.'
exempt-issue-labels: 'lifecycle/frozen'
days-before-stale: 90
days-before-close: 30
enable-statistics: false
operations-per-run: 500

View File

@@ -1,36 +1,38 @@
linters:
enable-all: true
disable:
- containedctx
- contextcheck
- cyclop
- depguard
- dogsled
- dupl
- err113
- execinquery
- exhaustive
- exhaustruct
- exportloopref
- fatcontext
- funlen
- gochecknoglobals
- gocognit
- goconst
- gocyclo
- gomnd
- godox
- inamedparam
- ireturn
- lll
- maintidx
- mnd
- nlreturn
- noctx
- testpackage
- varnamelen
- wrapcheck
run:
timeout: 5m
- wsl
- execinquery
- gomnd
- stylecheck
- maintidx
linters-settings:
gosec:
excludes:
- G115 # integer overflow conversion
gci:
sections:
- prefix(github.com/prometheus-community/windows_exporter/pkg/initiate)
@@ -45,33 +47,7 @@ linters-settings:
# Support string case: `camel`, `pascal`, `kebab`, `snake`, `upperSnake`, `goCamel`, `goPascal`, `goKebab`, `goSnake`, `upper`, `lower`, `header`
json: camel
yaml: snake
gomoddirectives:
replace-allow-list:
- github.com/prometheus/common # https://github.com/prometheus/common/pull/694
forbidigo:
forbid:
- "^(fmt\\.Print(|f|ln)|print|println)$"
- p: "^syscall\\..*$"
msg: use golang.org/x/sys/windows instead of syscall
- p: "^windows\\.NewLazyDLL$"
msg: use NewLazySystemDLL instead NewLazyDLL
sloglint:
no-mixed-args: true
kv-only: false
attr-only: true
no-global: "all"
context: "scope"
static-msg: false
no-raw-keys: false
key-naming-case: snake
forbidden-keys:
- time
- level
- msg
- source
args-on-sep-lines: true
stylecheck:
checks: ["all", "-ST1003"]
issues:
exclude:
- don't use underscores in Go names
@@ -85,7 +61,3 @@ issues:
- text: "don't use ALL_CAPS in Go names; use CamelCase"
linters:
- revive
- path: pkg/perflib/
linters:
- godox
- stylecheck

View File

@@ -1,24 +1,19 @@
go:
# Whenever the Go version is updated here,
# .github/workflows should also be updated.
version: 1.23
version: 1.20
repository:
path: github.com/prometheus-community/windows_exporter
path: github.com/prometheus-community/windows_exporter
build:
binaries:
- name: windows_exporter
tags:
all:
- trimpath
ldflags: |
-X github.com/prometheus/common/version.Version={{.Version}}
-X github.com/prometheus/common/version.Revision={{.Revision}}
-X github.com/prometheus/common/version.Branch={{.Branch}}
-X github.com/prometheus/common/version.BuildUser={{user}}@{{host}}
-X github.com/prometheus/common/version.BuildDate={{date "20060102-15:04:05"}}
binaries:
- name: windows_exporter
ldflags: |
-X github.com/prometheus/common/version.Version={{.Version}}
-X github.com/prometheus/common/version.Revision={{.Revision}}
-X github.com/prometheus/common/version.Branch={{.Branch}}
-X github.com/prometheus/common/version.BuildUser={{user}}@{{host}}
-X github.com/prometheus/common/version.BuildDate={{date "20060102-15:04:05"}}
tarball:
files:
- LICENSE
files:
- LICENSE
crossbuild:
platforms:
- windows
platforms:
- windows

View File

@@ -7,7 +7,7 @@ DOCKER_REPO ?= prometheuscommunity
DOCKER_IMAGE_NAME ?= windows-exporter
# ALL_DOCKER_REPOS is the list of repositories to push the image to. ghcr.io requires that org name be the same as the image repo name.
ALL_DOCKER_REPOS ?= docker.io/$(DOCKER_REPO) ghcr.io/prometheus-community # quay.io/$(DOCKER_REPO)
ALL_DOCKER_REPOS ?= docker.io/$(DOCKER_REPO) ghcr.io/prometheus-community # quay.io/$(DOCKER_REPO)
# Image Variables for host process Container
# Windows image build is heavily influenced by https://github.com/kubernetes/kubernetes/blob/master/cluster/images/etcd/Makefile

View File

@@ -14,7 +14,7 @@ Name | Description | Enabled by default
[cache](docs/collector.cache.md) | Cache metrics |
[cpu](docs/collector.cpu.md) | CPU usage | &#10003;
[cpu_info](docs/collector.cpu_info.md) | CPU Information |
[cs](docs/collector.cs.md) | "Computer System" metrics (system properties, num cpus/total memory) |
[cs](docs/collector.cs.md) | "Computer System" metrics (system properties, num cpus/total memory) | &#10003;
[container](docs/collector.container.md) | Container metrics |
[diskdrive](docs/collector.diskdrive.md) | Diskdrive metrics |
[dfsr](docs/collector.dfsr.md) | DFSR metrics |
@@ -27,8 +27,12 @@ Name | Description | Enabled by default
[license](docs/collector.license.md) | Windows license status |
[logical_disk](docs/collector.logical_disk.md) | Logical disks, disk I/O | &#10003;
[logon](docs/collector.logon.md) | User logon sessions |
[memory](docs/collector.memory.md) | Memory usage metrics | &#10003;
[mscluster](docs/collector.mscluster.md) | MSCluster metrics |
[memory](docs/collector.memory.md) | Memory usage metrics |
[mscluster_cluster](docs/collector.mscluster_cluster.md) | MSCluster cluster metrics |
[mscluster_network](docs/collector.mscluster_network.md) | MSCluster network metrics |
[mscluster_node](docs/collector.mscluster_node.md) | MSCluster Node metrics |
[mscluster_resource](docs/collector.mscluster_resource.md) | MSCluster Resource metrics |
[mscluster_resourcegroup](docs/collector.mscluster_resourcegroup.md) | MSCluster ResourceGroup metrics |
[msmq](docs/collector.msmq.md) | MSMQ queues |
[mssql](docs/collector.mssql.md) | [SQL Server Performance Objects](https://docs.microsoft.com/en-us/sql/relational-databases/performance-monitor/use-sql-server-objects#SQLServerPOs) metrics |
[netframework_clrexceptions](docs/collector.netframework_clrexceptions.md) | .NET Framework CLR Exceptions |
@@ -94,33 +98,23 @@ windows_exporter accepts flags to configure certain behaviours. The ones configu
| `--config.file.insecure-skip-verify` | Skip TLS when loading config file from URL | false |
## Installation
The latest release can be downloaded from the [releases page](https://github.com/prometheus-community/windows_exporter/releases).
Each release provides a .msi installer. The installer will setup the windows_exporter as a Windows service, as well as create an exception in the Windows Firewall.
If the installer is run without any parameters, the exporter will run with default settings for enabled collectors, ports, etc.
If the installer is run without any parameters, the exporter will run with default settings for enabled collectors, ports, etc. The following parameters are available:
The installer provides a configuration file to customize the exporter.
The configuration file
* is located in the same directory as the exporter executable.
* has the YAML format and is provided with the `--config.file` parameter.
* can be used to enable or disable collectors, set collector-specific parameters, and set global parameters.
The following parameters are available:
| Name | Description |
|----------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `ENABLED_COLLECTORS` | As the `--collectors.enabled` flag, provide a comma-separated list of enabled collectors |
| `LISTEN_ADDR` | The IP address to bind to. Defaults to an empty string. (any local address) |
| `LISTEN_PORT` | The port to bind to. Defaults to `9182`. |
| `METRICS_PATH` | The path at which to serve metrics. Defaults to `/metrics` |
| `TEXTFILE_DIRS` | Use the `--collector.textfile.directories` flag to specify one or more directories, separated by commas, where the collector should read text files containing metrics |
| `REMOTE_ADDR` | Allows setting comma separated remote IP addresses for the Windows Firewall exception (allow list). Defaults to an empty string (any remote address). |
| `EXTRA_FLAGS` | Allows passing full CLI flags. Defaults to an empty string. |
| `ADDLOCAL` | Enables features within the windows_exporter installer. Supported values: `FirewallException` |
| `REMOVE` | Disables features within the windows_exporter installer. Supported values: `FirewallException` |
| Name | Description |
|----------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------|
| `ENABLED_COLLECTORS` | As the `--collectors.enabled` flag, provide a comma-separated list of enabled collectors |
| `LISTEN_ADDR` | The IP address to bind to. Defaults to an empty string. (any local address) |
| `LISTEN_PORT` | The port to bind to. Defaults to `9182`. |
| `METRICS_PATH` | The path at which to serve metrics. Defaults to `/metrics` |
| `TEXTFILE_DIRS` | As the `--collector.textfile.directories` flag, provide a directory to read text files with metrics from |
| `REMOTE_ADDR` | Allows setting comma separated remote IP addresses for the Windows Firewall exception (allow list). Defaults to an empty string (any remote address). |
| `EXTRA_FLAGS` | Allows passing full CLI flags. Defaults to an empty string. |
| `ADD_FIREWALL_EXCEPTION` | Setup an firewall exception for windows_exporter. Defaults to `yes`. |
| `ENABLE_V1_PERFORMANCE_COUNTERS` | Enables V1 performance counter on modern systems. Defaults to `yes`. |
Parameters are sent to the installer via `msiexec`. Example invocations:
@@ -142,7 +136,13 @@ msiexec /i C:\Users\Administrator\Downloads\windows_exporter.msi ENABLED_COLLECT
To install the exporter with creating a firewall exception, use the following command:
```powershell
msiexec /i <path-to-msi-file> ADDLOCAL=FirewallException
msiexec /i <path-to-msi-file> ADD_FIREWALL_EXCEPTION=yes
```
To repair an installation, e.g force re-creating Windows service:
```powershell
msiexec /fa <path-to-msi-file>
```
@@ -218,7 +218,7 @@ If you need to skip TLS verification, you can use the `--config.file.insecure-sk
```yaml
collectors:
enabled: cpu,net,service
enabled: cpu,cs,net,service
collector:
service:
services-where: "Name='windows_exporter'"

View File

@@ -1,5 +1,5 @@
collectors:
enabled: cpu,cpu_info,exchange,iis,logical_disk,logon,memory,net,os,process,remote_fx,service,system,tcp,time,terminal_services,textfile
enabled: cpu,cpu_info,cs,exchange,iis,logical_disk,logon,memory,net,os,process,remote_fx,service,system,tcp,time,terminal_services,textfile
collector:
service:
services-where: "Name='windows_exporter'"

View File

@@ -21,7 +21,11 @@ This directory contains documentation of the collectors in the windows_exporter,
- [`logical_disk`](collector.logical_disk.md)
- [`logon`](collector.logon.md)
- [`memory`](collector.memory.md)
- [`mscluster`](collector.mscluster.md)
- [`mscluster_cluster`](collector.mscluster_cluster.md)
- [`mscluster_network`](collector.mscluster_network.md)
- [`mscluster_node`](collector.mscluster_node.md)
- [`mscluster_resource`](collector.mscluster_resource.md)
- [`mscluster_resourcegroup`](collector.mscluster_resourcegroup.md)
- [`msmq`](collector.msmq.md)
- [`mssql`](collector.mssql.md)
- [`net`](collector.net.md)

View File

@@ -16,22 +16,26 @@ None
## Metrics
These metrics are available on all versions of Windows:
| Name | Description | Type | Labels |
|--------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|-----------------|
| `windows_cpu_logical_processor` | Number of installed logical processors | counter | `core`, `state` |
| `windows_cpu_cstate_seconds_total` | Time spent in low-power idle states | counter | `core`, `state` |
| `windows_cpu_time_total` | Time that processor spent in different modes (dpc, idle, interrupt, privileged, user) | counter | `core`, `mode` |
| `windows_cpu_interrupts_total` | Total number of received and serviced hardware interrupts | counter | `core` |
| `windows_cpu_dpcs_total` | Total number of received and serviced deferred procedure calls (DPCs) | counter | `core` |
| `windows_cpu_clock_interrupts_total` | Total number of received and serviced clock tick interrupts | counter | `core` |
| `windows_cpu_idle_break_events_total` | Total number of time processor was woken from idle | counter | `core` |
| `windows_cpu_parking_status` | Parking Status represents whether a processor is parked or not | gauge | `core` |
| `windows_cpu_core_frequency_mhz` | Core frequency in megahertz | gauge | `core` |
| `windows_cpu_processor_performance_total` | Processor Performance is the number of CPU cycles executing instructions by each core; it is believed to be similar to the value that the APERF MSR would show, were it exposed | counter | `core` |
| `windows_cpu_processor_mperf_total` | Processor MPerf Total is proportioanl to the number of TSC ticks each core has accumulated while executing instructions. Due to the manner in which it is presented, it should be scaled by 1e2 to properly line up with Processor Performance Total. As above, it is believed to be closely related to the MPERF MSR. | counter | `core` |
| `windows_cpu_processor_rtc_total` | RTC total is assumed to represent the 64Hz tick rate in Windows. It is not by itself useful, but can be used with `windows_cpu_processor_utility_total` to more accurately measure CPU utilisation than with `windows_cpu_time_total` | counter | `core` |
| `windows_cpu_processor_utility_total` | Processor Utility Total is a newer, more accurate measure of CPU utilization, in particular handling modern CPUs with variant CPU frequencies. The rate of this counter divided by the rate of `windows_cpu_processor_rtc_total` should provide an accurate view of CPU utilisation on modern systems, as observed in Task Manager. | counter | `core` |
| `windows_cpu_processor_privileged_utility_total` | Processor Privileged Utility Total, when used in a similar fashion to `windows_cpu_processor_utility_total` will show the portion of CPU utilization which is happening in privileged mode. | counter | `core` |
Name | Description | Type | Labels
-----|-------------|------|-------
`windows_cpu_cstate_seconds_total` | Time spent in low-power idle states | counter | `core`, `state`
`windows_cpu_time_total` | Time that processor spent in different modes (dpc, idle, interrupt, privileged, user) | counter | `core`, `mode`
`windows_cpu_interrupts_total` | Total number of received and serviced hardware interrupts | counter | `core`
`windows_cpu_dpcs_total` | Total number of received and serviced deferred procedure calls (DPCs) | counter | `core`
These metrics are only exposed on Windows Server 2008R2 and later:
Name | Description | Type | Labels
-----|-------------|------|-------
`windows_cpu_clock_interrupts_total` | Total number of received and serviced clock tick interrupts | counter | `core`
`windows_cpu_idle_break_events_total` | Total number of time processor was woken from idle | counter | `core`
`windows_cpu_parking_status` | Parking Status represents whether a processor is parked or not | gauge | `core`
`windows_cpu_core_frequency_mhz` | Core frequency in megahertz | gauge | `core`
`windows_cpu_processor_performance_total` | Processor Performance is the number of CPU cycles executing instructions by each core; it is believed to be similar to the value that the APERF MSR would show, were it exposed | counter | `core`
`windows_cpu_processor_mperf_total` | Processor MPerf Total is proportioanl to the number of TSC ticks each core has accumulated while executing instructions. Due to the manner in which it is presented, it should be scaled by 1e2 to properly line up with Processor Performance Total. As above, it is believed to be closely related to the MPERF MSR. | counter | `core`
`windows_cpu_processor_rtc_total` | RTC total is assumed to represent the 64Hz tick rate in Windows. It is not by itself useful, but can be used with `windows_cpu_processor_utility_total` to more accurately measure CPU utilisation than with `windows_cpu_time_total` | counter | `core`
`windows_cpu_processor_utility_total` | Processor Utility Total is a newer, more accurate measure of CPU utilization, in particular handling modern CPUs with variant CPU frequencies. The rate of this counter divided by the rate of `windows_cpu_processor_rtc_total` should provide an accurate view of CPU utilisation on modern systems, as observed in Task Manager. | counter | `core`
`windows_cpu_processor_privileged_utility_total` | Processor Privileged Utility Total, when used in a similar fashion to `windows_cpu_processor_utility_total` will show the portion of CPU utilization which is happening in privileged mode. | counter | `core`
### Example metric
Show frequency of host CPU cores

View File

@@ -15,39 +15,13 @@ None
## Metrics
| Name | Description | Type | Labels |
|--------------------------------------------|--------------------------------------|-------|--------------------------------------------------------------|
| `windows_cpu_info` | Labelled CPU information | gauge | `architecture`, `description`, `device_id`, `family`, `name` |
| `windows_cpu_info_core` | Number of cores per CPU | gauge | `device_id` |
| `windows_cpu_info_enabled_core` | Number of enabled cores per CPU | gauge | `device_id` |
| `windows_cpu_info_l2_cache_size` | Size of L2 cache per CPU | gauge | `device_id` |
| `windows_cpu_info_l3_cache_size` | Size of L3 cache per CPU | gauge | `device_id` |
| `windows_cpu_info_logical_processor` | Number of logical processors per CPU | gauge | `device_id` |
| `windows_cpu_info_thread` | Number of threads per CPU | gauge | `device_id` |
Name | Description | Type | Labels
-----|-------------|------|-------
`windows_cpu_info` | Labelled CPU information | gauge | `architecture`, `device_id`, `description`, `family`, `l2_cache_size` `l3_cache_size`, `name`
### Example metric
```
# HELP windows_cpu_info Labelled CPU information as provided by Win32_Processor
# TYPE windows_cpu_info gauge
windows_cpu_info{architecture="9",description="AMD64 Family 25 Model 33 Stepping 2",device_id="CPU0",family="107",name="AMD Ryzen 9 5900X 12-Core Processor"} 1
# HELP windows_cpu_info_core Number of cores per CPU
# TYPE windows_cpu_info_core gauge
windows_cpu_info_core{device_id="CPU0"} 12
# HELP windows_cpu_info_enabled_core Number of enabled cores per CPU
# TYPE windows_cpu_info_enabled_core gauge
windows_cpu_info_enabled_core{device_id="CPU0"} 12
# HELP windows_cpu_info_l2_cache_size Size of L2 cache per CPU
# TYPE windows_cpu_info_l2_cache_size gauge
windows_cpu_info_l2_cache_size{device_id="CPU0"} 6144
# HELP windows_cpu_info_l3_cache_size Size of L3 cache per CPU
# TYPE windows_cpu_info_l3_cache_size gauge
windows_cpu_info_l3_cache_size{device_id="CPU0"} 65536
# HELP windows_cpu_info_logical_processor Number of logical processors per CPU
# TYPE windows_cpu_info_logical_processor gauge
windows_cpu_info_logical_processor{device_id="CPU0"} 24
# HELP windows_cpu_info_thread Number of threads per CPU
# TYPE windows_cpu_info_thread gauge
windows_cpu_info_thread{device_id="CPU0"} 24
windows_cpu_info{architecture="9",description="AMD64 Family 23 Model 49 Stepping 0",device_id="CPU0",family="107",l2_cache_size="32768",l3_cache_size="262144",name="AMD EPYC 7702P 64-Core Processor"} 1
```
The value of the metric is irrelevant, but the labels expose some useful information on the CPU installed in each socket.

View File

@@ -1,9 +1,5 @@
# cs collector
> [!CAUTION]
> This collector is deprecated and will be removed in a future release.
> See https://github.com/prometheus-community/windows_exporter/pull/1596 for more information.
The cs collector exposes metrics detailing the hardware of the computer system
|||

View File

@@ -5,9 +5,9 @@ The memory collector exposes metrics about system memory usage
|||
-|-
Metric name prefix | `memory`
Data source | Performance Counters
Classes | -
Enabled by default? | Yes
Data source | Perflib
Classes | `Win32_PerfRawData_PerfOS_Memory`
Enabled by default? | No
## Flags
@@ -15,73 +15,46 @@ None
## Metrics
| Name | Description | Type | Labels |
|------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|--------|
| `windows_memory_available_bytes` | The amount of physical memory immediately available for allocation to a process or for system use. It is equal to the sum of memory assigned to the standby (cached), free and zero page lists | gauge | None |
| `windows_memory_cache_bytes` | Number of bytes currently being used by the file system cache | gauge | None |
| `windows_memory_cache_bytes_peak` | Maximum number of CacheBytes after the system was last restarted | gauge | None |
| `windows_memory_cache_faults_total` | Number of faults which occur when a page sought in the file system cache is not found there and must be retrieved from elsewhere in memory (soft fault) or from disk (hard fault) | counter | None |
| `windows_memory_commit_limit` | Amount of virtual memory, in bytes, that can be committed without having to extend the paging file(s) | gauge | None |
| `windows_memory_committed_bytes` | Amount of committed virtual memory, in bytes | gauge | None |
| `windows_memory_demand_zero_faults_total` | The number of zeroed pages required to satisfy faults. Zeroed pages, pages emptied of previously stored data and filled with zeros, are a security feature of Windows that prevent processes from seeing data stored by earlier processes that used the memory space | counter | None |
| `windows_memory_free_and_zero_page_list_bytes` | The amount of physical memory, in bytes, that is assigned to the free and zero page lists. This memory does not contain cached data. It is immediately available for allocation to a process or for system use | gauge | None |
| `windows_memory_free_system_page_table_entries` | Number of page table entries not being used by the system | gauge | None |
| `windows_memory_modified_page_list_bytes` | The amount of physical memory, in bytes, that is assigned to the modified page list. This memory contains cached data and code that is not actively in use by processes, the system and the system cache. This memory needs to be written out before it will be available for allocation to a process or for system use | gauge | None |
| `windows_memory_page_faults_total` | Overall rate at which faulted pages are handled by the processor | counter | None |
| `windows_memory_swap_page_reads_total` | Number of disk page reads (a single read operation reading several pages is still only counted once) | counter | None |
| `windows_memory_swap_pages_read_total` | Number of pages read across all page reads (ie counting all pages read even if they are read in a single operation) | counter | None |
| `windows_memory_swap_pages_written_total` | Number of pages written across all page writes (ie counting all pages written even if they are written in a single operation) | counter | None |
| `windows_memory_swap_page_operations_total` | Total number of swap page read and writes (PagesPersec) | counter | None |
| `windows_memory_swap_page_writes_total` | Number of disk page writes (a single write operation writing several pages is still only counted once) | counter | None |
| `windows_memory_physical_free_bytes` | Bytes of physical memory currently unused and available | gauge | None |
| `windows_memory_physical_total_bytes` | Total bytes of physical memory available to the operating system. This value does not necessarily indicate the true amount of physical memory, but what is reported to the operating system as available to it | gauge | None |
| `windows_memory_pool_nonpaged_allocs_total` | The number of calls to allocate space in the nonpaged pool. The nonpaged pool is an area of system memory area for objects that cannot be written to disk, and must remain in physical memory as long as they are allocated | counter | None |
| `windows_memory_pool_nonpaged_bytes` | Number of bytes in the non-paged pool, an area of the system virtual memory that is used for objects that cannot be written to disk, but must remain in physical memory as long as they are allocated | gauge | None |
| `windows_memory_pool_paged_allocs_total` | Number of calls to allocate space in the paged pool, regardless of the amount of space allocated in each call | counter | None |
| `windows_memory_pool_paged_bytes` | Number of bytes in the paged pool | gauge | None |
| `windows_memory_pool_paged_resident_bytes` | The size, in bytes, of the portion of the paged pool that is currently resident and active in physical memory. The paged pool is an area of the system virtual memory that is used for objects that can be written to disk when they are not being used | gauge | None |
| `windows_memory_process_memory_limit_bytes` | Maximum number of bytes of memory that can be allocated to a process | gauge | None |
| `windows_memory_standby_cache_core_bytes` | The amount of physical memory, in bytes, that is assigned to the core standby cache page lists. This memory contains cached data and code that is not actively in use by processes, the system and the system cache. It is immediately available for allocation to a process or for system use. If the system runs out of available free and zero memory, memory on lower priority standby cache page lists will be repurposed before memory on higher priority standby cache page lists | gauge | None |
| `windows_memory_standby_cache_normal_priority_bytes` | The amount of physical memory, in bytes, that is assigned to the normal priority standby cache page lists. This memory contains cached data and code that is not actively in use by processes, the system and the system cache. It is immediately available for allocation to a process or for system use. If the system runs out of available free and zero memory, memory on lower priority standby cache page lists will be repurposed before memory on higher priority standby cache page lists | gauge | None |
| `windows_memory_standby_cache_reserve_bytes` | The amount of physical memory, in bytes, that is assigned to the reserve standby cache page lists. This memory contains cached data and code that is not actively in use by processes, the system and the system cache. It is immediately available for allocation to a process or for system use. If the system runs out of available free and zero memory, memory on lower priority standby cache page lists will be repurposed before memory on higher priority standby cache page lists | gauge | None |
| `windows_memory_system_cache_resident_bytes` | The size, in bytes, of the portion of the system file cache which is currently resident and active in physical memory | gauge | None |
| `windows_memory_system_code_resident_bytes` | The size, in bytes, of the pageable operating system code that is currently resident and active in physical memory. This value is a component of Memory\\System Code Total Bytes. Memory\\System Code Resident Bytes (and Memory\\System Code Total Bytes) does not include code that must remain in physical memory and cannot be written to disk | gauge | None |
| `windows_memory_system_code_total_bytes` | The size, in bytes, of the pageable operating system code currently mapped into the system virtual address space. This value is calculated by summing the bytes in Ntoskrnl.exe, Hal.dll, the boot drivers, and file systems loaded by Ntldr/osloader. This counter does not include code that must remain in physical memory and cannot be written to disk | gauge | None |
| `windows_memory_system_driver_resident_bytes` | The size, in bytes, of the pageable physical memory being used by device drivers. It is the working set (physical memory area) of the drivers. This value is a component of Memory\\System Driver Total Bytes, which also includes driver memory that has been written to disk. Neither Memory\\System Driver Resident Bytes nor Memory\\System Driver Total Bytes includes memory that cannot be written to disk | gauge | None |
| `windows_memory_system_driver_total_bytes` | The size, in bytes, of the pageable virtual memory currently being used by device drivers. Pageable memory can be written to disk when it is not being used. It includes both physical memory (Memory\\System Driver Resident Bytes) and code and data paged to disk. It is a component of Memory\\System Code Total Bytes | gauge | None |
| `windows_memory_transition_faults_total` | Number of faults rate at which page faults are resolved by recovering pages that were being used by another process sharing the page, or were on the modified page list or the standby list, or were being written to disk at the time of the page fault. The pages were recovered without additional disk activity. Transition faults are counted in numbers of faults; because only one page is faulted in each operation, it is also equal to the number of pages faulted | counter | None |
| `windows_memory_transition_pages_repurposed_total` | Transition Pages RePurposed is the rate at which the number of transition cache pages were reused for a different purpose. These pages would have otherwise remained in the page cache to provide a (fast) soft fault (instead of retrieving it from backing store) in the event the page was accessed in the future | counter | None |
| `windows_memory_write_copies_total` | The number of page faults caused by attempting to write that were satisfied by copying the page from elsewhere in physical memory | counter | None |
Name | Description | Type | Labels
-----|-------------|------|-------
`windows_memory_available_bytes` | The amount of physical memory immediately available for allocation to a process or for system use. It is equal to the sum of memory assigned to the standby (cached), free and zero page lists | gauge | None
`windows_memory_cache_bytes` | Number of bytes currently being used by the file system cache | gauge | None
`windows_memory_cache_bytes_peak` | Maximum number of CacheBytes after the system was last restarted | gauge | None
`windows_memory_cache_faults_total` | Number of faults which occur when a page sought in the file system cache is not found there and must be retrieved from elsewhere in memory (soft fault) or from disk (hard fault) | counter | None
`windows_memory_commit_limit` | Amount of virtual memory, in bytes, that can be committed without having to extend the paging file(s) | gauge | None
`windows_memory_committed_bytes` | Amount of committed virtual memory, in bytes | gauge | None
`windows_memory_demand_zero_faults_total` | The number of zeroed pages required to satisfy faults. Zeroed pages, pages emptied of previously stored data and filled with zeros, are a security feature of Windows that prevent processes from seeing data stored by earlier processes that used the memory space | counter | None
`windows_memory_free_and_zero_page_list_bytes` | The amount of physical memory, in bytes, that is assigned to the free and zero page lists. This memory does not contain cached data. It is immediately available for allocation to a process or for system use | gauge | None
`windows_memory_free_system_page_table_entries` | Number of page table entries not being used by the system | gauge | None
`windows_memory_modified_page_list_bytes` | The amount of physical memory, in bytes, that is assigned to the modified page list. This memory contains cached data and code that is not actively in use by processes, the system and the system cache. This memory needs to be written out before it will be available for allocation to a process or for system use | gauge | None
`windows_memory_page_faults_total` | Overall rate at which faulted pages are handled by the processor | counter | None
`windows_memory_swap_page_reads_total` | Number of disk page reads (a single read operation reading several pages is still only counted once) | counter | None
`windows_memory_swap_pages_read_total` | Number of pages read across all page reads (ie counting all pages read even if they are read in a single operation) | counter | None
`windows_memory_swap_pages_written_total` | Number of pages written across all page writes (ie counting all pages written even if they are written in a single operation) | counter | None
`windows_memory_swap_page_operations_total` | Total number of swap page read and writes (PagesPersec) | counter | None
`windows_memory_swap_page_writes_total` | Number of disk page writes (a single write operation writing several pages is still only counted once) | counter | None
`windows_memory_pool_nonpaged_allocs_total` | The number of calls to allocate space in the nonpaged pool. The nonpaged pool is an area of system memory area for objects that cannot be written to disk, and must remain in physical memory as long as they are allocated | counter | None
`windows_memory_pool_nonpaged_bytes` | Number of bytes in the non-paged pool, an area of the system virtual memory that is used for objects that cannot be written to disk, but must remain in physical memory as long as they are allocated | gauge | None
`windows_memory_pool_paged_allocs_total` | Number of calls to allocate space in the paged pool, regardless of the amount of space allocated in each call | counter | None
`windows_memory_pool_paged_bytes` | Number of bytes in the paged pool | gauge | None
`windows_memory_pool_paged_resident_bytes` | The size, in bytes, of the portion of the paged pool that is currently resident and active in physical memory. The paged pool is an area of the system virtual memory that is used for objects that can be written to disk when they are not being used | gauge | None
`windows_memory_standby_cache_core_bytes` | The amount of physical memory, in bytes, that is assigned to the core standby cache page lists. This memory contains cached data and code that is not actively in use by processes, the system and the system cache. It is immediately available for allocation to a process or for system use. If the system runs out of available free and zero memory, memory on lower priority standby cache page lists will be repurposed before memory on higher priority standby cache page lists | gauge | None
`windows_memory_standby_cache_normal_priority_bytes` | The amount of physical memory, in bytes, that is assigned to the normal priority standby cache page lists. This memory contains cached data and code that is not actively in use by processes, the system and the system cache. It is immediately available for allocation to a process or for system use. If the system runs out of available free and zero memory, memory on lower priority standby cache page lists will be repurposed before memory on higher priority standby cache page lists | gauge | None
`windows_memory_standby_cache_reserve_bytes` | The amount of physical memory, in bytes, that is assigned to the reserve standby cache page lists. This memory contains cached data and code that is not actively in use by processes, the system and the system cache. It is immediately available for allocation to a process or for system use. If the system runs out of available free and zero memory, memory on lower priority standby cache page lists will be repurposed before memory on higher priority standby cache page lists | gauge | None
`windows_memory_system_cache_resident_bytes` | The size, in bytes, of the portion of the system file cache which is currently resident and active in physical memory | gauge | None
`windows_memory_system_code_resident_bytes` | The size, in bytes, of the pageable operating system code that is currently resident and active in physical memory. This value is a component of Memory\\System Code Total Bytes. Memory\\System Code Resident Bytes (and Memory\\System Code Total Bytes) does not include code that must remain in physical memory and cannot be written to disk | gauge | None
`windows_memory_system_code_total_bytes` | The size, in bytes, of the pageable operating system code currently mapped into the system virtual address space. This value is calculated by summing the bytes in Ntoskrnl.exe, Hal.dll, the boot drivers, and file systems loaded by Ntldr/osloader. This counter does not include code that must remain in physical memory and cannot be written to disk | gauge | None
`windows_memory_system_driver_resident_bytes` | The size, in bytes, of the pageable physical memory being used by device drivers. It is the working set (physical memory area) of the drivers. This value is a component of Memory\\System Driver Total Bytes, which also includes driver memory that has been written to disk. Neither Memory\\System Driver Resident Bytes nor Memory\\System Driver Total Bytes includes memory that cannot be written to disk | gauge | None
`windows_memory_system_driver_total_bytes` | The size, in bytes, of the pageable virtual memory currently being used by device drivers. Pageable memory can be written to disk when it is not being used. It includes both physical memory (Memory\\System Driver Resident Bytes) and code and data paged to disk. It is a component of Memory\\System Code Total Bytes | gauge | None
`windows_memory_transition_faults_total` | Number of faults rate at which page faults are resolved by recovering pages that were being used by another process sharing the page, or were on the modified page list or the standby list, or were being written to disk at the time of the page fault. The pages were recovered without additional disk activity. Transition faults are counted in numbers of faults; because only one page is faulted in each operation, it is also equal to the number of pages faulted | counter | None
`windows_memory_transition_pages_repurposed_total` | Transition Pages RePurposed is the rate at which the number of transition cache pages were reused for a different purpose. These pages would have otherwise remained in the page cache to provide a (fast) soft fault (instead of retrieving it from backing store) in the event the page was accessed in the future | counter | None
`windows_memory_write_copies_total` | The number of page faults caused by attempting to write that were satisfied by copying the page from elsewhere in physical memory | counter | None
### Example metric
_This collector does not yet have explained examples, we would appreciate your help adding them!_
## Useful queries
_This collector does not yet have any useful queries added, we would appreciate your help adding them!_
Show memory usage for instance (%)
```
100 - 100 * windows_memory_physical_free_bytes{instance="localhost"} / windows_memory_physical_total_bytes
```
## Alerting examples
**prometheus.rules**
```yaml
# Alert on hosts that have exhausted all available physical memory
- alert: MemoryExhausted
expr: windows_os_physical_memory_free_bytes == 0
for: 10m
labels:
severity: high
annotations:
summary: "Host {{ $labels.instance }} is out of memory"
description: "{{ $labels.instance }} has exhausted all available physical memory"
# Alert on hosts with greater than 90% memory usage
- alert: MemoryLow
expr: 100 - 100 * windows_memory_physical_free_bytes{instance="localhost"} / windows_memory_physical_total_bytes > 90
for: 10m
labels:
severity: warning
annotations:
summary: "Memory usage for host {{ $labels.instance }} is greater than 90%"
```
_This collector does not yet have alerting examples, we would appreciate your help adding them!_

View File

@@ -1,186 +0,0 @@
# mscluster_cluster collector
The MSCluster_Cluster class is a dynamic WMI class that represents a cluster.
|||
-|-
Metric name prefix | `mscluster`
Classes | `MSCluster_Cluster`,`MSCluster_Network`,`MSCluster_Node`,`MSCluster_Resource`,`MSCluster_ResourceGroup`
Enabled by default? | No
## Flags
### `--collectors.mscluster.enabled`
Comma-separated list of collectors to use, for example:
`--collectors.mscluster.enabled=cluster,network,node,resource,resouregroup`.
Matching is case-sensitive.
## Metrics
### Cluster
| Name | Description | Type | Labels |
|-------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------|--------|
| `mscluster_cluster_AddEvictDelay` | Provides access to the cluster's AddEvictDelay property, which is the number a seconds that a new node is delayed after an eviction of another node. | gauge | `name` |
| `mscluster_cluster_AdminAccessPoint` | The type of the cluster administrative access point. | gauge | `name` |
| `mscluster_cluster_AutoAssignNodeSite` | Determines whether or not the cluster will attempt to automatically assign nodes to sites based on networks and Active Directory Site information. | gauge | `name` |
| `mscluster_cluster_AutoBalancerLevel` | Determines the level of aggressiveness of AutoBalancer. | gauge | `name` |
| `mscluster_cluster_AutoBalancerMode` | Determines whether or not the auto balancer is enabled. | gauge | `name` |
| `mscluster_cluster_BackupInProgress` | Indicates whether a backup is in progress. | gauge | `name` |
| `mscluster_cluster_BlockCacheSize` | CSV BlockCache Size in MB. | gauge | `name` |
| `mscluster_cluster_ClusSvcHangTimeout` | Controls how long the cluster network driver waits between Failover Cluster Service heartbeats before it determines that the Failover Cluster Service has stopped responding. | gauge | `name` |
| `mscluster_cluster_ClusSvcRegroupOpeningTimeout` | Controls how long a node will wait on other nodes in the opening stage before deciding that they failed. | gauge | `name` |
| `mscluster_cluster_ClusSvcRegroupPruningTimeout` | Controls how long the membership leader will wait to reach full connectivity between cluster nodes. | gauge | `name` |
| `mscluster_cluster_ClusSvcRegroupStageTimeout` | Controls how long a node will wait on other nodes in a membership stage before deciding that they failed. | gauge | `name` |
| `mscluster_cluster_ClusSvcRegroupTickInMilliseconds` | Controls how frequently the membership algorithm is sending periodic membership messages. | gauge | `name` |
| `mscluster_cluster_ClusterEnforcedAntiAffinity` | Enables or disables hard enforcement of group anti-affinity classes. | gauge | `name` |
| `mscluster_cluster_ClusterFunctionalLevel` | The functional level the cluster is currently running in. | gauge | `name` |
| `mscluster_cluster_ClusterGroupWaitDelay` | Maximum time in seconds that a group waits for its preferred node to come online during cluster startup before coming online on a different node. | gauge | `name` |
| `mscluster_cluster_ClusterLogLevel` | Controls the level of cluster logging. | gauge | `name` |
| `mscluster_cluster_ClusterLogSize` | Controls the maximum size of the cluster log files on each of the nodes. | gauge | `name` |
| `mscluster_cluster_ClusterUpgradeVersion` | Specifies the upgrade version the cluster is currently running in. | gauge | `name` |
| `mscluster_cluster_CrossSiteDelay` | Controls how long the cluster network driver waits in milliseconds between sending Cluster Service heartbeats across sites. | gauge | `name` |
| `mscluster_cluster_CrossSiteThreshold` | Controls how many Cluster Service heartbeats can be missed across sites before it determines that Cluster Service has stopped responding. | gauge | `name` |
| `mscluster_cluster_CrossSubnetDelay` | Controls how long the cluster network driver waits in milliseconds between sending Cluster Service heartbeats across subnets. | gauge | `name` |
| `mscluster_cluster_CrossSubnetThreshold` | Controls how many Cluster Service heartbeats can be missed across subnets before it determines that Cluster Service has stopped responding. | gauge | `name` |
| `mscluster_cluster_CsvBalancer` | Whether automatic balancing for CSV is enabled. | gauge | `name` |
| `mscluster_cluster_DatabaseReadWriteMode` | Sets the database read and write mode. | gauge | `name` |
| `mscluster_cluster_DefaultNetworkRole` | Provides access to the cluster's DefaultNetworkRole property. | gauge | `name` |
| `mscluster_cluster_DetectedCloudPlatform` | | gauge | `name` |
| `mscluster_cluster_DetectManagedEvents` | | gauge | `name` |
| `mscluster_cluster_DetectManagedEventsThreshold` | | gauge | `name` |
| `mscluster_cluster_DisableGroupPreferredOwnerRandomization` | | gauge | `name` |
| `mscluster_cluster_DrainOnShutdown` | Whether to drain the node when cluster service is being stopped. | gauge | `name` |
| `mscluster_cluster_DynamicQuorumEnabled` | Allows cluster service to adjust node weights as needed to increase availability. | gauge | `name` |
| `mscluster_cluster_EnableSharedVolumes` | Enables or disables cluster shared volumes on this cluster. | gauge | `name` |
| `mscluster_cluster_FixQuorum` | Provides access to the cluster's FixQuorum property, which specifies if the cluster is in a fix quorum state. | gauge | `name` |
| `mscluster_cluster_GracePeriodEnabled` | Whether the node grace period feature of this cluster is enabled. | gauge | `name` |
| `mscluster_cluster_GracePeriodTimeout` | The grace period timeout in milliseconds. | gauge | `name` |
| `mscluster_cluster_GroupDependencyTimeout` | The timeout after which a group will be brought online despite unsatisfied dependencies | gauge | `name` |
| `mscluster_cluster_HangRecoveryAction` | Controls the action to take if the user-mode processes have stopped responding. | gauge | `name` |
| `mscluster_cluster_IgnorePersistentStateOnStartup` | Provides access to the cluster's IgnorePersistentStateOnStartup property, which specifies whether the cluster will bring online groups that were online when the cluster was shut down. | gauge | `name` |
| `mscluster_cluster_LogResourceControls` | Controls the logging of resource controls. | gauge | `name` |
| `mscluster_cluster_LowerQuorumPriorityNodeId` | Specifies the Node ID that has a lower priority when voting for quorum is performed. If the quorum vote is split 50/50%, the specified node's vote would be ignored to break the tie. If this is not set then the cluster will pick a node at random to break the tie. | gauge | `name` |
| `mscluster_cluster_MaxNumberOfNodes` | Indicates the maximum number of nodes that may participate in the Cluster. | gauge | `name` |
| `mscluster_cluster_MessageBufferLength` | The maximum unacknowledged message count for GEM. | gauge | `name` |
| `mscluster_cluster_MinimumNeverPreemptPriority` | Groups with this priority or higher cannot be preempted. | gauge | `name` |
| `mscluster_cluster_MinimumPreemptorPriority` | Minimum priority a cluster group must have to be able to preempt another group. | gauge | `name` |
| `mscluster_cluster_NetftIPSecEnabled` | Whether IPSec is enabled for cluster internal traffic. | gauge | `name` |
| `mscluster_cluster_PlacementOptions` | Various option flags to modify default placement behavior. | gauge | `name` |
| `mscluster_cluster_PlumbAllCrossSubnetRoutes` | Plumbs all possible cross subnet routes to all nodes. | gauge | `name` |
| `mscluster_cluster_PreventQuorum` | Whether the cluster will ignore group persistent state on startup. | gauge | `name` |
| `mscluster_cluster_QuarantineDuration` | The quarantine period timeout in milliseconds. | gauge | `name` |
| `mscluster_cluster_QuarantineThreshold` | Number of node failures before it will be quarantined. | gauge | `name` |
| `mscluster_cluster_QuorumArbitrationTimeMax` | Controls the maximum time necessary to decide the Quorum owner node. | gauge | `name` |
| `mscluster_cluster_QuorumArbitrationTimeMin` | Controls the minimum time necessary to decide the Quorum owner node. | gauge | `name` |
| `mscluster_cluster_QuorumLogFileSize` | This property is obsolete. | gauge | `name` |
| `mscluster_cluster_QuorumTypeValue` | Get the current quorum type value. -1: Unknown; 1: Node; 2: FileShareWitness; 3: Storage; 4: None | gauge | `name` |
| `mscluster_cluster_RequestReplyTimeout` | Controls the request reply time-out period. | gauge | `name` |
| `mscluster_cluster_ResiliencyDefaultPeriod` | The default resiliency period, in seconds, for the cluster. | gauge | `name` |
| `mscluster_cluster_ResiliencyLevel` | The resiliency level for the cluster. | gauge | `name` |
| `mscluster_cluster_ResourceDllDeadlockPeriod` | This property is obsolete. | gauge | `name` |
| `mscluster_cluster_RootMemoryReserved` | Controls the amount of memory reserved for the parent partition on all cluster nodes. | gauge | `name` |
| `mscluster_cluster_RouteHistoryLength` | The history length for routes to help finding network issues. | gauge | `name` |
| `mscluster_cluster_S2DBusTypes` | Bus types for storage spaces direct. | gauge | `name` |
| `mscluster_cluster_S2DCacheDesiredState` | Desired state of the storage spaces direct cache. | gauge | `name` |
| `mscluster_cluster_S2DCacheFlashReservePercent` | Percentage of allocated flash space to utilize when caching. | gauge | `name` |
| `mscluster_cluster_S2DCachePageSizeKBytes` | Page size in KB used by S2D cache. | gauge | `name` |
| `mscluster_cluster_S2DEnabled` | Whether direct attached storage (DAS) is enabled. | gauge | `name` |
| `mscluster_cluster_S2DIOLatencyThreshold` | The I/O latency threshold for storage spaces direct. | gauge | `name` |
| `mscluster_cluster_S2DOptimizations` | Optimization flags for storage spaces direct. | gauge | `name` |
| `mscluster_cluster_SameSubnetDelay` | Controls how long the cluster network driver waits in milliseconds between sending Cluster Service heartbeats on the same subnet. | gauge | `name` |
| `mscluster_cluster_SameSubnetThreshold` | Controls how many Cluster Service heartbeats can be missed on the same subnet before it determines that Cluster Service has stopped responding. | gauge | `name` |
| `mscluster_cluster_SecurityLevel` | Controls the level of security that should apply to intracluster messages. 0: Clear Text; 1: Sign; 2: Encrypt | gauge | `name` |
| `mscluster_cluster_SecurityLevelForStorage` | | gauge | `name` |
| `mscluster_cluster_SharedVolumeVssWriterOperationTimeout` | CSV VSS Writer operation timeout in seconds. | gauge | `name` |
| `mscluster_cluster_ShutdownTimeoutInMinutes` | The maximum time in minutes allowed for cluster resources to come offline during cluster service shutdown. | gauge | `name` |
| `mscluster_cluster_UseClientAccessNetworksForSharedVolumes` | Whether the use of client access networks for cluster shared volumes feature of this cluster is enabled. 0: Disabled; 1: Enabled; 2: Auto | gauge | `name` |
| `mscluster_cluster_WitnessDatabaseWriteTimeout` | Controls the maximum time in seconds that a cluster database write to a witness can take before the write is abandoned. | gauge | `name` |
| `mscluster_cluster_WitnessDynamicWeight` | The weight of the configured witness. | gauge | `name` |
| `mscluster_cluster_WitnessRestartInterval` | Controls the witness restart interval. | gauge | `name` |
### Network
| Name | Description | Type | Labels |
|-------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------|--------|
| `mscluster_network_Characteristics` | Provides the characteristics of the network. The cluster defines characteristics only for resources. For a description of these characteristics, see [CLUSCTL_RESOURCE_GET_CHARACTERISTICS](https://msdn.microsoft.com/library/aa367466). | gauge | `name` |
| `mscluster_network_Flags` | Provides access to the flags set for the network. The cluster defines flags only for resources. For a description of these flags, see [CLUSCTL_RESOURCE_GET_FLAGS](https://docs.microsoft.com/en-us/previous-versions/windows/desktop/mscs/clusctl-resource-get-flags). | gauge | `name` |
| `mscluster_network_Metric` | The metric of a cluster network (networks with lower values are used first). If this value is set, then the AutoMetric property is set to false. | gauge | `name` |
| `mscluster_network_Role` | Provides access to the network's Role property. The Role property describes the role of the network in the cluster. 0: None; 1: Cluster; 2: Client; 3: Both | gauge | `name` |
| `mscluster_network_State` | Provides the current state of the network. 1-1: Unknown; 0: Unavailable; 1: Down; 2: Partitioned; 3: Up | gauge | `name` |
### Network
| Name | Description | Type | Labels |
|----------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------|--------|
| `mscluster_node_BuildNumber` | Provides access to the node's BuildNumber property. | gauge | `name` |
| `mscluster_node_Characteristics` | Provides access to the characteristics set for the node. For a list of possible characteristics, see [CLUSCTL_NODE_GET_CHARACTERISTICS](https://docs.microsoft.com/en-us/previous-versions/windows/desktop/mscs/clusctl-node-get-characteristics). | gauge | `name` |
| `mscluster_node_DetectedCloudPlatform` | The dynamic vote weight of the node adjusted by dynamic quorum feature. | gauge | `name` |
| `mscluster_node_DynamicWeight` | The dynamic vote weight of the node adjusted by dynamic quorum feature. | gauge | `name` |
| `mscluster_node_Flags` | Provides access to the flags set for the node. For a list of possible characteristics, see [CLUSCTL_NODE_GET_FLAGS](https://docs.microsoft.com/en-us/previous-versions/windows/desktop/mscs/clusctl-node-get-flags). | gauge | `name` |
| `mscluster_node_MajorVersion` | Provides access to the node's MajorVersion property, which specifies the major portion of the Windows version installed. | gauge | `name` |
| `mscluster_node_MinorVersion` | Provides access to the node's MinorVersion property, which specifies the minor portion of the Windows version installed. | gauge | `name` |
| `mscluster_node_NeedsPreventQuorum` | Whether the cluster service on that node should be started with prevent quorum flag. | gauge | `name` |
| `mscluster_node_NodeDrainStatus` | The current node drain status of a node. 0: Not Initiated; 1: In Progress; 2: Completed; 3: Failed | gauge | `name` |
| `mscluster_node_NodeHighestVersion` | Provides access to the node's NodeHighestVersion property, which specifies the highest possible version of the cluster service with which the node can join or communicate. | gauge | `name` |
| `mscluster_node_NodeLowestVersion` | Provides access to the node's NodeLowestVersion property, which specifies the lowest possible version of the cluster service with which the node can join or communicate. | gauge | `name` |
| `mscluster_node_NodeWeight` | The vote weight of the node. | gauge | `name` |
| `mscluster_node_State` | Returns the current state of a node. -1: Unknown; 0: Up; 1: Down; 2: Paused; 3: Joining | gauge | `name` |
| `mscluster_node_StatusInformation` | The isolation or quarantine status of the node. | gauge | `name` |
### Resource
| Name | Description | Type | Labels |
|---------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------|--------------------------------------------|
| `mscluster_resource_Characteristics` | Provides the characteristics of the object. The cluster defines characteristics only for resources. For a description of these characteristics, see [CLUSCTL_RESOURCE_GET_CHARACTERISTICS](https://docs.microsoft.com/en-us/previous-versions/windows/desktop/mscs/clusctl-resource-get-characteristics). | gauge | `type`, `owner_group`, `name` |
| `mscluster_resource_DeadlockTimeout` | Indicates the length of time to wait, in milliseconds, before declaring a deadlock in any call into a resource. | gauge | `type`, `owner_group`, `name` |
| `mscluster_resource_EmbeddedFailureAction` | The time, in milliseconds, that a resource should remain in a failed state before the Cluster service attempts to restart it. | gauge | `type`, `owner_group`, `name` |
| `mscluster_resource_Flags` | Provides access to the flags set for the object. The cluster defines flags only for resources. For a description of these flags, see [CLUSCTL_RESOURCE_GET_FLAGS](https://docs.microsoft.com/en-us/previous-versions/windows/desktop/mscs/clusctl-resource-get-flags). | gauge | `type`, `owner_group`, `name` |
| `mscluster_resource_IsAlivePollInterval` | Provides access to the resource's IsAlivePollInterval property, which is the recommended interval in milliseconds at which the Cluster Service should poll the resource to determine whether it is operational. If the property is set to 0xFFFFFFFF, the Cluster Service uses the IsAlivePollInterval property for the resource type associated with the resource. | gauge | `type`, `owner_group`, `name` |
| `mscluster_resource_LooksAlivePollInterval` | Provides access to the resource's LooksAlivePollInterval property, which is the recommended interval in milliseconds at which the Cluster Service should poll the resource to determine whether it appears operational. If the property is set to 0xFFFFFFFF, the Cluster Service uses the LooksAlivePollInterval property for the resource type associated with the resource. | gauge | `type`, `owner_group`, `name` |
| `mscluster_resource_MonitorProcessId` | Provides the process ID of the resource host service that is currently hosting the resource. | gauge | `type`, `owner_group`, `name` |
| `mscluster_resource_OwnerNode` | The node hosting the resource. | gauge | `type`, `owner_group`, `node_name`, `name` |
| `mscluster_resource_PendingTimeout` | Provides access to the resource's PendingTimeout property. If a resource cannot be brought online or taken offline in the number of milliseconds specified by the PendingTimeout property, the resource is forcibly terminated. | gauge | `type`, `owner_group`, `name` |
| `mscluster_resource_ResourceClass` | Gets or sets the resource class of a resource. 0: Unknown; 1: Storage; 2: Network; 32768: Unknown | gauge | `type`, `owner_group`, `name` |
| `mscluster_resource_RestartAction` | Provides access to the resource's RestartAction property, which is the action to be taken by the Cluster Service if the resource fails. | gauge | `type`, `owner_group`, `name` |
| `mscluster_resource_RestartDelay` | Indicates the time delay before a failed resource is restarted. | gauge | `type`, `owner_group`, `name` |
| `mscluster_resource_RestartPeriod` | Provides access to the resource's RestartPeriod property, which is interval of time, in milliseconds, during which a specified number of restart attempts can be made on a nonresponsive resource. | gauge | `type`, `owner_group`, `name` |
| `mscluster_resource_RestartThreshold` | Provides access to the resource's RestartThreshold property which is the maximum number of restart attempts that can be made on a resource within an interval defined by the RestartPeriod property before the Cluster Service initiates the action specified by the RestartAction property. | gauge | `type`, `owner_group`, `name` |
| `mscluster_resource_RetryPeriodOnFailure` | Provides access to the resource's RetryPeriodOnFailure property, which is the interval of time (in milliseconds) that a resource should remain in a failed state before the Cluster service attempts to restart it. | gauge | `type`, `owner_group`, `name` |
| `mscluster_resource_State` | The current state of the resource. -1: Unknown; 0: Inherited; 1: Initializing; 2: Online; 3: Offline; 4: Failed; 128: Pending; 129: Online Pending; 130: Offline Pending | gauge | `type`, `owner_group`, `name` |
| `mscluster_resource_Subclass` | Provides the list of references to nodes that can be the owner of this resource. | gauge | `type`, `owner_group`, `name` |
## ResourceGroup
| Name | Description | Type | Labels |
|-----------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------|---------------------|
| `mscluster_resourcegroup_AutoFailbackType` | Provides access to the group's AutoFailbackType property. | gauge | `name` |
| `mscluster_resourcegroup_Characteristics` | Provides the characteristics of the group. The cluster defines characteristics only for resources. For a description of these characteristics, see [CLUSCTL_RESOURCE_GET_CHARACTERISTICS](https://docs.microsoft.com/en-us/previous-versions/windows/desktop/mscs/clusctl-resource-get-characteristics). | gauge | `name` |
| `mscluster_resourcegroup_ColdStartSetting` | Indicates whether a group can start after a cluster cold start. | gauge | `name` |
| `mscluster_resourcegroup_DefaultOwner` | Number of the last node the resource group was activated on or explicitly moved to. | gauge | `name` |
| `mscluster_resourcegroup_FailbackWindowEnd` | The FailbackWindowEnd property provides the latest time that the group can be moved back to the node identified as its preferred node. | gauge | `name` |
| `mscluster_resourcegroup_FailbackWindowStart` | The FailbackWindowStart property provides the earliest time (that is, local time as kept by the cluster) that the group can be moved back to the node identified as its preferred node. | gauge | `name` |
| `mscluster_resourcegroup_FailoverPeriod` | The FailoverPeriod property specifies a number of hours during which a maximum number of failover attempts, specified by the FailoverThreshold property, can occur. | gauge | `name` |
| `mscluster_resourcegroup_FailoverThreshold` | The FailoverThreshold property specifies the maximum number of failover attempts. | gauge | `name` |
| `mscluster_resourcegroup_Flags` | Provides access to the flags set for the group. The cluster defines flags only for resources. For a description of these flags, see [CLUSCTL_RESOURCE_GET_FLAGS](https://docs.microsoft.com/en-us/previous-versions/windows/desktop/mscs/clusctl-resource-get-flags). | gauge | `name` |
| `mscluster_resourcegroup_GroupType` | The Type of the resource group. | gauge | `name` |
| `mscluster_resourcegroup_OwnerNode` | The node hosting the resource group. | gauge | `node_name`, `name` |
| `mscluster_resourcegroup_Priority` | Priority value of the resource group | gauge | `name` |
| `mscluster_resourcegroup_ResiliencyPeriod` | The resiliency period for this group, in seconds. | gauge | `name` |
| `mscluster_resourcegroup_State` | The current state of the resource group. -1: Unknown; 0: Online; 1: Offline; 2: Failed; 3: Partial Online; 4: Pending | gauge | `name` |
| `mscluster_resourcegroup_UpdateDomain` | | gauge | `name` |
### Example metric
Query the state of all cluster resource owned by node1
```
windows_mscluster_resource_owner_node{node_name="node1"}
```
## Useful queries
Counts the number of Network Name cluster resource
```
count(windows_mscluster_resource_state{type="Network Name"})
```
## Alerting examples
_This collector does not yet have alerting examples, we would appreciate your help adding them!_

View File

@@ -0,0 +1,104 @@
# mscluster_cluster collector
The MSCluster_Cluster class is a dynamic WMI class that represents a cluster.
|||
-|-
Metric name prefix | `mscluster_cluster`
Classes | `MSCluster_Cluster`
Enabled by default? | No
## Flags
None
## Metrics
Name | Description | Type | Labels
-----|-------------|------|-------
`AddEvictDelay` | Provides access to the cluster's AddEvictDelay property, which is the number a seconds that a new node is delayed after an eviction of another node. | gauge | `name`
`AdminAccessPoint` | The type of the cluster administrative access point. | gauge | `name`
`AutoAssignNodeSite` | Determines whether or not the cluster will attempt to automatically assign nodes to sites based on networks and Active Directory Site information. | gauge | `name`
`AutoBalancerLevel` | Determines the level of aggressiveness of AutoBalancer. | gauge | `name`
`AutoBalancerMode` | Determines whether or not the auto balancer is enabled. | gauge | `name`
`BackupInProgress` | Indicates whether a backup is in progress. | gauge | `name`
`BlockCacheSize` | CSV BlockCache Size in MB. | gauge | `name`
`ClusSvcHangTimeout` | Controls how long the cluster network driver waits between Failover Cluster Service heartbeats before it determines that the Failover Cluster Service has stopped responding. | gauge | `name`
`ClusSvcRegroupOpeningTimeout` | Controls how long a node will wait on other nodes in the opening stage before deciding that they failed. | gauge | `name`
`ClusSvcRegroupPruningTimeout` | Controls how long the membership leader will wait to reach full connectivity between cluster nodes. | gauge | `name`
`ClusSvcRegroupStageTimeout` | Controls how long a node will wait on other nodes in a membership stage before deciding that they failed. | gauge | `name`
`ClusSvcRegroupTickInMilliseconds` | Controls how frequently the membership algorithm is sending periodic membership messages. | gauge | `name`
`ClusterEnforcedAntiAffinity` | Enables or disables hard enforcement of group anti-affinity classes. | gauge | `name`
`ClusterFunctionalLevel` | The functional level the cluster is currently running in. | gauge | `name`
`ClusterGroupWaitDelay` | Maximum time in seconds that a group waits for its preferred node to come online during cluster startup before coming online on a different node. | gauge | `name`
`ClusterLogLevel` | Controls the level of cluster logging. | gauge | `name`
`ClusterLogSize` | Controls the maximum size of the cluster log files on each of the nodes. | gauge | `name`
`ClusterUpgradeVersion` | Specifies the upgrade version the cluster is currently running in. | gauge | `name`
`CrossSiteDelay` | Controls how long the cluster network driver waits in milliseconds between sending Cluster Service heartbeats across sites. | gauge | `name`
`CrossSiteThreshold` | Controls how many Cluster Service heartbeats can be missed across sites before it determines that Cluster Service has stopped responding. | gauge | `name`
`CrossSubnetDelay` | Controls how long the cluster network driver waits in milliseconds between sending Cluster Service heartbeats across subnets. | gauge | `name`
`CrossSubnetThreshold` | Controls how many Cluster Service heartbeats can be missed across subnets before it determines that Cluster Service has stopped responding. | gauge | `name`
`CsvBalancer` | Whether automatic balancing for CSV is enabled. | gauge | `name`
`DatabaseReadWriteMode` | Sets the database read and write mode. | gauge | `name`
`DefaultNetworkRole` | Provides access to the cluster's DefaultNetworkRole property. | gauge | `name`
`DetectedCloudPlatform` | | gauge | `name`
`DetectManagedEvents` | | gauge | `name`
`DetectManagedEventsThreshold` | | gauge | `name`
`DisableGroupPreferredOwnerRandomization` | | gauge | `name`
`DrainOnShutdown` | Whether to drain the node when cluster service is being stopped. | gauge | `name`
`DynamicQuorumEnabled` | Allows cluster service to adjust node weights as needed to increase availability. | gauge | `name`
`EnableSharedVolumes` | Enables or disables cluster shared volumes on this cluster. | gauge | `name`
`FixQuorum` | Provides access to the cluster's FixQuorum property, which specifies if the cluster is in a fix quorum state. | gauge | `name`
`GracePeriodEnabled` | Whether the node grace period feature of this cluster is enabled. | gauge | `name`
`GracePeriodTimeout` | The grace period timeout in milliseconds. | gauge | `name`
`GroupDependencyTimeout` | The timeout after which a group will be brought online despite unsatisfied dependencies | gauge | `name`
`HangRecoveryAction` | Controls the action to take if the user-mode processes have stopped responding. | gauge | `name`
`IgnorePersistentStateOnStartup` | Provides access to the cluster's IgnorePersistentStateOnStartup property, which specifies whether the cluster will bring online groups that were online when the cluster was shut down. | gauge | `name`
`LogResourceControls` | Controls the logging of resource controls. | gauge | `name`
`LowerQuorumPriorityNodeId` | Specifies the Node ID that has a lower priority when voting for quorum is performed. If the quorum vote is split 50/50%, the specified node's vote would be ignored to break the tie. If this is not set then the cluster will pick a node at random to break the tie. | gauge | `name`
`MaxNumberOfNodes` | Indicates the maximum number of nodes that may participate in the Cluster. | gauge | `name`
`MessageBufferLength` | The maximum unacknowledged message count for GEM. | gauge | `name`
`MinimumNeverPreemptPriority` | Groups with this priority or higher cannot be preempted. | gauge | `name`
`MinimumPreemptorPriority` | Minimum priority a cluster group must have to be able to preempt another group. | gauge | `name`
`NetftIPSecEnabled` | Whether IPSec is enabled for cluster internal traffic. | gauge | `name`
`PlacementOptions` | Various option flags to modify default placement behavior. | gauge | `name`
`PlumbAllCrossSubnetRoutes` | Plumbs all possible cross subnet routes to all nodes. | gauge | `name`
`PreventQuorum` | Whether the cluster will ignore group persistent state on startup. | gauge | `name`
`QuarantineDuration` | The quarantine period timeout in milliseconds. | gauge | `name`
`QuarantineThreshold` | Number of node failures before it will be quarantined. | gauge | `name`
`QuorumArbitrationTimeMax` | Controls the maximum time necessary to decide the Quorum owner node. | gauge | `name`
`QuorumArbitrationTimeMin` | Controls the minimum time necessary to decide the Quorum owner node. | gauge | `name`
`QuorumLogFileSize` | This property is obsolete. | gauge | `name`
`QuorumTypeValue` | Get the current quorum type value. -1: Unknown; 1: Node; 2: FileShareWitness; 3: Storage; 4: None | gauge | `name`
`RequestReplyTimeout` | Controls the request reply time-out period. | gauge | `name`
`ResiliencyDefaultPeriod` | The default resiliency period, in seconds, for the cluster. | gauge | `name`
`ResiliencyLevel` | The resiliency level for the cluster. | gauge | `name`
`ResourceDllDeadlockPeriod` | This property is obsolete. | gauge | `name`
`RootMemoryReserved` | Controls the amount of memory reserved for the parent partition on all cluster nodes. | gauge | `name`
`RouteHistoryLength` | The history length for routes to help finding network issues. | gauge | `name`
`S2DBusTypes` | Bus types for storage spaces direct. | gauge | `name`
`S2DCacheDesiredState` | Desired state of the storage spaces direct cache. | gauge | `name`
`S2DCacheFlashReservePercent` | Percentage of allocated flash space to utilize when caching. | gauge | `name`
`S2DCachePageSizeKBytes` | Page size in KB used by S2D cache. | gauge | `name`
`S2DEnabled` | Whether direct attached storage (DAS) is enabled. | gauge | `name`
`S2DIOLatencyThreshold` | The I/O latency threshold for storage spaces direct. | gauge | `name`
`S2DOptimizations` | Optimization flags for storage spaces direct. | gauge | `name`
`SameSubnetDelay` | Controls how long the cluster network driver waits in milliseconds between sending Cluster Service heartbeats on the same subnet. | gauge | `name`
`SameSubnetThreshold` | Controls how many Cluster Service heartbeats can be missed on the same subnet before it determines that Cluster Service has stopped responding. | gauge | `name`
`SecurityLevel` | Controls the level of security that should apply to intracluster messages. 0: Clear Text; 1: Sign; 2: Encrypt | gauge | `name`
`SecurityLevelForStorage` | | gauge | `name`
`SharedVolumeVssWriterOperationTimeout` | CSV VSS Writer operation timeout in seconds. | gauge | `name`
`ShutdownTimeoutInMinutes` | The maximum time in minutes allowed for cluster resources to come offline during cluster service shutdown. | gauge | `name`
`UseClientAccessNetworksForSharedVolumes` | Whether the use of client access networks for cluster shared volumes feature of this cluster is enabled. 0: Disabled; 1: Enabled; 2: Auto | gauge | `name`
`WitnessDatabaseWriteTimeout` | Controls the maximum time in seconds that a cluster database write to a witness can take before the write is abandoned. | gauge | `name`
`WitnessDynamicWeight` | The weight of the configured witness. | gauge | `name`
`WitnessRestartInterval` | Controls the witness restart interval. | gauge | `name`
### Example metric
_This collector does not yet have explained examples, we would appreciate your help adding them!_
## Useful queries
_This collector does not yet have any useful queries added, we would appreciate your help adding them!_
## Alerting examples
_This collector does not yet have alerting examples, we would appreciate your help adding them!_

View File

@@ -0,0 +1,32 @@
# mscluster_network collector
The MSCluster_Network class is a dynamic WMI class that represents cluster networks.
|||
-|-
Metric name prefix | `mscluster_network`
Classes | `MSCluster_Network`
Enabled by default? | No
## Flags
None
## Metrics
Name | Description | Type | Labels
-----|-------------|------|-------
`Characteristics` | Provides the characteristics of the network. The cluster defines characteristics only for resources. For a description of these characteristics, see [CLUSCTL_RESOURCE_GET_CHARACTERISTICS](https://msdn.microsoft.com/library/aa367466). | gauge | `name`
`Flags` | Provides access to the flags set for the network. The cluster defines flags only for resources. For a description of these flags, see [CLUSCTL_RESOURCE_GET_FLAGS](https://docs.microsoft.com/en-us/previous-versions/windows/desktop/mscs/clusctl-resource-get-flags). | gauge | `name`
`Metric` | The metric of a cluster network (networks with lower values are used first). If this value is set, then the AutoMetric property is set to false. | gauge | `name`
`Role` | Provides access to the network's Role property. The Role property describes the role of the network in the cluster. 0: None; 1: Cluster; 2: Client; 3: Both | gauge | `name`
`State` | Provides the current state of the network. 1-1: Unknown; 0: Unavailable; 1: Down; 2: Partitioned; 3: Up | gauge | `name`
### Example metric
_This collector does not yet have explained examples, we would appreciate your help adding them!_
## Useful queries
_This collector does not yet have any useful queries added, we would appreciate your help adding them!_
## Alerting examples
_This collector does not yet have alerting examples, we would appreciate your help adding them!_

View File

@@ -0,0 +1,41 @@
# mscluster_node collector
The MSCluster_Node class is a dynamic WMI class that represents a cluster node.
|||
-|-
Metric name prefix | `mscluster_node`
Classes | `MSCluster_Node`
Enabled by default? | No
## Flags
None
## Metrics
Name | Description | Type | Labels
-----|-------------|------|-------
`BuildNumber` | Provides access to the node's BuildNumber property. | gauge | `name`
`Characteristics` | Provides access to the characteristics set for the node. For a list of possible characteristics, see [CLUSCTL_NODE_GET_CHARACTERISTICS](https://docs.microsoft.com/en-us/previous-versions/windows/desktop/mscs/clusctl-node-get-characteristics). | gauge | `name`
`DetectedCloudPlatform` | The dynamic vote weight of the node adjusted by dynamic quorum feature. | gauge | `name`
`DynamicWeight` | The dynamic vote weight of the node adjusted by dynamic quorum feature. | gauge | `name`
`Flags` | Provides access to the flags set for the node. For a list of possible characteristics, see [CLUSCTL_NODE_GET_FLAGS](https://docs.microsoft.com/en-us/previous-versions/windows/desktop/mscs/clusctl-node-get-flags). | gauge | `name`
`MajorVersion` | Provides access to the node's MajorVersion property, which specifies the major portion of the Windows version installed. | gauge | `name`
`MinorVersion` | Provides access to the node's MinorVersion property, which specifies the minor portion of the Windows version installed. | gauge | `name`
`NeedsPreventQuorum` | Whether the cluster service on that node should be started with prevent quorum flag. | gauge | `name`
`NodeDrainStatus` | The current node drain status of a node. 0: Not Initiated; 1: In Progress; 2: Completed; 3: Failed | gauge | `name`
`NodeHighestVersion` | Provides access to the node's NodeHighestVersion property, which specifies the highest possible version of the cluster service with which the node can join or communicate. | gauge | `name`
`NodeLowestVersion` | Provides access to the node's NodeLowestVersion property, which specifies the lowest possible version of the cluster service with which the node can join or communicate. | gauge | `name`
`NodeWeight` | The vote weight of the node. | gauge | `name`
`State` | Returns the current state of a node. -1: Unknown; 0: Up; 1: Down; 2: Paused; 3: Joining | gauge | `name`
`StatusInformation` | The isolation or quarantine status of the node. | gauge | `name`
### Example metric
_This collector does not yet have explained examples, we would appreciate your help adding them!_
## Useful queries
_This collector does not yet have any useful queries added, we would appreciate your help adding them!_
## Alerting examples
_This collector does not yet have alerting examples, we would appreciate your help adding them!_

View File

@@ -0,0 +1,50 @@
# mscluster_resource collector
The MSCluster_resource class is a dynamic WMI class that represents a cluster resource.
|||
-|-
Metric name prefix | `mscluster_resource`
Classes | `MSCluster_Resource`
Enabled by default? | No
## Flags
None
## Metrics
Name | Description | Type | Labels
-----|-------------|------|-------
`Characteristics` | Provides the characteristics of the object. The cluster defines characteristics only for resources. For a description of these characteristics, see [CLUSCTL_RESOURCE_GET_CHARACTERISTICS](https://docs.microsoft.com/en-us/previous-versions/windows/desktop/mscs/clusctl-resource-get-characteristics). | gauge | `type`, `owner_group`, `name`
`DeadlockTimeout` | Indicates the length of time to wait, in milliseconds, before declaring a deadlock in any call into a resource. | gauge | `type`, `owner_group`, `name`
`EmbeddedFailureAction` | The time, in milliseconds, that a resource should remain in a failed state before the Cluster service attempts to restart it. | gauge | `type`, `owner_group`, `name`
`Flags` | Provides access to the flags set for the object. The cluster defines flags only for resources. For a description of these flags, see [CLUSCTL_RESOURCE_GET_FLAGS](https://docs.microsoft.com/en-us/previous-versions/windows/desktop/mscs/clusctl-resource-get-flags). | gauge | `type`, `owner_group`, `name`
`IsAlivePollInterval` | Provides access to the resource's IsAlivePollInterval property, which is the recommended interval in milliseconds at which the Cluster Service should poll the resource to determine whether it is operational. If the property is set to 0xFFFFFFFF, the Cluster Service uses the IsAlivePollInterval property for the resource type associated with the resource. | gauge | `type`, `owner_group`, `name`
`LooksAlivePollInterval` | Provides access to the resource's LooksAlivePollInterval property, which is the recommended interval in milliseconds at which the Cluster Service should poll the resource to determine whether it appears operational. If the property is set to 0xFFFFFFFF, the Cluster Service uses the LooksAlivePollInterval property for the resource type associated with the resource. | gauge | `type`, `owner_group`, `name`
`MonitorProcessId` | Provides the process ID of the resource host service that is currently hosting the resource. | gauge | `type`, `owner_group`, `name`
`OwnerNode` | The node hosting the resource. | gauge | `type`, `owner_group`, `node_name`, `name`
`PendingTimeout` | Provides access to the resource's PendingTimeout property. If a resource cannot be brought online or taken offline in the number of milliseconds specified by the PendingTimeout property, the resource is forcibly terminated. | gauge | `type`, `owner_group`, `name`
`ResourceClass` | Gets or sets the resource class of a resource. 0: Unknown; 1: Storage; 2: Network; 32768: Unknown | gauge | `type`, `owner_group`, `name`
`RestartAction` | Provides access to the resource's RestartAction property, which is the action to be taken by the Cluster Service if the resource fails. | gauge | `type`, `owner_group`, `name`
`RestartDelay` | Indicates the time delay before a failed resource is restarted. | gauge | `type`, `owner_group`, `name`
`RestartPeriod` | Provides access to the resource's RestartPeriod property, which is interval of time, in milliseconds, during which a specified number of restart attempts can be made on a nonresponsive resource. | gauge | `type`, `owner_group`, `name`
`RestartThreshold` | Provides access to the resource's RestartThreshold property which is the maximum number of restart attempts that can be made on a resource within an interval defined by the RestartPeriod property before the Cluster Service initiates the action specified by the RestartAction property. | gauge | `type`, `owner_group`, `name`
`RetryPeriodOnFailure` | Provides access to the resource's RetryPeriodOnFailure property, which is the interval of time (in milliseconds) that a resource should remain in a failed state before the Cluster service attempts to restart it. | gauge | `type`, `owner_group`, `name`
`State` | The current state of the resource. -1: Unknown; 0: Inherited; 1: Initializing; 2: Online; 3: Offline; 4: Failed; 128: Pending; 129: Online Pending; 130: Offline Pending | gauge | `type`, `owner_group`, `name`
`Subclass` | Provides the list of references to nodes that can be the owner of this resource. | gauge | `type`, `owner_group`, `name`
### Example metric
Query the state of all cluster resource owned by node1
```
windows_mscluster_resource_owner_node{node_name="node1"}
```
## Useful queries
Counts the number of Network Name cluster resource
```
count(windows_mscluster_resource_state{type="Network Name"})
```
## Alerting examples
_This collector does not yet have alerting examples, we would appreciate your help adding them!_

View File

@@ -0,0 +1,48 @@
# mscluster_resourcegroup collector
The MSCluster_ResourceGroup class is a dynamic WMI class that represents a cluster group.
|||
-|-
Metric name prefix | `mscluster_resourcegroup`
Classes | `MSCluster_ResourceGroup`
Enabled by default? | No
## Flags
None
## Metrics
Name | Description | Type | Labels
-----|-------------|------|-------
`AutoFailbackType` | Provides access to the group's AutoFailbackType property. | gauge | `name`
`Characteristics` | Provides the characteristics of the group. The cluster defines characteristics only for resources. For a description of these characteristics, see [CLUSCTL_RESOURCE_GET_CHARACTERISTICS](https://docs.microsoft.com/en-us/previous-versions/windows/desktop/mscs/clusctl-resource-get-characteristics). | gauge | `name`
`ColdStartSetting` | Indicates whether a group can start after a cluster cold start. | gauge | `name`
`DefaultOwner` | Number of the last node the resource group was activated on or explicitly moved to. | gauge | `name`
`FailbackWindowEnd` | The FailbackWindowEnd property provides the latest time that the group can be moved back to the node identified as its preferred node. | gauge | `name`
`FailbackWindowStart` | The FailbackWindowStart property provides the earliest time (that is, local time as kept by the cluster) that the group can be moved back to the node identified as its preferred node. | gauge | `name`
`FailoverPeriod` | The FailoverPeriod property specifies a number of hours during which a maximum number of failover attempts, specified by the FailoverThreshold property, can occur. | gauge | `name`
`FailoverThreshold` | The FailoverThreshold property specifies the maximum number of failover attempts. | gauge | `name`
`Flags` | Provides access to the flags set for the group. The cluster defines flags only for resources. For a description of these flags, see [CLUSCTL_RESOURCE_GET_FLAGS](https://docs.microsoft.com/en-us/previous-versions/windows/desktop/mscs/clusctl-resource-get-flags). | gauge | `name`
`GroupType` | The Type of the resource group. | gauge | `name`
`OwnerNode` | The node hosting the resource group. | gauge | `node_name`, `name`
`Priority` | Priority value of the resource group | gauge | `name`
`ResiliencyPeriod` | The resiliency period for this group, in seconds. | gauge | `name`
`State` | The current state of the resource group. -1: Unknown; 0: Online; 1: Offline; 2: Failed; 3: Partial Online; 4: Pending | gauge | `name`
`UpdateDomain` | | gauge | `name`
### Example metric
Query the state of all cluster group owned by node1
```
windows_mscluster_resourcegroup_owner_node{node_name="node1"}
```
## Useful queries
Counts the number of cluster group by type
```
count_values("count", windows_mscluster_resourcegroup_group_type)
```
## Alerting examples
_This collector does not yet have alerting examples, we would appreciate your help adding them!_

View File

@@ -14,26 +14,58 @@ None
## Metrics
| Name | Description | Type | Labels |
|---------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------|------------------------------------------------------------------------|
| `windows_os_info` | Contains full product name & version in labels. Note that the `major_version` for Windows 11 is "10"; a build number greater than 22000 represents Windows 11. | gauge | `product`, `version`, `major_version`, `minor_version`, `build_number` |
| `windows_os_paging_limit_bytes` | Total number of bytes that can be stored in the operating system paging files. 0 (zero) indicates that there are no paging files | gauge | None |
| `windows_os_paging_free_bytes` | Number of bytes that can be mapped into the operating system paging files without causing any other pages to be swapped out | gauge | None |
Name | Description | Type | Labels
-----|-------------|------|-------
`windows_os_info` | Contains full product name & version in labels. Note that the `major_version` for Windows 11 is "10"; a build number greater than 22000 represents Windows 11. | gauge | `product`, `version`, `major_version`, `minor_version`, `build_number`
`windows_os_paging_limit_bytes` | Total number of bytes that can be stored in the operating system paging files. 0 (zero) indicates that there are no paging files | gauge | None
`windows_os_paging_free_bytes` | Number of bytes that can be mapped into the operating system paging files without causing any other pages to be swapped out | gauge | None
`windows_os_physical_memory_free_bytes` | Bytes of physical memory currently unused and available | gauge | None
`windows_os_time` | Current time as reported by the operating system, in [Unix time](https://en.wikipedia.org/wiki/Unix_time). See [time.Unix()](https://golang.org/pkg/time/#Unix) for details | gauge | None
`windows_os_timezone` | Current timezone as reported by the operating system. See [time.Zone()](https://golang.org/pkg/time/#Time.Zone) for details | gauge | `timezone`
`windows_os_processes` | Number of process contexts currently loaded or running on the operating system | gauge | None
`windows_os_processes_limit` | Maximum number of process contexts the operating system can support. The default value set by the provider is 4294967295 (0xFFFFFFFF) | gauge | None
`windows_os_process_memory_limit_bytes` | Maximum number of bytes of memory that can be allocated to a process | gauge | None
`windows_os_users` | Number of user sessions for which the operating system is storing state information currently. For a list of current active logon sessions, see [`logon`](collector.logon.md) | gauge | None
`windows_os_virtual_memory_bytes` | Bytes of virtual memory | gauge | None
`windows_os_visible_memory_bytes` | Total bytes of physical memory available to the operating system. This value does not necessarily indicate the true amount of physical memory, but what is reported to the operating system as available to it | gauge | None
`windows_os_virtual_memory_free_bytes` | Bytes of virtual memory currently unused and available | gauge | None
### Example metric
Show current number of processes
```
# HELP windows_os_hostname Labelled system hostname information as provided by ComputerSystem.DNSHostName and ComputerSystem.Domain
# TYPE windows_os_hostname gauge
windows_os_hostname{domain="",fqdn="PC",hostname="PC"} 1
# HELP windows_os_info Contains full product name & version in labels. Note that the "major_version" for Windows 11 is \\"10\\"; a build number greater than 22000 represents Windows 11.
# TYPE windows_os_info gauge
windows_os_info{build_number="19045",major_version="10",minor_version="0",product="Windows 10 Pro",revision="4842",version="10.0.19045"} 1
windows_os_processes{instance="localhost"}
```
## Useful queries
_This collector does not yet have useful queries, we would appreciate your help adding them!_
Find all devices not set to UTC timezone
```
windows_os_timezone{timezone != "UTC"}
```
Show memory usage for instance (%)
```
100 - 100 * windows_os_physical_memory_free_bytes{instance="localhost"} / windows_cs_physical_memory_bytes{instance="localhost"}
```
## Alerting examples
_This collector does not yet have alerting examples, we would appreciate your help adding them!_
**prometheus.rules**
```yaml
# Alert on hosts that have exhausted all available physical memory
- alert: MemoryExhausted
expr: windows_os_physical_memory_free_bytes == 0
for: 10m
labels:
severity: high
annotations:
summary: "Host {{ $labels.instance }} is out of memory"
description: "{{ $labels.instance }} has exhausted all available physical memory"
# Alert on hosts with greater than 90% memory usage
- alert: MemoryLow
expr: 100 - 100 * windows_os_physical_memory_free_bytes / windows_cs_physical_memory_bytes > 90
for: 10m
labels:
severity: warning
annotations:
summary: "Memory usage for host {{ $labels.instance }} is greater than 90%"
```

View File

@@ -1,114 +0,0 @@
# Perfdata collector
The perfdata collector exposes any configured metric.
| | |
|---------------------|-------------------------|
| Metric name prefix | `perfdata` |
| Data source | Performance Data Helper |
| Enabled by default? | No |
## Flags
### `--collector.perfdata.objects`
Objects is a list of objects to collect metrics from. The value takes the form of a JSON array of strings. YAML is also supported.
The collector supports only english named counter. Localized counter-names are not supported.
#### Schema
YAML:
```yaml
- object: "Processor Information"
instances: ["*"]
instance_label: "core"
counters:
"% Processor Time": {}
- object: "Memory"
counters:
"Cache Faults/sec":
type: "counter"
```
JSON:
```json
[
{"object":"Processor Information","instance_label": "core","instances":["*"],"counters": {"% Processor Time": {}}},
{"object":"Memory","counters": {"Cache Faults/sec": {"type": "counter"}}}
]
```
#### name
ObjectName is the Object to query for, like Processor, DirectoryServices, LogicalDisk or similar.
The collector supports only english named counter. Localized counter-names are not supported.
#### instances
The instances key (this is an array) declares the instances of a counter you would like returned, it can be one or more values.
Example: Instances = `["C:","D:","E:"]`
This will return only for the instances C:, D: and E: where relevant. To get all instances of a Counter, use `["*"]` only.
Some Objects like `Memory` do not have instances to select from at all. In this case, the `instances` key can be omitted.
#### counters
The Counters key (this is an object) declares the counters of the ObjectName you would like returned, it can also be one or more values.
Example: Counters = `{"% Idle Time": {}, "% Disk Read Time": {}, "% Disk Write Time": {}}`
This must be specified for every counter you want the results. Wildcards are not supported.
#### counters Sub-Schema
##### type
This key is optional. It indicates the type of the counter. The value can be `counter` or `gauge`.
If not specified, the windows_exporter will try to determine the type based on the counter type.
### Example
```
# HELP windows_perfdata_memory_cache_faults_sec
# TYPE windows_perfdata_memory_cache_faults_sec counter
windows_perfdata_memory_cache_faults_sec 2.369977e+07
# HELP windows_perfdata_processor_information__processor_time
# TYPE windows_perfdata_processor_information__processor_time gauge
windows_perfdata_processor_information__processor_time{instance="0,0"} 1.7259640625e+11
windows_perfdata_processor_information__processor_time{instance="0,1"} 1.7576796875e+11
windows_perfdata_processor_information__processor_time{instance="0,10"} 2.2704234375e+11
windows_perfdata_processor_information__processor_time{instance="0,11"} 2.3069296875e+11
windows_perfdata_processor_information__processor_time{instance="0,12"} 2.3302265625e+11
windows_perfdata_processor_information__processor_time{instance="0,13"} 2.32851875e+11
windows_perfdata_processor_information__processor_time{instance="0,14"} 2.3282421875e+11
windows_perfdata_processor_information__processor_time{instance="0,15"} 2.3271234375e+11
windows_perfdata_processor_information__processor_time{instance="0,16"} 2.329590625e+11
windows_perfdata_processor_information__processor_time{instance="0,17"} 2.32800625e+11
windows_perfdata_processor_information__processor_time{instance="0,18"} 2.3194359375e+11
windows_perfdata_processor_information__processor_time{instance="0,19"} 2.32380625e+11
windows_perfdata_processor_information__processor_time{instance="0,2"} 1.954765625e+11
windows_perfdata_processor_information__processor_time{instance="0,20"} 2.3259765625e+11
windows_perfdata_processor_information__processor_time{instance="0,21"} 2.3268515625e+11
windows_perfdata_processor_information__processor_time{instance="0,22"} 2.3301765625e+11
windows_perfdata_processor_information__processor_time{instance="0,23"} 2.3264328125e+11
windows_perfdata_processor_information__processor_time{instance="0,3"} 1.94745625e+11
windows_perfdata_processor_information__processor_time{instance="0,4"} 2.2011453125e+11
windows_perfdata_processor_information__processor_time{instance="0,5"} 2.27244375e+11
windows_perfdata_processor_information__processor_time{instance="0,6"} 2.25501875e+11
windows_perfdata_processor_information__processor_time{instance="0,7"} 2.2995265625e+11
windows_perfdata_processor_information__processor_time{instance="0,8"} 2.2929890625e+11
windows_perfdata_processor_information__processor_time{instance="0,9"} 2.313540625e+11
windows_perfdata_processor_information__processor_time{instance="0,_Total"} 2.23009459635e+11
```
## Metrics
The perfdata collector returns metrics based on the user configuration.
The metrics are named based on the object name and the counter name.
The instance name is added as a label to the metric.

View File

@@ -37,6 +37,11 @@ Enables IIS process name queries. IIS process names are combined with their app
Disabled by default, and can be enabled with `--collector.process.iis=true`.
### `--collector.process.report-owner`
Enables reporting of the process owner. This is a potentially expensive operation.
Disabled by default, and can be enabled with `--collector.process.report-owner`.
### Example
To match all firefox processes: `--collector.process.include="firefox.*"`.
@@ -69,24 +74,23 @@ w3wp_Test
## Metrics
| Name | Description | Type | Labels |
|---------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|---------------------------------------------------------------------------------------|
| `windows_process_info` | A metric with a constant '1' value labeled with process information | gauge | `process`, `process_id`, `creating_process_id`, `process_group_id`,`owner`, `cmdline` |
| `windows_process_start_time` | Time of process start | gauge | `process`, `process_id` |
| `windows_process_cpu_time_total` | Returns elapsed time that all of the threads of this process used the processor to execute instructions by mode (privileged, user). An instruction is the basic unit of execution in a computer, a thread is the object that executes instructions, and a process is the object created when a program is run. Code executed to handle some hardware interrupts and trap conditions is included in this count. | counter | `process`, `process_id`, `mode` |
| `windows_process_handles` | Total number of handles the process has open. This number is the sum of the handles currently open by each thread in the process. | gauge | `process`, `process_id` |
| `windows_process_io_bytes_total` | Bytes issued to I/O operations in different modes (read, write, other). This property counts all I/O activity generated by the process to include file, network, and device I/Os. Read and write mode includes data operations; other mode includes those that do not involve data, such as control operations. | counter | `process`, `process_id`, `mode` |
| `windows_process_io_operations_total` | I/O operations issued in different modes (read, write, other). This property counts all I/O activity generated by the process to include file, network, and device I/Os. Read and write mode includes data operations; other mode includes those that do not involve data, such as control operations. | counter | `process`, `process_id`, `mode` |
| `windows_process_page_faults_total` | Page faults by the threads executing in this process. A page fault occurs when a thread refers to a virtual memory page that is not in its working set in main memory. This can cause the page not to be fetched from disk if it is on the standby list and hence already in main memory, or if it is in use by another process with which the page is shared. | counter | `process`, `process_id` |
| `windows_process_page_file_bytes` | Current number of bytes this process has used in the paging file(s). Paging files are used to store pages of memory used by the process that are not contained in other files. Paging files are shared by all processes, and lack of space in paging files can prevent other processes from allocating memory. | gauge | `process`, `process_id` |
| `windows_process_pool_bytes` | Pool Bytes is the last observed number of bytes in the paged or nonpaged pool. The nonpaged pool is an area of system memory (physical memory used by the operating system) for objects that cannot be written to disk, but must remain in physical memory as long as they are allocated. The paged pool is an area of system memory (physical memory used by the operating system) for objects that can be written to disk when they are not being used. Nonpaged pool bytes is calculated differently than paged pool bytes, so it might not equal the total of paged pool bytes. | gauge | `process`, `process_id`, `pool` |
| `windows_process_priority_base` | Current base priority of this process. Threads within a process can raise and lower their own base priority relative to the process base priority of the process. | gauge | `process`, `process_id` |
| `windows_process_private_bytes` | Current number of bytes this process has allocated that cannot be shared with other processes. | gauge | `process`, `process_id` |
| `windows_process_threads` | Number of threads currently active in this process. An instruction is the basic unit of execution in a processor, and a thread is the object that executes instructions. Every running process has at least one thread. | gauge | `process`, `process_id` |
| `windows_process_virtual_bytes` | Current size, in bytes, of the virtual address space that the process is using. Use of virtual address space does not necessarily imply corresponding use of either disk or main memory pages. Virtual space is finite and, by using too much, the process can limit its ability to load libraries. | gauge | `process`, `process_id` |
| `windows_process_working_set_private_bytes` | Size of the working set, in bytes, that is use for this process only and not shared nor shareable by other processes. | gauge | `process`, `process_id` |
| `windows_process_working_set_peak_bytes` | Maximum size, in bytes, of the Working Set of this process at any point in time. The Working Set is the set of memory pages touched recently by the threads in the process. If free memory in the computer is above a threshold, pages are left in the Working Set of a process even if they are not in use. When free memory falls below a threshold, pages are trimmed from Working Sets. If they are needed they will then be soft-faulted back into the Working Set before they leave main memory. | gauge | `process`, `process_id` |
| `windows_process_working_set_bytes` | Maximum number of bytes in the working set of this process at any point in time. The working set is the set of memory pages touched recently by the threads in the process. If free memory in the computer is above a threshold, pages are left in the working set of a process even if they are not in use. When free memory falls below a threshold, pages are trimmed from working sets. If they are needed, they are then soft-faulted back into the working set before they leave main memory. | gauge | `process`, `process_id` |
| Name | Description | Type | Labels |
|---------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|-----------------------------------------------------------------|
| `windows_process_start_time` | Time of process start | gauge | `process`, `process_id`, `creating_process_id`, `owner` |
| `windows_process_cpu_time_total` | Returns elapsed time that all of the threads of this process used the processor to execute instructions by mode (privileged, user). An instruction is the basic unit of execution in a computer, a thread is the object that executes instructions, and a process is the object created when a program is run. Code executed to handle some hardware interrupts and trap conditions is included in this count. | counter | `process`, `process_id`, `creating_process_id`, `owner`, `mode` |
| `windows_process_handles` | Total number of handles the process has open. This number is the sum of the handles currently open by each thread in the process. | gauge | `process`, `process_id`, `creating_process_id`, `owner` |
| `windows_process_io_bytes_total` | Bytes issued to I/O operations in different modes (read, write, other). This property counts all I/O activity generated by the process to include file, network, and device I/Os. Read and write mode includes data operations; other mode includes those that do not involve data, such as control operations. | counter | `process`, `process_id`, `creating_process_id`, `owner`, `mode` |
| `windows_process_io_operations_total` | I/O operations issued in different modes (read, write, other). This property counts all I/O activity generated by the process to include file, network, and device I/Os. Read and write mode includes data operations; other mode includes those that do not involve data, such as control operations. | counter | `process`, `process_id`, `creating_process_id`, `owner`, `mode` |
| `windows_process_page_faults_total` | Page faults by the threads executing in this process. A page fault occurs when a thread refers to a virtual memory page that is not in its working set in main memory. This can cause the page not to be fetched from disk if it is on the standby list and hence already in main memory, or if it is in use by another process with which the page is shared. | counter | `process`, `process_id`, `creating_process_id`, `owner` |
| `windows_process_page_file_bytes` | Current number of bytes this process has used in the paging file(s). Paging files are used to store pages of memory used by the process that are not contained in other files. Paging files are shared by all processes, and lack of space in paging files can prevent other processes from allocating memory. | gauge | `process`, `process_id`, `creating_process_id`, `owner` |
| `windows_process_pool_bytes` | Pool Bytes is the last observed number of bytes in the paged or nonpaged pool. The nonpaged pool is an area of system memory (physical memory used by the operating system) for objects that cannot be written to disk, but must remain in physical memory as long as they are allocated. The paged pool is an area of system memory (physical memory used by the operating system) for objects that can be written to disk when they are not being used. Nonpaged pool bytes is calculated differently than paged pool bytes, so it might not equal the total of paged pool bytes. | gauge | `process`, `process_id`, `creating_process_id`, `owner`, `pool` |
| `windows_process_priority_base` | Current base priority of this process. Threads within a process can raise and lower their own base priority relative to the process base priority of the process. | gauge | `process`, `process_id`, `creating_process_id`, `owner` |
| `windows_process_private_bytes` | Current number of bytes this process has allocated that cannot be shared with other processes. | gauge | `process`, `process_id`, `creating_process_id`, `owner` |
| `windows_process_threads` | Number of threads currently active in this process. An instruction is the basic unit of execution in a processor, and a thread is the object that executes instructions. Every running process has at least one thread. | gauge | `process`, `process_id`, `creating_process_id`, `owner` |
| `windows_process_virtual_bytes` | Current size, in bytes, of the virtual address space that the process is using. Use of virtual address space does not necessarily imply corresponding use of either disk or main memory pages. Virtual space is finite and, by using too much, the process can limit its ability to load libraries. | gauge | `process`, `process_id`, `creating_process_id`, `owner` |
| `windows_process_working_set_private_bytes` | Size of the working set, in bytes, that is use for this process only and not shared nor shareable by other processes. | gauge | `process`, `process_id`, `creating_process_id`, `owner` |
| `windows_process_working_set_peak_bytes` | Maximum size, in bytes, of the Working Set of this process at any point in time. The Working Set is the set of memory pages touched recently by the threads in the process. If free memory in the computer is above a threshold, pages are left in the Working Set of a process even if they are not in use. When free memory falls below a threshold, pages are trimmed from Working Sets. If they are needed they will then be soft-faulted back into the Working Set before they leave main memory. | gauge | `process`, `process_id`, `creating_process_id`, `owner` |
| `windows_process_working_set_bytes` | Maximum number of bytes in the working set of this process at any point in time. The working set is the set of memory pages touched recently by the threads in the process. If free memory in the computer is above a threshold, pages are left in the working set of a process even if they are not in use. When free memory falls below a threshold, pages are trimmed from working sets. If they are needed, they are then soft-faulted back into the working set before they leave main memory. | gauge | `process`, `process_id`, `creating_process_id`, `owner` |
### Example metric
_This collector does not yet have explained examples, we would appreciate your help adding them!_

View File

@@ -2,24 +2,47 @@
The service collector exposes metrics about Windows Services
|||
-|-
Metric name prefix | `service`
Classes | none
Enabled by default? | Yes
The collector exists in 2 different version. Version 1 is using WMI to query all services and is able to provide additional
information. Version 2 is a more efficient solution by directly connecting to the service manager, but is not able to
provide additional information like `run_as` or start configuration
## Flags
None
### `--collector.service.services-where`
A WMI filter on which services to include. Recommended to keep down number of returned metrics.
Example: `--collector.service.services-where="Name='windows_exporter'"`
Example config win_exporter.yml for multiple services: `services-where: Name='SQLServer' OR Name='Couchbase' OR Name='Spooler' OR Name='ActiveMQ'`
### `--collector.service.use-api`
Uses API calls instead of WMI for performance optimization. **Note** the previous flag (`--collector.service.services-where`) won't have any effect on this mode.
### `--collector.service.v2`
Version 2 of the service collector. Is using API calls for performance optimization. **Note** the previous flag (`--collector.service.services-where`) won't have any effect on this mode.
For additional performance reasons, it doesn't provide any additional information like `run_as` or start configuration.
# collector V1
|||
-|-
Metric name prefix | `service`
Classes | [`Win32_Service`](https://msdn.microsoft.com/en-us/library/aa394418(v=vs.85).aspx)
Enabled by default? | Yes
## Metrics
| Name | Description | Type | Labels |
|------------------------------|-----------------------------------------------------------------------------------------------|-------|---------------------------------------|
| `windows_service_info` | Contains service information run as user in labels, constant 1 | gauge | name, display_name, path_name, run_as |
| `windows_service_start_mode` | The start mode of the service, 1 if the current start mode, 0 otherwise | gauge | name, start_mode |
| `windows_service_state` | The state of the service, 1 if the current state, 0 otherwise | gauge | name, state |
| `windows_service_process` | Process of started service. The value is the creation time of the process as a unix timestamp | gauge | name, process_id |
Name | Description | Type | Labels
-----|-------------|------|-------
`windows_service_info` | Contains service information in labels, constant 1 | gauge | name, display_name, process_id, run_as
`windows_service_state` | The state of the service, 1 if the current state, 0 otherwise | gauge | name, state
`windows_service_start_mode` | The start mode of the service, 1 if the current start mode, 0 otherwise | gauge | name, start_mode
`windows_service_status` | The status of the service, 1 if the current status, 0 otherwise | gauge | name, status
For the values of the `state`, `start_mode`, `status` and `run_as` labels, see below.
### States
@@ -42,50 +65,81 @@ A service can have the following start modes:
- `manual`
- `disabled`
### Status (not available in API mode)
A service can have any of the following statuses:
- `ok`
- `error`
- `degraded`
- `unknown`
- `pred fail`
- `starting`
- `stopping`
- `service`
- `stressed`
- `nonrecover`
- `no contact`
- `lost comm`
Note that there is some overlap with service state.
### Run As
Account name under which a service runs. Depending on the service type, the account name may be in the form of "DomainName\Username" or UPN format ("Username@DomainName").
It corresponds to the `StartName` attribute of the `Win32_Service` class.
`StartName` attribute can be NULL and in such case the label is reported as an empty string. Notice that if the attribute is NULL the service is logged on as the `LocalSystem` account or, for kernel or system-level drive, it runs with a default object name created by the I/O system based on the service name, for example, DWDOM\Admin.
### Example metric
Lists the services that have a 'disabled' start mode.
```
windows_service_start_mode{exported_name=~"(mssqlserver|sqlserveragent)",start_mode="disabled"}
```
## Useful queries
Counts the number of Microsoft SQL Server/Agent Processes
```
count(windows_service_state{exported_name=~"(sqlserveragent|mssqlserver)",state="running"})
```
# collector V2
|||
-|-
Metric name prefix | `service`
Classes | none
Enabled by default? | No
## Metrics
Name | Description | Type | Labels
-----|-------------|------|-------
`windows_service_state` | The state of the service, 1 if the current state, 0 otherwise | gauge | name, display_name, state
### States
A service can be in the following states:
- `stopped`
- `start pending`
- `stop pending`
- `running`
- `continue pending`
- `pause pending`
- `paused`
- `unknown`
### Example metric
```
# HELP windows_service_info A metric with a constant '1' value labeled with service information
# TYPE windows_service_info gauge
windows_service_info{display_name="Declared Configuration(DC) service",name="dcsvc",path_name="C:\\WINDOWS\\system32\\svchost.exe -k netsvcs -p",run_as="LocalSystem"} 1
windows_service_info{display_name="Designs",name="Themes",path_name="C:\\WINDOWS\\System32\\svchost.exe -k netsvcs -p",run_as="LocalSystem"} 1
# HELP windows_service_process Process of started service. The value is the creation time of the process as a unix timestamp.
# TYPE windows_service_process gauge
windows_service_process{name="Themes",process_id="2856"} 1.7244891e+09
# HELP windows_service_start_mode The start mode of the service (StartMode)
# TYPE windows_service_start_mode gauge
windows_service_start_mode{name="Themes",start_mode="auto"} 1
windows_service_start_mode{name="Themes",start_mode="boot"} 0
windows_service_start_mode{name="Themes",start_mode="disabled"} 0
windows_service_start_mode{name="Themes",start_mode="manual"} 0
windows_service_start_mode{name="Themes",start_mode="system"} 0
windows_service_start_mode{name="dcsvc",start_mode="auto"} 0
windows_service_start_mode{name="dcsvc",start_mode="boot"} 0
windows_service_start_mode{name="dcsvc",start_mode="disabled"} 0
windows_service_start_mode{name="dcsvc",start_mode="manual"} 1
windows_service_start_mode{name="dcsvc",start_mode="system"} 0
# HELP windows_service_state The state of the service (State)
# TYPE windows_service_state gauge
windows_service_state{name="Themes",status="continue pending"} 0
windows_service_state{name="Themes",status="pause pending"} 0
windows_service_state{name="Themes",status="paused"} 0
windows_service_state{name="Themes",status="running"} 1
windows_service_state{name="Themes",status="start pending"} 0
windows_service_state{name="Themes",status="stop pending"} 0
windows_service_state{name="Themes",status="stopped"} 0
windows_service_state{name="dcsvc",status="continue pending"} 0
windows_service_state{name="dcsvc",status="pause pending"} 0
windows_service_state{name="dcsvc",status="paused"} 0
windows_service_state{name="dcsvc",status="running"} 0
windows_service_state{name="dcsvc",status="start pending"} 0
windows_service_state{name="dcsvc",status="stop pending"} 0
windows_service_state{name="dcsvc",status="stopped"} 1
windows_service_state{display_name="Declared Configuration(DC) service",name="dcsvc",status="continue pending"} 0
windows_service_state{display_name="Declared Configuration(DC) service",name="dcsvc",status="pause pending"} 0
windows_service_state{display_name="Declared Configuration(DC) service",name="dcsvc",status="paused"} 0
windows_service_state{display_name="Declared Configuration(DC) service",name="dcsvc",status="running"} 0
windows_service_state{display_name="Declared Configuration(DC) service",name="dcsvc",status="start pending"} 0
windows_service_state{display_name="Declared Configuration(DC) service",name="dcsvc",status="stop pending"} 0
windows_service_state{display_name="Declared Configuration(DC) service",name="dcsvc",status="stopped"} 1
```
## Useful queries
@@ -109,8 +163,8 @@ groups:
labels:
severity: high
annotations:
summary: "Service {{ $labels.name }} down"
description: "Service {{ $labels.name }} on instance {{ $labels.instance }} has been down for more than 3 minutes."
summary: "Service {{ $labels.exported_name }} down"
description: "Service {{ $labels.exported_name }} on instance {{ $labels.instance }} has been down for more than 3 minutes."
# Sends an alert when the 'mssqlserver' service is not in the running state for 3 minutes.
- alert: SQL Server DOWN
@@ -119,7 +173,7 @@ groups:
labels:
severity: high
annotations:
summary: "Service {{ $labels.name }} down"
description: "Service {{ $labels.name }} on instance {{ $labels.instance }} has been down for more than 3 minutes."
summary: "Service {{ $labels.exported_name }} down"
description: "Service {{ $labels.exported_name }} on instance {{ $labels.instance }} has been down for more than 3 minutes."
```
In this example, `instance` is the target label of the host. So each alert will be processed per host, which is then used in the alert description.

View File

@@ -5,7 +5,8 @@ The system collector exposes metrics about ...
|||
-|-
Metric name prefix | `system`
Data source | Performance Counters
Data source | Perflib
Classes | [`Win32_PerfRawData_PerfOS_System`](https://web.archive.org/web/20050830140516/http://msdn.microsoft.com/library/en-us/wmisdk/wmi/win32_perfrawdata_perfos_system.asp)
Enabled by default? | Yes
## Flags
@@ -14,18 +15,14 @@ None
## Metrics
| Name | Description | Type | Labels |
|---------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|--------|
| `windows_system_context_switches_total` | Total number of [context switches](https://en.wikipedia.org/wiki/Context_switch) | counter | None |
| `windows_system_exception_dispatches_total` | Total exceptions dispatched by the system | counter | None |
| `windows_system_processes` | Number of process contexts currently loaded or running on the operating system | gauge | None |
| `windows_system_process_limit` | The size of the user-mode portion of the virtual address space of the calling process, in bytes. This value depends on the type of process, the type of processor, and the configuration of the operating system. | gauge | None |
| `windows_system_processor_queue_length` | Number of threads in the processor queue. There is a single queue for processor time even on computers with multiple processors. | gauge | None |
| `windows_system_system_calls_total` | Total combined calls to Windows NT system service routines by all processes running on the computer | counter | None |
| `windows_system_system_up_time` | Time of last boot of system | gauge | None |
| `windows_system_threads` | Number of Windows system [threads](https://en.wikipedia.org/wiki/Thread_(computing)) | gauge | None |
Name | Description | Type | Labels
-----|-------------|------|-------
`windows_system_context_switches_total` | Total number of [context switches](https://en.wikipedia.org/wiki/Context_switch) | counter | None
`windows_system_exception_dispatches_total` | Total exceptions dispatched by the system | counter | None
`windows_system_processor_queue_length` | Number of threads in the processor queue. There is a single queue for processor time even on computers with multiple processors. | gauge | None
`windows_system_system_calls_total` | Total combined calls to Windows NT system service routines by all processes running on the computer | counter | None
`windows_system_system_up_time` | Time of last boot of system | gauge | None
`windows_system_threads` | Number of Windows system [threads](https://en.wikipedia.org/wiki/Thread_(computing)) | gauge | None
### Example metric
Show current number of system threads
@@ -33,11 +30,6 @@ Show current number of system threads
windows_system_threads{instance="localhost"}
```
Show current number of processes
```
windows_system_processes{instance="localhost"}
```
## Useful queries
Find hosts that have rebooted in the last 24 hours
```

View File

@@ -19,6 +19,7 @@ None
| Name | Description | Type | Labels |
|------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|-----------------|
| `windows_terminal_services_session_info` | Info about active WTS sessions | gauge | host,user,state |
| `windows_terminal_services_local_session_count` | Number of local Terminal Services sessions. | gauge | `session` |
| `windows_terminal_services_connection_broker_performance_total`* | The total number of connections handled by the Connection Brokers since the service started. | counter | `connection` |
| `windows_terminal_services_handles` | Total number of handles currently opened by this process. This number is the sum of the handles currently opened by each thread in this process. | gauge | `session_name` |
| `windows_terminal_services_page_fault_total` | Rate at which page faults occur in the threads executing in this process. A page fault occurs when a thread refers to a virtual memory page that is not in its working set in main memory. The page may not be retrieved from disk if it is on the standby list and therefore already in main memory. The page also may not be retrieved if it is in use by another process which shares the page. | counter | `session_name` |

View File

@@ -17,16 +17,14 @@ None
## Metrics
| Name | Description | Type | Labels |
|-----------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|------------|
| `windows_time_clock_frequency_adjustment_ppb_total` | Total adjustment made to the local system clock frequency by W32Time in parts per billion (PPB) units. 1 PPB adjustment implies the system clock was adjusted at a rate of 1 nanosecond per second (1 ns/s). The smallest possible adjustment can vary and is expected to be in the order of 100's of PPB. | counter | None |
| `windows_time_computed_time_offset_seconds` | The absolute time offset between the system clock and the chosen time source, as computed by the W32Time service in microseconds. When a new valid sample is available, the computed time is updated with the time offset indicated by the sample. This time is the actual time offset of the local clock. W32Time initiates clock correction by using this offset and updates the computed time in between samples with the remaining time offset that needs to be applied to the local clock. Clock accuracy can be tracked by using this performance counter with a low polling interval (for example, 256 seconds or less) and looking for the counter value to be smaller than the desired clock accuracy limit. | gauge | None |
| `windows_time_ntp_client_time_sources` | Active number of NTP Time sources being used by the client. This is a count of active, distinct IP addresses of time servers that are responding to this client's requests. | gauge | None |
| `windows_time_ntp_round_trip_delay_seconds` | Total roundtrip delay experienced by the NTP client in receiving a response from the server for the most recent request, in seconds. This is the time elapsed on the NTP client between transmitting a request to the NTP server and receiving a valid response from the server. | gauge | None |
| `windows_time_ntp_server_outgoing_responses_total` | Total number of requests responded to by the NTP server. | counter | None |
| `windows_time_ntp_server_incoming_requests_total` | Total number of requests received by the NTP server. | counter | None |
| `windows_time_current_timestamp_seconds` | Current time as reported by the operating system, in [Unix time](https://en.wikipedia.org/wiki/Unix_time). See [time.Unix()](https://golang.org/pkg/time/#Unix) for details | gauge | None |
| `windows_time_timezone` | Current timezone as reported by the operating system. | gauge | `timezone` |
| Name | Description | Type | Labels |
|-----------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|--------|
| `windows_time_clock_frequency_adjustment_ppb_total` | Total adjustment made to the local system clock frequency by W32Time in parts per billion (PPB) units. 1 PPB adjustment implies the system clock was adjusted at a rate of 1 nanosecond per second (1 ns/s). The smallest possible adjustment can vary and is expected to be in the order of 100's of PPB. | counter | None |
| `windows_time_computed_time_offset_seconds` | The absolute time offset between the system clock and the chosen time source, as computed by the W32Time service in microseconds. When a new valid sample is available, the computed time is updated with the time offset indicated by the sample. This time is the actual time offset of the local clock. W32Time initiates clock correction by using this offset and updates the computed time in between samples with the remaining time offset that needs to be applied to the local clock. Clock accuracy can be tracked by using this performance counter with a low polling interval (for example, 256 seconds or less) and looking for the counter value to be smaller than the desired clock accuracy limit. | gauge | None |
| `windows_time_ntp_client_time_sources` | Active number of NTP Time sources being used by the client. This is a count of active, distinct IP addresses of time servers that are responding to this client's requests. | gauge | None |
| `windows_time_ntp_round_trip_delay_seconds` | Total roundtrip delay experienced by the NTP client in receiving a response from the server for the most recent request, in seconds. This is the time elapsed on the NTP client between transmitting a request to the NTP server and receiving a valid response from the server. | gauge | None |
| `windows_time_ntp_server_outgoing_responses_total` | Total number of requests responded to by the NTP server. | counter | None |
| `windows_time_ntp_server_incoming_requests_total` | Total number of requests received by the NTP server. | counter | None |
### Example metric
_This collector does not yet have explained examples, we would appreciate your help adding them!_

View File

@@ -11,9 +11,8 @@ import (
"github.com/prometheus-community/windows_exporter/pkg/initiate"
"context"
"errors"
"encoding/json"
"fmt"
"log/slog"
"net/http"
"net/http/pprof"
"os"
@@ -25,19 +24,31 @@ import (
"time"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/collector"
"github.com/prometheus-community/windows_exporter/pkg/config"
"github.com/prometheus-community/windows_exporter/pkg/httphandler"
winlog "github.com/prometheus-community/windows_exporter/pkg/log"
"github.com/prometheus-community/windows_exporter/pkg/log/flag"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus-community/windows_exporter/pkg/utils"
"github.com/prometheus-community/windows_exporter/pkg/wmi"
"github.com/prometheus/common/version"
"github.com/prometheus/exporter-toolkit/web"
webflag "github.com/prometheus/exporter-toolkit/web/kingpinflag"
"golang.org/x/sys/windows"
)
// Same struct prometheus uses for their /version endpoint.
// Separate copy to avoid pulling all of prometheus as a dependency.
type prometheusVersion struct {
Version string `json:"version"`
Revision string `json:"revision"`
Branch string `json:"branch"`
BuildUser string `json:"buildUser"`
BuildDate string `json:"buildDate"`
GoVersion string `json:"goVersion"`
}
// Mapping of priority names to uin32 values required by windows.SetPriorityClass.
var priorityStringToInt = map[string]uint32{
"realtime": windows.REALTIME_PRIORITY_CLASS,
@@ -48,19 +59,35 @@ var priorityStringToInt = map[string]uint32{
"low": windows.IDLE_PRIORITY_CLASS,
}
func main() {
os.Exit(run())
func setPriorityWindows(pid int, priority uint32) error {
// https://learn.microsoft.com/en-us/windows/win32/procthread/process-security-and-access-rights
handle, err := windows.OpenProcess(
windows.STANDARD_RIGHTS_REQUIRED|windows.SYNCHRONIZE|windows.SPECIFIC_RIGHTS_ALL,
false, uint32(pid),
)
if err != nil {
return err
}
if err = windows.SetPriorityClass(handle, priority); err != nil {
return err
}
if err = windows.CloseHandle(handle); err != nil {
return fmt.Errorf("failed to close handle: %w", err)
}
return nil
}
func run() int {
func main() {
app := kingpin.New("windows_exporter", "A metrics collector for Windows.")
var (
configFile = app.Flag(
"config.file",
"YAML configuration file to use. Values set in this file will be overridden by CLI flags.",
).String()
insecureSkipVerify = app.Flag(
insecure_skip_verify = app.Flag(
"config.file.insecure-skip-verify",
"Skip TLS verification in loading YAML configuration.",
).Default("false").Bool()
@@ -110,41 +137,25 @@ func run() int {
// Load values from configuration file(s). Executable flags must first be parsed, in order
// to load the specified file(s).
if _, err := app.Parse(os.Args[1:]); err != nil {
//nolint:sloglint // we do not have an logger yet
slog.Error("Failed to parse CLI args",
slog.Any("err", err),
)
return 1
}
kingpin.MustParse(app.Parse(os.Args[1:]))
logger, err := winlog.New(winlogConfig)
if err != nil {
//nolint:sloglint // we do not have an logger yet
slog.Error("failed to create logger",
slog.Any("err", err),
)
return 1
_ = level.Error(logger).Log("err", err)
os.Exit(1)
}
_ = level.Debug(logger).Log("msg", "Logging has Started")
if *configFile != "" {
resolver, err := config.NewResolver(*configFile, logger, *insecureSkipVerify)
resolver, err := config.NewResolver(*configFile, logger, *insecure_skip_verify)
if err != nil {
logger.Error("could not load config file",
slog.Any("err", err),
)
return 1
_ = level.Error(logger).Log("msg", "could not load config file", "err", err)
os.Exit(1)
}
if err = resolver.Bind(app, os.Args[1:]); err != nil {
logger.Error("Failed to bind configuration",
slog.Any("err", err),
)
return 1
err = resolver.Bind(app, os.Args[1:])
if err != nil {
_ = level.Error(logger).Log("err", err)
os.Exit(1)
}
// NOTE: This is temporary fix for issue #1092, calling kingpin.Parse
@@ -153,112 +164,120 @@ func run() int {
*webConfig.WebListenAddresses = (*webConfig.WebListenAddresses)[1:]
// Parse flags once more to include those discovered in configuration file(s).
if _, err = app.Parse(os.Args[1:]); err != nil {
logger.Error("Failed to parse CLI args from YAML file",
slog.Any("err", err),
)
return 1
}
kingpin.MustParse(app.Parse(os.Args[1:]))
logger, err = winlog.New(winlogConfig)
if err != nil {
//nolint:sloglint // we do not have an logger yet
slog.Error("failed to create logger",
slog.Any("err", err),
)
return 1
_ = level.Error(logger).Log("err", err)
os.Exit(1)
}
}
logger.Debug("Logging has Started")
if *printCollectors {
printCollectorsToStdout()
collectorNames := collector.Available()
sort.Strings(collectorNames)
return 0
fmt.Printf("Available collectors:\n") //nolint:forbidigo
for _, n := range collectorNames {
fmt.Printf(" - %s\n", n) //nolint:forbidigo
}
return
}
// Only set process priority if a non-default and valid value has been set
if priority, ok := priorityStringToInt[*processPriority]; ok && priority != windows.NORMAL_PRIORITY_CLASS {
logger.Debug("setting process priority to " + *processPriority)
if err = setPriorityWindows(os.Getpid(), priority); err != nil {
logger.Error("failed to set process priority",
slog.Any("err", err),
)
return 1
if *processPriority != "normal" && priorityStringToInt[*processPriority] != 0 {
_ = level.Debug(logger).Log("msg", "setting process priority to "+*processPriority)
err = setPriorityWindows(os.Getpid(), priorityStringToInt[*processPriority])
if err != nil {
_ = level.Error(logger).Log("msg", "failed to set process priority", "err", err)
os.Exit(1)
}
}
if err = wmi.InitWbem(logger); err != nil {
_ = level.Error(logger).Log("err", err)
os.Exit(1)
}
enabledCollectorList := utils.ExpandEnabledCollectors(*enabledCollectors)
collectors.Enable(enabledCollectorList)
collectors.SetLogger(logger)
// Initialize collectors before loading
if err = collectors.Build(logger); err != nil {
logger.Error("Couldn't load collectors",
slog.Any("err", err),
)
return 1
err = collectors.Build()
if err != nil {
_ = level.Error(logger).Log("msg", "Couldn't load collectors", "err", err)
os.Exit(1)
}
err = collectors.SetPerfCounterQuery()
if err != nil {
_ = level.Error(logger).Log("msg", "Couldn't set performance counter query", "err", err)
os.Exit(1)
}
if err = collectors.SetPerfCounterQuery(logger); err != nil {
logger.Error("Couldn't set performance counter query",
slog.Any("err", err),
)
if u, err := user.Current(); err != nil {
_ = level.Warn(logger).Log("msg", "Unable to determine which user is running this exporter. More info: https://github.com/golang/go/issues/37348")
} else {
_ = level.Info(logger).Log("msg", fmt.Sprintf("Running as %v", u.Username))
return 1
if strings.Contains(u.Username, "ContainerAdministrator") || strings.Contains(u.Username, "ContainerUser") {
_ = level.Warn(logger).Log("msg", "Running as a preconfigured Windows Container user. This may mean you do not have Windows HostProcess containers configured correctly and some functionality will not work as expected.")
}
}
logCurrentUser(logger)
logger.Info("Enabled collectors: " + strings.Join(enabledCollectorList, ", "))
_ = level.Info(logger).Log("msg", fmt.Sprintf("Enabled collectors: %v", strings.Join(enabledCollectorList, ", ")))
mux := http.NewServeMux()
mux.Handle("GET /health", httphandler.NewHealthHandler())
mux.Handle("GET /version", httphandler.NewVersionHandler())
mux.Handle("GET "+*metricsPath, httphandler.New(logger, collectors, &httphandler.Options{
DisableExporterMetrics: *disableExporterMetrics,
TimeoutMargin: *timeoutMargin,
MaxRequests: *maxRequests,
}))
mux.HandleFunc(*metricsPath, withConcurrencyLimit(*maxRequests, collectors.BuildServeHTTP(*disableExporterMetrics, *timeoutMargin)))
mux.HandleFunc("/health", func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, err := fmt.Fprintln(w, `{"status":"ok"}`)
if err != nil {
_ = level.Debug(logger).Log("msg", "Failed to write to stream", "err", err)
}
})
mux.HandleFunc("/version", func(w http.ResponseWriter, _ *http.Request) {
// we can't use "version" directly as it is a package, and not an object that
// can be serialized.
err := json.NewEncoder(w).Encode(prometheusVersion{
Version: version.Version,
Revision: version.Revision,
Branch: version.Branch,
BuildUser: version.BuildUser,
BuildDate: version.BuildDate,
GoVersion: version.GoVersion,
})
if err != nil {
http.Error(w, fmt.Sprintf("error encoding JSON: %s", err), http.StatusInternalServerError)
}
})
if *debugEnabled {
mux.HandleFunc("GET /debug/pprof/", pprof.Index)
mux.HandleFunc("GET /debug/pprof/cmdline", pprof.Cmdline)
mux.HandleFunc("GET /debug/pprof/profile", pprof.Profile)
mux.HandleFunc("GET /debug/pprof/symbol", pprof.Symbol)
mux.HandleFunc("GET /debug/pprof/trace", pprof.Trace)
mux.HandleFunc("/debug/pprof/", pprof.Index)
mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
}
logger.Info("Starting windows_exporter",
slog.String("version", version.Version),
slog.String("branch", version.Branch),
slog.String("revision", version.GetRevision()),
slog.String("goversion", version.GoVersion),
slog.String("builddate", version.BuildDate),
slog.Int("maxprocs", runtime.GOMAXPROCS(0)),
)
_ = level.Info(logger).Log("msg", "Starting windows_exporter", "version", version.Info())
_ = level.Info(logger).Log("msg", "Build context", "build_context", version.BuildContext())
_ = level.Debug(logger).Log("msg", "Go MAXPROCS", "procs", runtime.GOMAXPROCS(0))
server := &http.Server{
ReadHeaderTimeout: 5 * time.Second,
IdleTimeout: 60 * time.Second,
ReadTimeout: 5 * time.Second,
WriteTimeout: 5 * time.Minute,
WriteTimeout: 10 * time.Minute,
Handler: mux,
}
errCh := make(chan error, 1)
go func() {
if err := web.ListenAndServe(server, webConfig, logger); err != nil && !errors.Is(err, http.ErrServerClosed) {
errCh <- err
if err := web.ListenAndServe(server, webConfig, logger); err != nil {
_ = level.Error(logger).Log("msg", "cannot start windows_exporter", "err", err)
os.Exit(1)
}
errCh <- nil
}()
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, os.Kill)
@@ -266,17 +285,9 @@ func run() int {
select {
case <-ctx.Done():
logger.Info("Shutting down windows_exporter via kill signal")
_ = level.Info(logger).Log("msg", "Shutting down windows_exporter via kill signal")
case <-initiate.StopCh:
logger.Info("Shutting down windows_exporter via service control")
case err := <-errCh:
if err != nil {
logger.Error("Failed to start windows_exporter",
slog.Any("err", err),
)
return 1
}
_ = level.Info(logger).Log("msg", "Shutting down windows_exporter via service control")
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
@@ -284,53 +295,24 @@ func run() int {
_ = server.Shutdown(ctx)
logger.Info("windows_exporter has shut down")
return 0
_ = level.Info(logger).Log("msg", "windows_exporter has shut down")
}
func printCollectorsToStdout() {
collectorNames := collector.Available()
sort.Strings(collectorNames)
fmt.Println("Available collectors:") //nolint:forbidigo
for _, n := range collectorNames {
fmt.Printf(" - %s\n", n) //nolint:forbidigo
func withConcurrencyLimit(n int, next http.HandlerFunc) http.HandlerFunc {
if n <= 0 {
return next
}
}
func logCurrentUser(logger *slog.Logger) {
if u, err := user.Current(); err == nil {
logger.Info("Running as " + u.Username)
if strings.Contains(u.Username, "ContainerAdministrator") || strings.Contains(u.Username, "ContainerUser") {
logger.Warn("Running as a preconfigured Windows Container user. This may mean you do not have Windows HostProcess containers configured correctly and some functionality will not work as expected.")
sem := make(chan struct{}, n)
return func(w http.ResponseWriter, r *http.Request) {
select {
case sem <- struct{}{}:
defer func() { <-sem }()
default:
w.WriteHeader(http.StatusServiceUnavailable)
_, _ = w.Write([]byte("Too many concurrent requests"))
return
}
return
next(w, r)
}
logger.Warn("Unable to determine which user is running this exporter. More info: https://github.com/golang/go/issues/37348")
}
func setPriorityWindows(pid int, priority uint32) error {
// https://learn.microsoft.com/en-us/windows/win32/procthread/process-security-and-access-rights
handle, err := windows.OpenProcess(
windows.STANDARD_RIGHTS_REQUIRED|windows.SYNCHRONIZE|windows.SPECIFIC_RIGHTS_ALL,
false, uint32(pid),
)
if err != nil {
return fmt.Errorf("failed to open own process: %w", err)
}
if err = windows.SetPriorityClass(handle, priority); err != nil {
return fmt.Errorf("failed to set priority class: %w", err)
}
if err = windows.CloseHandle(handle); err != nil {
return fmt.Errorf("failed to close handle: %w", err)
}
return nil
}

50
go.mod
View File

@@ -1,55 +1,53 @@
module github.com/prometheus-community/windows_exporter
go 1.23
go 1.22
require (
github.com/Microsoft/hcsshim v0.12.6
github.com/Microsoft/hcsshim v0.12.5
github.com/alecthomas/kingpin/v2 v2.4.0
github.com/dimchansky/utfbom v1.1.1
github.com/go-kit/log v0.2.1
github.com/go-ole/go-ole v1.3.0
github.com/google/uuid v1.6.0
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.20.3
github.com/prometheus/client_golang v1.19.1
github.com/prometheus/client_model v0.6.1
github.com/prometheus/common v0.59.1
github.com/prometheus/exporter-toolkit v0.13.0
github.com/prometheus/common v0.55.0
github.com/prometheus/exporter-toolkit v0.11.0
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/stretchr/testify v1.9.0
github.com/yusufpapurcu/wmi v1.2.4
golang.org/x/sys v0.25.0
go.opencensus.io v0.24.0 // indirect
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa
golang.org/x/sys v0.24.0
gopkg.in/yaml.v3 v3.0.1
)
require github.com/pkg/errors v0.9.1
require (
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30 // indirect
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/containerd/cgroups/v3 v3.0.3 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/containerd/cgroups/v3 v3.0.2 // indirect
github.com/containerd/errdefs v0.1.0 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-logfmt/logfmt v0.5.1 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/jpillora/backoff v1.0.0 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/mdlayher/socket v0.5.1 // indirect
github.com/mdlayher/vsock v1.2.1 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
go.opencensus.io v0.24.0 // indirect
golang.org/x/crypto v0.27.0 // indirect
golang.org/x/net v0.29.0 // indirect
golang.org/x/oauth2 v0.23.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/text v0.18.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
google.golang.org/grpc v1.66.0 // indirect
golang.org/x/crypto v0.24.0 // indirect
golang.org/x/net v0.26.0 // indirect
golang.org/x/oauth2 v0.21.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/text v0.16.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect
google.golang.org/grpc v1.62.0 // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
// https://github.com/prometheus/common/pull/694
replace github.com/prometheus/common v0.59.1 => github.com/jkroepke/prometheus-common v0.0.0-20240907211841-5f9af24b97ad

82
go.sum
View File

@@ -2,21 +2,21 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/Microsoft/hcsshim v0.12.6 h1:qEnZjoHXv+4/s0LmKZWE0/AiZmMWEIkFfWBSf1a0wlU=
github.com/Microsoft/hcsshim v0.12.6/go.mod h1:ZABCLVcvLMjIkzr9rUGcQ1QA0p0P3Ps+d3N1g2DsFfk=
github.com/Microsoft/hcsshim v0.12.5 h1:bpTInLlDy/nDRWFVcefDZZ1+U8tS+rz3MxjKgu9boo0=
github.com/Microsoft/hcsshim v0.12.5/go.mod h1:tIUGego4G1EN5Hb6KC90aDYiUI2dqLSTTOCjVNpOgZ8=
github.com/alecthomas/kingpin/v2 v2.4.0 h1:f48lwail6p8zpO1bC4TxtqACaGqHYA22qkHjHpqDjYY=
github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE=
github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30 h1:t3eaIm0rUkzbrIewtiFmMK5RXHej2XnoXNhxVsAYUfg=
github.com/alecthomas/units v0.0.0-20240626203959-61d1e3462e30/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs=
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc=
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/containerd/cgroups/v3 v3.0.3 h1:S5ByHZ/h9PMe5IOQoN7E+nMc2UcLEM/V48DGDJ9kip0=
github.com/containerd/cgroups/v3 v3.0.3/go.mod h1:8HBe7V3aWGLFPd/k03swSIsGjZhHI2WzJmticMgVuz0=
github.com/containerd/cgroups/v3 v3.0.2 h1:f5WFqIVSgo5IZmtTT3qVBo6TzI1ON6sycSBKkymb9L0=
github.com/containerd/cgroups/v3 v3.0.2/go.mod h1:JUgITrzdFqp42uI2ryGA+ge0ap/nxzYgkGmIcetmErE=
github.com/containerd/errdefs v0.1.0 h1:m0wCRBiu1WJT/Fr+iOoQHMQS/eP5myQ8lCv4Dz5ZURM=
github.com/containerd/errdefs v0.1.0/go.mod h1:YgWiiHtLmSeBrvpw+UfPijzbLaB77mEG1WwJTDETIV0=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
@@ -30,6 +30,10 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU=
github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA=
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
@@ -48,6 +52,8 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -57,24 +63,12 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jkroepke/prometheus-common v0.0.0-20240907211841-5f9af24b97ad h1:sFDfDs4nDXjES8PdrFPiXeYt8dtaxn10M/Ebxe4IuiI=
github.com/jkroepke/prometheus-common v0.0.0-20240907211841-5f9af24b97ad/go.mod h1:GpWM7dewqmVYcd7SmRaiWVe9SSqjf0UrwnYnpEZNuT0=
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos=
github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
github.com/mdlayher/vsock v1.2.1 h1:pC1mTJTvjo1r9n9fbm7S1j04rCgCzhCOS5DY0zqHlnQ=
github.com/mdlayher/vsock v1.2.1/go.mod h1:NRfCibel++DgeMD8z/hP+PPTjlNJsdPOmxcnENvE+SE=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
@@ -83,13 +77,15 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.20.3 h1:oPksm4K8B+Vt35tUhw6GbSNSgVlVSBH0qELP/7u83l4=
github.com/prometheus/client_golang v1.20.3/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/exporter-toolkit v0.13.0 h1:lmA0Q+8IaXgmFRKw09RldZmZdnvu9wwcDLIXGmTPw1c=
github.com/prometheus/exporter-toolkit v0.13.0/go.mod h1:2uop99EZl80KdXhv/MxVI2181fMcwlsumFOqBecGkG0=
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
github.com/prometheus/exporter-toolkit v0.11.0 h1:yNTsuZ0aNCNFQ3aFTD2uhPOvr4iD7fdBvKPAEGkNf+g=
github.com/prometheus/exporter-toolkit v0.11.0/go.mod h1:BVnENhnNecpwoTLiABx7mrPB/OLRIgN74qlQbV+FK1Q=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
@@ -99,12 +95,11 @@ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVs
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc=
@@ -115,9 +110,11 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
@@ -127,16 +124,16 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -144,12 +141,12 @@ golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@@ -161,15 +158,15 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c=
google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk=
google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -184,6 +181,7 @@ google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWn
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -28,7 +28,7 @@ Copy-Item -Force $PathToExecutable Work/windows_exporter.exe
Write-Verbose "Creating windows_exporter-${Version}-${Arch}.msi"
$wixArch = @{"amd64" = "x64"; "arm64" = "arm64"}[$Arch]
Invoke-Expression "wix build -arch $wixArch -o .\windows_exporter-$($Version)-$($Arch).msi .\files.wxs .\main.wxs -d ProductName=windows_exporter -d Version=$($MsiVersion) -ext WixToolset.Firewall.wixext -ext WixToolset.UI.wixext -ext WixToolset.Util.wixext"
Invoke-Expression "wix build -arch $wixArch -o .\windows_exporter-$($Version)-$($Arch).msi .\windows_exporter.wxs -d Version=$($MsiVersion) -ext WixToolset.Firewall.wixext -ext WixToolset.Util.wixext"
Write-Verbose "Done!"
Pop-Location

View File

@@ -1,38 +0,0 @@
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:netfx="http://wixtoolset.org/schemas/v4/wxs/netfx"
xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
<Fragment>
<DirectoryRef Id="APPLICATIONFOLDER">
<Component Transitive="yes">
<File Id="windows_exporter.exe" Name="windows_exporter.exe" Source="Work\windows_exporter.exe" KeyPath="yes" Vital="yes" Checksum="yes"/>
<ServiceInstall
Id="InstallExporterService"
Name="windows_exporter"
DisplayName="windows_exporter"
Description="Exports Prometheus metrics about the system"
ErrorControl="normal"
Start="auto"
Type="ownProcess"
Vital="yes"
Arguments="--log.file eventlog --config.file=&quot;[APPLICATIONFOLDER]config.yaml&quot; [CollectorsFlag] [ListenFlag] [MetricsPathFlag] [TextfileDirsFlag] [ExtraFlags]">
<util:ServiceConfig
ResetPeriodInDays="1"
FirstFailureActionType="restart"
SecondFailureActionType="restart"
ThirdFailureActionType="restart"
RestartServiceDelayInSeconds="60"
/>
<ServiceDependency Id="wmiApSrv" />
</ServiceInstall>
<ServiceControl Id="ServiceStateControl" Name="windows_exporter" Remove="uninstall" Start="install" Stop="both"/>
</Component>
<Component Id="CreateTextfileDirectory" Directory="textfile_inputs" Guid="d03ef58a-9cbf-4165-ad39-d143e9b27e14">
<CreateFolder />
</Component>
</DirectoryRef>
<ComponentGroup Id="CG_Files">
<ComponentRef Id="windows_exporter.exe" />
<ComponentRef Id="CreateTextfileDirectory" />
</ComponentGroup>
</Fragment>
</Wix>

View File

@@ -1,211 +0,0 @@
<?xml version="1.0"?>
<?ifndef Version?>
<?error Version must be defined?>
<?endif?>
<?if $(sys.BUILDARCH)=x64 ?>
<?define PlatformProgramFiles = "ProgramFiles64Folder" ?>
<?else?>
<?error Platform $(var.Platform) is not supported?>
<?endif?>
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"
xmlns:fw="http://wixtoolset.org/schemas/v4/wxs/firewall"
xmlns:ui="http://wixtoolset.org/schemas/v4/wxs/ui">
<Package UpgradeCode="66a6eb5b-1fc2-4b14-a362-5ceec6413308" Name="$(var.ProductName)" Version="$(var.Version)"
Manufacturer="prometheus-community" Language="1033" Scope="perMachine">
<SummaryInformation Manufacturer="prometheus-community" Description="$(var.ProductName) $(var.Version) installer" />
<Upgrade Id="66a6eb5b-1fc2-4b14-a362-5ceec6413308">
<UpgradeVersion IncludeMinimum="no"
Minimum="$(var.Version)"
OnlyDetect="no"
Property="NEWERVERSIONDETECTED" />
<UpgradeVersion IncludeMaximum="yes"
IncludeMinimum="yes"
Maximum="$(var.Version)"
Minimum="0.0.0.0"
Property="OLDERVERSIONBEINGUPGRADED" />
</Upgrade>
<CustomAction Id="set_maintenance" Property="MAINTENANCE" Value="true" />
<!-- Set to reinstall all features. -->
<CustomAction Id="set_reinstall_all_property"
Property="REINSTALL"
Value="ALL" />
<!-- "amus" will force reinstall all files.
See https://docs.microsoft.com/en-us/windows/desktop/Msi/reinstallmode -->
<CustomAction Id="set_reinstallmode_property"
Property="REINSTALLMODE"
Value="amus" />
<SetProperty
Id="CreateConfigFile"
Value="&quot;[%ComSpec]&quot; /c TYPE NUL >>&quot;[APPLICATIONFOLDER]config.yaml&quot;"
Before="CreateConfigFile"
Sequence="execute"
/>
<CustomAction
Id="CreateConfigFile"
BinaryRef="Wix4UtilCA_$(sys.BUILDARCHSHORT)"
DllEntry="WixQuietExec"
Execute="deferred"
Return="check"
Impersonate="no"
/>
<InstallExecuteSequence>
<!-- Set REINSTALL=all and REINSTALLMODE=amus if the user reruns the
MSI, which will force reinstalling all files and services. -->
<Custom Action="set_maintenance" Before="set_reinstall_all_property"
Condition="Installed AND (NOT REMOVE) AND (NOT UPGRADINGPRODUCTCODE)"/>
<Custom Action="set_reinstall_all_property" Before="set_reinstallmode_property" Condition="MAINTENANCE"/>
<Custom Action="set_reinstallmode_property" Before="LaunchConditions" Condition="MAINTENANCE"/>
<Custom Action="CreateConfigFile" Before="InstallServices" />
</InstallExecuteSequence>
<Media Id="1" Cabinet="windows_exporter.cab" EmbedCab="yes" />
<MajorUpgrade Schedule="afterInstallInitialize" DowngradeErrorMessage="A later version of [ProductName] is already installed. Setup will now exit." AllowSameVersionUpgrades="yes" />
<Property Id="ENABLED_COLLECTORS" Secure="yes" Value="[defaults]" />
<SetProperty Id="CollectorsFlag" After="InstallFiles" Sequence="execute" Value="--collectors.enabled [ENABLED_COLLECTORS]" Condition="ENABLED_COLLECTORS" />
<Property Id="EXTRA_FLAGS" Secure="yes" />
<SetProperty Id="ExtraFlags" After="InstallFiles" Sequence="execute" Value="[EXTRA_FLAGS]" Condition="EXTRA_FLAGS" />
<Property Id="LISTEN_PORT" Secure="yes" Value="9182" />
<SetProperty Id="ListenFlag" After="InstallFiles" Sequence="execute" Value="--web.listen-address [LISTEN_ADDR]:[LISTEN_PORT]" Condition="LISTEN_ADDR&lt;&gt;&quot;&quot; OR LISTEN_PORT&lt;&gt;9182" />
<Property Id="METRICS_PATH" Secure="yes" />
<SetProperty Id="MetricsPathFlag" After="InstallFiles" Sequence="execute" Value="--telemetry.path [METRICS_PATH]" Condition="METRICS_PATH" />
<Property Id="REMOTE_ADDR" Secure="yes" />
<SetProperty Id="RemoteAddressFlag" After="InstallFiles" Sequence="execute" Value="[REMOTE_ADDR]" Condition="REMOTE_ADDR" />
<Property Id="TEXTFILE_DIRS" Secure="yes" />
<SetProperty Id="TextfileDirsFlag" After="InstallFiles" Sequence="execute" Value="--collector.textfile.directories [TEXTFILE_DIRS]" Condition="TEXTFILE_DIRS" />
<Property Id="ARPHELPLINK" Value="https://github.com/prometheus-community/windows_exporter/issues" />
<Property Id="ARPSIZE" Value="9000" />
<Property Id="ARPURLINFOABOUT" Value="https://github.com/prometheus-community/windows_exporter" />
<!--<Property Id="ARPNOMODIFY" Value="0" />-->
<!--<Property Id="ARPNOREPAIR" Value="1" />-->
<Property Id="START_MENU_FOLDER" Value="0" />
<Property Id="NOSTART" Value="0" />
<Feature
Id="DefaultFeature"
Level="1"
Title="$(var.ProductName) $(var.Version)"
Description="The binary and configuration files for $(var.ProductName)"
Display="expand"
ConfigurableDirectory="APPLICATIONFOLDER"
AllowAdvertise="no"
InstallDefault="local"
AllowAbsent="no"
>
<ComponentGroupRef Id="CG_Files" />
<Feature
Id="FirewallException"
Level="2"
Title="Firewall Exception"
Description="Allow $(var.ProductName) to listen on a port"
Display="expand"
AllowAdvertise="no"
AllowAbsent="yes">
<ComponentGroupRef Id="CG_FirewallException" />
</Feature>
</Feature>
<UI Id="FeatureTree">
<ui:WixUI Id="WixUI_FeatureTree" />
<UIRef Id="WixUI_ErrorProgressText" />
<Dialog Id="CustomPropertiesDlg" Width="370" Height="270" Title="windows_exporter configuration">
<Control Id="BannerBitmap" Type="Bitmap" X="0" Y="0" Width="370" Height="44" TabSkip="no" Text="!(loc.CustomizeDlgBannerBitmap)" />
<!--<Control Id="Text" Type="Text" X="25" Y="55" Width="320" Height="20" Text="Text" />-->
<Control Id="BannerLine" Type="Line" X="0" Y="44" Width="370" Height="0" />
<Control Id="BottomLine" Type="Line" X="0" Y="234" Width="370" Height="0" />
<Control Id="Description" Type="Text" X="25" Y="23" Width="280" Height="15" Transparent="yes" NoPrefix="yes" Text="This pages contains configuration related to windows_exporter" />
<Control Id="Title" Type="Text" X="15" Y="6" Width="210" Height="15" Transparent="yes" NoPrefix="yes" Text="{\WixUI_Font_Title}windows_exporter configuration" />
<!-- Edit box for property input -->
<!-- cpu,cs,logical_disk,physical_disk,net,os,service,system -->
<Control Id="PropertyEdit_ENABLED_COLLECTORS_Title1" Type="Text" X="25" Y="55" Width="300" Height="15" Transparent="yes" NoPrefix="yes" Text="Comma-separated list of collectors to use. Use '[\[]defaults[\]]' as a placeholder for all" />
<Control Id="PropertyEdit_ENABLED_COLLECTORS_Title2" Type="Text" X="25" Y="65" Width="300" Height="15" Transparent="yes" NoPrefix="yes" Text="the collectors enabled by default." />
<Control Id="PropertyEdit_ENABLED_COLLECTORS" Type="Edit" X="24" Y="77" Width="300" Height="18" Property="ENABLED_COLLECTORS" Text="[ENABLED_COLLECTORS]" Indirect="no" />
<Control Id="PropertyEdit_EXTRA_FLAGS_Title" Type="Text" X="25" Y="100" Width="210" Height="15" Transparent="yes" NoPrefix="yes" Text="Additional command line flags" />
<Control Id="PropertyEdit_EXTRA_FLAGS" Type="Edit" X="24" Y="112" Width="300" Height="18" Property="EXTRA_FLAGS" Text="[EXTRA_FLAGS]" Indirect="no" />
<Control Id="PropertyEdit_LISTEN_PORT_Title" Type="Text" X="25" Y="135" Width="210" Height="15" Transparent="yes" NoPrefix="yes" Text="Port to listen" />
<Control Id="PropertyEdit_LISTEN_PORT" Type="Edit" X="24" Y="147" Width="300" Height="18" Property="LISTEN_PORT" Text="[LISTEN_PORT]" Indirect="no" />
<Control Id="Next" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" Text="!(loc.WixUINext)">
<Publish Event="NewDialog" Value="VerifyReadyDlg" />
</Control>
<Control Id="Back" Type="PushButton" X="180" Y="243" Width="56" Height="17" Text="!(loc.WixUIBack)">
<Publish Event="NewDialog" Value="CustomizeDlg" />
</Control>
<Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Cancel="yes" Text="!(loc.WixUICancel)">
<Publish Event="SpawnDialog" Value="CancelDlg" />
</Control>
</Dialog>
<!-- skip the license agreement dialog; higher Order takes priority (weird) -->
<Publish
Condition="NOT Installed"
Dialog="WelcomeDlg"
Control="Next"
Event="NewDialog"
Value="CustomizeDlg"
Order="10"/>
<Publish
Condition="NOT Installed"
Dialog="CustomizeDlg"
Control="Back"
Event="NewDialog"
Value="WelcomeDlg"
Order="10"/>
<Publish
Dialog="CustomizeDlg"
Control="Next"
Event="NewDialog"
Value="CustomPropertiesDlg"
Order="10"/>
<Publish
Dialog="VerifyReadyDlg"
Control="Back"
Event="NewDialog"
Value="CustomPropertiesDlg"
Order="10"/>
<!--CustomPropertyDlg-->
</UI>
<!-- InstallLocation key -->
<CustomAction Id="SetInstallLocation" Property="ARPINSTALLLOCATION" Value="[APPLICATIONFOLDER]" />
<StandardDirectory Id="ProgramFiles64Folder">
<Directory Id="APPLICATIONFOLDER" Name="windows_exporter">
<Directory Id="textfile_inputs" Name="textfile_inputs" />
</Directory>
</StandardDirectory>
<ComponentGroup Id="CG_FirewallException">
<Component Directory="APPLICATIONFOLDER" Id="C_FirewallException" Guid="9f522655-ac0e-42d2-a512-a7b19ebec7f7">
<fw:FirewallException
Id="MetricsEndpoint"
Name="$(var.ProductName)"
Description="$(var.ProductName) HTTP endpoint"
Program="[#windows_exporter.exe]"
Port="[LISTEN_PORT]"
Protocol="tcp">
<fw:RemoteAddress Value="[REMOTE_ADDR]" />
</fw:FirewallException>
</Component>
</ComponentGroup>
</Package>
</Wix>

View File

@@ -0,0 +1,110 @@
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs" xmlns:fw="http://wixtoolset.org/schemas/v4/wxs/firewall" xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
<?if $(sys.BUILDARCH)=x64 ?>
<?define PlatformProgramFiles = "ProgramFiles64Folder" ?>
<?else?>
<?define PlatformProgramFiles = "ProgramFilesFolder" ?>
<?endif?>
<Package UpgradeCode="66a6eb5b-1fc2-4b14-a362-5ceec6413308" Name="windows_exporter" Version="$(var.Version)" Manufacturer="prometheus-community" Language="1033" Codepage="1252">
<SummaryInformation Manufacturer="prometheus-community" Description="windows_exporter $(var.Version) installer" />
<Media Id="1" Cabinet="windows_exporter.cab" EmbedCab="yes" />
<MajorUpgrade Schedule="afterInstallInitialize" DowngradeErrorMessage="A later version of [ProductName] is already installed. Setup will now exit." />
<Property Id="ENABLED_COLLECTORS" Secure="yes" />
<SetProperty Id="CollectorsFlag" After="InstallFiles" Sequence="execute" Value="--collectors.enabled [ENABLED_COLLECTORS]" Condition="ENABLED_COLLECTORS" />
<Property Id="EXTRA_FLAGS" Secure="yes" />
<SetProperty Id="ExtraFlags" After="InstallFiles" Sequence="execute" Value="[EXTRA_FLAGS]" Condition="EXTRA_FLAGS" />
<Property Id="ADD_FIREWALL_EXCEPTION" Secure="yes" />
<Property Id="ENABLE_V1_PERFORMANCE_COUNTERS" Secure="yes" />
<Property Id="LISTEN_PORT" Secure="yes" Value="9182" />
<SetProperty Id="ListenFlag" After="InstallFiles" Sequence="execute" Value="--web.listen-address [LISTEN_ADDR]:[LISTEN_PORT]" Condition="LISTEN_ADDR&lt;&gt;&quot;&quot; OR LISTEN_PORT&lt;&gt;9182" />
<Property Id="METRICS_PATH" Secure="yes" />
<SetProperty Id="MetricsPathFlag" After="InstallFiles" Sequence="execute" Value="--telemetry.path [METRICS_PATH]" Condition="METRICS_PATH" />
<Property Id="REMOTE_ADDR" Secure="yes" />
<SetProperty Id="RemoteAddressFlag" After="InstallFiles" Sequence="execute" Value="[REMOTE_ADDR]" Condition="REMOTE_ADDR" />
<!-- https://github.com/prometheus-community/windows_exporter/issues/1318 -->
<!-- https://wixtoolset.org/docs/tools/wixext/quietexec/ -->
<SetProperty
Id="WixQuietExecCmdLine"
Value="&quot;[%ComSpec]&quot; /c reg delete HKLM\SYSTEM\CurrentControlSet\Services\EventLog\Application\windows_exporter /f"
Before="RemoveEventSource"
Sequence="execute"
/>
<CustomAction
Id="RemoveEventSource"
BinaryRef="Wix4UtilCA_$(sys.BUILDARCHSHORT)"
DllEntry="WixSilentExec"
Execute="deferred"
Impersonate="no"
Return="ignore"
/>
<InstallExecuteSequence>
<Custom Action="RemoveEventSource" After="InstallInitialize" />
</InstallExecuteSequence>
<SetProperty
Id="EnableV1PerformanceCounters"
Value="&quot;[%ComSpec]&quot; /c lodctr.exe /E:Lsa &amp; lodctr.exe /E:PerfProc &amp; lodctr.exe /R"
Before="EnableV1PerformanceCounters"
Sequence="execute"
/>
<CustomAction
Id="EnableV1PerformanceCounters"
BinaryRef="Wix4UtilCA_$(sys.BUILDARCHSHORT)"
DllEntry="WixSilentExec"
Execute="deferred"
Impersonate="no"
Return="check"
/>
<InstallExecuteSequence>
<Custom Action="EnableV1PerformanceCounters" Before="InstallFinalize" Condition="ENABLE_V1_PERFORMANCE_COUNTERS=&quot;yes&quot;"/>
</InstallExecuteSequence>
<Property Id="TEXTFILE_DIRS" Secure="yes" />
<SetProperty Id="TextfileDirsFlag" After="InstallFiles" Sequence="execute" Value="--collector.textfile.directories [TEXTFILE_DIRS]" Condition="TEXTFILE_DIRS" />
<ComponentGroup Id="Files">
<Component Directory="APPLICATIONROOTDIRECTORY">
<File Id="windows_exporter.exe" Name="windows_exporter.exe" Source="Work\windows_exporter.exe" KeyPath="yes" />
<ServiceInstall Id="InstallExporterService" Name="windows_exporter" DisplayName="windows_exporter" Description="Exports Prometheus metrics about the system" ErrorControl="normal" Start="auto" Type="ownProcess" Arguments="--log.file eventlog [CollectorsFlag] [ListenFlag] [MetricsPathFlag] [TextfileDirsFlag] [ExtraFlags]">
<util:ServiceConfig FirstFailureActionType="restart" SecondFailureActionType="restart" ThirdFailureActionType="restart" RestartServiceDelayInSeconds="60" />
<ServiceDependency Id="wmiApSrv" />
</ServiceInstall>
<ServiceControl Id="ServiceStateControl" Name="windows_exporter" Remove="uninstall" Start="install" Stop="both" />
</Component>
<Component Id="CreateTextfileDirectory" Directory="textfile_inputs" Guid="d03ef58a-9cbf-4165-ad39-d143e9b27e14">
<CreateFolder />
</Component>
</ComponentGroup>
<ComponentGroup Id="CG_FirewallException">
<Component Condition="ADD_FIREWALL_EXCEPTION=&quot;yes&quot;" Directory="APPLICATIONROOTDIRECTORY" Id="C_FirewallException" Guid="9f522655-ac0e-42d2-a512-a7b19ebec7f7">
<fw:FirewallException Id="MetricsEndpoint" Name="windows_exporter (HTTP [LISTEN_PORT])" Description="windows_exporter HTTP endpoint" Port="[LISTEN_PORT]" Protocol="tcp" IgnoreFailure="yes">
<fw:RemoteAddress Value="[REMOTE_ADDR]" />
</fw:FirewallException>
</Component>
</ComponentGroup>
<Feature Id="DefaultFeature" Level="1">
<ComponentGroupRef Id="Files" />
</Feature>
<Feature Id="FirewallException" Level="1">
<ComponentGroupRef Id="CG_FirewallException" />
</Feature>
<StandardDirectory Id="ProgramFiles64Folder">
<Directory Id="APPLICATIONROOTDIRECTORY" Name="windows_exporter">
<Directory Id="textfile_inputs" Name="textfile_inputs" />
</Directory>
</StandardDirectory>
</Package>
</Wix>

View File

@@ -4,12 +4,13 @@ package ad
import (
"errors"
"log/slog"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus-community/windows_exporter/pkg/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "ad"
@@ -20,8 +21,8 @@ var ConfigDefaults = Config{}
// A Collector is a Prometheus Collector for WMI Win32_PerfRawData_DirectoryServices_DirectoryServices metrics.
type Collector struct {
config Config
wmiClient *wmi.Client
config Config
logger log.Logger
addressBookClientSessions *prometheus.Desc
addressBookOperationsTotal *prometheus.Desc
@@ -87,7 +88,7 @@ type Collector struct {
tombstonesObjectsVisitedTotal *prometheus.Desc
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -96,6 +97,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -107,21 +110,19 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
return errors.New("wmiClient or SWbemServicesClient is nil")
}
c.wmiClient = wmiClient
func (c *Collector) Build() error {
c.addressBookOperationsTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "address_book_operations_total"),
"",
@@ -501,16 +502,11 @@ func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) Collect(_ *types.ScrapeContext, ch chan<- prometheus.Metric) error {
if err := c.collect(ch); err != nil {
logger.Error("failed collecting ad metrics",
slog.Any("err", err),
)
_ = level.Error(c.logger).Log("msg", "failed collecting ad metrics", "err", err)
return err
}
return nil
}
@@ -668,10 +664,10 @@ type Win32_PerfRawData_DirectoryServices_DirectoryServices struct {
func (c *Collector) collect(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_DirectoryServices_DirectoryServices
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_DirectoryServices_DirectoryServices", &dst); err != nil {
q := wmi.QueryAll(&dst, c.logger)
if err := wmi.Query(q, &dst); err != nil {
return err
}
if len(dst) == 0 {
return errors.New("WMI query returned empty result set")
}

View File

@@ -4,15 +4,15 @@ package adcs
import (
"errors"
"log/slog"
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/perflib"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus-community/windows_exporter/pkg/utils"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "adcs"
@@ -23,6 +23,7 @@ var ConfigDefaults = Config{}
type Collector struct {
config Config
logger log.Logger
challengeResponseProcessingTime *prometheus.Desc
challengeResponsesPerSecond *prometheus.Desc
@@ -39,7 +40,7 @@ type Collector struct {
signedCertificateTimestampListsPerSecond *prometheus.Desc
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -48,6 +49,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -59,15 +62,19 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{"Certification Authority"}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build() error {
c.requestsPerSecond = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "requests_total"),
"Total certificate requests processed",
@@ -150,16 +157,11 @@ func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
return nil
}
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
if err := c.collectADCSCounters(ctx, logger, ch); err != nil {
logger.Error("failed collecting ADCS metrics",
slog.Any("err", err),
)
func (c *Collector) Collect(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
if err := c.collectADCSCounters(ctx, ch); err != nil {
_ = level.Error(c.logger).Log("msg", "failed collecting ADCS metrics", "err", err)
return err
}
return nil
}
@@ -180,18 +182,15 @@ type perflibADCS struct {
SignedCertificateTimestampListProcessingTime float64 `perflib:"Signed Certificate Timestamp List processing time (ms)"`
}
func (c *Collector) collectADCSCounters(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
func (c *Collector) collectADCSCounters(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
dst := make([]perflibADCS, 0)
if _, ok := ctx.PerfObjects["Certification Authority"]; !ok {
return errors.New("perflib did not contain an entry for Certification Authority")
}
err := perflib.UnmarshalObject(ctx.PerfObjects["Certification Authority"], &dst, logger)
err := perflib.UnmarshalObject(ctx.PerfObjects["Certification Authority"], &dst, c.logger)
if err != nil {
return err
}
if len(dst) == 0 {
return errors.New("perflib query for Certification Authority (ADCS) returned empty result set")
}

View File

@@ -3,14 +3,13 @@
package adfs
import (
"log/slog"
"math"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/prometheus-community/windows_exporter/pkg/perflib"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "adfs"
@@ -21,6 +20,7 @@ var ConfigDefaults = Config{}
type Collector struct {
config Config
logger log.Logger
adLoginConnectionFailures *prometheus.Desc
artifactDBFailures *prometheus.Desc
@@ -67,7 +67,7 @@ type Collector struct {
wstrustTokenRequests *prometheus.Desc
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -76,6 +76,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -87,15 +89,19 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{"AD FS"}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build() error {
c.adLoginConnectionFailures = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "ad_login_connection_failures_total"),
"Total number of connection failures to an Active Directory domain controller",
@@ -404,12 +410,9 @@ type perflibADFS struct {
FederationMetadataRequests float64 `perflib:"Federation Metadata Requests"`
}
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) Collect(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
var adfsData []perflibADFS
err := perflib.UnmarshalObject(ctx.PerfObjects["AD FS"], &adfsData, logger)
err := perflib.UnmarshalObject(ctx.PerfObjects["AD FS"], &adfsData, c.logger)
if err != nil {
return err
}
@@ -671,6 +674,5 @@ func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch ch
prometheus.CounterValue,
adfsData[0].FederationMetadataRequests,
)
return nil
}

View File

@@ -3,14 +3,13 @@
package cache
import (
"log/slog"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/pkg/errors"
"github.com/prometheus-community/windows_exporter/pkg/perflib"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "cache"
@@ -22,6 +21,7 @@ var ConfigDefaults = Config{}
// A Collector is a Prometheus Collector for Perflib Cache metrics.
type Collector struct {
config Config
logger log.Logger
asyncCopyReadsTotal *prometheus.Desc
asyncDataMapsTotal *prometheus.Desc
@@ -54,7 +54,7 @@ type Collector struct {
syncPinReadsTotal *prometheus.Desc
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -63,6 +63,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -74,15 +76,19 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{"Cache"}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build() error {
c.asyncCopyReadsTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "async_copy_reads_total"),
"(AsyncCopyReadsTotal)",
@@ -257,17 +263,13 @@ func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
nil,
nil,
)
return nil
}
// Collect implements the Collector interface.
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
if err := c.collect(ctx, logger, ch); err != nil {
logger.Error("failed collecting cache metrics",
slog.Any("err", err),
)
func (c *Collector) Collect(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
if err := c.collect(ctx, ch); err != nil {
_ = level.Error(c.logger).Log("msg", "failed collecting cache metrics", "err", err)
return err
}
@@ -283,23 +285,20 @@ type perflibCache struct {
AsyncFastReadsTotal float64 `perflib:"Async Fast Reads/sec"`
AsyncMDLReadsTotal float64 `perflib:"Async MDL Reads/sec"`
AsyncPinReadsTotal float64 `perflib:"Async Pin Reads/sec"`
CopyReadHitsTotal float64 `perflib:"Copy Read Hits %"`
CopyReadHitsTotal float64 `perflib:"Copy Read Hits/sec"`
CopyReadsTotal float64 `perflib:"Copy Reads/sec"`
DataFlushesTotal float64 `perflib:"Data Flushes/sec"`
DataFlushPagesTotal float64 `perflib:"Data Flush Pages/sec"`
DataMapHitsPercent float64 `perflib:"Data Map Hits %"`
DataMapPinsTotal float64 `perflib:"Data Map Pins/sec"`
DataMapsTotal float64 `perflib:"Data Maps/sec"`
DirtyPages float64 `perflib:"Dirty Pages"`
DirtyPageThreshold float64 `perflib:"Dirty Page Threshold"`
FastReadNotPossiblesTotal float64 `perflib:"Fast Read Not Possibles/sec"`
FastReadResourceMissesTotal float64 `perflib:"Fast Read Resource Misses/sec"`
FastReadsTotal float64 `perflib:"Fast Reads/sec"`
LazyWriteFlushesTotal float64 `perflib:"Lazy Write Flushes/sec"`
LazyWritePagesTotal float64 `perflib:"Lazy Write Pages/sec"`
MDLReadHitsTotal float64 `perflib:"MDL Read Hits %"`
MDLReadHitsTotal float64 `perflib:"MDL Read Hits/sec"`
MDLReadsTotal float64 `perflib:"MDL Reads/sec"`
PinReadHitsTotal float64 `perflib:"Pin Read Hits %"`
PinReadHitsTotal float64 `perflib:"Pin Read Hits/sec"`
PinReadsTotal float64 `perflib:"Pin Reads/sec"`
ReadAheadsTotal float64 `perflib:"Read Aheads/sec"`
SyncCopyReadsTotal float64 `perflib:"Sync Copy Reads/sec"`
@@ -307,14 +306,14 @@ type perflibCache struct {
SyncFastReadsTotal float64 `perflib:"Sync Fast Reads/sec"`
SyncMDLReadsTotal float64 `perflib:"Sync MDL Reads/sec"`
SyncPinReadsTotal float64 `perflib:"Sync Pin Reads/sec"`
DirtyPages float64 `perflib:"Dirty Pages"`
DirtyPageThreshold float64 `perflib:"Dirty Page Threshold"`
DataMapHitsPercent float64 `perflib:"Data Map Hits %"`
}
func (c *Collector) collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) collect(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
var dst []perflibCache // Single-instance class, array is required but will have single entry.
if err := perflib.UnmarshalObject(ctx.PerfObjects["Cache"], &dst, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects["Cache"], &dst, c.logger); err != nil {
return err
}
@@ -327,174 +326,146 @@ func (c *Collector) collect(ctx *types.ScrapeContext, logger *slog.Logger, ch ch
prometheus.CounterValue,
dst[0].AsyncCopyReadsTotal,
)
ch <- prometheus.MustNewConstMetric(
c.asyncDataMapsTotal,
prometheus.CounterValue,
dst[0].AsyncDataMapsTotal,
)
ch <- prometheus.MustNewConstMetric(
c.asyncFastReadsTotal,
prometheus.CounterValue,
dst[0].AsyncFastReadsTotal,
)
ch <- prometheus.MustNewConstMetric(
c.asyncMDLReadsTotal,
prometheus.CounterValue,
dst[0].AsyncMDLReadsTotal,
)
ch <- prometheus.MustNewConstMetric(
c.asyncPinReadsTotal,
prometheus.CounterValue,
dst[0].AsyncPinReadsTotal,
)
ch <- prometheus.MustNewConstMetric(
c.copyReadHitsTotal,
prometheus.GaugeValue,
prometheus.CounterValue,
dst[0].CopyReadHitsTotal,
)
ch <- prometheus.MustNewConstMetric(
c.copyReadsTotal,
prometheus.CounterValue,
dst[0].CopyReadsTotal,
)
ch <- prometheus.MustNewConstMetric(
c.dataFlushesTotal,
prometheus.CounterValue,
dst[0].DataFlushesTotal,
)
ch <- prometheus.MustNewConstMetric(
c.dataFlushPagesTotal,
prometheus.CounterValue,
dst[0].DataFlushPagesTotal,
)
ch <- prometheus.MustNewConstMetric(
c.dataMapPinsTotal,
prometheus.CounterValue,
dst[0].DataMapPinsTotal,
)
ch <- prometheus.MustNewConstMetric(
c.dataMapsTotal,
prometheus.CounterValue,
dst[0].DataMapsTotal,
)
ch <- prometheus.MustNewConstMetric(
c.fastReadNotPossiblesTotal,
prometheus.CounterValue,
dst[0].FastReadNotPossiblesTotal,
)
ch <- prometheus.MustNewConstMetric(
c.fastReadResourceMissesTotal,
prometheus.CounterValue,
dst[0].FastReadResourceMissesTotal,
)
ch <- prometheus.MustNewConstMetric(
c.fastReadsTotal,
prometheus.CounterValue,
dst[0].FastReadsTotal,
)
ch <- prometheus.MustNewConstMetric(
c.lazyWriteFlushesTotal,
prometheus.CounterValue,
dst[0].LazyWriteFlushesTotal,
)
ch <- prometheus.MustNewConstMetric(
c.lazyWritePagesTotal,
prometheus.CounterValue,
dst[0].LazyWritePagesTotal,
)
ch <- prometheus.MustNewConstMetric(
c.mdlReadHitsTotal,
prometheus.CounterValue,
dst[0].MDLReadHitsTotal,
)
ch <- prometheus.MustNewConstMetric(
c.mdlReadsTotal,
prometheus.CounterValue,
dst[0].MDLReadsTotal,
)
ch <- prometheus.MustNewConstMetric(
c.pinReadHitsTotal,
prometheus.CounterValue,
dst[0].PinReadHitsTotal,
)
ch <- prometheus.MustNewConstMetric(
c.pinReadsTotal,
prometheus.CounterValue,
dst[0].PinReadsTotal,
)
ch <- prometheus.MustNewConstMetric(
c.readAheadsTotal,
prometheus.CounterValue,
dst[0].ReadAheadsTotal,
)
ch <- prometheus.MustNewConstMetric(
c.syncCopyReadsTotal,
prometheus.CounterValue,
dst[0].SyncCopyReadsTotal,
)
ch <- prometheus.MustNewConstMetric(
c.syncDataMapsTotal,
prometheus.CounterValue,
dst[0].SyncDataMapsTotal,
)
ch <- prometheus.MustNewConstMetric(
c.syncFastReadsTotal,
prometheus.CounterValue,
dst[0].SyncFastReadsTotal,
)
ch <- prometheus.MustNewConstMetric(
c.syncMDLReadsTotal,
prometheus.CounterValue,
dst[0].SyncMDLReadsTotal,
)
ch <- prometheus.MustNewConstMetric(
c.syncPinReadsTotal,
prometheus.CounterValue,
dst[0].SyncPinReadsTotal,
)
ch <- prometheus.MustNewConstMetric(
c.dirtyPages,
prometheus.GaugeValue,
dst[0].DirtyPages,
)
ch <- prometheus.MustNewConstMetric(
c.dirtyPageThreshold,
prometheus.GaugeValue,
dst[0].DirtyPageThreshold,
)
ch <- prometheus.MustNewConstMetric(
c.dataMapHitsPercent,
prometheus.GaugeValue,
dst[0].DataMapHitsPercent,
)
ch <- prometheus.MustNewConstMetric(
c.dataMapPinsTotal,
prometheus.CounterValue,
dst[0].DataMapPinsTotal,
)
ch <- prometheus.MustNewConstMetric(
c.dataMapsTotal,
prometheus.CounterValue,
dst[0].DataMapsTotal,
)
ch <- prometheus.MustNewConstMetric(
c.dirtyPages,
prometheus.GaugeValue,
dst[0].DirtyPages,
)
ch <- prometheus.MustNewConstMetric(
c.dirtyPageThreshold,
prometheus.GaugeValue,
dst[0].DirtyPageThreshold,
)
ch <- prometheus.MustNewConstMetric(
c.fastReadNotPossiblesTotal,
prometheus.CounterValue,
dst[0].FastReadNotPossiblesTotal,
)
ch <- prometheus.MustNewConstMetric(
c.fastReadResourceMissesTotal,
prometheus.CounterValue,
dst[0].FastReadResourceMissesTotal,
)
ch <- prometheus.MustNewConstMetric(
c.fastReadsTotal,
prometheus.CounterValue,
dst[0].FastReadsTotal,
)
ch <- prometheus.MustNewConstMetric(
c.lazyWriteFlushesTotal,
prometheus.CounterValue,
dst[0].LazyWriteFlushesTotal,
)
ch <- prometheus.MustNewConstMetric(
c.lazyWritePagesTotal,
prometheus.CounterValue,
dst[0].LazyWritePagesTotal,
)
ch <- prometheus.MustNewConstMetric(
c.mdlReadHitsTotal,
prometheus.CounterValue,
dst[0].MDLReadHitsTotal,
)
ch <- prometheus.MustNewConstMetric(
c.mdlReadsTotal,
prometheus.CounterValue,
dst[0].MDLReadsTotal,
)
ch <- prometheus.MustNewConstMetric(
c.pinReadHitsTotal,
prometheus.CounterValue,
dst[0].PinReadHitsTotal,
)
ch <- prometheus.MustNewConstMetric(
c.pinReadsTotal,
prometheus.CounterValue,
dst[0].PinReadsTotal,
)
ch <- prometheus.MustNewConstMetric(
c.readAheadsTotal,
prometheus.CounterValue,
dst[0].ReadAheadsTotal,
)
ch <- prometheus.MustNewConstMetric(
c.syncCopyReadsTotal,
prometheus.CounterValue,
dst[0].SyncCopyReadsTotal,
)
ch <- prometheus.MustNewConstMetric(
c.syncDataMapsTotal,
prometheus.CounterValue,
dst[0].SyncDataMapsTotal,
)
ch <- prometheus.MustNewConstMetric(
c.syncFastReadsTotal,
prometheus.CounterValue,
dst[0].SyncFastReadsTotal,
)
ch <- prometheus.MustNewConstMetric(
c.syncMDLReadsTotal,
prometheus.CounterValue,
dst[0].SyncMDLReadsTotal,
)
ch <- prometheus.MustNewConstMetric(
c.syncPinReadsTotal,
prometheus.CounterValue,
dst[0].SyncPinReadsTotal,
)
return nil
}

View File

@@ -4,12 +4,11 @@ package collector
import (
"errors"
"fmt"
"log/slog"
"slices"
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/prometheus-community/windows_exporter/pkg/collector/ad"
"github.com/prometheus-community/windows_exporter/pkg/collector/adcs"
"github.com/prometheus-community/windows_exporter/pkg/collector/adfs"
@@ -30,7 +29,11 @@ import (
"github.com/prometheus-community/windows_exporter/pkg/collector/logical_disk"
"github.com/prometheus-community/windows_exporter/pkg/collector/logon"
"github.com/prometheus-community/windows_exporter/pkg/collector/memory"
"github.com/prometheus-community/windows_exporter/pkg/collector/mscluster"
"github.com/prometheus-community/windows_exporter/pkg/collector/mscluster_cluster"
"github.com/prometheus-community/windows_exporter/pkg/collector/mscluster_network"
"github.com/prometheus-community/windows_exporter/pkg/collector/mscluster_node"
"github.com/prometheus-community/windows_exporter/pkg/collector/mscluster_resource"
"github.com/prometheus-community/windows_exporter/pkg/collector/mscluster_resourcegroup"
"github.com/prometheus-community/windows_exporter/pkg/collector/msmq"
"github.com/prometheus-community/windows_exporter/pkg/collector/mssql"
"github.com/prometheus-community/windows_exporter/pkg/collector/net"
@@ -44,7 +47,6 @@ import (
"github.com/prometheus-community/windows_exporter/pkg/collector/netframework_clrsecurity"
"github.com/prometheus-community/windows_exporter/pkg/collector/nps"
"github.com/prometheus-community/windows_exporter/pkg/collector/os"
"github.com/prometheus-community/windows_exporter/pkg/collector/perfdata"
"github.com/prometheus-community/windows_exporter/pkg/collector/physical_disk"
"github.com/prometheus-community/windows_exporter/pkg/collector/printer"
"github.com/prometheus-community/windows_exporter/pkg/collector/process"
@@ -65,11 +67,10 @@ import (
"github.com/prometheus-community/windows_exporter/pkg/collector/vmware_blast"
"github.com/prometheus-community/windows_exporter/pkg/perflib"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/yusufpapurcu/wmi"
)
// NewWithFlags To be called by the exporter for collector initialization before running kingpin.Parse.
func NewWithFlags(app *kingpin.Application) *MetricCollectors {
func NewWithFlags(app *kingpin.Application) Collectors {
collectors := map[string]Collector{}
for name, builder := range BuildersWithFlags {
@@ -79,79 +80,93 @@ func NewWithFlags(app *kingpin.Application) *MetricCollectors {
return New(collectors)
}
func NewBuilderWithFlags[C Collector](fn BuilderWithFlags[C]) BuilderWithFlags[Collector] {
return func(app *kingpin.Application) Collector {
return fn(app)
}
}
// NewWithConfig To be called by the external libraries for collector initialization without running kingpin.Parse
//
//goland:noinspection GoUnusedExportedFunction
func NewWithConfig(config Config) *MetricCollectors {
collectors := Map{}
collectors[ad.Name] = ad.New(&config.AD)
collectors[adcs.Name] = adcs.New(&config.ADCS)
collectors[adfs.Name] = adfs.New(&config.ADFS)
collectors[cache.Name] = cache.New(&config.Cache)
collectors[container.Name] = container.New(&config.Container)
collectors[cpu.Name] = cpu.New(&config.CPU)
collectors[cpu_info.Name] = cpu_info.New(&config.CPUInfo)
collectors[cs.Name] = cs.New(&config.Cs)
collectors[dfsr.Name] = dfsr.New(&config.DFSR)
collectors[dhcp.Name] = dhcp.New(&config.Dhcp)
collectors[diskdrive.Name] = diskdrive.New(&config.DiskDrive)
collectors[dns.Name] = dns.New(&config.DNS)
collectors[exchange.Name] = exchange.New(&config.Exchange)
collectors[fsrmquota.Name] = fsrmquota.New(&config.Fsrmquota)
collectors[hyperv.Name] = hyperv.New(&config.Hyperv)
collectors[iis.Name] = iis.New(&config.IIS)
collectors[license.Name] = license.New(&config.License)
collectors[logical_disk.Name] = logical_disk.New(&config.LogicalDisk)
collectors[logon.Name] = logon.New(&config.Logon)
collectors[memory.Name] = memory.New(&config.Memory)
collectors[mscluster.Name] = mscluster.New(&config.Mscluster)
collectors[msmq.Name] = msmq.New(&config.Msmq)
collectors[mssql.Name] = mssql.New(&config.Mssql)
collectors[net.Name] = net.New(&config.Net)
collectors[netframework_clrexceptions.Name] = netframework_clrexceptions.New(&config.NetframeworkClrexceptions)
collectors[netframework_clrinterop.Name] = netframework_clrinterop.New(&config.NetframeworkClrinterop)
collectors[netframework_clrjit.Name] = netframework_clrjit.New(&config.NetframeworkClrjit)
collectors[netframework_clrloading.Name] = netframework_clrloading.New(&config.NetframeworkClrloading)
collectors[netframework_clrlocksandthreads.Name] = netframework_clrlocksandthreads.New(&config.NetframeworkClrlocksandthreads)
collectors[netframework_clrmemory.Name] = netframework_clrmemory.New(&config.NetframeworkClrmemory)
collectors[netframework_clrremoting.Name] = netframework_clrremoting.New(&config.NetframeworkClrremoting)
collectors[netframework_clrsecurity.Name] = netframework_clrsecurity.New(&config.NetframeworkClrsecurity)
collectors[nps.Name] = nps.New(&config.Nps)
collectors[os.Name] = os.New(&config.Os)
collectors[perfdata.Name] = perfdata.New(&config.PerfData)
collectors[physical_disk.Name] = physical_disk.New(&config.PhysicalDisk)
collectors[printer.Name] = printer.New(&config.Printer)
collectors[process.Name] = process.New(&config.Process)
collectors[remote_fx.Name] = remote_fx.New(&config.RemoteFx)
collectors[scheduled_task.Name] = scheduled_task.New(&config.ScheduledTask)
collectors[service.Name] = service.New(&config.Service)
collectors[smb.Name] = smb.New(&config.SMB)
collectors[smbclient.Name] = smbclient.New(&config.SMBClient)
collectors[smtp.Name] = smtp.New(&config.SMTP)
collectors[system.Name] = system.New(&config.System)
collectors[teradici_pcoip.Name] = teradici_pcoip.New(&config.TeradiciPcoip)
collectors[tcp.Name] = tcp.New(&config.TCP)
collectors[terminal_services.Name] = terminal_services.New(&config.TerminalServices)
collectors[textfile.Name] = textfile.New(&config.Textfile)
collectors[thermalzone.Name] = thermalzone.New(&config.Thermalzone)
collectors[time.Name] = time.New(&config.Time)
collectors[vmware.Name] = vmware.New(&config.Vmware)
collectors[vmware_blast.Name] = vmware_blast.New(&config.VmwareBlast)
func NewWithConfig(logger log.Logger, config Config) Collectors {
collectors := map[string]Collector{}
collectors[ad.Name] = ad.New(logger, &config.AD)
collectors[adcs.Name] = adcs.New(logger, &config.ADCS)
collectors[adfs.Name] = adfs.New(logger, &config.ADFS)
collectors[cache.Name] = cache.New(logger, &config.Cache)
collectors[container.Name] = container.New(logger, &config.Container)
collectors[cpu.Name] = cpu.New(logger, &config.CPU)
collectors[cpu_info.Name] = cpu_info.New(logger, &config.CPUInfo)
collectors[cs.Name] = cs.New(logger, &config.Cs)
collectors[dfsr.Name] = dfsr.New(logger, &config.DFSR)
collectors[dhcp.Name] = dhcp.New(logger, &config.Dhcp)
collectors[diskdrive.Name] = diskdrive.New(logger, &config.DiskDrive)
collectors[dns.Name] = dns.New(logger, &config.DNS)
collectors[exchange.Name] = exchange.New(logger, &config.Exchange)
collectors[fsrmquota.Name] = fsrmquota.New(logger, &config.Fsrmquota)
collectors[hyperv.Name] = hyperv.New(logger, &config.Hyperv)
collectors[iis.Name] = iis.New(logger, &config.IIS)
collectors[license.Name] = license.New(logger, &config.License)
collectors[logical_disk.Name] = logical_disk.New(logger, &config.LogicalDisk)
collectors[logon.Name] = logon.New(logger, &config.Logon)
collectors[memory.Name] = memory.New(logger, &config.Memory)
collectors[mscluster_cluster.Name] = mscluster_cluster.New(logger, &config.MsclusterCluster)
collectors[mscluster_network.Name] = mscluster_network.New(logger, &config.MsclusterNetwork)
collectors[mscluster_node.Name] = mscluster_node.New(logger, &config.MsclusterNode)
collectors[mscluster_resource.Name] = mscluster_resource.New(logger, &config.MsclusterResource)
collectors[mscluster_resourcegroup.Name] = mscluster_resourcegroup.New(logger, &config.MsclusterResourceGroup)
collectors[msmq.Name] = msmq.New(logger, &config.Msmq)
collectors[mssql.Name] = mssql.New(logger, &config.Mssql)
collectors[net.Name] = net.New(logger, &config.Net)
collectors[netframework_clrexceptions.Name] = netframework_clrexceptions.New(logger, &config.NetframeworkClrexceptions)
collectors[netframework_clrinterop.Name] = netframework_clrinterop.New(logger, &config.NetframeworkClrinterop)
collectors[netframework_clrjit.Name] = netframework_clrjit.New(logger, &config.NetframeworkClrjit)
collectors[netframework_clrloading.Name] = netframework_clrloading.New(logger, &config.NetframeworkClrloading)
collectors[netframework_clrlocksandthreads.Name] = netframework_clrlocksandthreads.New(logger, &config.NetframeworkClrlocksandthreads)
collectors[netframework_clrmemory.Name] = netframework_clrmemory.New(logger, &config.NetframeworkClrmemory)
collectors[netframework_clrremoting.Name] = netframework_clrremoting.New(logger, &config.NetframeworkClrremoting)
collectors[netframework_clrsecurity.Name] = netframework_clrsecurity.New(logger, &config.NetframeworkClrsecurity)
collectors[nps.Name] = nps.New(logger, &config.Nps)
collectors[os.Name] = os.New(logger, &config.Os)
collectors[physical_disk.Name] = physical_disk.New(logger, &config.PhysicalDisk)
collectors[printer.Name] = printer.New(logger, &config.Printer)
collectors[process.Name] = process.New(logger, &config.Process)
collectors[remote_fx.Name] = remote_fx.New(logger, &config.RemoteFx)
collectors[scheduled_task.Name] = scheduled_task.New(logger, &config.ScheduledTask)
collectors[service.Name] = service.New(logger, &config.Service)
collectors[smb.Name] = smb.New(logger, &config.SMB)
collectors[smbclient.Name] = smbclient.New(logger, &config.SMBClient)
collectors[smtp.Name] = smtp.New(logger, &config.SMTP)
collectors[system.Name] = system.New(logger, &config.System)
collectors[teradici_pcoip.Name] = teradici_pcoip.New(logger, &config.TeradiciPcoip)
collectors[tcp.Name] = tcp.New(logger, &config.TCP)
collectors[terminal_services.Name] = terminal_services.New(logger, &config.TerminalServices)
collectors[textfile.Name] = textfile.New(logger, &config.Textfile)
collectors[thermalzone.Name] = thermalzone.New(logger, &config.Thermalzone)
collectors[time.Name] = time.New(logger, &config.Time)
collectors[vmware.Name] = vmware.New(logger, &config.Vmware)
collectors[vmware_blast.Name] = vmware_blast.New(logger, &config.VmwareBlast)
return New(collectors)
}
// New To be called by the external libraries for collector initialization.
func New(collectors Map) *MetricCollectors {
return &MetricCollectors{
Collectors: collectors,
WMIClient: &wmi.Client{
AllowMissingFields: true,
},
func New(collectors Map) Collectors {
return Collectors{
collectors: collectors,
}
}
func (c *MetricCollectors) SetPerfCounterQuery(logger *slog.Logger) error {
func (c *Collectors) SetLogger(logger log.Logger) {
c.logger = logger
for _, collector := range c.collectors {
collector.SetLogger(logger)
}
}
func (c *Collectors) SetPerfCounterQuery() error {
var (
err error
@@ -159,10 +174,10 @@ func (c *MetricCollectors) SetPerfCounterQuery(logger *slog.Logger) error {
perfIndicies []string
)
perfCounterDependencies := make([]string, 0, len(c.Collectors))
perfCounterDependencies := make([]string, 0, len(c.collectors))
for _, collector := range c.Collectors {
perfCounterNames, err = collector.GetPerfCounter(logger)
for _, collector := range c.collectors {
perfCounterNames, err = collector.GetPerfCounter()
if err != nil {
return err
}
@@ -175,32 +190,27 @@ func (c *MetricCollectors) SetPerfCounterQuery(logger *slog.Logger) error {
perfCounterDependencies = append(perfCounterDependencies, strings.Join(perfIndicies, " "))
}
c.PerfCounterQuery = strings.Join(perfCounterDependencies, " ")
c.perfCounterQuery = strings.Join(perfCounterDependencies, " ")
return nil
}
// Enable removes all collectors that not enabledCollectors.
func (c *MetricCollectors) Enable(enabledCollectors []string) {
for name := range c.Collectors {
func (c *Collectors) Enable(enabledCollectors []string) {
for name := range c.collectors {
if !slices.Contains(enabledCollectors, name) {
delete(c.Collectors, name)
delete(c.collectors, name)
}
}
}
// Build To be called by the exporter for collector initialization.
func (c *MetricCollectors) Build(logger *slog.Logger) error {
func (c *Collectors) Build() error {
var err error
c.WMIClient.SWbemServicesClient, err = wmi.InitializeSWbemServices(c.WMIClient)
if err != nil {
return fmt.Errorf("initialize SWbemServices: %w", err)
}
for _, collector := range c.Collectors {
if err = collector.Build(logger, c.WMIClient); err != nil {
return fmt.Errorf("error build collector %s: %w", collector.GetName(), err)
for _, collector := range c.collectors {
if err = collector.Build(); err != nil {
return err
}
}
@@ -208,12 +218,8 @@ func (c *MetricCollectors) Build(logger *slog.Logger) error {
}
// PrepareScrapeContext creates a ScrapeContext to be used during a single scrape.
func (c *MetricCollectors) PrepareScrapeContext() (*types.ScrapeContext, error) {
if c.PerfCounterQuery == "" { // if perfCounterQuery is empty, no perf counters are needed.
return &types.ScrapeContext{}, nil
}
objs, err := perflib.GetPerflibSnapshot(c.PerfCounterQuery)
func (c *Collectors) PrepareScrapeContext() (*types.ScrapeContext, error) {
objs, err := perflib.GetPerflibSnapshot(c.perfCounterQuery)
if err != nil {
return nil, err
}
@@ -222,17 +228,11 @@ func (c *MetricCollectors) PrepareScrapeContext() (*types.ScrapeContext, error)
}
// Close To be called by the exporter for collector cleanup.
func (c *MetricCollectors) Close(logger *slog.Logger) error {
errs := make([]error, 0, len(c.Collectors))
func (c *Collectors) Close() error {
errs := make([]error, 0, len(c.collectors))
for _, collector := range c.Collectors {
if err := collector.Close(logger); err != nil {
errs = append(errs, err)
}
}
if c.WMIClient != nil && c.WMIClient.SWbemServicesClient != nil {
if err := c.WMIClient.SWbemServicesClient.Close(); err != nil {
for _, collector := range c.collectors {
if err := collector.Build(); err != nil {
errs = append(errs, err)
}
}

View File

@@ -21,7 +21,11 @@ import (
"github.com/prometheus-community/windows_exporter/pkg/collector/logical_disk"
"github.com/prometheus-community/windows_exporter/pkg/collector/logon"
"github.com/prometheus-community/windows_exporter/pkg/collector/memory"
"github.com/prometheus-community/windows_exporter/pkg/collector/mscluster"
"github.com/prometheus-community/windows_exporter/pkg/collector/mscluster_cluster"
"github.com/prometheus-community/windows_exporter/pkg/collector/mscluster_network"
"github.com/prometheus-community/windows_exporter/pkg/collector/mscluster_node"
"github.com/prometheus-community/windows_exporter/pkg/collector/mscluster_resource"
"github.com/prometheus-community/windows_exporter/pkg/collector/mscluster_resourcegroup"
"github.com/prometheus-community/windows_exporter/pkg/collector/msmq"
"github.com/prometheus-community/windows_exporter/pkg/collector/mssql"
"github.com/prometheus-community/windows_exporter/pkg/collector/net"
@@ -35,7 +39,6 @@ import (
"github.com/prometheus-community/windows_exporter/pkg/collector/netframework_clrsecurity"
"github.com/prometheus-community/windows_exporter/pkg/collector/nps"
"github.com/prometheus-community/windows_exporter/pkg/collector/os"
"github.com/prometheus-community/windows_exporter/pkg/collector/perfdata"
"github.com/prometheus-community/windows_exporter/pkg/collector/physical_disk"
"github.com/prometheus-community/windows_exporter/pkg/collector/printer"
"github.com/prometheus-community/windows_exporter/pkg/collector/process"
@@ -77,7 +80,11 @@ type Config struct {
LogicalDisk logical_disk.Config `yaml:"logical_disk"`
Logon logon.Config `yaml:"logon"`
Memory memory.Config `yaml:"memory"`
Mscluster mscluster.Config `yaml:"mscluster"`
MsclusterCluster mscluster_cluster.Config `yaml:"mscluster_cluster"`
MsclusterNetwork mscluster_network.Config `yaml:"mscluster_network"`
MsclusterNode mscluster_node.Config `yaml:"mscluster_node"`
MsclusterResource mscluster_resource.Config `yaml:"mscluster_resource"`
MsclusterResourceGroup mscluster_resourcegroup.Config `yaml:"mscluster_resourcegroup"` //nolint:tagliatelle
Msmq msmq.Config `yaml:"msmq"`
Mssql mssql.Config `yaml:"mssql"`
Net net.Config `yaml:"net"`
@@ -91,7 +98,6 @@ type Config struct {
NetframeworkClrsecurity netframework_clrsecurity.Config `yaml:"netframework_clrsecurity"`
Nps nps.Config `yaml:"nps"`
Os os.Config `yaml:"os"`
PerfData perfdata.Config `yaml:"perf_data"`
PhysicalDisk physical_disk.Config `yaml:"physical_disk"`
Printer printer.Config `yaml:"printer"`
Process process.Config `yaml:"process"`
@@ -136,7 +142,11 @@ var ConfigDefaults = Config{
LogicalDisk: logical_disk.ConfigDefaults,
Logon: logon.ConfigDefaults,
Memory: memory.ConfigDefaults,
Mscluster: mscluster.ConfigDefaults,
MsclusterCluster: mscluster_cluster.ConfigDefaults,
MsclusterNetwork: mscluster_network.ConfigDefaults,
MsclusterNode: mscluster_node.ConfigDefaults,
MsclusterResource: mscluster_resource.ConfigDefaults,
MsclusterResourceGroup: mscluster_resourcegroup.ConfigDefaults,
Msmq: msmq.ConfigDefaults,
Mssql: mssql.ConfigDefaults,
Net: net.ConfigDefaults,
@@ -150,7 +160,6 @@ var ConfigDefaults = Config{
NetframeworkClrsecurity: netframework_clrsecurity.ConfigDefaults,
Nps: nps.ConfigDefaults,
Os: os.ConfigDefaults,
PerfData: perfdata.ConfigDefaults,
PhysicalDisk: physical_disk.ConfigDefaults,
Printer: printer.ConfigDefaults,
Process: process.ConfigDefaults,

View File

@@ -3,17 +3,15 @@
package container
import (
"errors"
"fmt"
"log/slog"
"strings"
"github.com/Microsoft/hcsshim"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/perflib"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "container"
@@ -25,6 +23,7 @@ var ConfigDefaults = Config{}
// A Collector is a Prometheus Collector for containers metrics.
type Collector struct {
config Config
logger log.Logger
// Presence
containerAvailable *prometheus.Desc
@@ -58,7 +57,7 @@ type Collector struct {
}
// New constructs a new Collector.
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -67,6 +66,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -78,15 +79,19 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build() error {
c.containerAvailable = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "available"),
"Available",
@@ -195,33 +200,32 @@ func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
[]string{"container_id"},
nil,
)
return nil
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
if err := c.collect(logger, ch); err != nil {
logger.Error("failed collecting collector metrics",
slog.Any("err", err),
)
func (c *Collector) Collect(_ *types.ScrapeContext, ch chan<- prometheus.Metric) error {
if err := c.collect(ch); err != nil {
_ = level.Error(c.logger).Log("msg", "failed collecting collector metrics", "err", err)
return err
}
return nil
}
func (c *Collector) collect(logger *slog.Logger, ch chan<- prometheus.Metric) error {
// containerClose closes the container resource.
func (c *Collector) containerClose(container hcsshim.Container) {
err := container.Close()
if err != nil {
_ = level.Error(c.logger).Log("err", err)
}
}
func (c *Collector) collect(ch chan<- prometheus.Metric) error {
// Types Container is passed to get the containers compute systems only
containers, err := hcsshim.GetContainers(hcsshim.ComputeSystemQuery{Types: []string{"Container"}})
if err != nil {
logger.Error("Err in Getting containers",
slog.Any("err", err),
)
_ = level.Error(c.logger).Log("msg", "Err in Getting containers", "err", err)
return err
}
@@ -232,181 +236,129 @@ func (c *Collector) collect(logger *slog.Logger, ch chan<- prometheus.Metric) er
prometheus.GaugeValue,
float64(count),
)
if count == 0 {
return nil
}
containerPrefixes := make(map[string]string)
collectErrors := make([]error, 0, len(containers))
for _, containerDetails := range containers {
containerIdWithPrefix := getContainerIdWithPrefix(containerDetails)
if err = c.collectContainer(logger, ch, containerDetails, containerIdWithPrefix); err != nil {
if hcsshim.IsNotExist(err) {
logger.Debug("err in fetching container statistics",
slog.String("container_id", containerDetails.ID),
slog.Any("err", err),
)
} else {
logger.Error("err in fetching container statistics",
slog.String("container_id", containerDetails.ID),
slog.Any("err", err),
)
collectErrors = append(collectErrors, err)
// https://stackoverflow.com/questions/45617758/proper-way-to-release-resources-with-defer-in-a-loop
func() {
container, err := hcsshim.OpenContainer(containerDetails.ID)
if container != nil {
defer c.containerClose(container)
}
if err != nil {
_ = level.Error(c.logger).Log("msg", "err in opening container", "containerId", containerDetails.ID, "err", err)
return
}
continue
}
cstats, err := container.Statistics()
if err != nil {
_ = level.Error(c.logger).Log("msg", "err in fetching container Statistics", "containerId", containerDetails.ID, "err", err)
return
}
containerPrefixes[containerDetails.ID] = containerIdWithPrefix
}
containerIdWithPrefix := getContainerIdWithPrefix(containerDetails)
containerPrefixes[containerDetails.ID] = containerIdWithPrefix
if err = c.collectNetworkMetrics(logger, ch, containerPrefixes); err != nil {
return fmt.Errorf("error in fetching container network statistics: %w", err)
}
if len(collectErrors) > 0 {
return fmt.Errorf("errors while fetching container statistics: %w", errors.Join(collectErrors...))
}
return nil
}
func (c *Collector) collectContainer(logger *slog.Logger, ch chan<- prometheus.Metric, containerDetails hcsshim.ContainerProperties, containerIdWithPrefix string) error {
container, err := hcsshim.OpenContainer(containerDetails.ID)
if err != nil {
return fmt.Errorf("error in opening container: %w", err)
}
defer func() {
if container == nil {
return
}
if err := container.Close(); err != nil {
logger.Error("error in closing container",
slog.Any("err", err),
ch <- prometheus.MustNewConstMetric(
c.containerAvailable,
prometheus.CounterValue,
1,
containerIdWithPrefix,
)
}
}()
containerStats, err := container.Statistics()
if err != nil {
return fmt.Errorf("error in fetching container statistics: %w", err)
ch <- prometheus.MustNewConstMetric(
c.usageCommitBytes,
prometheus.GaugeValue,
float64(cstats.Memory.UsageCommitBytes),
containerIdWithPrefix,
)
ch <- prometheus.MustNewConstMetric(
c.usageCommitPeakBytes,
prometheus.GaugeValue,
float64(cstats.Memory.UsageCommitPeakBytes),
containerIdWithPrefix,
)
ch <- prometheus.MustNewConstMetric(
c.usagePrivateWorkingSetBytes,
prometheus.GaugeValue,
float64(cstats.Memory.UsagePrivateWorkingSetBytes),
containerIdWithPrefix,
)
ch <- prometheus.MustNewConstMetric(
c.runtimeTotal,
prometheus.CounterValue,
float64(cstats.Processor.TotalRuntime100ns)*perflib.TicksToSecondScaleFactor,
containerIdWithPrefix,
)
ch <- prometheus.MustNewConstMetric(
c.runtimeUser,
prometheus.CounterValue,
float64(cstats.Processor.RuntimeUser100ns)*perflib.TicksToSecondScaleFactor,
containerIdWithPrefix,
)
ch <- prometheus.MustNewConstMetric(
c.runtimeKernel,
prometheus.CounterValue,
float64(cstats.Processor.RuntimeKernel100ns)*perflib.TicksToSecondScaleFactor,
containerIdWithPrefix,
)
ch <- prometheus.MustNewConstMetric(
c.readCountNormalized,
prometheus.CounterValue,
float64(cstats.Storage.ReadCountNormalized),
containerIdWithPrefix,
)
ch <- prometheus.MustNewConstMetric(
c.readSizeBytes,
prometheus.CounterValue,
float64(cstats.Storage.ReadSizeBytes),
containerIdWithPrefix,
)
ch <- prometheus.MustNewConstMetric(
c.writeCountNormalized,
prometheus.CounterValue,
float64(cstats.Storage.WriteCountNormalized),
containerIdWithPrefix,
)
ch <- prometheus.MustNewConstMetric(
c.writeSizeBytes,
prometheus.CounterValue,
float64(cstats.Storage.WriteSizeBytes),
containerIdWithPrefix,
)
}()
}
ch <- prometheus.MustNewConstMetric(
c.containerAvailable,
prometheus.CounterValue,
1,
containerIdWithPrefix,
)
ch <- prometheus.MustNewConstMetric(
c.usageCommitBytes,
prometheus.GaugeValue,
float64(containerStats.Memory.UsageCommitBytes),
containerIdWithPrefix,
)
ch <- prometheus.MustNewConstMetric(
c.usageCommitPeakBytes,
prometheus.GaugeValue,
float64(containerStats.Memory.UsageCommitPeakBytes),
containerIdWithPrefix,
)
ch <- prometheus.MustNewConstMetric(
c.usagePrivateWorkingSetBytes,
prometheus.GaugeValue,
float64(containerStats.Memory.UsagePrivateWorkingSetBytes),
containerIdWithPrefix,
)
ch <- prometheus.MustNewConstMetric(
c.runtimeTotal,
prometheus.CounterValue,
float64(containerStats.Processor.TotalRuntime100ns)*perflib.TicksToSecondScaleFactor,
containerIdWithPrefix,
)
ch <- prometheus.MustNewConstMetric(
c.runtimeUser,
prometheus.CounterValue,
float64(containerStats.Processor.RuntimeUser100ns)*perflib.TicksToSecondScaleFactor,
containerIdWithPrefix,
)
ch <- prometheus.MustNewConstMetric(
c.runtimeKernel,
prometheus.CounterValue,
float64(containerStats.Processor.RuntimeKernel100ns)*perflib.TicksToSecondScaleFactor,
containerIdWithPrefix,
)
ch <- prometheus.MustNewConstMetric(
c.readCountNormalized,
prometheus.CounterValue,
float64(containerStats.Storage.ReadCountNormalized),
containerIdWithPrefix,
)
ch <- prometheus.MustNewConstMetric(
c.readSizeBytes,
prometheus.CounterValue,
float64(containerStats.Storage.ReadSizeBytes),
containerIdWithPrefix,
)
ch <- prometheus.MustNewConstMetric(
c.writeCountNormalized,
prometheus.CounterValue,
float64(containerStats.Storage.WriteCountNormalized),
containerIdWithPrefix,
)
ch <- prometheus.MustNewConstMetric(
c.writeSizeBytes,
prometheus.CounterValue,
float64(containerStats.Storage.WriteSizeBytes),
containerIdWithPrefix,
)
return nil
}
// collectNetworkMetrics collects network metrics for containers.
// With HNSv2, the network stats must be collected from hcsshim.HNSListEndpointRequest.
// Network statistics from the container.Statistics() are providing data only, if HNSv1 is used.
// Ref: https://github.com/prometheus-community/windows_exporter/pull/1218
func (c *Collector) collectNetworkMetrics(logger *slog.Logger, ch chan<- prometheus.Metric, containerPrefixes map[string]string) error {
hnsEndpoints, err := hcsshim.HNSListEndpointRequest()
if err != nil {
logger.Warn("Failed to collect network stats for containers")
_ = level.Warn(c.logger).Log("msg", "Failed to collect network stats for containers")
return err
}
if len(hnsEndpoints) == 0 {
logger.Info("No network stats for containers to collect")
_ = level.Info(c.logger).Log("msg", "No network stats for containers to collect")
return nil
}
for _, endpoint := range hnsEndpoints {
endpointStats, err := hcsshim.GetHNSEndpointStats(endpoint.Id)
if err != nil {
logger.Warn("Failed to collect network stats for interface "+endpoint.Id,
slog.Any("err", err),
)
_ = level.Warn(c.logger).Log("msg", "Failed to collect network stats for interface "+endpoint.Id, "err", err)
continue
}
for _, containerId := range endpoint.SharedContainers {
containerIdWithPrefix, ok := containerPrefixes[containerId]
endpointId := strings.ToUpper(endpoint.Id)
if !ok {
logger.Debug("Failed to collect network stats for container " + containerId)
_ = level.Warn(c.logger).Log("msg", "Failed to collect network stats for container "+containerId)
continue
}
endpointId := strings.ToUpper(endpoint.Id)
ch <- prometheus.MustNewConstMetric(
c.bytesReceived,
prometheus.CounterValue,

View File

@@ -3,14 +3,14 @@
package cpu
import (
"log/slog"
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/prometheus-community/windows_exporter/pkg/perflib"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus-community/windows_exporter/pkg/winversion"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "cpu"
@@ -21,8 +21,8 @@ var ConfigDefaults = Config{}
type Collector struct {
config Config
logger log.Logger
logicalProcessors *prometheus.Desc
cStateSecondsTotal *prometheus.Desc
timeTotal *prometheus.Desc
interruptsTotal *prometheus.Desc
@@ -38,7 +38,7 @@ type Collector struct {
processorPrivilegedUtility *prometheus.Desc
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -47,6 +47,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -58,22 +60,22 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
return []string{"Processor Information"}, nil
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) GetPerfCounter() ([]string, error) {
if winversion.WindowsVersionFloat > 6.05 {
return []string{"Processor Information"}, nil
}
return []string{"Processor"}, nil
}
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
c.logicalProcessors = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "logical_processor"),
"Total number of logical processors",
nil,
nil,
)
func (c *Collector) Build() error {
c.cStateSecondsTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "cstate_seconds_total"),
"Time spent in low-power idle state",
@@ -99,6 +101,16 @@ func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
nil,
)
// For Windows 2008 (version 6.0) or earlier we only have the "Processor"
// class. As of Windows 2008 R2 (version 6.1) the more detailed
// "Processor Information" set is available (although some of the counters
// are added in later versions, so we aren't guaranteed to get all of
// them).
// Value 6.05 was selected to split between Windows versions.
if winversion.WindowsVersionFloat < 6.05 {
return nil
}
c.cStateSecondsTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "cstate_seconds_total"),
"Time spent in low-power idle state",
@@ -181,10 +193,111 @@ func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
return nil
}
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) Collect(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
if winversion.WindowsVersionFloat > 6.05 {
return c.CollectFull(ctx, ch)
}
return c.CollectFull(ctx, logger, ch)
return c.CollectBasic(ctx, ch)
}
type perflibProcessor struct {
Name string
C1Transitions float64 `perflib:"C1 Transitions/sec"`
C2Transitions float64 `perflib:"C2 Transitions/sec"`
C3Transitions float64 `perflib:"C3 Transitions/sec"`
DPCRate float64 `perflib:"DPC Rate"`
DPCsQueued float64 `perflib:"DPCs Queued/sec"`
Interrupts float64 `perflib:"Interrupts/sec"`
PercentC1Time float64 `perflib:"% C1 Time"`
PercentC2Time float64 `perflib:"% C2 Time"`
PercentC3Time float64 `perflib:"% C3 Time"`
PercentDPCTime float64 `perflib:"% DPC Time"`
PercentIdleTime float64 `perflib:"% Idle Time"`
PercentInterruptTime float64 `perflib:"% Interrupt Time"`
PercentPrivilegedTime float64 `perflib:"% Privileged Time"`
PercentProcessorTime float64 `perflib:"% Processor Time"`
PercentUserTime float64 `perflib:"% User Time"`
}
func (c *Collector) CollectBasic(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
data := make([]perflibProcessor, 0)
err := perflib.UnmarshalObject(ctx.PerfObjects["Processor"], &data, c.logger)
if err != nil {
return err
}
for _, cpu := range data {
if strings.Contains(strings.ToLower(cpu.Name), "_total") {
continue
}
core := cpu.Name
ch <- prometheus.MustNewConstMetric(
c.cStateSecondsTotal,
prometheus.CounterValue,
cpu.PercentC1Time,
core, "c1",
)
ch <- prometheus.MustNewConstMetric(
c.cStateSecondsTotal,
prometheus.CounterValue,
cpu.PercentC2Time,
core, "c2",
)
ch <- prometheus.MustNewConstMetric(
c.cStateSecondsTotal,
prometheus.CounterValue,
cpu.PercentC3Time,
core, "c3",
)
ch <- prometheus.MustNewConstMetric(
c.timeTotal,
prometheus.CounterValue,
cpu.PercentIdleTime,
core, "idle",
)
ch <- prometheus.MustNewConstMetric(
c.timeTotal,
prometheus.CounterValue,
cpu.PercentInterruptTime,
core, "interrupt",
)
ch <- prometheus.MustNewConstMetric(
c.timeTotal,
prometheus.CounterValue,
cpu.PercentDPCTime,
core, "dpc",
)
ch <- prometheus.MustNewConstMetric(
c.timeTotal,
prometheus.CounterValue,
cpu.PercentPrivilegedTime,
core, "privileged",
)
ch <- prometheus.MustNewConstMetric(
c.timeTotal,
prometheus.CounterValue,
cpu.PercentUserTime,
core, "user",
)
ch <- prometheus.MustNewConstMetric(
c.interruptsTotal,
prometheus.CounterValue,
cpu.Interrupts,
core,
)
ch <- prometheus.MustNewConstMetric(
c.dpcsTotal,
prometheus.CounterValue,
cpu.DPCsQueued,
core,
)
}
return nil
}
type perflibProcessorInformation struct {
@@ -216,26 +329,19 @@ type perflibProcessorInformation struct {
UserTimeSeconds float64 `perflib:"% User Time"`
}
func (c *Collector) CollectFull(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) CollectFull(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
data := make([]perflibProcessorInformation, 0)
err := perflib.UnmarshalObject(ctx.PerfObjects["Processor Information"], &data, logger)
err := perflib.UnmarshalObject(ctx.PerfObjects["Processor Information"], &data, c.logger)
if err != nil {
return err
}
var coreCount float64
for _, cpu := range data {
if strings.Contains(strings.ToLower(cpu.Name), "_total") {
continue
}
core := cpu.Name
coreCount++
ch <- prometheus.MustNewConstMetric(
c.cStateSecondsTotal,
prometheus.CounterValue,
@@ -356,11 +462,5 @@ func (c *Collector) CollectFull(ctx *types.ScrapeContext, logger *slog.Logger, c
)
}
ch <- prometheus.MustNewConstMetric(
c.logicalProcessors,
prometheus.GaugeValue,
coreCount,
)
return nil
}

View File

@@ -4,18 +4,21 @@ package cpu_info
import (
"errors"
"log/slog"
"strconv"
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus-community/windows_exporter/pkg/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const (
Name = "cpu_info"
// If you are adding additional labels to the metric, make sure that they get added in here as well. See below for explanation.
win32ProcessorQuery = "SELECT Architecture, DeviceId, Description, Family, L2CacheSize, L3CacheSize, Name FROM Win32_Processor"
)
type Config struct{}
@@ -25,19 +28,12 @@ var ConfigDefaults = Config{}
// A Collector is a Prometheus Collector for a few WMI metrics in Win32_Processor.
type Collector struct {
config Config
logger log.Logger
wmiClient *wmi.Client
cpuInfo *prometheus.Desc
cpuCoreCount *prometheus.Desc
cpuEnabledCoreCount *prometheus.Desc
cpuLogicalProcessorsCount *prometheus.Desc
cpuThreadCount *prometheus.Desc
cpuL2CacheSize *prometheus.Desc
cpuL3CacheSize *prometheus.Desc
cpuInfo *prometheus.Desc
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -46,6 +42,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -57,20 +55,19 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
return errors.New("wmiClient or SWbemServicesClient is nil")
}
c.wmiClient = wmiClient
func (c *Collector) Build() error {
c.cpuInfo = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, "", Name),
"Labelled CPU information as provided by Win32_Processor",
@@ -79,100 +76,44 @@ func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
"device_id",
"description",
"family",
"l2_cache_size",
"l3_cache_size",
"name",
},
nil,
)
c.cpuThreadCount = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "thread"),
"Number of threads per CPU",
[]string{
"device_id",
},
nil,
)
c.cpuCoreCount = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "core"),
"Number of cores per CPU",
[]string{
"device_id",
},
nil,
)
c.cpuEnabledCoreCount = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "enabled_core"),
"Number of enabled cores per CPU",
[]string{
"device_id",
},
nil,
)
c.cpuLogicalProcessorsCount = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "logical_processor"),
"Number of logical processors per CPU",
[]string{
"device_id",
},
nil,
)
c.cpuL2CacheSize = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "l2_cache_size"),
"Size of L2 cache per CPU",
[]string{
"device_id",
},
nil,
)
c.cpuL3CacheSize = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "l3_cache_size"),
"Size of L3 cache per CPU",
[]string{
"device_id",
},
nil,
)
return nil
}
type win32Processor struct {
Architecture uint32
DeviceID string
Description string
Family uint16
L2CacheSize uint32
L3CacheSize uint32
Name string
ThreadCount uint32
NumberOfCores uint32
NumberOfEnabledCore uint32
NumberOfLogicalProcessors uint32
type win32_Processor struct {
Architecture uint32
DeviceID string
Description string
Family uint16
L2CacheSize uint32
L3CacheSize uint32
Name string
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) Collect(_ *types.ScrapeContext, ch chan<- prometheus.Metric) error {
if err := c.collect(ch); err != nil {
logger.Error("failed collecting cpu_info metrics",
slog.Any("err", err),
)
_ = level.Error(c.logger).Log("msg", "failed collecting cpu_info metrics", "err", err)
return err
}
return nil
}
func (c *Collector) collect(ch chan<- prometheus.Metric) error {
var dst []win32Processor
var dst []win32_Processor
// We use a static query here because the provided methods in wmi.go all issue a SELECT *;
// This results in the time-consuming LoadPercentage field being read which seems to measure each CPU
// serially over a 1 second interval, so the scrape time is at least 1s * num_sockets
if err := c.wmiClient.Query("SELECT Architecture, DeviceId, Description, Family, L2CacheSize, L3CacheSize, Name, ThreadCount, NumberOfCores, NumberOfEnabledCore, NumberOfLogicalProcessors FROM Win32_Processor", &dst); err != nil {
if err := wmi.Query(win32ProcessorQuery, &dst); err != nil {
return err
}
if len(dst) == 0 {
return errors.New("WMI query returned empty result set")
}
@@ -187,44 +128,10 @@ func (c *Collector) collect(ch chan<- prometheus.Metric) error {
strings.TrimRight(processor.DeviceID, " "),
strings.TrimRight(processor.Description, " "),
strconv.Itoa(int(processor.Family)),
strconv.Itoa(int(processor.L2CacheSize)),
strconv.Itoa(int(processor.L3CacheSize)),
strings.TrimRight(processor.Name, " "),
)
ch <- prometheus.MustNewConstMetric(
c.cpuCoreCount,
prometheus.GaugeValue,
float64(processor.NumberOfCores),
strings.TrimRight(processor.DeviceID, " "),
)
ch <- prometheus.MustNewConstMetric(
c.cpuEnabledCoreCount,
prometheus.GaugeValue,
float64(processor.NumberOfEnabledCore),
strings.TrimRight(processor.DeviceID, " "),
)
ch <- prometheus.MustNewConstMetric(
c.cpuLogicalProcessorsCount,
prometheus.GaugeValue,
float64(processor.NumberOfLogicalProcessors),
strings.TrimRight(processor.DeviceID, " "),
)
ch <- prometheus.MustNewConstMetric(
c.cpuThreadCount,
prometheus.GaugeValue,
float64(processor.ThreadCount),
strings.TrimRight(processor.DeviceID, " "),
)
ch <- prometheus.MustNewConstMetric(
c.cpuL2CacheSize,
prometheus.GaugeValue,
float64(processor.L2CacheSize),
strings.TrimRight(processor.DeviceID, " "),
)
ch <- prometheus.MustNewConstMetric(
c.cpuL3CacheSize,
prometheus.GaugeValue,
float64(processor.L3CacheSize),
strings.TrimRight(processor.DeviceID, " "),
)
}
return nil

View File

@@ -3,13 +3,12 @@
package cs
import (
"log/slog"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/headers/sysinfoapi"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "cs"
@@ -21,19 +20,14 @@ var ConfigDefaults = Config{}
// A Collector is a Prometheus Collector for WMI metrics.
type Collector struct {
config Config
logger log.Logger
// physicalMemoryBytes
// Deprecated: Use windows_cpu_logical_processor instead
physicalMemoryBytes *prometheus.Desc
// logicalProcessors
// Deprecated: Use windows_physical_memory_total_bytes instead
logicalProcessors *prometheus.Desc
// hostname
// Deprecated: Use windows_os_hostname instead
hostname *prometheus.Desc
logicalProcessors *prometheus.Desc
hostname *prometheus.Desc
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -42,6 +36,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -53,35 +49,34 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error {
logger.Warn("The cs collector is deprecated and will be removed in a future release. " +
"Logical processors has been moved to cpu_info collector. " +
"Physical memory has been moved to memory collector. " +
"Hostname has been moved to os collector.")
func (c *Collector) Build() error {
c.logicalProcessors = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "logical_processors"),
"Deprecated: Use windows_cpu_logical_processor instead",
"ComputerSystem.NumberOfLogicalProcessors",
nil,
nil,
)
c.physicalMemoryBytes = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "physical_memory_bytes"),
"Deprecated: Use windows_physical_memory_total_bytes instead",
"ComputerSystem.TotalPhysicalMemory",
nil,
nil,
)
c.hostname = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "hostname"),
"Deprecated: Use windows_os_hostname instead",
"Labelled system hostname information as provided by ComputerSystem.DNSHostName and ComputerSystem.Domain",
[]string{
"hostname",
"domain",
@@ -89,22 +84,16 @@ func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error {
},
nil,
)
return nil
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) Collect(_ *types.ScrapeContext, ch chan<- prometheus.Metric) error {
if err := c.collect(ch); err != nil {
logger.Error("failed collecting cs metrics",
slog.Any("err", err),
)
_ = level.Error(c.logger).Log("msg", "failed collecting cs metrics", "err", err)
return err
}
return nil
}
@@ -134,12 +123,10 @@ func (c *Collector) collect(ch chan<- prometheus.Metric) error {
if err != nil {
return err
}
domain, err := sysinfoapi.GetComputerName(sysinfoapi.ComputerNameDNSDomain)
if err != nil {
return err
}
fqdn, err := sysinfoapi.GetComputerName(sysinfoapi.ComputerNameDNSFullyQualified)
if err != nil {
return err

View File

@@ -3,15 +3,15 @@
package dfsr
import (
"log/slog"
"slices"
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/perflib"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "dfsr"
@@ -27,6 +27,7 @@ var ConfigDefaults = Config{
// Collector contains the metric and state data of the DFSR collectors.
type Collector struct {
config Config
logger log.Logger
// connection source
connectionBandwidthSavingsUsingDFSReplicationTotal *prometheus.Desc
@@ -79,14 +80,13 @@ type Collector struct {
dfsrChildCollectors []dfsrCollectorFunc
}
type dfsrCollectorFunc func(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error
type dfsrCollectorFunc func(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error
// Map Perflib sources to DFSR Collector names
// e.g, volume -> DFS Replication Service Volumes.
func dfsrGetPerfObjectName(collector string) string {
prefix := "DFS "
suffix := ""
switch collector {
case "connection":
suffix = "Replication Connections"
@@ -95,11 +95,10 @@ func dfsrGetPerfObjectName(collector string) string {
case "volume":
suffix = "Replication Service Volumes"
}
return prefix + suffix
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -112,6 +111,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -138,7 +139,11 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
// Perflib sources are dynamic, depending on the enabled child collectors
expandedChildCollectors := slices.Compact(c.config.CollectorsEnabled)
perflibDependencies := make([]string, 0, len(expandedChildCollectors))
@@ -150,14 +155,12 @@ func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
return perflibDependencies, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error {
logger = logger.With(slog.String("collector", Name))
logger.Info("dfsr collector is in an experimental state! Metrics for this collector have not been tested.")
func (c *Collector) Build() error {
_ = level.Info(c.logger).Log("msg", "dfsr collector is in an experimental state! Metrics for this collector have not been tested.")
// connection
c.connectionBandwidthSavingsUsingDFSReplicationTotal = prometheus.NewDesc(
@@ -460,7 +463,6 @@ func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error {
// for use in Collector.Collect().
func (c *Collector) getDFSRChildCollectors(enabledCollectors []string) []dfsrCollectorFunc {
var dfsrCollectors []dfsrCollectorFunc
for _, collector := range enabledCollectors {
switch collector {
case "connection":
@@ -477,15 +479,13 @@ func (c *Collector) getDFSRChildCollectors(enabledCollectors []string) []dfsrCol
// Collect implements the Collector interface.
// Sends metric values for each metric to the provided prometheus Metric channel.
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) Collect(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
for _, fn := range c.dfsrChildCollectors {
err := fn(ctx, logger, ch)
err := fn(ctx, ch)
if err != nil {
return err
}
}
return nil
}
@@ -504,12 +504,9 @@ type PerflibDFSRConnection struct {
SizeOfFilesReceivedTotal float64 `perflib:"Size of Files Received"`
}
func (c *Collector) collectConnection(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) collectConnection(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
var dst []PerflibDFSRConnection
if err := perflib.UnmarshalObject(ctx.PerfObjects["DFS Replication Connections"], &dst, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects["DFS Replication Connections"], &dst, c.logger); err != nil {
return err
}
@@ -577,7 +574,6 @@ func (c *Collector) collectConnection(ctx *types.ScrapeContext, logger *slog.Log
connection.Name,
)
}
return nil
}
@@ -614,12 +610,9 @@ type perflibDFSRFolder struct {
UpdatesDroppedTotal float64 `perflib:"Updates Dropped"`
}
func (c *Collector) collectFolder(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) collectFolder(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
var dst []perflibDFSRFolder
if err := perflib.UnmarshalObject(ctx.PerfObjects["DFS Replicated Folders"], &dst, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects["DFS Replicated Folders"], &dst, c.logger); err != nil {
return err
}
@@ -813,7 +806,6 @@ func (c *Collector) collectFolder(ctx *types.ScrapeContext, logger *slog.Logger,
folder.Name,
)
}
return nil
}
@@ -828,12 +820,9 @@ type perflibDFSRVolume struct {
USNJournalUnreadPercentage float64 `perflib:"USN Journal Records Unread Percentage"`
}
func (c *Collector) collectVolume(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) collectVolume(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
var dst []perflibDFSRVolume
if err := perflib.UnmarshalObject(ctx.PerfObjects["DFS Replication Service Volumes"], &dst, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects["DFS Replication Service volumes"], &dst, c.logger); err != nil {
return err
}
@@ -873,6 +862,5 @@ func (c *Collector) collectVolume(ctx *types.ScrapeContext, logger *slog.Logger,
volume.Name,
)
}
return nil
}

View File

@@ -3,13 +3,11 @@
package dhcp
import (
"log/slog"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/prometheus-community/windows_exporter/pkg/perflib"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "dhcp"
@@ -21,6 +19,7 @@ var ConfigDefaults = Config{}
// A Collector is a Prometheus Collector perflib DHCP metrics.
type Collector struct {
config Config
logger log.Logger
acksTotal *prometheus.Desc
activeQueueLength *prometheus.Desc
@@ -49,7 +48,7 @@ type Collector struct {
requestsTotal *prometheus.Desc
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -58,6 +57,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -69,15 +70,19 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{"DHCP Server"}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build() error {
c.packetsReceivedTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "packets_received_total"),
"Total number of packets received by the DHCP server (PacketsReceivedTotal)",
@@ -228,7 +233,6 @@ func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
nil,
nil,
)
return nil
}
@@ -263,12 +267,9 @@ type dhcpPerf struct {
FailoverBndupdDropped float64 `perflib:"Failover: BndUpd Dropped."`
}
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) Collect(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
var dhcpPerfs []dhcpPerf
if err := perflib.UnmarshalObject(ctx.PerfObjects["DHCP Server"], &dhcpPerfs, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects["DHCP Server"], &dhcpPerfs, c.logger); err != nil {
return err
}

View File

@@ -4,13 +4,14 @@ package diskdrive
import (
"errors"
"log/slog"
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus-community/windows_exporter/pkg/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const (
@@ -24,8 +25,8 @@ var ConfigDefaults = Config{}
// A Collector is a Prometheus Collector for a few WMI metrics in Win32_DiskDrive.
type Collector struct {
config Config
wmiClient *wmi.Client
config Config
logger log.Logger
availability *prometheus.Desc
diskInfo *prometheus.Desc
@@ -34,7 +35,7 @@ type Collector struct {
status *prometheus.Desc
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -43,6 +44,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -54,20 +57,19 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
return errors.New("wmiClient or SWbemServicesClient is nil")
}
c.wmiClient = wmiClient
func (c *Collector) Build() error {
c.diskInfo = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "info"),
"General drive information",
@@ -160,26 +162,20 @@ var (
)
// Collect sends the metric values for each metric to the provided prometheus Metric channel.
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) Collect(_ *types.ScrapeContext, ch chan<- prometheus.Metric) error {
if err := c.collect(ch); err != nil {
logger.Error("failed collecting disk_drive_info metrics",
slog.Any("err", err),
)
_ = level.Error(c.logger).Log("msg", "failed collecting disk_drive_info metrics", "err", err)
return err
}
return nil
}
func (c *Collector) collect(ch chan<- prometheus.Metric) error {
var dst []win32_DiskDrive
if err := c.wmiClient.Query(win32DiskQuery, &dst); err != nil {
if err := wmi.Query(win32DiskQuery, &dst); err != nil {
return err
}
if len(dst) == 0 {
return errors.New("WMI query returned empty result set")
}

View File

@@ -4,12 +4,13 @@ package dns
import (
"errors"
"log/slog"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus-community/windows_exporter/pkg/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "dns"
@@ -20,8 +21,8 @@ var ConfigDefaults = Config{}
// A Collector is a Prometheus Collector for WMI Win32_PerfRawData_DNS_DNS metrics.
type Collector struct {
config Config
wmiClient *wmi.Client
config Config
logger log.Logger
dynamicUpdatesFailures *prometheus.Desc
dynamicUpdatesQueued *prometheus.Desc
@@ -47,7 +48,7 @@ type Collector struct {
zoneTransferSuccessSent *prometheus.Desc
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -56,6 +57,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -67,21 +70,19 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
return errors.New("wmiClient or SWbemServicesClient is nil")
}
c.wmiClient = wmiClient
func (c *Collector) Build() error {
c.zoneTransferRequestsReceived = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "zone_transfer_requests_received_total"),
"Number of zone transfer requests (AXFR/IXFR) received by the master DNS server",
@@ -214,22 +215,16 @@ func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
nil,
nil,
)
return nil
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) Collect(_ *types.ScrapeContext, ch chan<- prometheus.Metric) error {
if err := c.collect(ch); err != nil {
logger.Error("failed collecting dns metrics",
slog.Any("err", err),
)
_ = level.Error(c.logger).Log("msg", "failed collecting dns metrics", "err", err)
return err
}
return nil
}
@@ -281,10 +276,10 @@ type Win32_PerfRawData_DNS_DNS struct {
func (c *Collector) collect(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_DNS_DNS
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_DNS_DNS", &dst); err != nil {
q := wmi.QueryAll(&dst, c.logger)
if err := wmi.Query(q, &dst); err != nil {
return err
}
if len(dst) == 0 {
return errors.New("WMI query returned empty result set")
}

View File

@@ -4,15 +4,16 @@ package exchange
import (
"fmt"
"log/slog"
"os"
"slices"
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/perflib"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "exchange"
@@ -38,6 +39,7 @@ var ConfigDefaults = Config{
type Collector struct {
config Config
logger log.Logger
activeMailboxDeliveryQueueLength *prometheus.Desc
activeSyncRequestsPerSec *prometheus.Desc
@@ -77,9 +79,11 @@ type Collector struct {
unreachableQueueLength *prometheus.Desc
userCount *prometheus.Desc
yieldedTasks *prometheus.Desc
enabledCollectors []string
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -92,6 +96,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -102,7 +108,6 @@ func NewWithFlags(app *kingpin.Application) *Collector {
c.config.CollectorsEnabled = make([]string, 0)
var listAllCollectors bool
var collectorsEnabled string
app.Flag(
@@ -158,7 +163,11 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{
"MSExchange ADAccess Processes",
"MSExchangeTransport Queues",
@@ -173,11 +182,11 @@ func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build() error {
// desc creates a new prometheus description
desc := func(metricName string, description string, labels ...string) *prometheus.Desc {
return prometheus.NewDesc(
@@ -227,13 +236,24 @@ func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
c.syncCommandsPerSec = desc("activesync_sync_cmds_total", "Number of sync commands processed per second. Clients use this command to synchronize items within a folder")
c.activeUserCountMapiHttpEmsMDB = desc("mapihttp_emsmdb_active_user_count", "Number of unique outlook users that have shown some kind of activity in the last 2 minutes")
c.enabledCollectors = make([]string, 0, len(ConfigDefaults.CollectorsEnabled))
for _, collectorName := range c.config.CollectorsEnabled {
if !slices.Contains(ConfigDefaults.CollectorsEnabled, collectorName) {
return fmt.Errorf("unknown exchange collector: %s", collectorName)
}
c.enabledCollectors = append(c.enabledCollectors, collectorName)
}
c.enabledCollectors = slices.Clip(c.enabledCollectors)
return nil
}
// Collect collects exchange metrics and sends them to prometheus.
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
collectorFuncs := map[string]func(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error{
func (c *Collector) Collect(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
collectorFuncs := map[string]func(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error{
"ADAccessProcesses": c.collectADAccessProcesses,
"TransportQueues": c.collectTransportQueues,
"HttpProxy": c.collectHTTPProxy,
@@ -246,16 +266,12 @@ func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch ch
"MapiHttpEmsmdb": c.collectMapiHttpEmsmdb,
}
for _, collectorName := range c.config.CollectorsEnabled {
if err := collectorFuncs[collectorName](ctx, logger, ch); err != nil {
logger.Error("Error in "+collectorName,
slog.Any("err", err),
)
for _, collectorName := range c.enabledCollectors {
if err := collectorFuncs[collectorName](ctx, ch); err != nil {
_ = level.Error(c.logger).Log("msg", "Error in "+collectorName, "err", err)
return err
}
}
return nil
}
@@ -270,17 +286,13 @@ type perflibADAccessProcesses struct {
LongRunningLDAPOperationsPerMin float64 `perflib:"Long Running LDAP Operations/min"`
}
func (c *Collector) collectADAccessProcesses(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) collectADAccessProcesses(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
var data []perflibADAccessProcesses
if err := perflib.UnmarshalObject(ctx.PerfObjects["MSExchange ADAccess Processes"], &data, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects["MSExchange ADAccess Processes"], &data, c.logger); err != nil {
return err
}
labelUseCount := make(map[string]int)
for _, proc := range data {
labelName := c.toLabelName(proc.Name)
if strings.HasSuffix(labelName, "_total") {
@@ -324,7 +336,6 @@ func (c *Collector) collectADAccessProcesses(ctx *types.ScrapeContext, logger *s
labelName,
)
}
return nil
}
@@ -333,12 +344,9 @@ type perflibAvailabilityService struct {
RequestsSec float64 `perflib:"Availability Requests (sec)"`
}
func (c *Collector) collectAvailabilityService(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) collectAvailabilityService(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
var data []perflibAvailabilityService
if err := perflib.UnmarshalObject(ctx.PerfObjects["MSExchange Availability Service"], &data, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects["MSExchange Availability Service"], &data, c.logger); err != nil {
return err
}
@@ -349,7 +357,6 @@ func (c *Collector) collectAvailabilityService(ctx *types.ScrapeContext, logger
availservice.RequestsSec,
)
}
return nil
}
@@ -365,12 +372,9 @@ type perflibHTTPProxy struct {
ProxyRequestsPerSec float64 `perflib:"Proxy Requests/Sec"`
}
func (c *Collector) collectHTTPProxy(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) collectHTTPProxy(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
var data []perflibHTTPProxy
if err := perflib.UnmarshalObject(ctx.PerfObjects["MSExchange HttpProxy"], &data, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects["MSExchange HttpProxy"], &data, c.logger); err != nil {
return err
}
@@ -413,7 +417,6 @@ func (c *Collector) collectHTTPProxy(ctx *types.ScrapeContext, logger *slog.Logg
labelName,
)
}
return nil
}
@@ -423,12 +426,9 @@ type perflibOWA struct {
RequestsPerSec float64 `perflib:"Requests/sec"`
}
func (c *Collector) collectOWA(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) collectOWA(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
var data []perflibOWA
if err := perflib.UnmarshalObject(ctx.PerfObjects["MSExchange OWA"], &data, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects["MSExchange OWA"], &data, c.logger); err != nil {
return err
}
@@ -444,7 +444,6 @@ func (c *Collector) collectOWA(ctx *types.ScrapeContext, logger *slog.Logger, ch
owa.RequestsPerSec,
)
}
return nil
}
@@ -455,12 +454,9 @@ type perflibActiveSync struct {
SyncCommandsPerSec float64 `perflib:"Sync Commands/sec"`
}
func (c *Collector) collectActiveSync(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) collectActiveSync(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
var data []perflibActiveSync
if err := perflib.UnmarshalObject(ctx.PerfObjects["MSExchange ActiveSync"], &data, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects["MSExchange ActiveSync"], &data, c.logger); err != nil {
return err
}
@@ -481,7 +477,6 @@ func (c *Collector) collectActiveSync(ctx *types.ScrapeContext, logger *slog.Log
instance.SyncCommandsPerSec,
)
}
return nil
}
@@ -495,12 +490,9 @@ type perflibRPCClientAccess struct {
UserCount float64 `perflib:"User Count"`
}
func (c *Collector) collectRPC(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) collectRPC(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
var data []perflibRPCClientAccess
if err := perflib.UnmarshalObject(ctx.PerfObjects["MSExchange RpcClientAccess"], &data, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects["MSExchange RpcClientAccess"], &data, c.logger); err != nil {
return err
}
@@ -554,12 +546,9 @@ type perflibTransportQueues struct {
PoisonQueueLength float64 `perflib:"Poison Queue Length"`
}
func (c *Collector) collectTransportQueues(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) collectTransportQueues(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
var data []perflibTransportQueues
if err := perflib.UnmarshalObject(ctx.PerfObjects["MSExchangeTransport Queues"], &data, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects["MSExchangeTransport Queues"], &data, c.logger); err != nil {
return err
}
@@ -617,7 +606,6 @@ func (c *Collector) collectTransportQueues(ctx *types.ScrapeContext, logger *slo
labelName,
)
}
return nil
}
@@ -632,12 +620,9 @@ type perflibWorkloadManagementWorkloads struct {
IsActive float64 `perflib:"Active"`
}
func (c *Collector) collectWorkloadManagementWorkloads(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) collectWorkloadManagementWorkloads(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
var data []perflibWorkloadManagementWorkloads
if err := perflib.UnmarshalObject(ctx.PerfObjects["MSExchange WorkloadManagement Workloads"], &data, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects["MSExchange WorkloadManagement Workloads"], &data, c.logger); err != nil {
return err
}
@@ -686,15 +671,11 @@ type perflibAutodiscover struct {
RequestsPerSec float64 `perflib:"Requests/sec"`
}
func (c *Collector) collectAutoDiscover(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) collectAutoDiscover(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
var data []perflibAutodiscover
if err := perflib.UnmarshalObject(ctx.PerfObjects["MSExchangeAutodiscover"], &data, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects["MSExchangeAutodiscover"], &data, c.logger); err != nil {
return err
}
for _, autodisc := range data {
ch <- prometheus.MustNewConstMetric(
c.autoDiscoverRequestsPerSec,
@@ -702,7 +683,6 @@ func (c *Collector) collectAutoDiscover(ctx *types.ScrapeContext, logger *slog.L
autodisc.RequestsPerSec,
)
}
return nil
}
@@ -711,12 +691,9 @@ type perflibMapiHttpEmsmdb struct {
ActiveUserCount float64 `perflib:"Active User Count"`
}
func (c *Collector) collectMapiHttpEmsmdb(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) collectMapiHttpEmsmdb(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
var data []perflibMapiHttpEmsmdb
if err := perflib.UnmarshalObject(ctx.PerfObjects["MSExchange MapiHttp Emsmdb"], &data, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects["MSExchange MapiHttp Emsmdb"], &data, c.logger); err != nil {
return err
}
@@ -735,7 +712,6 @@ func (c *Collector) collectMapiHttpEmsmdb(ctx *types.ScrapeContext, logger *slog
func (c *Collector) toLabelName(name string) string {
s := strings.ReplaceAll(strings.Join(strings.Fields(strings.ToLower(name)), "_"), ".", "_")
s = strings.ReplaceAll(s, "__", "_")
return s
}

View File

@@ -3,14 +3,13 @@
package fsrmquota
import (
"errors"
"log/slog"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus-community/windows_exporter/pkg/utils"
"github.com/prometheus-community/windows_exporter/pkg/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "fsrmquota"
@@ -20,8 +19,8 @@ type Config struct{}
var ConfigDefaults = Config{}
type Collector struct {
config Config
wmiClient *wmi.Client
config Config
logger log.Logger
quotasCount *prometheus.Desc
peakUsage *prometheus.Desc
@@ -35,7 +34,7 @@ type Collector struct {
template *prometheus.Desc
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -44,6 +43,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -55,21 +56,19 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
return errors.New("wmiClient or SWbemServicesClient is nil")
}
c.wmiClient = wmiClient
func (c *Collector) Build() error {
c.quotasCount = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "count"),
"Number of Quotas",
@@ -124,22 +123,16 @@ func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
[]string{"path", "template"},
nil,
)
return nil
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) Collect(_ *types.ScrapeContext, ch chan<- prometheus.Metric) error {
if err := c.collect(ch); err != nil {
logger.Error("failed collecting fsrmquota metrics",
slog.Any("err", err),
)
_ = level.Error(c.logger).Log("msg", "failed collecting fsrmquota metrics", "err", err)
return err
}
return nil
}
@@ -162,10 +155,11 @@ type MSFT_FSRMQuota struct {
func (c *Collector) collect(ch chan<- prometheus.Metric) error {
var dst []MSFT_FSRMQuota
q := wmi.QueryAll(&dst, c.logger)
var count int
if err := c.wmiClient.Query("SELECT * FROM MSFT_FSRMQuota", &dst, nil, "root/microsoft/windows/fsrm"); err != nil {
if err := wmi.QueryNamespace(q, &dst, "root/microsoft/windows/fsrm"); err != nil {
return err
}
@@ -230,6 +224,5 @@ func (c *Collector) collect(ch chan<- prometheus.Metric) error {
prometheus.GaugeValue,
float64(count),
)
return nil
}

83
pkg/collector/handler.go Normal file
View File

@@ -0,0 +1,83 @@
//go:build windows
package collector
import (
"fmt"
stdlog "log"
"net/http"
"strconv"
"time"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/collectors"
"github.com/prometheus/client_golang/prometheus/collectors/version"
"github.com/prometheus/client_golang/prometheus/promhttp"
)
func (c *Collectors) BuildServeHTTP(disableExporterMetrics bool, timeoutMargin float64) http.HandlerFunc {
collectorFactory := func(timeout time.Duration, requestedCollectors []string) (error, *Prometheus) {
filteredCollectors := make(map[string]Collector)
// scrape all enabled collectors if no collector is requested
if len(requestedCollectors) == 0 {
filteredCollectors = c.collectors
}
for _, name := range requestedCollectors {
col, exists := c.collectors[name]
if !exists {
return fmt.Errorf("unavailable collector: %s", name), nil
}
filteredCollectors[name] = col
}
filtered := Collectors{
logger: c.logger,
collectors: filteredCollectors,
perfCounterQuery: c.perfCounterQuery,
}
return nil, NewPrometheus(timeout, &filtered, c.logger)
}
return func(w http.ResponseWriter, r *http.Request) {
const defaultTimeout = 10.0
var timeoutSeconds float64
if v := r.Header.Get("X-Prometheus-Scrape-Timeout-Seconds"); v != "" {
var err error
timeoutSeconds, err = strconv.ParseFloat(v, 64)
if err != nil {
_ = level.Warn(c.logger).Log("msg", fmt.Sprintf("Couldn't parse X-Prometheus-Scrape-Timeout-Seconds: %q. Defaulting timeout to %f", v, defaultTimeout))
}
}
if timeoutSeconds == 0 {
timeoutSeconds = defaultTimeout
}
timeoutSeconds -= timeoutMargin
reg := prometheus.NewRegistry()
err, wc := collectorFactory(time.Duration(timeoutSeconds*float64(time.Second)), r.URL.Query()["collect[]"])
if err != nil {
_ = level.Warn(c.logger).Log("msg", "Couldn't create filtered metrics handler", "err", err)
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(fmt.Sprintf("Couldn't create filtered metrics handler: %s", err))) //nolint:errcheck
return
}
reg.MustRegister(wc)
if !disableExporterMetrics {
reg.MustRegister(
collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}),
collectors.NewGoCollector(),
version.NewCollector("windows_exporter"),
)
}
h := promhttp.HandlerFor(reg, promhttp.HandlerOpts{
ErrorLog: stdlog.New(log.NewStdlibAdapter(level.Error(c.logger)), "", stdlog.Lshortfile),
})
h.ServeHTTP(w, r)
}
}

View File

@@ -3,15 +3,15 @@
package hyperv
import (
"errors"
"fmt"
"log/slog"
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus-community/windows_exporter/pkg/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "hyperv"
@@ -22,8 +22,8 @@ var ConfigDefaults = Config{}
// Collector is a Prometheus Collector for hyper-v.
type Collector struct {
config Config
wmiClient *wmi.Client
config Config
logger log.Logger
// Win32_PerfRawData_VmmsVirtualMachineStats_HyperVVirtualMachineHealthSummary
healthCritical *prometheus.Desc
@@ -140,7 +140,7 @@ type Collector struct {
vmMemoryRemovedMemory *prometheus.Desc
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -149,6 +149,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -160,21 +162,19 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
return errors.New("wmiClient or SWbemServicesClient is nil")
}
c.wmiClient = wmiClient
func (c *Collector) Build() error {
buildSubsystemName := func(component string) string { return "hyperv_" + component }
c.healthCritical = prometheus.NewDesc(
@@ -749,107 +749,69 @@ func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
[]string{"vm"},
nil,
)
return nil
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) Collect(_ *types.ScrapeContext, ch chan<- prometheus.Metric) error {
if err := c.collectVmHealth(ch); err != nil {
logger.Error("failed collecting hyperV health status metrics",
slog.Any("err", err),
)
_ = level.Error(c.logger).Log("msg", "failed collecting hyperV health status metrics", "err", err)
return err
}
if err := c.collectVmVid(ch); err != nil {
logger.Error("failed collecting hyperV pages metrics",
slog.Any("err", err),
)
_ = level.Error(c.logger).Log("msg", "failed collecting hyperV pages metrics", "err", err)
return err
}
if err := c.collectVmHv(ch); err != nil {
logger.Error("failed collecting hyperV hv status metrics",
slog.Any("err", err),
)
_ = level.Error(c.logger).Log("msg", "failed collecting hyperV hv status metrics", "err", err)
return err
}
if err := c.collectVmProcessor(ch); err != nil {
logger.Error("failed collecting hyperV processor metrics",
slog.Any("err", err),
)
_ = level.Error(c.logger).Log("msg", "failed collecting hyperV processor metrics", "err", err)
return err
}
if err := c.collectHostLPUsage(logger, ch); err != nil {
logger.Error("failed collecting hyperV host logical processors metrics",
slog.Any("err", err),
)
if err := c.collectHostLPUsage(ch); err != nil {
_ = level.Error(c.logger).Log("msg", "failed collecting hyperV host logical processors metrics", "err", err)
return err
}
if err := c.collectHostCpuUsage(logger, ch); err != nil {
logger.Error("failed collecting hyperV host CPU metrics",
slog.Any("err", err),
)
if err := c.collectHostCpuUsage(ch); err != nil {
_ = level.Error(c.logger).Log("msg", "failed collecting hyperV host CPU metrics", "err", err)
return err
}
if err := c.collectVmCpuUsage(logger, ch); err != nil {
logger.Error("failed collecting hyperV VM CPU metrics",
slog.Any("err", err),
)
if err := c.collectVmCpuUsage(ch); err != nil {
_ = level.Error(c.logger).Log("msg", "failed collecting hyperV VM CPU metrics", "err", err)
return err
}
if err := c.collectVmSwitch(ch); err != nil {
logger.Error("failed collecting hyperV switch metrics",
slog.Any("err", err),
)
_ = level.Error(c.logger).Log("msg", "failed collecting hyperV switch metrics", "err", err)
return err
}
if err := c.collectVmEthernet(ch); err != nil {
logger.Error("failed collecting hyperV ethernet metrics",
slog.Any("err", err),
)
_ = level.Error(c.logger).Log("msg", "failed collecting hyperV ethernet metrics", "err", err)
return err
}
if err := c.collectVmStorage(ch); err != nil {
logger.Error("failed collecting hyperV virtual storage metrics",
slog.Any("err", err),
)
_ = level.Error(c.logger).Log("msg", "failed collecting hyperV virtual storage metrics", "err", err)
return err
}
if err := c.collectVmNetwork(ch); err != nil {
logger.Error("failed collecting hyperV virtual network metrics",
slog.Any("err", err),
)
_ = level.Error(c.logger).Log("msg", "failed collecting hyperV virtual network metrics", "err", err)
return err
}
if err := c.collectVmMemory(ch); err != nil {
logger.Error("failed collecting hyperV virtual memory metrics",
slog.Any("err", err),
)
_ = level.Error(c.logger).Log("msg", "failed collecting hyperV virtual memory metrics", "err", err)
return err
}
@@ -864,7 +826,8 @@ type Win32_PerfRawData_VmmsVirtualMachineStats_HyperVVirtualMachineHealthSummary
func (c *Collector) collectVmHealth(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_VmmsVirtualMachineStats_HyperVVirtualMachineHealthSummary
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_VmmsVirtualMachineStats_HyperVVirtualMachineHealthSummary", &dst); err != nil {
q := wmi.QueryAll(&dst, c.logger)
if err := wmi.Query(q, &dst); err != nil {
return err
}
@@ -895,7 +858,8 @@ type Win32_PerfRawData_VidPerfProvider_HyperVVMVidPartition struct {
func (c *Collector) collectVmVid(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_VidPerfProvider_HyperVVMVidPartition
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_VidPerfProvider_HyperVVMVidPartition", &dst); err != nil {
q := wmi.QueryAll(&dst, c.logger)
if err := wmi.Query(q, &dst); err != nil {
return err
}
@@ -957,7 +921,8 @@ type Win32_PerfRawData_HvStats_HyperVHypervisorRootPartition struct {
func (c *Collector) collectVmHv(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_HvStats_HyperVHypervisorRootPartition
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_HvStats_HyperVHypervisorRootPartition", &dst); err != nil {
q := wmi.QueryAll(&dst, c.logger)
if err := wmi.Query(q, &dst); err != nil {
return err
}
@@ -1093,7 +1058,8 @@ type Win32_PerfRawData_HvStats_HyperVHypervisor struct {
func (c *Collector) collectVmProcessor(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_HvStats_HyperVHypervisor
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_HvStats_HyperVHypervisor", &dst); err != nil {
q := wmi.QueryAll(&dst, c.logger)
if err := wmi.Query(q, &dst); err != nil {
return err
}
@@ -1122,9 +1088,10 @@ type Win32_PerfRawData_HvStats_HyperVHypervisorLogicalProcessor struct {
PercentTotalRunTime uint
}
func (c *Collector) collectHostLPUsage(logger *slog.Logger, ch chan<- prometheus.Metric) error {
func (c *Collector) collectHostLPUsage(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_HvStats_HyperVHypervisorLogicalProcessor
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_HvStats_HyperVHypervisorLogicalProcessor", &dst); err != nil {
q := wmi.QueryAll(&dst, c.logger)
if err := wmi.Query(q, &dst); err != nil {
return err
}
@@ -1132,15 +1099,12 @@ func (c *Collector) collectHostLPUsage(logger *slog.Logger, ch chan<- prometheus
if strings.Contains(obj.Name, "_Total") {
continue
}
// The name format is Hv LP <core id>
parts := strings.Split(obj.Name, " ")
if len(parts) != 3 {
logger.Warn(fmt.Sprintf("Unexpected format of Name in collectHostLPUsage: %q", obj.Name))
_ = level.Warn(c.logger).Log("msg", fmt.Sprintf("Unexpected format of Name in collectHostLPUsage: %q", obj.Name))
continue
}
coreId := parts[2]
ch <- prometheus.MustNewConstMetric(
@@ -1178,9 +1142,10 @@ type Win32_PerfRawData_HvStats_HyperVHypervisorRootVirtualProcessor struct {
CPUWaitTimePerDispatch uint64
}
func (c *Collector) collectHostCpuUsage(logger *slog.Logger, ch chan<- prometheus.Metric) error {
func (c *Collector) collectHostCpuUsage(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_HvStats_HyperVHypervisorRootVirtualProcessor
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_HvStats_HyperVHypervisorRootVirtualProcessor", &dst); err != nil {
q := wmi.QueryAll(&dst, c.logger)
if err := wmi.Query(q, &dst); err != nil {
return err
}
@@ -1188,15 +1153,12 @@ func (c *Collector) collectHostCpuUsage(logger *slog.Logger, ch chan<- prometheu
if strings.Contains(obj.Name, "_Total") {
continue
}
// The name format is Root VP <core id>
parts := strings.Split(obj.Name, " ")
if len(parts) != 3 {
logger.Warn("Unexpected format of Name in collectHostCpuUsage: " + obj.Name)
_ = level.Warn(c.logger).Log("msg", "Unexpected format of Name in collectHostCpuUsage: "+obj.Name)
continue
}
coreId := parts[2]
ch <- prometheus.MustNewConstMetric(
@@ -1248,9 +1210,10 @@ type Win32_PerfRawData_HvStats_HyperVHypervisorVirtualProcessor struct {
CPUWaitTimePerDispatch uint64
}
func (c *Collector) collectVmCpuUsage(logger *slog.Logger, ch chan<- prometheus.Metric) error {
func (c *Collector) collectVmCpuUsage(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_HvStats_HyperVHypervisorVirtualProcessor
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_HvStats_HyperVHypervisorVirtualProcessor", &dst); err != nil {
q := wmi.QueryAll(&dst, c.logger)
if err := wmi.Query(q, &dst); err != nil {
return err
}
@@ -1258,22 +1221,17 @@ func (c *Collector) collectVmCpuUsage(logger *slog.Logger, ch chan<- prometheus.
if strings.Contains(obj.Name, "_Total") {
continue
}
// The name format is <VM Name>:Hv VP <vcore id>
parts := strings.Split(obj.Name, ":")
if len(parts) != 2 {
logger.Warn(fmt.Sprintf("Unexpected format of Name in collectVmCpuUsage: %q, expected %q. Skipping.", obj.Name, "<VM Name>:Hv VP <vcore id>"))
_ = level.Warn(c.logger).Log("msg", fmt.Sprintf("Unexpected format of Name in collectVmCpuUsage: %q, expected %q. Skipping.", obj.Name, "<VM Name>:Hv VP <vcore id>"))
continue
}
coreParts := strings.Split(parts[1], " ")
if len(coreParts) != 3 {
logger.Warn(fmt.Sprintf("Unexpected format of core identifier in collectVmCpuUsage: %q, expected %q. Skipping.", parts[1], "Hv VP <vcore id>"))
_ = level.Warn(c.logger).Log("msg", fmt.Sprintf("Unexpected format of core identifier in collectVmCpuUsage: %q, expected %q. Skipping.", parts[1], "Hv VP <vcore id>"))
continue
}
vmName := parts[0]
coreId := coreParts[2]
@@ -1347,7 +1305,8 @@ type Win32_PerfRawData_NvspSwitchStats_HyperVVirtualSwitch struct {
func (c *Collector) collectVmSwitch(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_NvspSwitchStats_HyperVVirtualSwitch
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_NvspSwitchStats_HyperVVirtualSwitch", &dst); err != nil {
q := wmi.QueryAll(&dst, c.logger)
if err := wmi.Query(q, &dst); err != nil {
return err
}
@@ -1511,7 +1470,8 @@ type Win32_PerfRawData_EthernetPerfProvider_HyperVLegacyNetworkAdapter struct {
func (c *Collector) collectVmEthernet(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_EthernetPerfProvider_HyperVLegacyNetworkAdapter
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_EthernetPerfProvider_HyperVLegacyNetworkAdapter", &dst); err != nil {
q := wmi.QueryAll(&dst, c.logger)
if err := wmi.Query(q, &dst); err != nil {
return err
}
@@ -1579,7 +1539,8 @@ type Win32_PerfRawData_Counters_HyperVVirtualStorageDevice struct {
func (c *Collector) collectVmStorage(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_Counters_HyperVVirtualStorageDevice
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_Counters_HyperVVirtualStorageDevice", &dst); err != nil {
q := wmi.QueryAll(&dst, c.logger)
if err := wmi.Query(q, &dst); err != nil {
return err
}
@@ -1647,7 +1608,8 @@ type Win32_PerfRawData_NvspNicStats_HyperVVirtualNetworkAdapter struct {
func (c *Collector) collectVmNetwork(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_NvspNicStats_HyperVVirtualNetworkAdapter
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_NvspNicStats_HyperVVirtualNetworkAdapter", &dst); err != nil {
q := wmi.QueryAll(&dst, c.logger)
if err := wmi.Query(q, &dst); err != nil {
return err
}
@@ -1719,7 +1681,8 @@ type Win32_PerfRawData_BalancerStats_HyperVDynamicMemoryVM struct {
func (c *Collector) collectVmMemory(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_BalancerStats_HyperVDynamicMemoryVM
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_BalancerStats_HyperVDynamicMemoryVM", &dst); err != nil {
q := wmi.QueryAll(&dst, c.logger)
if err := wmi.Query(q, &dst); err != nil {
return err
}

View File

@@ -4,16 +4,16 @@ package iis
import (
"fmt"
"log/slog"
"regexp"
"sort"
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/perflib"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
"golang.org/x/sys/windows/registry"
)
@@ -35,6 +35,7 @@ var ConfigDefaults = Config{
type Collector struct {
config Config
logger log.Logger
info *prometheus.Desc
@@ -161,7 +162,7 @@ type Collector struct {
iisVersion simpleVersion
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -186,6 +187,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -249,7 +252,11 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{
"Web Service",
"APP_POOL_WAS",
@@ -258,14 +265,12 @@ func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error {
logger = logger.With(slog.String("collector", Name))
c.iisVersion = getIISVersion(logger)
func (c *Collector) Build() error {
c.iisVersion = getIISVersion(c.logger)
c.info = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "info"),
@@ -893,44 +898,31 @@ type simpleVersion struct {
minor uint64
}
func getIISVersion(logger *slog.Logger) simpleVersion {
func getIISVersion(logger log.Logger) simpleVersion {
k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\InetStp\`, registry.QUERY_VALUE)
if err != nil {
logger.Warn("Couldn't open registry to determine IIS version",
slog.Any("err", err),
)
_ = level.Warn(logger).Log("msg", "Couldn't open registry to determine IIS version", "err", err)
return simpleVersion{}
}
defer func() {
err = k.Close()
if err != nil {
logger.Warn("Failed to close registry key",
slog.Any("err", err),
)
_ = level.Warn(logger).Log("msg", "Failed to close registry key", "err", err)
}
}()
major, _, err := k.GetIntegerValue("MajorVersion")
if err != nil {
logger.Warn("Couldn't open registry to determine IIS version",
slog.Any("err", err),
)
_ = level.Warn(logger).Log("msg", "Couldn't open registry to determine IIS version", "err", err)
return simpleVersion{}
}
minor, _, err := k.GetIntegerValue("MinorVersion")
if err != nil {
logger.Warn("Couldn't open registry to determine IIS version",
slog.Any("err", err),
)
_ = level.Warn(logger).Log("msg", "Couldn't open registry to determine IIS version", "err", err)
return simpleVersion{}
}
logger.Debug(fmt.Sprintf("Detected IIS %d.%d\n", major, minor))
_ = level.Debug(logger).Log("msg", fmt.Sprintf("Detected IIS %d.%d\n", major, minor))
return simpleVersion{
major: major,
@@ -940,37 +932,24 @@ func getIISVersion(logger *slog.Logger) simpleVersion {
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
if err := c.collectWebService(ctx, logger, ch); err != nil {
logger.Error("failed collecting iis metrics",
slog.Any("err", err),
)
func (c *Collector) Collect(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
if err := c.collectWebService(ctx, ch); err != nil {
_ = level.Error(c.logger).Log("msg", "failed collecting iis metrics", "err", err)
return err
}
if err := c.collectAPP_POOL_WAS(ctx, logger, ch); err != nil {
logger.Error("failed collecting iis metrics",
slog.Any("err", err),
)
if err := c.collectAPP_POOL_WAS(ctx, ch); err != nil {
_ = level.Error(c.logger).Log("msg", "failed collecting iis metrics", "err", err)
return err
}
if err := c.collectW3SVC_W3WP(ctx, logger, ch); err != nil {
logger.Error("failed collecting iis metrics",
slog.Any("err", err),
)
if err := c.collectW3SVC_W3WP(ctx, ch); err != nil {
_ = level.Error(c.logger).Log("msg", "failed collecting iis metrics", "err", err)
return err
}
if err := c.collectWebServiceCache(ctx, logger, ch); err != nil {
logger.Error("failed collecting iis metrics",
slog.Any("err", err),
)
if err := c.collectWebServiceCache(ctx, ch); err != nil {
_ = level.Error(c.logger).Log("msg", "failed collecting iis metrics", "err", err)
return err
}
@@ -1064,16 +1043,12 @@ func dedupIISNames[V hasGetIISName](services []V) map[string]V {
name := strings.Split(entry.getIISName(), "#")[0]
webServiceDeDuplicated[name] = entry
}
return webServiceDeDuplicated
}
func (c *Collector) collectWebService(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) collectWebService(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
var webService []perflibWebService
if err := perflib.UnmarshalObject(ctx.PerfObjects["Web Service"], &webService, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects["Web Service"], &webService, c.logger); err != nil {
return err
}
@@ -1363,12 +1338,9 @@ var applicationStates = map[uint32]string{
7: "Delete Pending",
}
func (c *Collector) collectAPP_POOL_WAS(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) collectAPP_POOL_WAS(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
var APP_POOL_WAS []perflibAPP_POOL_WAS
if err := perflib.UnmarshalObject(ctx.PerfObjects["APP_POOL_WAS"], &APP_POOL_WAS, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects["APP_POOL_WAS"], &APP_POOL_WAS, c.logger); err != nil {
return err
}
@@ -1543,12 +1515,9 @@ type perflibW3SVC_W3WP_IIS8 struct {
WebSocketConnectionsRejected float64 `perflib:"WebSocket Connections Rejected / Sec"`
}
func (c *Collector) collectW3SVC_W3WP(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) collectW3SVC_W3WP(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
var W3SVC_W3WP []perflibW3SVC_W3WP
if err := perflib.UnmarshalObject(ctx.PerfObjects["W3SVC_W3WP"], &W3SVC_W3WP, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects["W3SVC_W3WP"], &W3SVC_W3WP, c.logger); err != nil {
return err
}
@@ -1557,7 +1526,6 @@ func (c *Collector) collectW3SVC_W3WP(ctx *types.ScrapeContext, logger *slog.Log
for w3Name, app := range w3svcW3WPDeduplicated {
// Extract the apppool name from the format <PID>_<NAME>
pid := workerProcessNameExtractor.ReplaceAllString(w3Name, "$1")
name := workerProcessNameExtractor.ReplaceAllString(w3Name, "$2")
if name == "" || name == "_Total" ||
c.config.AppExclude.MatchString(name) ||
@@ -1807,7 +1775,7 @@ func (c *Collector) collectW3SVC_W3WP(ctx *types.ScrapeContext, logger *slog.Log
if c.iisVersion.major >= 8 {
var W3SVC_W3WP_IIS8 []perflibW3SVC_W3WP_IIS8
if err := perflib.UnmarshalObject(ctx.PerfObjects["W3SVC_W3WP"], &W3SVC_W3WP_IIS8, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects["W3SVC_W3WP"], &W3SVC_W3WP_IIS8, c.logger); err != nil {
return err
}
@@ -1816,7 +1784,6 @@ func (c *Collector) collectW3SVC_W3WP(ctx *types.ScrapeContext, logger *slog.Log
for w3Name, app := range w3svcW3WPIIS8Deduplicated {
// Extract the apppool name from the format <PID>_<NAME>
pid := workerProcessNameExtractor.ReplaceAllString(w3Name, "$1")
name := workerProcessNameExtractor.ReplaceAllString(w3Name, "$2")
if name == "" || name == "_Total" ||
c.config.AppExclude.MatchString(name) ||
@@ -1945,12 +1912,9 @@ type perflibWebServiceCache struct {
ServiceCache_OutputCacheQueriesTotal float64
}
func (c *Collector) collectWebServiceCache(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) collectWebServiceCache(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
var WebServiceCache []perflibWebServiceCache
if err := perflib.UnmarshalObject(ctx.PerfObjects["Web Service Cache"], &WebServiceCache, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects["Web Service Cache"], &WebServiceCache, c.logger); err != nil {
return err
}

View File

@@ -3,13 +3,12 @@
package license
import (
"log/slog"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/headers/slc"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "license"
@@ -29,11 +28,12 @@ var ConfigDefaults = Config{}
// A Collector is a Prometheus Collector for WMI Win32_PerfRawData_DNS_DNS metrics.
type Collector struct {
config Config
logger log.Logger
licenseStatus *prometheus.Desc
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -42,6 +42,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -53,15 +55,19 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build() error {
c.licenseStatus = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "status"),
"Status of windows license",
@@ -74,16 +80,11 @@ func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) Collect(_ *types.ScrapeContext, ch chan<- prometheus.Metric) error {
if err := c.collect(ch); err != nil {
logger.Error("failed collecting license metrics",
slog.Any("err", err),
)
_ = level.Error(c.logger).Log("msg", "failed collecting license metrics", "err", err)
return err
}
return nil
}

View File

@@ -5,17 +5,17 @@ package logical_disk
import (
"encoding/binary"
"fmt"
"log/slog"
"regexp"
"slices"
"strconv"
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/perflib"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
"golang.org/x/sys/windows"
)
@@ -34,6 +34,7 @@ var ConfigDefaults = Config{
// A Collector is a Prometheus Collector for perflib logicalDisk metrics.
type Collector struct {
config Config
logger log.Logger
avgReadQueue *prometheus.Desc
avgWriteQueue *prometheus.Desc
@@ -63,7 +64,7 @@ type volumeInfo struct {
readonly float64
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -80,6 +81,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -123,15 +126,19 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{"LogicalDisk"}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build() error {
c.information = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "info"),
"A metric with a constant '1' value labeled with logical disk information",
@@ -261,16 +268,11 @@ func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
if err := c.collect(ctx, logger, ch); err != nil {
logger.Error("failed collecting logical_disk metrics",
slog.Any("err", err),
)
func (c *Collector) Collect(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
if err := c.collect(ctx, ch); err != nil {
_ = level.Error(c.logger).Log("msg", "failed collecting logical_disk metrics", "err", err)
return err
}
return nil
}
@@ -297,9 +299,7 @@ type logicalDisk struct {
AvgDiskSecPerTransfer float64 `perflib:"Avg. Disk sec/Transfer"`
}
func (c *Collector) collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) collect(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
var (
err error
diskID string
@@ -307,7 +307,7 @@ func (c *Collector) collect(ctx *types.ScrapeContext, logger *slog.Logger, ch ch
dst []logicalDisk
)
if err = perflib.UnmarshalObject(ctx.PerfObjects["LogicalDisk"], &dst, logger); err != nil {
if err = perflib.UnmarshalObject(ctx.PerfObjects["LogicalDisk"], &dst, c.logger); err != nil {
return err
}
@@ -320,16 +320,12 @@ func (c *Collector) collect(ctx *types.ScrapeContext, logger *slog.Logger, ch ch
diskID, err = getDiskIDByVolume(volume.Name)
if err != nil {
logger.Warn("failed to get disk ID for "+volume.Name,
slog.Any("err", err),
)
_ = level.Warn(c.logger).Log("msg", "failed to get disk ID for "+volume.Name, "err", err)
}
info, err = getVolumeInfo(volume.Name)
if err != nil {
logger.Warn("failed to get volume information for %s"+volume.Name,
slog.Any("err", err),
)
_ = level.Warn(c.logger).Log("msg", "failed to get volume information for %s"+volume.Name, "err", err)
}
ch <- prometheus.MustNewConstMetric(
@@ -488,13 +484,11 @@ const diskExtentSize = 24
func getDiskIDByVolume(rootDrive string) (string, error) {
// Open a volume handle to the Disk Root.
var err error
var f windows.Handle
// mode has to include FILE_SHARE permission to allow concurrent access to the disk.
// use 0 as access mode to avoid admin permission.
mode := uint32(windows.FILE_SHARE_READ | windows.FILE_SHARE_WRITE | windows.FILE_SHARE_DELETE)
f, err = windows.CreateFile(
windows.StringToUTF16Ptr(`\\.\`+rootDrive),
0, mode, nil, windows.OPEN_EXISTING, uint32(windows.FILE_ATTRIBUTE_READONLY), 0)
@@ -508,7 +502,6 @@ func getDiskIDByVolume(rootDrive string) (string, error) {
volumeDiskExtents := make([]byte, 16*1024)
var bytesReturned uint32
err = windows.DeviceIoControl(f, controlCode, nil, 0, &volumeDiskExtents[0], uint32(len(volumeDiskExtents)), &bytesReturned, nil)
if err != nil {
return "", fmt.Errorf("could not identify physical drive for %s: %w", rootDrive, err)

View File

@@ -4,12 +4,13 @@ package logon
import (
"errors"
"log/slog"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus-community/windows_exporter/pkg/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "logon"
@@ -20,13 +21,13 @@ var ConfigDefaults = Config{}
// A Collector is a Prometheus Collector for WMI metrics.
type Collector struct {
config Config
wmiClient *wmi.Client
config Config
logger log.Logger
logonType *prometheus.Desc
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -35,6 +36,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -46,42 +49,35 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
return errors.New("wmiClient or SWbemServicesClient is nil")
}
c.wmiClient = wmiClient
func (c *Collector) Build() error {
c.logonType = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "logon_type"),
"Number of active logon sessions (LogonSession.LogonType)",
[]string{"status"},
nil,
)
return nil
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) Collect(_ *types.ScrapeContext, ch chan<- prometheus.Metric) error {
if err := c.collect(ch); err != nil {
logger.Error("failed collecting user metrics",
slog.Any("err", err),
)
_ = level.Error(c.logger).Log("msg", "failed collecting user metrics", "err", err)
return err
}
return nil
}
@@ -93,10 +89,10 @@ type Win32_LogonSession struct {
func (c *Collector) collect(ch chan<- prometheus.Metric) error {
var dst []Win32_LogonSession
if err := c.wmiClient.Query("SELECT * FROM Win32_LogonSession", &dst); err != nil {
q := wmi.QueryAll(&dst, c.logger)
if err := wmi.Query(q, &dst); err != nil {
return err
}
if len(dst) == 0 {
return errors.New("WMI query returned empty result set")
}
@@ -237,6 +233,5 @@ func (c *Collector) collect(ch chan<- prometheus.Metric) error {
float64(cachedunlock),
"cached_unlock",
)
return nil
}

View File

@@ -1,10 +1,6 @@
package collector
import (
"maps"
"slices"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/pkg/collector/ad"
"github.com/prometheus-community/windows_exporter/pkg/collector/adcs"
"github.com/prometheus-community/windows_exporter/pkg/collector/adfs"
@@ -25,7 +21,11 @@ import (
"github.com/prometheus-community/windows_exporter/pkg/collector/logical_disk"
"github.com/prometheus-community/windows_exporter/pkg/collector/logon"
"github.com/prometheus-community/windows_exporter/pkg/collector/memory"
"github.com/prometheus-community/windows_exporter/pkg/collector/mscluster"
"github.com/prometheus-community/windows_exporter/pkg/collector/mscluster_cluster"
"github.com/prometheus-community/windows_exporter/pkg/collector/mscluster_network"
"github.com/prometheus-community/windows_exporter/pkg/collector/mscluster_node"
"github.com/prometheus-community/windows_exporter/pkg/collector/mscluster_resource"
"github.com/prometheus-community/windows_exporter/pkg/collector/mscluster_resourcegroup"
"github.com/prometheus-community/windows_exporter/pkg/collector/msmq"
"github.com/prometheus-community/windows_exporter/pkg/collector/mssql"
"github.com/prometheus-community/windows_exporter/pkg/collector/net"
@@ -39,7 +39,6 @@ import (
"github.com/prometheus-community/windows_exporter/pkg/collector/netframework_clrsecurity"
"github.com/prometheus-community/windows_exporter/pkg/collector/nps"
"github.com/prometheus-community/windows_exporter/pkg/collector/os"
"github.com/prometheus-community/windows_exporter/pkg/collector/perfdata"
"github.com/prometheus-community/windows_exporter/pkg/collector/physical_disk"
"github.com/prometheus-community/windows_exporter/pkg/collector/printer"
"github.com/prometheus-community/windows_exporter/pkg/collector/process"
@@ -58,14 +57,9 @@ import (
"github.com/prometheus-community/windows_exporter/pkg/collector/time"
"github.com/prometheus-community/windows_exporter/pkg/collector/vmware"
"github.com/prometheus-community/windows_exporter/pkg/collector/vmware_blast"
"golang.org/x/exp/maps"
)
func NewBuilderWithFlags[C Collector](fn BuilderWithFlags[C]) BuilderWithFlags[Collector] {
return func(app *kingpin.Application) Collector {
return fn(app)
}
}
var BuildersWithFlags = map[string]BuilderWithFlags[Collector]{
ad.Name: NewBuilderWithFlags(ad.NewWithFlags),
adcs.Name: NewBuilderWithFlags(adcs.NewWithFlags),
@@ -87,7 +81,11 @@ var BuildersWithFlags = map[string]BuilderWithFlags[Collector]{
logical_disk.Name: NewBuilderWithFlags(logical_disk.NewWithFlags),
logon.Name: NewBuilderWithFlags(logon.NewWithFlags),
memory.Name: NewBuilderWithFlags(memory.NewWithFlags),
mscluster.Name: NewBuilderWithFlags(mscluster.NewWithFlags),
mscluster_cluster.Name: NewBuilderWithFlags(mscluster_cluster.NewWithFlags),
mscluster_network.Name: NewBuilderWithFlags(mscluster_network.NewWithFlags),
mscluster_node.Name: NewBuilderWithFlags(mscluster_node.NewWithFlags),
mscluster_resource.Name: NewBuilderWithFlags(mscluster_resource.NewWithFlags),
mscluster_resourcegroup.Name: NewBuilderWithFlags(mscluster_resourcegroup.NewWithFlags),
msmq.Name: NewBuilderWithFlags(msmq.NewWithFlags),
mssql.Name: NewBuilderWithFlags(mssql.NewWithFlags),
net.Name: NewBuilderWithFlags(net.NewWithFlags),
@@ -101,7 +99,6 @@ var BuildersWithFlags = map[string]BuilderWithFlags[Collector]{
netframework_clrsecurity.Name: NewBuilderWithFlags(netframework_clrsecurity.NewWithFlags),
nps.Name: NewBuilderWithFlags(nps.NewWithFlags),
os.Name: NewBuilderWithFlags(os.NewWithFlags),
perfdata.Name: NewBuilderWithFlags(perfdata.NewWithFlags),
physical_disk.Name: NewBuilderWithFlags(physical_disk.NewWithFlags),
printer.Name: NewBuilderWithFlags(printer.NewWithFlags),
process.Name: NewBuilderWithFlags(process.NewWithFlags),
@@ -123,5 +120,5 @@ var BuildersWithFlags = map[string]BuilderWithFlags[Collector]{
}
func Available() []string {
return slices.Sorted(maps.Keys(BuildersWithFlags))
return maps.Keys(BuildersWithFlags)
}

View File

@@ -6,16 +6,12 @@
package memory
import (
"errors"
"fmt"
"log/slog"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/pkg/headers/sysinfoapi"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/perflib"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "memory"
@@ -27,8 +23,8 @@ var ConfigDefaults = Config{}
// A Collector is a Prometheus Collector for perflib Memory metrics.
type Collector struct {
config Config
logger log.Logger
// Performance metrics
availableBytes *prometheus.Desc
cacheBytes *prometheus.Desc
cacheBytesPeak *prometheus.Desc
@@ -61,14 +57,9 @@ type Collector struct {
transitionFaultsTotal *prometheus.Desc
transitionPagesRepurposedTotal *prometheus.Desc
writeCopiesTotal *prometheus.Desc
// Global memory status
processMemoryLimitBytes *prometheus.Desc
physicalMemoryTotalBytes *prometheus.Desc
physicalMemoryFreeBytes *prometheus.Desc
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -77,6 +68,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -88,15 +81,19 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{"Memory"}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build() error {
c.availableBytes = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "available_bytes"),
"The amount of physical memory immediately available for allocation to a process or for system use. It is equal to the sum of memory assigned to"+
@@ -301,78 +298,16 @@ func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
nil,
nil,
)
c.processMemoryLimitBytes = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "process_memory_limit_bytes"),
"The size of the user-mode portion of the virtual address space of the calling process, in bytes. This value depends on the type of process, the type of processor, and the configuration of the operating system.",
nil,
nil,
)
c.physicalMemoryTotalBytes = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "physical_total_bytes"),
"The amount of actual physical memory, in bytes.",
nil,
nil,
)
c.physicalMemoryFreeBytes = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "physical_free_bytes"),
"The amount of physical memory currently available, in bytes. This is the amount of physical memory that can be immediately reused without having to write its contents to disk first. It is the sum of the size of the standby, free, and zero lists.",
nil,
nil,
)
return nil
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
errs := make([]error, 0, 2)
if err := c.collectPerformanceData(ctx, logger, ch); err != nil {
logger.Error("failed collecting memory metrics",
slog.Any("err", err),
)
errs = append(errs, err)
func (c *Collector) Collect(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
if err := c.collect(ctx, ch); err != nil {
_ = level.Error(c.logger).Log("msg", "failed collecting memory metrics", "err", err)
return err
}
if err := c.collectGlobalMemoryStatus(ch); err != nil {
logger.Error("failed collecting memory metrics",
slog.Any("err", err),
)
errs = append(errs, err)
}
return errors.Join(errs...)
}
func (c *Collector) collectGlobalMemoryStatus(ch chan<- prometheus.Metric) error {
memoryStatusEx, err := sysinfoapi.GlobalMemoryStatusEx()
if err != nil {
return fmt.Errorf("failed to get memory status: %w", err)
}
ch <- prometheus.MustNewConstMetric(
c.processMemoryLimitBytes,
prometheus.GaugeValue,
float64(memoryStatusEx.TotalVirtual),
)
ch <- prometheus.MustNewConstMetric(
c.physicalMemoryTotalBytes,
prometheus.GaugeValue,
float64(memoryStatusEx.TotalPhys),
)
ch <- prometheus.MustNewConstMetric(
c.physicalMemoryFreeBytes,
prometheus.GaugeValue,
float64(memoryStatusEx.AvailPhys),
)
return nil
}
@@ -413,12 +348,9 @@ type memory struct {
WriteCopiesPersec float64 `perflib:"Write Copies/sec"`
}
func (c *Collector) collectPerformanceData(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) collect(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
var dst []memory
if err := perflib.UnmarshalObject(ctx.PerfObjects["Memory"], &dst, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects["Memory"], &dst, c.logger); err != nil {
return err
}

View File

@@ -1,306 +0,0 @@
package mscluster
import (
"errors"
"fmt"
"log/slog"
"slices"
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "mscluster"
type Config struct {
CollectorsEnabled []string `yaml:"collectors_enabled"`
}
var ConfigDefaults = Config{
CollectorsEnabled: []string{
"cluster",
"network",
"node",
"resource",
"resourcegroup",
},
}
// A Collector is a Prometheus Collector for WMI MSCluster_Cluster metrics.
type Collector struct {
config Config
wmiClient *wmi.Client
// cluster
clusterAddEvictDelay *prometheus.Desc
clusterAdminAccessPoint *prometheus.Desc
clusterAutoAssignNodeSite *prometheus.Desc
clusterAutoBalancerLevel *prometheus.Desc
clusterAutoBalancerMode *prometheus.Desc
clusterBackupInProgress *prometheus.Desc
clusterBlockCacheSize *prometheus.Desc
clusterClusSvcHangTimeout *prometheus.Desc
clusterClusSvcRegroupOpeningTimeout *prometheus.Desc
clusterClusSvcRegroupPruningTimeout *prometheus.Desc
clusterClusSvcRegroupStageTimeout *prometheus.Desc
clusterClusSvcRegroupTickInMilliseconds *prometheus.Desc
clusterClusterEnforcedAntiAffinity *prometheus.Desc
clusterClusterFunctionalLevel *prometheus.Desc
clusterClusterGroupWaitDelay *prometheus.Desc
clusterClusterLogLevel *prometheus.Desc
clusterClusterLogSize *prometheus.Desc
clusterClusterUpgradeVersion *prometheus.Desc
clusterCrossSiteDelay *prometheus.Desc
clusterCrossSiteThreshold *prometheus.Desc
clusterCrossSubnetDelay *prometheus.Desc
clusterCrossSubnetThreshold *prometheus.Desc
clusterCsvBalancer *prometheus.Desc
clusterDatabaseReadWriteMode *prometheus.Desc
clusterDefaultNetworkRole *prometheus.Desc
clusterDetectedCloudPlatform *prometheus.Desc
clusterDetectManagedEvents *prometheus.Desc
clusterDetectManagedEventsThreshold *prometheus.Desc
clusterDisableGroupPreferredOwnerRandomization *prometheus.Desc
clusterDrainOnShutdown *prometheus.Desc
clusterDynamicQuorumEnabled *prometheus.Desc
clusterEnableSharedVolumes *prometheus.Desc
clusterFixQuorum *prometheus.Desc
clusterGracePeriodEnabled *prometheus.Desc
clusterGracePeriodTimeout *prometheus.Desc
clusterGroupDependencyTimeout *prometheus.Desc
clusterHangRecoveryAction *prometheus.Desc
clusterIgnorePersistentStateOnStartup *prometheus.Desc
clusterLogResourceControls *prometheus.Desc
clusterLowerQuorumPriorityNodeId *prometheus.Desc
clusterMaxNumberOfNodes *prometheus.Desc
clusterMessageBufferLength *prometheus.Desc
clusterMinimumNeverPreemptPriority *prometheus.Desc
clusterMinimumPreemptorPriority *prometheus.Desc
clusterNetftIPSecEnabled *prometheus.Desc
clusterPlacementOptions *prometheus.Desc
clusterPlumbAllCrossSubnetRoutes *prometheus.Desc
clusterPreventQuorum *prometheus.Desc
clusterQuarantineDuration *prometheus.Desc
clusterQuarantineThreshold *prometheus.Desc
clusterQuorumArbitrationTimeMax *prometheus.Desc
clusterQuorumArbitrationTimeMin *prometheus.Desc
clusterQuorumLogFileSize *prometheus.Desc
clusterQuorumTypeValue *prometheus.Desc
clusterRequestReplyTimeout *prometheus.Desc
clusterResiliencyDefaultPeriod *prometheus.Desc
clusterResiliencyLevel *prometheus.Desc
clusterResourceDllDeadlockPeriod *prometheus.Desc
clusterRootMemoryReserved *prometheus.Desc
clusterRouteHistoryLength *prometheus.Desc
clusterS2DBusTypes *prometheus.Desc
clusterS2DCacheDesiredState *prometheus.Desc
clusterS2DCacheFlashReservePercent *prometheus.Desc
clusterS2DCachePageSizeKBytes *prometheus.Desc
clusterS2DEnabled *prometheus.Desc
clusterS2DIOLatencyThreshold *prometheus.Desc
clusterS2DOptimizations *prometheus.Desc
clusterSameSubnetDelay *prometheus.Desc
clusterSameSubnetThreshold *prometheus.Desc
clusterSecurityLevel *prometheus.Desc
clusterSecurityLevelForStorage *prometheus.Desc
clusterSharedVolumeVssWriterOperationTimeout *prometheus.Desc
clusterShutdownTimeoutInMinutes *prometheus.Desc
clusterUseClientAccessNetworksForSharedVolumes *prometheus.Desc
clusterWitnessDatabaseWriteTimeout *prometheus.Desc
clusterWitnessDynamicWeight *prometheus.Desc
clusterWitnessRestartInterval *prometheus.Desc
// network
networkCharacteristics *prometheus.Desc
networkFlags *prometheus.Desc
networkMetric *prometheus.Desc
networkRole *prometheus.Desc
networkState *prometheus.Desc
// node
nodeBuildNumber *prometheus.Desc
nodeCharacteristics *prometheus.Desc
nodeDetectedCloudPlatform *prometheus.Desc
nodeDynamicWeight *prometheus.Desc
nodeFlags *prometheus.Desc
nodeMajorVersion *prometheus.Desc
nodeMinorVersion *prometheus.Desc
nodeNeedsPreventQuorum *prometheus.Desc
nodeNodeDrainStatus *prometheus.Desc
nodeNodeHighestVersion *prometheus.Desc
nodeNodeLowestVersion *prometheus.Desc
nodeNodeWeight *prometheus.Desc
nodeState *prometheus.Desc
nodeStatusInformation *prometheus.Desc
resourceCharacteristics *prometheus.Desc
resourceDeadlockTimeout *prometheus.Desc
resourceEmbeddedFailureAction *prometheus.Desc
resourceFlags *prometheus.Desc
resourceIsAlivePollInterval *prometheus.Desc
resourceLooksAlivePollInterval *prometheus.Desc
resourceMonitorProcessId *prometheus.Desc
resourceOwnerNode *prometheus.Desc
resourcePendingTimeout *prometheus.Desc
resourceResourceClass *prometheus.Desc
resourceRestartAction *prometheus.Desc
resourceRestartDelay *prometheus.Desc
resourceRestartPeriod *prometheus.Desc
resourceRestartThreshold *prometheus.Desc
resourceRetryPeriodOnFailure *prometheus.Desc
resourceState *prometheus.Desc
resourceSubClass *prometheus.Desc
// ResourceGroup
resourceGroupAutoFailbackType *prometheus.Desc
resourceGroupCharacteristics *prometheus.Desc
resourceGroupColdStartSetting *prometheus.Desc
resourceGroupDefaultOwner *prometheus.Desc
resourceGroupFailbackWindowEnd *prometheus.Desc
resourceGroupFailbackWindowStart *prometheus.Desc
resourceGroupFailOverPeriod *prometheus.Desc
resourceGroupFailOverThreshold *prometheus.Desc
resourceGroupFlags *prometheus.Desc
resourceGroupGroupType *prometheus.Desc
resourceGroupOwnerNode *prometheus.Desc
resourceGroupPriority *prometheus.Desc
resourceGroupResiliencyPeriod *prometheus.Desc
resourceGroupState *prometheus.Desc
}
func New(config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
if config.CollectorsEnabled == nil {
config.CollectorsEnabled = ConfigDefaults.CollectorsEnabled
}
c := &Collector{
config: *config,
}
return c
}
func NewWithFlags(app *kingpin.Application) *Collector {
c := &Collector{
config: ConfigDefaults,
}
c.config.CollectorsEnabled = make([]string, 0)
var collectorsEnabled string
app.Flag(
"collectors.mscluster.enabled",
"Comma-separated list of collectors to use.",
).Default(strings.Join(ConfigDefaults.CollectorsEnabled, ",")).StringVar(&collectorsEnabled)
app.Action(func(*kingpin.ParseContext) error {
c.config.CollectorsEnabled = strings.Split(collectorsEnabled, ",")
return nil
})
return c
}
func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
return []string{"Memory"}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
if len(c.config.CollectorsEnabled) == 0 {
return nil
}
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
return errors.New("wmiClient or SWbemServicesClient is nil")
}
c.wmiClient = wmiClient
if slices.Contains(c.config.CollectorsEnabled, "cluster") {
c.buildCluster()
}
if slices.Contains(c.config.CollectorsEnabled, "network") {
c.buildNetwork()
}
if slices.Contains(c.config.CollectorsEnabled, "node") {
c.buildNode()
}
if slices.Contains(c.config.CollectorsEnabled, "resource") {
c.buildResource()
}
if slices.Contains(c.config.CollectorsEnabled, "resourcegroup") {
c.buildResourceGroup()
}
return nil
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) Collect(_ *types.ScrapeContext, _ *slog.Logger, ch chan<- prometheus.Metric) error {
if len(c.config.CollectorsEnabled) == 0 {
return nil
}
var (
err error
errs []error
nodeNames []string
)
if slices.Contains(c.config.CollectorsEnabled, "cluster") {
if err = c.collectCluster(ch); err != nil {
errs = append(errs, fmt.Errorf("failed to collect cluster metrics: %w", err))
}
}
if slices.Contains(c.config.CollectorsEnabled, "network") {
if err = c.collectNetwork(ch); err != nil {
errs = append(errs, fmt.Errorf("failed to collect network metrics: %w", err))
}
}
if slices.Contains(c.config.CollectorsEnabled, "node") {
if nodeNames, err = c.collectNode(ch); err != nil {
errs = append(errs, fmt.Errorf("failed to collect node metrics: %w", err))
}
}
if slices.Contains(c.config.CollectorsEnabled, "resource") {
if err = c.collectResource(ch, nodeNames); err != nil {
errs = append(errs, fmt.Errorf("failed to collect resource metrics: %w", err))
}
}
if slices.Contains(c.config.CollectorsEnabled, "resourcegroup") {
if err = c.collectResourceGroup(ch, nodeNames); err != nil {
errs = append(errs, fmt.Errorf("failed to collect resource group metrics: %w", err))
}
}
if len(errs) > 0 {
return errors.Join(errs...)
}
return nil
}

View File

@@ -1,102 +0,0 @@
package mscluster
import (
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus/client_golang/prometheus"
)
const nameNetwork = Name + "_network"
// msClusterNetwork represents the MSCluster_Network WMI class
// - https://docs.microsoft.com/en-us/previous-versions/windows/desktop/cluswmi/mscluster-network
type msClusterNetwork struct {
Name string
Characteristics uint
Flags uint
Metric uint
Role uint
State uint
}
func (c *Collector) buildNetwork() {
c.networkCharacteristics = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameNetwork, "characteristics"),
"Provides the characteristics of the network.",
[]string{"name"},
nil,
)
c.networkFlags = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameNetwork, "flags"),
"Provides access to the flags set for the node. ",
[]string{"name"},
nil,
)
c.networkMetric = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameNetwork, "metric"),
"The metric of a cluster network (networks with lower values are used first). If this value is set, then the AutoMetric property is set to false.",
[]string{"name"},
nil,
)
c.networkRole = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameNetwork, "role"),
"Provides access to the network's Role property. The Role property describes the role of the network in the cluster. 0: None; 1: Cluster; 2: Client; 3: Both ",
[]string{"name"},
nil,
)
c.networkState = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameNetwork, "state"),
"Provides the current state of the network. 1-1: Unknown; 0: Unavailable; 1: Down; 2: Partitioned; 3: Up",
[]string{"name"},
nil,
)
}
// Collect sends the metric values for each metric
// to the provided prometheus metric channel.
func (c *Collector) collectNetwork(ch chan<- prometheus.Metric) error {
var dst []msClusterNetwork
if err := c.wmiClient.Query("SELECT * FROM MSCluster_Network", &dst, nil, "root/MSCluster"); err != nil {
return err
}
for _, v := range dst {
ch <- prometheus.MustNewConstMetric(
c.networkCharacteristics,
prometheus.GaugeValue,
float64(v.Characteristics),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.networkFlags,
prometheus.GaugeValue,
float64(v.Flags),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.networkMetric,
prometheus.GaugeValue,
float64(v.Metric),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.networkRole,
prometheus.GaugeValue,
float64(v.Role),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.networkState,
prometheus.GaugeValue,
float64(v.State),
v.Name,
)
}
return nil
}

View File

@@ -1,240 +0,0 @@
package mscluster
import (
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus/client_golang/prometheus"
)
const nameResourceGroup = Name + "_resourcegroup"
// msClusterResourceGroup represents the MSCluster_ResourceGroup WMI class
// - https://docs.microsoft.com/en-us/previous-versions/windows/desktop/cluswmi/mscluster-resourcegroup
type msClusterResourceGroup struct {
Name string
AutoFailbackType uint
Characteristics uint
ColdStartSetting uint
DefaultOwner uint
FailbackWindowEnd int
FailbackWindowStart int
FailoverPeriod uint
FailoverThreshold uint
Flags uint
GroupType uint
OwnerNode string
Priority uint
ResiliencyPeriod uint
State uint
}
func (c *Collector) buildResourceGroup() {
c.resourceGroupAutoFailbackType = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameResourceGroup, "auto_failback_type"),
"Provides access to the group's AutoFailbackType property.",
[]string{"name"},
nil,
)
c.resourceGroupCharacteristics = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameResourceGroup, "characteristics"),
"Provides the characteristics of the group.",
[]string{"name"},
nil,
)
c.resourceGroupColdStartSetting = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameResourceGroup, "cold_start_setting"),
"Indicates whether a group can start after a cluster cold start.",
[]string{"name"},
nil,
)
c.resourceGroupDefaultOwner = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameResourceGroup, "default_owner"),
"Number of the last node the resource group was activated on or explicitly moved to.",
[]string{"name"},
nil,
)
c.resourceGroupFailbackWindowEnd = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameResourceGroup, "failback_window_end"),
"The FailbackWindowEnd property provides the latest time that the group can be moved back to the node identified as its preferred node.",
[]string{"name"},
nil,
)
c.resourceGroupFailbackWindowStart = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameResourceGroup, "failback_window_start"),
"The FailbackWindowStart property provides the earliest time (that is, local time as kept by the cluster) that the group can be moved back to the node identified as its preferred node.",
[]string{"name"},
nil,
)
c.resourceGroupFailOverPeriod = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameResourceGroup, "failover_period"),
"The FailoverPeriod property specifies a number of hours during which a maximum number of failover attempts, specified by the FailoverThreshold property, can occur.",
[]string{"name"},
nil,
)
c.resourceGroupFailOverThreshold = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameResourceGroup, "failover_threshold"),
"The FailoverThreshold property specifies the maximum number of failover attempts.",
[]string{"name"},
nil,
)
c.resourceGroupFlags = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameResourceGroup, "flags"),
"Provides access to the flags set for the group. ",
[]string{"name"},
nil,
)
c.resourceGroupGroupType = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameResourceGroup, "group_type"),
"The Type of the resource group.",
[]string{"name"},
nil,
)
c.resourceGroupOwnerNode = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameResourceGroup, "owner_node"),
"The node hosting the resource group. 0: Not hosted; 1: Hosted",
[]string{"node_name", "name"},
nil,
)
c.resourceGroupOwnerNode = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameResourceGroup, "owner_node"),
"The node hosting the resource group. 0: Not hosted; 1: Hosted",
[]string{"node_name", "name"},
nil,
)
c.resourceGroupPriority = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameResourceGroup, "priority"),
"Priority value of the resource group",
[]string{"name"},
nil,
)
c.resourceGroupResiliencyPeriod = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameResourceGroup, "resiliency_period"),
"The resiliency period for this group, in seconds.",
[]string{"name"},
nil,
)
c.resourceGroupState = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameResourceGroup, "state"),
"The current state of the resource group. -1: Unknown; 0: Online; 1: Offline; 2: Failed; 3: Partial Online; 4: Pending",
[]string{"name"},
nil,
)
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) collectResourceGroup(ch chan<- prometheus.Metric, nodeNames []string) error {
var dst []msClusterResourceGroup
if err := c.wmiClient.Query("SELECT * FROM MSCluster_ResourceGroup", &dst, nil, "root/MSCluster"); err != nil {
return err
}
for _, v := range dst {
ch <- prometheus.MustNewConstMetric(
c.resourceGroupAutoFailbackType,
prometheus.GaugeValue,
float64(v.AutoFailbackType),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.resourceGroupCharacteristics,
prometheus.GaugeValue,
float64(v.Characteristics),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.resourceGroupColdStartSetting,
prometheus.GaugeValue,
float64(v.ColdStartSetting),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.resourceGroupDefaultOwner,
prometheus.GaugeValue,
float64(v.DefaultOwner),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.resourceGroupFailbackWindowEnd,
prometheus.GaugeValue,
float64(v.FailbackWindowEnd),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.resourceGroupFailbackWindowStart,
prometheus.GaugeValue,
float64(v.FailbackWindowStart),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.resourceGroupFailOverPeriod,
prometheus.GaugeValue,
float64(v.FailoverPeriod),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.resourceGroupFailOverThreshold,
prometheus.GaugeValue,
float64(v.FailoverThreshold),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.resourceGroupFlags,
prometheus.GaugeValue,
float64(v.Flags),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.resourceGroupGroupType,
prometheus.GaugeValue,
float64(v.GroupType),
v.Name,
)
for _, nodeName := range nodeNames {
isCurrentState := 0.0
if v.OwnerNode == nodeName {
isCurrentState = 1.0
}
ch <- prometheus.MustNewConstMetric(
c.resourceGroupOwnerNode,
prometheus.GaugeValue,
isCurrentState,
nodeName, v.Name,
)
}
ch <- prometheus.MustNewConstMetric(
c.resourceGroupPriority,
prometheus.GaugeValue,
float64(v.Priority),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.resourceGroupResiliencyPeriod,
prometheus.GaugeValue,
float64(v.ResiliencyPeriod),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.resourceGroupState,
prometheus.GaugeValue,
float64(v.State),
v.Name,
)
}
return nil
}

View File

@@ -0,0 +1,156 @@
package mscluster_network
import (
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus-community/windows_exporter/pkg/wmi"
"github.com/prometheus/client_golang/prometheus"
)
const Name = "mscluster_network"
type Config struct{}
var ConfigDefaults = Config{}
// A Collector is a Prometheus Collector for WMI MSCluster_Network metrics.
type Collector struct {
config Config
logger log.Logger
characteristics *prometheus.Desc
flags *prometheus.Desc
metric *prometheus.Desc
role *prometheus.Desc
state *prometheus.Desc
}
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
c := &Collector{
config: *config,
}
c.SetLogger(logger)
return c
}
func NewWithFlags(_ *kingpin.Application) *Collector {
return &Collector{}
}
func (c *Collector) GetName() string {
return Name
}
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{"Memory"}, nil
}
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build() error {
c.characteristics = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "characteristics"),
"Provides the characteristics of the network.",
[]string{"name"},
nil,
)
c.flags = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "flags"),
"Provides access to the flags set for the node. ",
[]string{"name"},
nil,
)
c.metric = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "metric"),
"The metric of a cluster network (networks with lower values are used first). If this value is set, then the AutoMetric property is set to false.",
[]string{"name"},
nil,
)
c.role = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "role"),
"Provides access to the network's Role property. The Role property describes the role of the network in the cluster. 0: None; 1: Cluster; 2: Client; 3: Both ",
[]string{"name"},
nil,
)
c.state = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "state"),
"Provides the current state of the network. 1-1: Unknown; 0: Unavailable; 1: Down; 2: Partitioned; 3: Up",
[]string{"name"},
nil,
)
return nil
}
// MSCluster_Network docs:
// - https://docs.microsoft.com/en-us/previous-versions/windows/desktop/cluswmi/mscluster-network
type MSCluster_Network struct {
Name string
Characteristics uint
Flags uint
Metric uint
Role uint
State uint
}
// Collect sends the metric values for each metric
// to the provided prometheus metric channel.
func (c *Collector) Collect(_ *types.ScrapeContext, ch chan<- prometheus.Metric) error {
var dst []MSCluster_Network
q := wmi.QueryAll(&dst, c.logger)
if err := wmi.QueryNamespace(q, &dst, "root/MSCluster"); err != nil {
return err
}
for _, v := range dst {
ch <- prometheus.MustNewConstMetric(
c.characteristics,
prometheus.GaugeValue,
float64(v.Characteristics),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.flags,
prometheus.GaugeValue,
float64(v.Flags),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.metric,
prometheus.GaugeValue,
float64(v.Metric),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.role,
prometheus.GaugeValue,
float64(v.Role),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.state,
prometheus.GaugeValue,
float64(v.State),
v.Name,
)
}
return nil
}

View File

@@ -1,15 +1,168 @@
package mscluster
package mscluster_node
import (
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus-community/windows_exporter/pkg/wmi"
"github.com/prometheus/client_golang/prometheus"
)
const nameNode = Name + "_node"
const Name = "mscluster_node"
// msClusterNode represents the MSCluster_Node WMI class
type Config struct{}
var ConfigDefaults = Config{}
// Variable used by mscluster_resource and mscluster_resourcegroup.
var NodeName []string
// A Collector is a Prometheus Collector for WMI MSCluster_Node metrics.
type Collector struct {
config Config
logger log.Logger
buildNumber *prometheus.Desc
characteristics *prometheus.Desc
detectedCloudPlatform *prometheus.Desc
dynamicWeight *prometheus.Desc
flags *prometheus.Desc
majorVersion *prometheus.Desc
minorVersion *prometheus.Desc
needsPreventQuorum *prometheus.Desc
nodeDrainStatus *prometheus.Desc
nodeHighestVersion *prometheus.Desc
nodeLowestVersion *prometheus.Desc
nodeWeight *prometheus.Desc
state *prometheus.Desc
statusInformation *prometheus.Desc
}
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
c := &Collector{
config: *config,
}
c.SetLogger(logger)
return c
}
func NewWithFlags(_ *kingpin.Application) *Collector {
return &Collector{}
}
func (c *Collector) GetName() string {
return Name
}
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{"Memory"}, nil
}
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build() error {
c.buildNumber = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "build_number"),
"Provides access to the node's BuildNumber property.",
[]string{"name"},
nil,
)
c.characteristics = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "characteristics"),
"Provides access to the characteristics set for the node.",
[]string{"name"},
nil,
)
c.detectedCloudPlatform = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "detected_cloud_platform"),
"(DetectedCloudPlatform)",
[]string{"name"},
nil,
)
c.dynamicWeight = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "dynamic_weight"),
"The dynamic vote weight of the node adjusted by dynamic quorum feature.",
[]string{"name"},
nil,
)
c.flags = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "flags"),
"Provides access to the flags set for the node.",
[]string{"name"},
nil,
)
c.majorVersion = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "major_version"),
"Provides access to the node's MajorVersion property, which specifies the major portion of the Windows version installed.",
[]string{"name"},
nil,
)
c.minorVersion = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "minor_version"),
"Provides access to the node's MinorVersion property, which specifies the minor portion of the Windows version installed.",
[]string{"name"},
nil,
)
c.needsPreventQuorum = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "needs_prevent_quorum"),
"Whether the cluster service on that node should be started with prevent quorum flag.",
[]string{"name"},
nil,
)
c.nodeDrainStatus = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "node_drain_status"),
"The current node drain status of a node. 0: Not Initiated; 1: In Progress; 2: Completed; 3: Failed",
[]string{"name"},
nil,
)
c.nodeHighestVersion = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "node_highest_version"),
"Provides access to the node's NodeHighestVersion property, which specifies the highest possible version of the cluster service with which the node can join or communicate.",
[]string{"name"},
nil,
)
c.nodeLowestVersion = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "node_lowest_version"),
"Provides access to the node's NodeLowestVersion property, which specifies the lowest possible version of the cluster service with which the node can join or communicate.",
[]string{"name"},
nil,
)
c.nodeWeight = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "node_weight"),
"The vote weight of the node.",
[]string{"name"},
nil,
)
c.state = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "state"),
"Returns the current state of a node. -1: Unknown; 0: Up; 1: Down; 2: Paused; 3: Joining",
[]string{"name"},
nil,
)
c.statusInformation = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "status_information"),
"The isolation or quarantine status of the node.",
[]string{"name"},
nil,
)
return nil
}
// MSCluster_Node docs:
// - https://docs.microsoft.com/en-us/previous-versions/windows/desktop/cluswmi/mscluster-node
type msClusterNode struct {
type MSCluster_Node struct {
Name string
BuildNumber uint
@@ -28,205 +181,118 @@ type msClusterNode struct {
StatusInformation uint
}
func (c *Collector) buildNode() {
c.nodeBuildNumber = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameNode, "build_number"),
"Provides access to the node's BuildNumber property.",
[]string{"name"},
nil,
)
c.nodeCharacteristics = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameNode, "characteristics"),
"Provides access to the characteristics set for the node.",
[]string{"name"},
nil,
)
c.nodeDetectedCloudPlatform = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameNode, "detected_cloud_platform"),
"(DetectedCloudPlatform)",
[]string{"name"},
nil,
)
c.nodeDynamicWeight = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameNode, "dynamic_weight"),
"The dynamic vote weight of the node adjusted by dynamic quorum feature.",
[]string{"name"},
nil,
)
c.nodeFlags = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameNode, "flags"),
"Provides access to the flags set for the node.",
[]string{"name"},
nil,
)
c.nodeMajorVersion = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameNode, "major_version"),
"Provides access to the node's MajorVersion property, which specifies the major portion of the Windows version installed.",
[]string{"name"},
nil,
)
c.nodeMinorVersion = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameNode, "minor_version"),
"Provides access to the node's MinorVersion property, which specifies the minor portion of the Windows version installed.",
[]string{"name"},
nil,
)
c.nodeNeedsPreventQuorum = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameNode, "needs_prevent_quorum"),
"Whether the cluster service on that node should be started with prevent quorum flag.",
[]string{"name"},
nil,
)
c.nodeNodeDrainStatus = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameNode, "node_drain_status"),
"The current node drain status of a node. 0: Not Initiated; 1: In Progress; 2: Completed; 3: Failed",
[]string{"name"},
nil,
)
c.nodeNodeHighestVersion = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameNode, "node_highest_version"),
"Provides access to the node's NodeHighestVersion property, which specifies the highest possible version of the cluster service with which the node can join or communicate.",
[]string{"name"},
nil,
)
c.nodeNodeLowestVersion = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameNode, "node_lowest_version"),
"Provides access to the node's NodeLowestVersion property, which specifies the lowest possible version of the cluster service with which the node can join or communicate.",
[]string{"name"},
nil,
)
c.nodeNodeWeight = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameNode, "node_weight"),
"The vote weight of the node.",
[]string{"name"},
nil,
)
c.nodeState = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameNode, "state"),
"Returns the current state of a node. -1: Unknown; 0: Up; 1: Down; 2: Paused; 3: Joining",
[]string{"name"},
nil,
)
c.nodeStatusInformation = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameNode, "status_information"),
"The isolation or quarantine status of the node.",
[]string{"name"},
nil,
)
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) collectNode(ch chan<- prometheus.Metric) ([]string, error) {
var dst []msClusterNode
if err := c.wmiClient.Query("SELECT * FROM MSCluster_Node", &dst, nil, "root/MSCluster"); err != nil {
return nil, err
func (c *Collector) Collect(_ *types.ScrapeContext, ch chan<- prometheus.Metric) error {
var dst []MSCluster_Node
q := wmi.QueryAll(&dst, c.logger)
if err := wmi.QueryNamespace(q, &dst, "root/MSCluster"); err != nil {
return err
}
nodeNames := make([]string, 0, len(dst))
NodeName = []string{}
for _, v := range dst {
ch <- prometheus.MustNewConstMetric(
c.nodeBuildNumber,
c.buildNumber,
prometheus.GaugeValue,
float64(v.BuildNumber),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.nodeCharacteristics,
c.characteristics,
prometheus.GaugeValue,
float64(v.Characteristics),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.nodeDetectedCloudPlatform,
c.detectedCloudPlatform,
prometheus.GaugeValue,
float64(v.DetectedCloudPlatform),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.nodeDynamicWeight,
c.dynamicWeight,
prometheus.GaugeValue,
float64(v.DynamicWeight),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.nodeFlags,
c.flags,
prometheus.GaugeValue,
float64(v.Flags),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.nodeMajorVersion,
c.majorVersion,
prometheus.GaugeValue,
float64(v.MajorVersion),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.nodeMinorVersion,
c.minorVersion,
prometheus.GaugeValue,
float64(v.MinorVersion),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.nodeNeedsPreventQuorum,
c.needsPreventQuorum,
prometheus.GaugeValue,
float64(v.NeedsPreventQuorum),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.nodeNodeDrainStatus,
c.nodeDrainStatus,
prometheus.GaugeValue,
float64(v.NodeDrainStatus),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.nodeNodeHighestVersion,
c.nodeHighestVersion,
prometheus.GaugeValue,
float64(v.NodeHighestVersion),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.nodeNodeLowestVersion,
c.nodeLowestVersion,
prometheus.GaugeValue,
float64(v.NodeLowestVersion),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.nodeNodeWeight,
c.nodeWeight,
prometheus.GaugeValue,
float64(v.NodeWeight),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.nodeState,
c.state,
prometheus.GaugeValue,
float64(v.State),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.nodeStatusInformation,
c.statusInformation,
prometheus.GaugeValue,
float64(v.StatusInformation),
v.Name,
)
nodeNames = append(nodeNames, v.Name)
NodeName = append(NodeName, v.Name)
}
return nodeNames, nil
return nil
}

View File

@@ -1,15 +1,193 @@
package mscluster
package mscluster_resource
import (
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/prometheus-community/windows_exporter/pkg/collector/mscluster_node"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus-community/windows_exporter/pkg/wmi"
"github.com/prometheus/client_golang/prometheus"
)
const nameResource = Name + "_resource"
const Name = "mscluster_resource"
// msClusterResource represents the MSCluster_Resource WMI class
type Config struct{}
var ConfigDefaults = Config{}
// A Collector is a Prometheus Collector for WMI MSCluster_Resource metrics.
type Collector struct {
config Config
logger log.Logger
characteristics *prometheus.Desc
deadlockTimeout *prometheus.Desc
embeddedFailureAction *prometheus.Desc
flags *prometheus.Desc
isAlivePollInterval *prometheus.Desc
looksAlivePollInterval *prometheus.Desc
monitorProcessId *prometheus.Desc
ownerNode *prometheus.Desc
pendingTimeout *prometheus.Desc
resourceClass *prometheus.Desc
restartAction *prometheus.Desc
restartDelay *prometheus.Desc
restartPeriod *prometheus.Desc
restartThreshold *prometheus.Desc
retryPeriodOnFailure *prometheus.Desc
state *prometheus.Desc
subclass *prometheus.Desc
}
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
c := &Collector{
config: *config,
}
c.SetLogger(logger)
return c
}
func NewWithFlags(_ *kingpin.Application) *Collector {
return &Collector{}
}
func (c *Collector) GetName() string {
return Name
}
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{"Memory"}, nil
}
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build() error {
c.characteristics = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "characteristics"),
"Provides the characteristics of the object.",
[]string{"type", "owner_group", "name"},
nil,
)
c.deadlockTimeout = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "deadlock_timeout"),
"Indicates the length of time to wait, in milliseconds, before declaring a deadlock in any call into a resource.",
[]string{"type", "owner_group", "name"},
nil,
)
c.embeddedFailureAction = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "embedded_failure_action"),
"The time, in milliseconds, that a resource should remain in a failed state before the Cluster service attempts to restart it.",
[]string{"type", "owner_group", "name"},
nil,
)
c.flags = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "flags"),
"Provides access to the flags set for the object.",
[]string{"type", "owner_group", "name"},
nil,
)
c.isAlivePollInterval = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "is_alive_poll_interval"),
"Provides access to the resource's IsAlivePollInterval property, which is the recommended interval in milliseconds at which the Cluster Service should poll the resource to determine whether it is operational. If the property is set to 0xFFFFFFFF, the Cluster Service uses the IsAlivePollInterval property for the resource type associated with the resource.",
[]string{"type", "owner_group", "name"},
nil,
)
c.looksAlivePollInterval = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "looks_alive_poll_interval"),
"Provides access to the resource's LooksAlivePollInterval property, which is the recommended interval in milliseconds at which the Cluster Service should poll the resource to determine whether it appears operational. If the property is set to 0xFFFFFFFF, the Cluster Service uses the LooksAlivePollInterval property for the resource type associated with the resource.",
[]string{"type", "owner_group", "name"},
nil,
)
c.monitorProcessId = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "monitor_process_id"),
"Provides the process ID of the resource host service that is currently hosting the resource.",
[]string{"type", "owner_group", "name"},
nil,
)
c.ownerNode = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "owner_node"),
"The node hosting the resource. 0: Not hosted; 1: Hosted",
[]string{"type", "owner_group", "node_name", "name"},
nil,
)
c.ownerNode = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "owner_node"),
"The node hosting the resource. 0: Not hosted; 1: Hosted",
[]string{"type", "owner_group", "node_name", "name"},
nil,
)
c.pendingTimeout = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "pending_timeout"),
"Provides access to the resource's PendingTimeout property. If a resource cannot be brought online or taken offline in the number of milliseconds specified by the PendingTimeout property, the resource is forcibly terminated.",
[]string{"type", "owner_group", "name"},
nil,
)
c.resourceClass = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "resource_class"),
"Gets or sets the resource class of a resource. 0: Unknown; 1: Storage; 2: Network; 32768: Unknown ",
[]string{"type", "owner_group", "name"},
nil,
)
c.restartAction = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "restart_action"),
"Provides access to the resource's RestartAction property, which is the action to be taken by the Cluster Service if the resource fails.",
[]string{"type", "owner_group", "name"},
nil,
)
c.restartDelay = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "restart_delay"),
"Indicates the time delay before a failed resource is restarted.",
[]string{"type", "owner_group", "name"},
nil,
)
c.restartPeriod = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "restart_period"),
"Provides access to the resource's RestartPeriod property, which is interval of time, in milliseconds, during which a specified number of restart attempts can be made on a nonresponsive resource.",
[]string{"type", "owner_group", "name"},
nil,
)
c.restartThreshold = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "restart_threshold"),
"Provides access to the resource's RestartThreshold property which is the maximum number of restart attempts that can be made on a resource within an interval defined by the RestartPeriod property before the Cluster Service initiates the action specified by the RestartAction property.",
[]string{"type", "owner_group", "name"},
nil,
)
c.retryPeriodOnFailure = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "retry_period_on_failure"),
"Provides access to the resource's RetryPeriodOnFailure property, which is the interval of time (in milliseconds) that a resource should remain in a failed state before the Cluster service attempts to restart it.",
[]string{"type", "owner_group", "name"},
nil,
)
c.state = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "state"),
"The current state of the resource. -1: Unknown; 0: Inherited; 1: Initializing; 2: Online; 3: Offline; 4: Failed; 128: Pending; 129: Online Pending; 130: Offline Pending ",
[]string{"type", "owner_group", "name"},
nil,
)
c.subclass = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "subclass"),
"Provides the list of references to nodes that can be the owner of this resource.",
[]string{"type", "owner_group", "name"},
nil,
)
return nil
}
// MSCluster_Resource docs:
// - https://docs.microsoft.com/en-us/previous-versions/windows/desktop/cluswmi/mscluster-resource
type msClusterResource struct {
type MSCluster_Resource struct {
Name string
Type string
OwnerGroup string
@@ -33,247 +211,138 @@ type msClusterResource struct {
Subclass uint
}
func (c *Collector) buildResource() {
c.resourceCharacteristics = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameResource, "characteristics"),
"Provides the characteristics of the object.",
[]string{"type", "owner_group", "name"},
nil,
)
c.resourceDeadlockTimeout = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameResource, "deadlock_timeout"),
"Indicates the length of time to wait, in milliseconds, before declaring a deadlock in any call into a resource.",
[]string{"type", "owner_group", "name"},
nil,
)
c.resourceEmbeddedFailureAction = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameResource, "embedded_failure_action"),
"The time, in milliseconds, that a resource should remain in a failed state before the Cluster service attempts to restart it.",
[]string{"type", "owner_group", "name"},
nil,
)
c.resourceFlags = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameResource, "flags"),
"Provides access to the flags set for the object.",
[]string{"type", "owner_group", "name"},
nil,
)
c.resourceIsAlivePollInterval = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameResource, "is_alive_poll_interval"),
"Provides access to the resource's IsAlivePollInterval property, which is the recommended interval in milliseconds at which the Cluster Service should poll the resource to determine whether it is operational. If the property is set to 0xFFFFFFFF, the Cluster Service uses the IsAlivePollInterval property for the resource type associated with the resource.",
[]string{"type", "owner_group", "name"},
nil,
)
c.resourceLooksAlivePollInterval = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameResource, "looks_alive_poll_interval"),
"Provides access to the resource's LooksAlivePollInterval property, which is the recommended interval in milliseconds at which the Cluster Service should poll the resource to determine whether it appears operational. If the property is set to 0xFFFFFFFF, the Cluster Service uses the LooksAlivePollInterval property for the resource type associated with the resource.",
[]string{"type", "owner_group", "name"},
nil,
)
c.resourceMonitorProcessId = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameResource, "monitor_process_id"),
"Provides the process ID of the resource host service that is currently hosting the resource.",
[]string{"type", "owner_group", "name"},
nil,
)
c.resourceOwnerNode = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameResource, "owner_node"),
"The node hosting the resource. 0: Not hosted; 1: Hosted",
[]string{"type", "owner_group", "node_name", "name"},
nil,
)
c.resourceOwnerNode = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameResource, "owner_node"),
"The node hosting the resource. 0: Not hosted; 1: Hosted",
[]string{"type", "owner_group", "node_name", "name"},
nil,
)
c.resourcePendingTimeout = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameResource, "pending_timeout"),
"Provides access to the resource's PendingTimeout property. If a resource cannot be brought online or taken offline in the number of milliseconds specified by the PendingTimeout property, the resource is forcibly terminated.",
[]string{"type", "owner_group", "name"},
nil,
)
c.resourceResourceClass = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameResource, "resource_class"),
"Gets or sets the resource class of a resource. 0: Unknown; 1: Storage; 2: Network; 32768: Unknown ",
[]string{"type", "owner_group", "name"},
nil,
)
c.resourceRestartAction = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameResource, "restart_action"),
"Provides access to the resource's RestartAction property, which is the action to be taken by the Cluster Service if the resource fails.",
[]string{"type", "owner_group", "name"},
nil,
)
c.resourceRestartDelay = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameResource, "restart_delay"),
"Indicates the time delay before a failed resource is restarted.",
[]string{"type", "owner_group", "name"},
nil,
)
c.resourceRestartPeriod = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameResource, "restart_period"),
"Provides access to the resource's RestartPeriod property, which is interval of time, in milliseconds, during which a specified number of restart attempts can be made on a nonresponsive resource.",
[]string{"type", "owner_group", "name"},
nil,
)
c.resourceRestartThreshold = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameResource, "restart_threshold"),
"Provides access to the resource's RestartThreshold property which is the maximum number of restart attempts that can be made on a resource within an interval defined by the RestartPeriod property before the Cluster Service initiates the action specified by the RestartAction property.",
[]string{"type", "owner_group", "name"},
nil,
)
c.resourceRetryPeriodOnFailure = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameResource, "retry_period_on_failure"),
"Provides access to the resource's RetryPeriodOnFailure property, which is the interval of time (in milliseconds) that a resource should remain in a failed state before the Cluster service attempts to restart it.",
[]string{"type", "owner_group", "name"},
nil,
)
c.resourceState = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameResource, "state"),
"The current state of the resource. -1: Unknown; 0: Inherited; 1: Initializing; 2: Online; 3: Offline; 4: Failed; 128: Pending; 129: Online Pending; 130: Offline Pending ",
[]string{"type", "owner_group", "name"},
nil,
)
c.resourceSubClass = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, nameResource, "subclass"),
"Provides the list of references to nodes that can be the owner of this resource.",
[]string{"type", "owner_group", "name"},
nil,
)
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) collectResource(ch chan<- prometheus.Metric, nodeNames []string) error {
var dst []msClusterResource
if err := c.wmiClient.Query("SELECT * FROM MSCluster_Resource", &dst, nil, "root/MSCluster"); err != nil {
func (c *Collector) Collect(_ *types.ScrapeContext, ch chan<- prometheus.Metric) error {
var dst []MSCluster_Resource
q := wmi.QueryAll(&dst, c.logger)
if err := wmi.QueryNamespace(q, &dst, "root/MSCluster"); err != nil {
return err
}
for _, v := range dst {
ch <- prometheus.MustNewConstMetric(
c.resourceCharacteristics,
c.characteristics,
prometheus.GaugeValue,
float64(v.Characteristics),
v.Type, v.OwnerGroup, v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.resourceDeadlockTimeout,
c.deadlockTimeout,
prometheus.GaugeValue,
float64(v.DeadlockTimeout),
v.Type, v.OwnerGroup, v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.resourceEmbeddedFailureAction,
c.embeddedFailureAction,
prometheus.GaugeValue,
float64(v.EmbeddedFailureAction),
v.Type, v.OwnerGroup, v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.resourceFlags,
c.flags,
prometheus.GaugeValue,
float64(v.Flags),
v.Type, v.OwnerGroup, v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.resourceIsAlivePollInterval,
c.isAlivePollInterval,
prometheus.GaugeValue,
float64(v.IsAlivePollInterval),
v.Type, v.OwnerGroup, v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.resourceLooksAlivePollInterval,
c.looksAlivePollInterval,
prometheus.GaugeValue,
float64(v.LooksAlivePollInterval),
v.Type, v.OwnerGroup, v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.resourceMonitorProcessId,
c.monitorProcessId,
prometheus.GaugeValue,
float64(v.MonitorProcessId),
v.Type, v.OwnerGroup, v.Name,
)
for _, nodeName := range nodeNames {
isCurrentState := 0.0
if v.OwnerNode == nodeName {
isCurrentState = 1.0
if mscluster_node.NodeName != nil {
for _, node_name := range mscluster_node.NodeName {
isCurrentState := 0.0
if v.OwnerNode == node_name {
isCurrentState = 1.0
}
ch <- prometheus.MustNewConstMetric(
c.ownerNode,
prometheus.GaugeValue,
isCurrentState,
v.Type, v.OwnerGroup, node_name, v.Name,
)
}
ch <- prometheus.MustNewConstMetric(
c.resourceOwnerNode,
prometheus.GaugeValue,
isCurrentState,
v.Type, v.OwnerGroup, nodeName, v.Name,
)
}
ch <- prometheus.MustNewConstMetric(
c.resourcePendingTimeout,
c.pendingTimeout,
prometheus.GaugeValue,
float64(v.PendingTimeout),
v.Type, v.OwnerGroup, v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.resourceResourceClass,
c.resourceClass,
prometheus.GaugeValue,
float64(v.ResourceClass),
v.Type, v.OwnerGroup, v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.resourceRestartAction,
c.restartAction,
prometheus.GaugeValue,
float64(v.RestartAction),
v.Type, v.OwnerGroup, v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.resourceRestartDelay,
c.restartDelay,
prometheus.GaugeValue,
float64(v.RestartDelay),
v.Type, v.OwnerGroup, v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.resourceRestartPeriod,
c.restartPeriod,
prometheus.GaugeValue,
float64(v.RestartPeriod),
v.Type, v.OwnerGroup, v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.resourceRestartThreshold,
c.restartThreshold,
prometheus.GaugeValue,
float64(v.RestartThreshold),
v.Type, v.OwnerGroup, v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.resourceRetryPeriodOnFailure,
c.retryPeriodOnFailure,
prometheus.GaugeValue,
float64(v.RetryPeriodOnFailure),
v.Type, v.OwnerGroup, v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.resourceState,
c.state,
prometheus.GaugeValue,
float64(v.State),
v.Type, v.OwnerGroup, v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.resourceSubClass,
c.subclass,
prometheus.GaugeValue,
float64(v.Subclass),
v.Type, v.OwnerGroup, v.Name,

View File

@@ -0,0 +1,306 @@
package mscluster_resourcegroup
import (
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/prometheus-community/windows_exporter/pkg/collector/mscluster_node"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus-community/windows_exporter/pkg/wmi"
"github.com/prometheus/client_golang/prometheus"
)
const Name = "mscluster_resourcegroup"
type Config struct{}
var ConfigDefaults = Config{}
// A Collector is a Prometheus Collector for WMI MSCluster_ResourceGroup metrics.
type Collector struct {
config Config
logger log.Logger
autoFailbackType *prometheus.Desc
characteristics *prometheus.Desc
coldStartSetting *prometheus.Desc
defaultOwner *prometheus.Desc
failbackWindowEnd *prometheus.Desc
failbackWindowStart *prometheus.Desc
failOverPeriod *prometheus.Desc
failOverThreshold *prometheus.Desc
flags *prometheus.Desc
groupType *prometheus.Desc
ownerNode *prometheus.Desc
priority *prometheus.Desc
resiliencyPeriod *prometheus.Desc
state *prometheus.Desc
}
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
c := &Collector{
config: *config,
}
c.SetLogger(logger)
return c
}
func NewWithFlags(_ *kingpin.Application) *Collector {
return &Collector{}
}
func (c *Collector) GetName() string {
return Name
}
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{"Memory"}, nil
}
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build() error {
c.autoFailbackType = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "auto_failback_type"),
"Provides access to the group's AutoFailbackType property.",
[]string{"name"},
nil,
)
c.characteristics = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "characteristics"),
"Provides the characteristics of the group.",
[]string{"name"},
nil,
)
c.coldStartSetting = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "cold_start_setting"),
"Indicates whether a group can start after a cluster cold start.",
[]string{"name"},
nil,
)
c.defaultOwner = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "default_owner"),
"Number of the last node the resource group was activated on or explicitly moved to.",
[]string{"name"},
nil,
)
c.failbackWindowEnd = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "failback_window_end"),
"The FailbackWindowEnd property provides the latest time that the group can be moved back to the node identified as its preferred node.",
[]string{"name"},
nil,
)
c.failbackWindowStart = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "failback_window_start"),
"The FailbackWindowStart property provides the earliest time (that is, local time as kept by the cluster) that the group can be moved back to the node identified as its preferred node.",
[]string{"name"},
nil,
)
c.failOverPeriod = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "failover_period"),
"The FailoverPeriod property specifies a number of hours during which a maximum number of failover attempts, specified by the FailoverThreshold property, can occur.",
[]string{"name"},
nil,
)
c.failOverThreshold = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "failover_threshold"),
"The FailoverThreshold property specifies the maximum number of failover attempts.",
[]string{"name"},
nil,
)
c.flags = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "flags"),
"Provides access to the flags set for the group. ",
[]string{"name"},
nil,
)
c.groupType = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "group_type"),
"The Type of the resource group.",
[]string{"name"},
nil,
)
c.ownerNode = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "owner_node"),
"The node hosting the resource group. 0: Not hosted; 1: Hosted",
[]string{"node_name", "name"},
nil,
)
c.ownerNode = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "owner_node"),
"The node hosting the resource group. 0: Not hosted; 1: Hosted",
[]string{"node_name", "name"},
nil,
)
c.priority = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "priority"),
"Priority value of the resource group",
[]string{"name"},
nil,
)
c.resiliencyPeriod = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "resiliency_period"),
"The resiliency period for this group, in seconds.",
[]string{"name"},
nil,
)
c.state = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "state"),
"The current state of the resource group. -1: Unknown; 0: Online; 1: Offline; 2: Failed; 3: Partial Online; 4: Pending",
[]string{"name"},
nil,
)
return nil
}
// MSCluster_ResourceGroup docs:
// - https://docs.microsoft.com/en-us/previous-versions/windows/desktop/cluswmi/mscluster-resourcegroup
type MSCluster_ResourceGroup struct {
Name string
AutoFailbackType uint
Characteristics uint
ColdStartSetting uint
DefaultOwner uint
FailbackWindowEnd int
FailbackWindowStart int
FailoverPeriod uint
FailoverThreshold uint
Flags uint
GroupType uint
OwnerNode string
Priority uint
ResiliencyPeriod uint
State uint
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) Collect(_ *types.ScrapeContext, ch chan<- prometheus.Metric) error {
var dst []MSCluster_ResourceGroup
q := wmi.QueryAll(&dst, c.logger)
if err := wmi.QueryNamespace(q, &dst, "root/MSCluster"); err != nil {
return err
}
for _, v := range dst {
ch <- prometheus.MustNewConstMetric(
c.autoFailbackType,
prometheus.GaugeValue,
float64(v.AutoFailbackType),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.characteristics,
prometheus.GaugeValue,
float64(v.Characteristics),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.coldStartSetting,
prometheus.GaugeValue,
float64(v.ColdStartSetting),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.defaultOwner,
prometheus.GaugeValue,
float64(v.DefaultOwner),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.failbackWindowEnd,
prometheus.GaugeValue,
float64(v.FailbackWindowEnd),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.failbackWindowStart,
prometheus.GaugeValue,
float64(v.FailbackWindowStart),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.failOverPeriod,
prometheus.GaugeValue,
float64(v.FailoverPeriod),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.failOverThreshold,
prometheus.GaugeValue,
float64(v.FailoverThreshold),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.flags,
prometheus.GaugeValue,
float64(v.Flags),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.groupType,
prometheus.GaugeValue,
float64(v.GroupType),
v.Name,
)
if mscluster_node.NodeName != nil {
for _, node_name := range mscluster_node.NodeName {
isCurrentState := 0.0
if v.OwnerNode == node_name {
isCurrentState = 1.0
}
ch <- prometheus.MustNewConstMetric(
c.ownerNode,
prometheus.GaugeValue,
isCurrentState,
node_name, v.Name,
)
}
}
ch <- prometheus.MustNewConstMetric(
c.priority,
prometheus.GaugeValue,
float64(v.Priority),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.resiliencyPeriod,
prometheus.GaugeValue,
float64(v.ResiliencyPeriod),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.state,
prometheus.GaugeValue,
float64(v.State),
v.Name,
)
}
return nil
}

View File

@@ -3,15 +3,15 @@
package msmq
import (
"errors"
"log/slog"
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus-community/windows_exporter/pkg/utils"
"github.com/prometheus-community/windows_exporter/pkg/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "msmq"
@@ -26,8 +26,8 @@ var ConfigDefaults = Config{
// A Collector is a Prometheus Collector for WMI Win32_PerfRawData_MSMQ_MSMQQueue metrics.
type Collector struct {
config Config
wmiClient *wmi.Client
config Config
logger log.Logger
bytesInJournalQueue *prometheus.Desc
bytesInQueue *prometheus.Desc
@@ -35,7 +35,7 @@ type Collector struct {
messagesInQueue *prometheus.Desc
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -48,6 +48,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -67,25 +69,21 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(logger *slog.Logger, wmiClient *wmi.Client) error {
logger = logger.With(slog.String("collector", Name))
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
return errors.New("wmiClient or SWbemServicesClient is nil")
}
c.wmiClient = wmiClient
func (c *Collector) Build() error {
if *c.config.QueryWhereClause == "" {
logger.Warn("No where-clause specified for msmq collector. This will generate a very large number of metrics!")
_ = level.Warn(c.logger).Log("msg", "No where-clause specified for msmq collector. This will generate a very large number of metrics!")
}
c.bytesInJournalQueue = prometheus.NewDesc(
@@ -112,22 +110,16 @@ func (c *Collector) Build(logger *slog.Logger, wmiClient *wmi.Client) error {
[]string{"name"},
nil,
)
return nil
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) Collect(_ *types.ScrapeContext, ch chan<- prometheus.Metric) error {
if err := c.collect(ch); err != nil {
logger.Error("failed collecting msmq metrics",
slog.Any("err", err),
)
_ = level.Error(c.logger).Log("msg", "failed collecting msmq metrics", "err", err)
return err
}
return nil
}
@@ -143,12 +135,8 @@ type msmqQueue struct {
func (c *Collector) collect(ch chan<- prometheus.Metric) error {
var dst []msmqQueue
query := "SELECT * FROM Win32_PerfRawData_MSMQ_MSMQQueue"
if *c.config.QueryWhereClause != "" {
query += " WHERE " + *c.config.QueryWhereClause
}
if err := c.wmiClient.Query(query, &dst); err != nil {
q := wmi.QueryAllForClassWhere(&dst, "Win32_PerfRawData_MSMQ_MSMQQueue", *c.config.QueryWhereClause, c.logger)
if err := wmi.Query(q, &dst); err != nil {
return err
}

View File

@@ -5,7 +5,6 @@ package mssql
import (
"errors"
"fmt"
"log/slog"
"os"
"sort"
"strings"
@@ -13,10 +12,11 @@ import (
"time"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/perflib"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
"golang.org/x/sys/windows/registry"
)
@@ -45,7 +45,7 @@ var ConfigDefaults = Config{
type mssqlInstancesType map[string]string
func getMSSQLInstances(logger *slog.Logger) mssqlInstancesType {
func getMSSQLInstances(logger log.Logger) mssqlInstancesType {
sqlInstances := make(mssqlInstancesType)
// in case querying the registry fails, return the default instance
@@ -53,31 +53,21 @@ func getMSSQLInstances(logger *slog.Logger) mssqlInstancesType {
sqlDefaultInstance["MSSQLSERVER"] = ""
regKey := `Software\Microsoft\Microsoft SQL Server\Instance Names\SQL`
k, err := registry.OpenKey(registry.LOCAL_MACHINE, regKey, registry.QUERY_VALUE)
if err != nil {
logger.Warn("Couldn't open registry to determine SQL instances",
slog.Any("err", err),
)
_ = level.Warn(logger).Log("msg", "Couldn't open registry to determine SQL instances", "err", err)
return sqlDefaultInstance
}
defer func() {
err = k.Close()
if err != nil {
logger.Warn("Failed to close registry key",
slog.Any("err", err),
)
_ = level.Warn(logger).Log("msg", "Failed to close registry key", "err", err)
}
}()
instanceNames, err := k.ReadValueNames(0)
if err != nil {
logger.Warn("Can't ReadSubKeyNames",
slog.Any("err", err),
)
_ = level.Warn(logger).Log("msg", "Can't ReadSubKeyNames", "err", err)
return sqlDefaultInstance
}
@@ -87,7 +77,7 @@ func getMSSQLInstances(logger *slog.Logger) mssqlInstancesType {
}
}
logger.Debug(fmt.Sprintf("Detected MSSQL Instances: %#v\n", sqlInstances))
_ = level.Debug(logger).Log("msg", fmt.Sprintf("Detected MSSQL Instances: %#v\n", sqlInstances))
return sqlInstances
}
@@ -119,9 +109,7 @@ func mssqlGetPerfObjectName(sqlInstance string, collector string) string {
if sqlInstance != "MSSQLSERVER" {
prefix = "MSSQL$" + sqlInstance + ":"
}
suffix := ""
switch collector {
case "accessmethods":
suffix = "Access Methods"
@@ -148,13 +136,13 @@ func mssqlGetPerfObjectName(sqlInstance string, collector string) string {
case "waitstats":
suffix = "Wait Statistics"
}
return prefix + suffix
}
// A Collector is a Prometheus Collector for various WMI Win32_PerfRawData_MSSQLSERVER_* metrics.
type Collector struct {
config Config
logger log.Logger
// meta
mssqlScrapeDurationDesc *prometheus.Desc
@@ -426,7 +414,7 @@ type Collector struct {
mssqlChildCollectorFailure int
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -439,6 +427,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -448,7 +438,6 @@ func NewWithFlags(app *kingpin.Application) *Collector {
}
var listAllCollectors bool
var collectorsEnabled string
app.Flag(
@@ -491,8 +480,12 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(logger *slog.Logger) ([]string, error) {
c.mssqlInstances = getMSSQLInstances(logger)
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
c.mssqlInstances = getMSSQLInstances(c.logger)
perfCounters := make([]string, 0, len(c.mssqlInstances)*len(c.config.CollectorsEnabled))
for instance := range c.mssqlInstances {
@@ -504,11 +497,11 @@ func (c *Collector) GetPerfCounter(logger *slog.Logger) ([]string, error) {
return perfCounters, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build() error {
// Result must order, to prevent test failures.
sort.Strings(c.config.CollectorsEnabled)
@@ -1984,30 +1977,24 @@ func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
return nil
}
type mssqlCollectorFunc func(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric, sqlInstance string) error
type mssqlCollectorFunc func(ctx *types.ScrapeContext, ch chan<- prometheus.Metric, sqlInstance string) error
func (c *Collector) execute(ctx *types.ScrapeContext, logger *slog.Logger, name string, fn mssqlCollectorFunc, ch chan<- prometheus.Metric, sqlInstance string, wg *sync.WaitGroup) {
func (c *Collector) execute(ctx *types.ScrapeContext, name string, fn mssqlCollectorFunc, ch chan<- prometheus.Metric, sqlInstance string, wg *sync.WaitGroup) {
// Reset failure counter on each scrape
c.mssqlChildCollectorFailure = 0
defer wg.Done()
begin := time.Now()
err := fn(ctx, logger, ch, sqlInstance)
err := fn(ctx, ch, sqlInstance)
duration := time.Since(begin)
var success float64
if err != nil {
logger.Error(fmt.Sprintf("mssql class collector %s failed after %fs", name, duration.Seconds()),
slog.Any("err", err),
)
_ = level.Error(c.logger).Log("msg", fmt.Sprintf("mssql class collector %s failed after %fs", name, duration.Seconds()), "err", err)
success = 0
c.mssqlChildCollectorFailure++
} else {
logger.Debug(fmt.Sprintf("mssql class collector %s succeeded after %fs.", name, duration.Seconds()))
_ = level.Debug(c.logger).Log("msg", fmt.Sprintf("mssql class collector %s succeeded after %fs.", name, duration.Seconds()))
success = 1
}
ch <- prometheus.MustNewConstMetric(
@@ -2026,8 +2013,7 @@ func (c *Collector) execute(ctx *types.ScrapeContext, logger *slog.Logger, name
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) Collect(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
wg := sync.WaitGroup{}
for sqlInstance := range c.mssqlInstances {
@@ -2035,8 +2021,7 @@ func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch ch
function := c.mssqlCollectors[name]
wg.Add(1)
go c.execute(ctx, logger, name, function, ch, sqlInstance, &wg)
go c.execute(ctx, name, function, ch, sqlInstance, &wg)
}
}
@@ -2099,12 +2084,11 @@ type mssqlAccessMethods struct {
WorktablesFromCacheRatioBase float64 `perflib:"Worktables From Cache Base_Base"`
}
func (c *Collector) collectAccessMethods(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric, sqlInstance string) error {
func (c *Collector) collectAccessMethods(ctx *types.ScrapeContext, ch chan<- prometheus.Metric, sqlInstance string) error {
var dst []mssqlAccessMethods
_ = level.Debug(c.logger).Log("msg", fmt.Sprintf("mssql_accessmethods collector iterating sql instance %s.", sqlInstance))
logger.Debug(fmt.Sprintf("mssql_accessmethods collector iterating sql instance %s.", sqlInstance))
if err := perflib.UnmarshalObject(ctx.PerfObjects[mssqlGetPerfObjectName(sqlInstance, "accessmethods")], &dst, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects[mssqlGetPerfObjectName(sqlInstance, "accessmethods")], &dst, c.logger); err != nil {
return err
}
@@ -2417,7 +2401,6 @@ func (c *Collector) collectAccessMethods(ctx *types.ScrapeContext, logger *slog.
sqlInstance,
)
}
return nil
}
@@ -2436,12 +2419,11 @@ type mssqlAvailabilityReplica struct {
SendstoTransportPerSec float64 `perflib:"Sends to Transport/sec"`
}
func (c *Collector) collectAvailabilityReplica(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric, sqlInstance string) error {
func (c *Collector) collectAvailabilityReplica(ctx *types.ScrapeContext, ch chan<- prometheus.Metric, sqlInstance string) error {
var dst []mssqlAvailabilityReplica
_ = level.Debug(c.logger).Log("msg", fmt.Sprintf("mssql_availreplica collector iterating sql instance %s.", sqlInstance))
logger.Debug(fmt.Sprintf("mssql_availreplica collector iterating sql instance %s.", sqlInstance))
if err := perflib.UnmarshalObject(ctx.PerfObjects[mssqlGetPerfObjectName(sqlInstance, "availreplica")], &dst, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects[mssqlGetPerfObjectName(sqlInstance, "availreplica")], &dst, c.logger); err != nil {
return err
}
@@ -2449,7 +2431,6 @@ func (c *Collector) collectAvailabilityReplica(ctx *types.ScrapeContext, logger
if strings.ToLower(v.Name) == "_total" {
continue
}
replicaName := v.Name
ch <- prometheus.MustNewConstMetric(
@@ -2515,7 +2496,6 @@ func (c *Collector) collectAvailabilityReplica(ctx *types.ScrapeContext, logger
sqlInstance, replicaName,
)
}
return nil
}
@@ -2547,12 +2527,11 @@ type mssqlBufferManager struct {
TargetPages float64 `perflib:"Target pages"`
}
func (c *Collector) collectBufferManager(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric, sqlInstance string) error {
func (c *Collector) collectBufferManager(ctx *types.ScrapeContext, ch chan<- prometheus.Metric, sqlInstance string) error {
var dst []mssqlBufferManager
_ = level.Debug(c.logger).Log("msg", fmt.Sprintf("mssql_bufman collector iterating sql instance %s.", sqlInstance))
logger.Debug(fmt.Sprintf("mssql_bufman collector iterating sql instance %s.", sqlInstance))
if err := perflib.UnmarshalObject(ctx.PerfObjects[mssqlGetPerfObjectName(sqlInstance, "bufman")], &dst, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects[mssqlGetPerfObjectName(sqlInstance, "bufman")], &dst, c.logger); err != nil {
return err
}
@@ -2752,12 +2731,11 @@ type mssqlDatabaseReplica struct {
TransactionDelay float64 `perflib:"Transaction Delay"`
}
func (c *Collector) collectDatabaseReplica(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric, sqlInstance string) error {
func (c *Collector) collectDatabaseReplica(ctx *types.ScrapeContext, ch chan<- prometheus.Metric, sqlInstance string) error {
var dst []mssqlDatabaseReplica
_ = level.Debug(c.logger).Log("msg", fmt.Sprintf("mssql_dbreplica collector iterating sql instance %s.", sqlInstance))
logger.Debug(fmt.Sprintf("mssql_dbreplica collector iterating sql instance %s.", sqlInstance))
if err := perflib.UnmarshalObject(ctx.PerfObjects[mssqlGetPerfObjectName(sqlInstance, "dbreplica")], &dst, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects[mssqlGetPerfObjectName(sqlInstance, "dbreplica")], &dst, c.logger); err != nil {
return err
}
@@ -2765,7 +2743,6 @@ func (c *Collector) collectDatabaseReplica(ctx *types.ScrapeContext, logger *slo
if strings.ToLower(v.Name) == "_total" {
continue
}
replicaName := v.Name
ch <- prometheus.MustNewConstMetric(
@@ -2936,7 +2913,6 @@ func (c *Collector) collectDatabaseReplica(ctx *types.ScrapeContext, logger *slo
sqlInstance, replicaName,
)
}
return nil
}
@@ -2994,12 +2970,11 @@ type mssqlDatabases struct {
XTPMemoryUsedKB float64 `perflib:"XTP Memory Used (KB)"`
}
func (c *Collector) collectDatabases(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric, sqlInstance string) error {
func (c *Collector) collectDatabases(ctx *types.ScrapeContext, ch chan<- prometheus.Metric, sqlInstance string) error {
var dst []mssqlDatabases
_ = level.Debug(c.logger).Log("msg", fmt.Sprintf("mssql_databases collector iterating sql instance %s.", sqlInstance))
logger.Debug(fmt.Sprintf("mssql_databases collector iterating sql instance %s.", sqlInstance))
if err := perflib.UnmarshalObject(ctx.PerfObjects[mssqlGetPerfObjectName(sqlInstance, "databases")], &dst, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects[mssqlGetPerfObjectName(sqlInstance, "databases")], &dst, c.logger); err != nil {
return err
}
@@ -3007,7 +2982,6 @@ func (c *Collector) collectDatabases(ctx *types.ScrapeContext, logger *slog.Logg
if strings.ToLower(v.Name) == "_total" {
continue
}
dbName := v.Name
ch <- prometheus.MustNewConstMetric(
@@ -3346,7 +3320,6 @@ func (c *Collector) collectDatabases(ctx *types.ScrapeContext, logger *slog.Logg
sqlInstance, dbName,
)
}
return nil
}
@@ -3379,12 +3352,11 @@ type mssqlGeneralStatistics struct {
UserConnections float64 `perflib:"User Connections"`
}
func (c *Collector) collectGeneralStatistics(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric, sqlInstance string) error {
func (c *Collector) collectGeneralStatistics(ctx *types.ScrapeContext, ch chan<- prometheus.Metric, sqlInstance string) error {
var dst []mssqlGeneralStatistics
_ = level.Debug(c.logger).Log("msg", fmt.Sprintf("mssql_genstats collector iterating sql instance %s.", sqlInstance))
logger.Debug(fmt.Sprintf("mssql_genstats collector iterating sql instance %s.", sqlInstance))
if err := perflib.UnmarshalObject(ctx.PerfObjects[mssqlGetPerfObjectName(sqlInstance, "genstats")], &dst, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects[mssqlGetPerfObjectName(sqlInstance, "genstats")], &dst, c.logger); err != nil {
return err
}
@@ -3575,12 +3547,11 @@ type mssqlLocks struct {
NumberOfDeadlocksPerSec float64 `perflib:"Number of Deadlocks/sec"`
}
func (c *Collector) collectLocks(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric, sqlInstance string) error {
func (c *Collector) collectLocks(ctx *types.ScrapeContext, ch chan<- prometheus.Metric, sqlInstance string) error {
var dst []mssqlLocks
_ = level.Debug(c.logger).Log("msg", fmt.Sprintf("mssql_locks collector iterating sql instance %s.", sqlInstance))
logger.Debug(fmt.Sprintf("mssql_locks collector iterating sql instance %s.", sqlInstance))
if err := perflib.UnmarshalObject(ctx.PerfObjects[mssqlGetPerfObjectName(sqlInstance, "locks")], &dst, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects[mssqlGetPerfObjectName(sqlInstance, "locks")], &dst, c.logger); err != nil {
return err
}
@@ -3588,7 +3559,6 @@ func (c *Collector) collectLocks(ctx *types.ScrapeContext, logger *slog.Logger,
if strings.ToLower(v.Name) == "_total" {
continue
}
lockResourceName := v.Name
ch <- prometheus.MustNewConstMetric(
@@ -3647,7 +3617,6 @@ func (c *Collector) collectLocks(ctx *types.ScrapeContext, logger *slog.Logger,
sqlInstance, lockResourceName,
)
}
return nil
}
@@ -3676,12 +3645,11 @@ type mssqlMemoryManager struct {
TotalServerMemoryKB float64 `perflib:"Total Server Memory (KB)"`
}
func (c *Collector) collectMemoryManager(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric, sqlInstance string) error {
func (c *Collector) collectMemoryManager(ctx *types.ScrapeContext, ch chan<- prometheus.Metric, sqlInstance string) error {
var dst []mssqlMemoryManager
_ = level.Debug(c.logger).Log("msg", fmt.Sprintf("mssql_memmgr collector iterating sql instance %s.", sqlInstance))
logger.Debug(fmt.Sprintf("mssql_memmgr collector iterating sql instance %s.", sqlInstance))
if err := perflib.UnmarshalObject(ctx.PerfObjects[mssqlGetPerfObjectName(sqlInstance, "memmgr")], &dst, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects[mssqlGetPerfObjectName(sqlInstance, "memmgr")], &dst, c.logger); err != nil {
return err
}
@@ -3846,12 +3814,11 @@ type mssqlSQLStatistics struct {
UnsafeAutoParamsPerSec float64 `perflib:"Unsafe Auto-Params/sec"`
}
func (c *Collector) collectSQLStats(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric, sqlInstance string) error {
func (c *Collector) collectSQLStats(ctx *types.ScrapeContext, ch chan<- prometheus.Metric, sqlInstance string) error {
var dst []mssqlSQLStatistics
_ = level.Debug(c.logger).Log("msg", fmt.Sprintf("mssql_sqlstats collector iterating sql instance %s.", sqlInstance))
logger.Debug(fmt.Sprintf("mssql_sqlstats collector iterating sql instance %s.", sqlInstance))
if err := perflib.UnmarshalObject(ctx.PerfObjects[mssqlGetPerfObjectName(sqlInstance, "sqlstats")], &dst, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects[mssqlGetPerfObjectName(sqlInstance, "sqlstats")], &dst, c.logger); err != nil {
return err
}
@@ -3955,12 +3922,11 @@ type mssqlWaitStatistics struct {
WaitStatsTransactionOwnershipWaits float64 `perflib:"Transaction ownership waits"`
}
func (c *Collector) collectWaitStats(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric, sqlInstance string) error {
func (c *Collector) collectWaitStats(ctx *types.ScrapeContext, ch chan<- prometheus.Metric, sqlInstance string) error {
var dst []mssqlWaitStatistics
_ = level.Debug(c.logger).Log("msg", fmt.Sprintf("mssql_waitstats collector iterating sql instance %s.", sqlInstance))
logger.Debug(fmt.Sprintf("mssql_waitstats collector iterating sql instance %s.", sqlInstance))
if err := perflib.UnmarshalObject(ctx.PerfObjects[mssqlGetPerfObjectName(sqlInstance, "waitstats")], &dst, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects[mssqlGetPerfObjectName(sqlInstance, "waitstats")], &dst, c.logger); err != nil {
return err
}
@@ -4062,12 +4028,11 @@ type mssqlSQLErrors struct {
// Win32_PerfRawData_MSSQLSERVER_SQLServerErrors docs:
// - https://docs.microsoft.com/en-us/sql/relational-databases/performance-monitor/sql-server-sql-errors-object
func (c *Collector) collectSQLErrors(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric, sqlInstance string) error {
func (c *Collector) collectSQLErrors(ctx *types.ScrapeContext, ch chan<- prometheus.Metric, sqlInstance string) error {
var dst []mssqlSQLErrors
_ = level.Debug(c.logger).Log("msg", fmt.Sprintf("mssql_sqlerrors collector iterating sql instance %s.", sqlInstance))
logger.Debug(fmt.Sprintf("mssql_sqlerrors collector iterating sql instance %s.", sqlInstance))
if err := perflib.UnmarshalObject(ctx.PerfObjects[mssqlGetPerfObjectName(sqlInstance, "sqlerrors")], &dst, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects[mssqlGetPerfObjectName(sqlInstance, "sqlerrors")], &dst, c.logger); err != nil {
return err
}
@@ -4075,7 +4040,6 @@ func (c *Collector) collectSQLErrors(ctx *types.ScrapeContext, logger *slog.Logg
if strings.ToLower(v.Name) == "_total" {
continue
}
resource := v.Name
ch <- prometheus.MustNewConstMetric(
@@ -4107,12 +4071,11 @@ type mssqlTransactions struct {
// Win32_PerfRawData_MSSQLSERVER_Transactions docs:
// - https://docs.microsoft.com/en-us/sql/relational-databases/performance-monitor/sql-server-transactions-object
func (c *Collector) collectTransactions(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric, sqlInstance string) error {
func (c *Collector) collectTransactions(ctx *types.ScrapeContext, ch chan<- prometheus.Metric, sqlInstance string) error {
var dst []mssqlTransactions
_ = level.Debug(c.logger).Log("msg", fmt.Sprintf("mssql_transactions collector iterating sql instance %s.", sqlInstance))
logger.Debug(fmt.Sprintf("mssql_transactions collector iterating sql instance %s.", sqlInstance))
if err := perflib.UnmarshalObject(ctx.PerfObjects[mssqlGetPerfObjectName(sqlInstance, "transactions")], &dst, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects[mssqlGetPerfObjectName(sqlInstance, "transactions")], &dst, c.logger); err != nil {
return err
}

View File

@@ -4,14 +4,14 @@ package net
import (
"fmt"
"log/slog"
"regexp"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/perflib"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "net"
@@ -31,6 +31,7 @@ var nicNameToUnderscore = regexp.MustCompile("[^a-zA-Z0-9]")
// A Collector is a Prometheus Collector for Perflib Network Interface metrics.
type Collector struct {
config Config
logger log.Logger
bytesReceivedTotal *prometheus.Desc
bytesSentTotal *prometheus.Desc
@@ -47,7 +48,7 @@ type Collector struct {
currentBandwidth *prometheus.Desc
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -64,6 +65,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -107,15 +110,19 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{"Network Interface"}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build() error {
c.bytesReceivedTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "bytes_received_total"),
"(Network.BytesReceivedPerSec)",
@@ -200,16 +207,11 @@ func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
if err := c.collect(ctx, logger, ch); err != nil {
logger.Error("failed collecting net metrics",
slog.Any("err", err),
)
func (c *Collector) Collect(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
if err := c.collect(ctx, ch); err != nil {
_ = level.Error(c.logger).Log("msg", "failed collecting net metrics", "err", err)
return err
}
return nil
}
@@ -238,12 +240,10 @@ type networkInterface struct {
CurrentBandwidth float64 `perflib:"Current Bandwidth"`
}
func (c *Collector) collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) collect(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
var dst []networkInterface
if err := perflib.UnmarshalObject(ctx.PerfObjects["Network Interface"], &dst, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects["Network Interface"], &dst, c.logger); err != nil {
return err
}

View File

@@ -3,13 +3,12 @@
package netframework_clrexceptions
import (
"errors"
"log/slog"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus-community/windows_exporter/pkg/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "netframework_clrexceptions"
@@ -20,8 +19,8 @@ var ConfigDefaults = Config{}
// A Collector is a Prometheus Collector for WMI Win32_PerfRawData_NETFramework_NETCLRExceptions metrics.
type Collector struct {
config Config
wmiClient *wmi.Client
config Config
logger log.Logger
numberOfExceptionsThrown *prometheus.Desc
numberOfFilters *prometheus.Desc
@@ -29,7 +28,7 @@ type Collector struct {
throwToCatchDepth *prometheus.Desc
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -38,6 +37,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -49,20 +50,19 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
return errors.New("wmiClient or SWbemServicesClient is nil")
}
c.wmiClient = wmiClient
func (c *Collector) Build() error {
c.numberOfExceptionsThrown = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "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.",
@@ -87,22 +87,16 @@ func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
[]string{"process"},
nil,
)
return nil
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) Collect(_ *types.ScrapeContext, ch chan<- prometheus.Metric) error {
if err := c.collect(ch); err != nil {
logger.Error("failed collecting win32_perfrawdata_netframework_netclrexceptions metrics",
slog.Any("err", err),
)
_ = level.Error(c.logger).Log("msg", "failed collecting win32_perfrawdata_netframework_netclrexceptions metrics", "err", err)
return err
}
return nil
}
@@ -118,7 +112,8 @@ type Win32_PerfRawData_NETFramework_NETCLRExceptions struct {
func (c *Collector) collect(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_NETFramework_NETCLRExceptions
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_NETFramework_NETCLRExceptions", &dst); err != nil {
q := wmi.QueryAll(&dst, c.logger)
if err := wmi.Query(q, &dst); err != nil {
return err
}

View File

@@ -3,13 +3,12 @@
package netframework_clrinterop
import (
"errors"
"log/slog"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus-community/windows_exporter/pkg/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "netframework_clrinterop"
@@ -20,15 +19,15 @@ var ConfigDefaults = Config{}
// A Collector is a Prometheus Collector for WMI Win32_PerfRawData_NETFramework_NETCLRInterop metrics.
type Collector struct {
config Config
wmiClient *wmi.Client
config Config
logger log.Logger
numberOfCCWs *prometheus.Desc
numberOfMarshalling *prometheus.Desc
numberOfStubs *prometheus.Desc
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -37,6 +36,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -48,20 +49,19 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
return errors.New("wmiClient or SWbemServicesClient is nil")
}
c.wmiClient = wmiClient
func (c *Collector) Build() error {
c.numberOfCCWs = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "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.",
@@ -80,22 +80,16 @@ func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
[]string{"process"},
nil,
)
return nil
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) Collect(_ *types.ScrapeContext, ch chan<- prometheus.Metric) error {
if err := c.collect(ch); err != nil {
logger.Error("failed collecting win32_perfrawdata_netframework_netclrinterop metrics",
slog.Any("err", err),
)
_ = level.Error(c.logger).Log("msg", "failed collecting win32_perfrawdata_netframework_netclrinterop metrics", "err", err)
return err
}
return nil
}
@@ -111,7 +105,8 @@ type Win32_PerfRawData_NETFramework_NETCLRInterop struct {
func (c *Collector) collect(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_NETFramework_NETCLRInterop
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_NETFramework_NETCLRInterop", &dst); err != nil {
q := wmi.QueryAll(&dst, c.logger)
if err := wmi.Query(q, &dst); err != nil {
return err
}

View File

@@ -3,12 +3,12 @@
package netframework_clrjit
import (
"log/slog"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus-community/windows_exporter/pkg/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "netframework_clrjit"
@@ -19,8 +19,8 @@ var ConfigDefaults = Config{}
// A Collector is a Prometheus Collector for WMI Win32_PerfRawData_NETFramework_NETCLRJit metrics.
type Collector struct {
config Config
wmiClient *wmi.Client
config Config
logger log.Logger
numberOfMethodsJitted *prometheus.Desc
timeInJit *prometheus.Desc
@@ -28,7 +28,7 @@ type Collector struct {
totalNumberOfILBytesJitted *prometheus.Desc
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -37,6 +37,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -48,15 +50,19 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build() error {
c.numberOfMethodsJitted = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "jit_methods_total"),
"Displays the total number of methods JIT-compiled since the application started. This counter does not include pre-JIT-compiled methods.",
@@ -81,22 +87,16 @@ func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
[]string{"process"},
nil,
)
return nil
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) Collect(_ *types.ScrapeContext, ch chan<- prometheus.Metric) error {
if err := c.collect(ch); err != nil {
logger.Error("failed collecting win32_perfrawdata_netframework_netclrjit metrics",
slog.Any("err", err),
)
_ = level.Error(c.logger).Log("msg", "failed collecting win32_perfrawdata_netframework_netclrjit metrics", "err", err)
return err
}
return nil
}
@@ -114,7 +114,8 @@ type Win32_PerfRawData_NETFramework_NETCLRJit struct {
func (c *Collector) collect(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_NETFramework_NETCLRJit
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_NETFramework_NETCLRJit", &dst); err != nil {
q := wmi.QueryAll(&dst, c.logger)
if err := wmi.Query(q, &dst); err != nil {
return err
}

View File

@@ -3,13 +3,12 @@
package netframework_clrloading
import (
"errors"
"log/slog"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus-community/windows_exporter/pkg/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "netframework_clrloading"
@@ -20,8 +19,8 @@ var ConfigDefaults = Config{}
// A Collector is a Prometheus Collector for WMI Win32_PerfRawData_NETFramework_NETCLRLoading metrics.
type Collector struct {
config Config
wmiClient *wmi.Client
config Config
logger log.Logger
bytesInLoaderHeap *prometheus.Desc
currentAppDomains *prometheus.Desc
@@ -34,7 +33,7 @@ type Collector struct {
totalNumberOfLoadFailures *prometheus.Desc
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -43,6 +42,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -54,21 +55,19 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
return errors.New("wmiClient or SWbemServicesClient is nil")
}
c.wmiClient = wmiClient
func (c *Collector) Build() error {
c.bytesInLoaderHeap = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "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.",
@@ -123,22 +122,16 @@ func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
[]string{"process"},
nil,
)
return nil
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) Collect(_ *types.ScrapeContext, ch chan<- prometheus.Metric) error {
if err := c.collect(ch); err != nil {
logger.Error("failed collecting win32_perfrawdata_netframework_netclrloading metrics",
slog.Any("err", err),
)
_ = level.Error(c.logger).Log("msg", "failed collecting win32_perfrawdata_netframework_netclrloading metrics", "err", err)
return err
}
return nil
}
@@ -165,7 +158,8 @@ type Win32_PerfRawData_NETFramework_NETCLRLoading struct {
func (c *Collector) collect(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_NETFramework_NETCLRLoading
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_NETFramework_NETCLRLoading", &dst); err != nil {
q := wmi.QueryAll(&dst, c.logger)
if err := wmi.Query(q, &dst); err != nil {
return err
}

View File

@@ -3,13 +3,12 @@
package netframework_clrlocksandthreads
import (
"errors"
"log/slog"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus-community/windows_exporter/pkg/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "netframework_clrlocksandthreads"
@@ -20,8 +19,8 @@ var ConfigDefaults = Config{}
// A Collector is a Prometheus Collector for WMI Win32_PerfRawData_NETFramework_NETCLRLocksAndThreads metrics.
type Collector struct {
config Config
wmiClient *wmi.Client
config Config
logger log.Logger
currentQueueLength *prometheus.Desc
numberOfCurrentLogicalThreads *prometheus.Desc
@@ -32,7 +31,7 @@ type Collector struct {
totalNumberOfContentions *prometheus.Desc
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -41,6 +40,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -52,21 +53,19 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
return errors.New("wmiClient or SWbemServicesClient is nil")
}
c.wmiClient = wmiClient
func (c *Collector) Build() error {
c.currentQueueLength = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "current_queue_length"),
"Displays the total number of threads that are currently waiting to acquire a managed lock in the application.",
@@ -109,22 +108,16 @@ func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
[]string{"process"},
nil,
)
return nil
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) Collect(_ *types.ScrapeContext, ch chan<- prometheus.Metric) error {
if err := c.collect(ch); err != nil {
logger.Error("failed collecting win32_perfrawdata_netframework_netclrlocksandthreads metrics",
slog.Any("err", err),
)
_ = level.Error(c.logger).Log("msg", "failed collecting win32_perfrawdata_netframework_netclrlocksandthreads metrics", "err", err)
return err
}
return nil
}
@@ -145,7 +138,8 @@ type Win32_PerfRawData_NETFramework_NETCLRLocksAndThreads struct {
func (c *Collector) collect(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_NETFramework_NETCLRLocksAndThreads
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_NETFramework_NETCLRLocksAndThreads", &dst); err != nil {
q := wmi.QueryAll(&dst, c.logger)
if err := wmi.Query(q, &dst); err != nil {
return err
}

View File

@@ -3,13 +3,12 @@
package netframework_clrmemory
import (
"errors"
"log/slog"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus-community/windows_exporter/pkg/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "netframework_clrmemory"
@@ -20,8 +19,8 @@ var ConfigDefaults = Config{}
// A Collector is a Prometheus Collector for WMI Win32_PerfRawData_NETFramework_NETCLRMemory metrics.
type Collector struct {
config Config
wmiClient *wmi.Client
config Config
logger log.Logger
allocatedBytes *prometheus.Desc
finalizationSurvivors *prometheus.Desc
@@ -37,7 +36,7 @@ type Collector struct {
timeInGC *prometheus.Desc
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -46,6 +45,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -57,21 +58,19 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
return errors.New("wmiClient or SWbemServicesClient is nil")
}
c.wmiClient = wmiClient
func (c *Collector) Build() error {
c.allocatedBytes = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "allocated_bytes_total"),
"Displays the total number of bytes allocated on the garbage collection heap.",
@@ -144,22 +143,16 @@ func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
[]string{"process"},
nil,
)
return nil
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) Collect(_ *types.ScrapeContext, ch chan<- prometheus.Metric) error {
if err := c.collect(ch); err != nil {
logger.Error("failed collecting win32_perfrawdata_netframework_netclrmemory metrics",
slog.Any("err", err),
)
_ = level.Error(c.logger).Log("msg", "failed collecting win32_perfrawdata_netframework_netclrmemory metrics", "err", err)
return err
}
return nil
}
@@ -199,7 +192,8 @@ type Win32_PerfRawData_NETFramework_NETCLRMemory struct {
func (c *Collector) collect(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_NETFramework_NETCLRMemory
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_NETFramework_NETCLRMemory", &dst); err != nil {
q := wmi.QueryAll(&dst, c.logger)
if err := wmi.Query(q, &dst); err != nil {
return err
}

View File

@@ -3,13 +3,12 @@
package netframework_clrremoting
import (
"errors"
"log/slog"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus-community/windows_exporter/pkg/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "netframework_clrremoting"
@@ -20,8 +19,8 @@ var ConfigDefaults = Config{}
// A Collector is a Prometheus Collector for WMI Win32_PerfRawData_NETFramework_NETCLRRemoting metrics.
type Collector struct {
config Config
wmiClient *wmi.Client
config Config
logger log.Logger
channels *prometheus.Desc
contextBoundClassesLoaded *prometheus.Desc
@@ -31,7 +30,7 @@ type Collector struct {
totalRemoteCalls *prometheus.Desc
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -40,6 +39,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -51,21 +52,19 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
return errors.New("wmiClient or SWbemServicesClient is nil")
}
c.wmiClient = wmiClient
func (c *Collector) Build() error {
c.channels = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "channels_total"),
"Displays the total number of remoting channels registered across all application domains since application started.",
@@ -102,22 +101,16 @@ func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
[]string{"process"},
nil,
)
return nil
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) Collect(_ *types.ScrapeContext, ch chan<- prometheus.Metric) error {
if err := c.collect(ch); err != nil {
logger.Error("failed collecting win32_perfrawdata_netframework_netclrremoting metrics",
slog.Any("err", err),
)
_ = level.Error(c.logger).Log("msg", "failed collecting win32_perfrawdata_netframework_netclrremoting metrics", "err", err)
return err
}
return nil
}
@@ -135,7 +128,8 @@ type Win32_PerfRawData_NETFramework_NETCLRRemoting struct {
func (c *Collector) collect(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_NETFramework_NETCLRRemoting
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_NETFramework_NETCLRRemoting", &dst); err != nil {
q := wmi.QueryAll(&dst, c.logger)
if err := wmi.Query(q, &dst); err != nil {
return err
}

View File

@@ -3,13 +3,12 @@
package netframework_clrsecurity
import (
"errors"
"log/slog"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus-community/windows_exporter/pkg/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "netframework_clrsecurity"
@@ -20,8 +19,8 @@ var ConfigDefaults = Config{}
// A Collector is a Prometheus Collector for WMI Win32_PerfRawData_NETFramework_NETCLRSecurity metrics.
type Collector struct {
config Config
wmiClient *wmi.Client
config Config
logger log.Logger
numberLinkTimeChecks *prometheus.Desc
timeInRTChecks *prometheus.Desc
@@ -29,7 +28,7 @@ type Collector struct {
totalRuntimeChecks *prometheus.Desc
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -38,6 +37,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -49,20 +50,19 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
return errors.New("wmiClient or SWbemServicesClient is nil")
}
c.wmiClient = wmiClient
func (c *Collector) Build() error {
c.numberLinkTimeChecks = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "link_time_checks_total"),
"Displays the total number of link-time code access security checks since the application started.",
@@ -87,22 +87,16 @@ func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
[]string{"process"},
nil,
)
return nil
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) Collect(_ *types.ScrapeContext, ch chan<- prometheus.Metric) error {
if err := c.collect(ch); err != nil {
logger.Error("failed collecting win32_perfrawdata_netframework_netclrsecurity metrics",
slog.Any("err", err),
)
_ = level.Error(c.logger).Log("msg", "failed collecting win32_perfrawdata_netframework_netclrsecurity metrics", "err", err)
return err
}
return nil
}
@@ -119,7 +113,8 @@ type Win32_PerfRawData_NETFramework_NETCLRSecurity struct {
func (c *Collector) collect(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_NETFramework_NETCLRSecurity
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_NETFramework_NETCLRSecurity", &dst); err != nil {
q := wmi.QueryAll(&dst, c.logger)
if err := wmi.Query(q, &dst); err != nil {
return err
}

View File

@@ -1,14 +1,14 @@
package nps
import (
"errors"
"fmt"
"log/slog"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus-community/windows_exporter/pkg/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "nps"
@@ -19,8 +19,8 @@ var ConfigDefaults = Config{}
// Collector is a Prometheus Collector for WMI Win32_PerfRawData_IAS_NPSAuthenticationServer and Win32_PerfRawData_IAS_NPSAccountingServer metrics.
type Collector struct {
config Config
wmiClient *wmi.Client
config Config
logger log.Logger
accessAccepts *prometheus.Desc
accessChallenges *prometheus.Desc
@@ -50,7 +50,7 @@ type Collector struct {
accountingUnknownType *prometheus.Desc
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -59,6 +59,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -70,20 +72,19 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
return errors.New("wmiClient or SWbemServicesClient is nil")
}
c.wmiClient = wmiClient
func (c *Collector) Build() error {
c.accessAccepts = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "access_accepts"),
"(AccessAccepts)",
@@ -235,26 +236,20 @@ func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
nil,
nil,
)
return nil
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) Collect(_ *types.ScrapeContext, ch chan<- prometheus.Metric) error {
if err := c.CollectAccept(ch); err != nil {
logger.Error(fmt.Sprintf("failed collecting NPS accept data: %s", err))
_ = level.Error(c.logger).Log("msg", fmt.Sprintf("failed collecting NPS accept data: %s", err))
return err
}
if err := c.CollectAccounting(ch); err != nil {
logger.Error(fmt.Sprintf("failed collecting NPS accounting data: %s", err))
_ = level.Error(c.logger).Log("msg", fmt.Sprintf("failed collecting NPS accounting data: %s", err))
return err
}
return nil
}
@@ -299,7 +294,8 @@ type Win32_PerfRawData_IAS_NPSAccountingServer struct {
// to the provided prometheus Metric channel.
func (c *Collector) CollectAccept(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_IAS_NPSAuthenticationServer
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_IAS_NPSAuthenticationServer", &dst); err != nil {
q := wmi.QueryAll(&dst, c.logger)
if err := wmi.Query(q, &dst); err != nil {
return err
}
@@ -386,7 +382,8 @@ func (c *Collector) CollectAccept(ch chan<- prometheus.Metric) error {
func (c *Collector) CollectAccounting(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_IAS_NPSAccountingServer
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_IAS_NPSAccountingServer", &dst); err != nil {
q := wmi.QueryAll(&dst, c.logger)
if err := wmi.Query(q, &dst); err != nil {
return err
}

View File

@@ -5,13 +5,15 @@ package os
import (
"errors"
"fmt"
"log/slog"
"os"
"strconv"
"strings"
"syscall"
"time"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/headers/kernel32"
"github.com/prometheus-community/windows_exporter/pkg/headers/netapi32"
"github.com/prometheus-community/windows_exporter/pkg/headers/psapi"
@@ -19,8 +21,6 @@ import (
"github.com/prometheus-community/windows_exporter/pkg/perflib"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/registry"
)
@@ -33,46 +33,21 @@ var ConfigDefaults = Config{}
// A Collector is a Prometheus Collector for WMI metrics.
type Collector struct {
config Config
logger log.Logger
hostname *prometheus.Desc
osInformation *prometheus.Desc
pagingFreeBytes *prometheus.Desc
pagingLimitBytes *prometheus.Desc
// users
// Deprecated: Use windows_system_processes instead.
processes *prometheus.Desc
// users
// Deprecated: Use windows_system_process_limit instead.
processesLimit *prometheus.Desc
// users
// Deprecated: Use count(windows_logon_logon_type) instead.
users *prometheus.Desc
// physicalMemoryFreeBytes
// Deprecated: Use windows_memory_physical_free_bytes instead.
osInformation *prometheus.Desc
pagingFreeBytes *prometheus.Desc
pagingLimitBytes *prometheus.Desc
physicalMemoryFreeBytes *prometheus.Desc
// processMemoryLimitBytes
// Deprecated: Use windows_memory_process_memory_limit_bytes instead.
processMemoryLimitBytes *prometheus.Desc
// time
// Deprecated: Use windows_time_current_timestamp_seconds instead.
time *prometheus.Desc
// timezone
// Deprecated: Use windows_time_timezone instead.
timezone *prometheus.Desc
// virtualMemoryBytes
// Deprecated: Use windows_memory_commit_limit instead.
virtualMemoryBytes *prometheus.Desc
// virtualMemoryFreeBytes
// Deprecated: Use windows_memory_commit_limit instead.
virtualMemoryFreeBytes *prometheus.Desc
// visibleMemoryBytes
// Deprecated: Use windows_memory_physical_total_bytes instead.
visibleMemoryBytes *prometheus.Desc
processes *prometheus.Desc
processesLimit *prometheus.Desc
time *prometheus.Desc
timezone *prometheus.Desc
users *prometheus.Desc
virtualMemoryBytes *prometheus.Desc
virtualMemoryFreeBytes *prometheus.Desc
visibleMemoryBytes *prometheus.Desc
}
type pagingFileCounter struct {
@@ -81,7 +56,7 @@ type pagingFileCounter struct {
UsagePeak float64 `perflib:"% Usage Peak"`
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -90,6 +65,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -101,50 +78,23 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{"Paging File"}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error {
logger.Warn("The os collect holds a number of deprecated metrics and will be removed mid 2025. " +
"See https://github.com/prometheus-community/windows_exporter/pull/1596 for more information.")
workstationInfo, err := netapi32.GetWorkstationInfo()
if err != nil {
return fmt.Errorf("failed to get workstation info: %w", err)
}
productName, buildNumber, revision, err := c.getWindowsVersion()
if err != nil {
return fmt.Errorf("failed to get Windows version: %w", err)
}
func (c *Collector) Build() error {
c.osInformation = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "info"),
`Contains full product name & version in labels. Note that the "major_version" for Windows 11 is \"10\"; a build number greater than 22000 represents Windows 11.`,
nil,
prometheus.Labels{
"product": productName,
"version": fmt.Sprintf("%d.%d.%s", workstationInfo.VersionMajor, workstationInfo.VersionMinor, buildNumber),
"major_version": strconv.FormatUint(uint64(workstationInfo.VersionMajor), 10),
"minor_version": strconv.FormatUint(uint64(workstationInfo.VersionMinor), 10),
"build_number": buildNumber,
"revision": revision,
},
)
c.hostname = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "hostname"),
"Labelled system hostname information as provided by ComputerSystem.DNSHostName and ComputerSystem.Domain",
[]string{
"hostname",
"domain",
"fqdn",
},
"OperatingSystem.Caption, OperatingSystem.Version",
[]string{"product", "version", "major_version", "minor_version", "build_number", "revision"},
nil,
)
c.pagingLimitBytes = prometheus.NewDesc(
@@ -161,228 +111,116 @@ func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error {
)
c.physicalMemoryFreeBytes = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "physical_memory_free_bytes"),
"Deprecated: Use `windows_memory_physical_free_bytes` instead.",
"OperatingSystem.FreePhysicalMemory",
nil,
nil,
)
c.time = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "time"),
"Deprecated: Use windows_time_current_timestamp_seconds instead.",
"OperatingSystem.LocalDateTime",
nil,
nil,
)
c.timezone = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "timezone"),
"Deprecated: Use windows_time_timezone instead.",
"OperatingSystem.LocalDateTime",
[]string{"timezone"},
nil,
)
c.processes = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "processes"),
"Deprecated: Use `windows_system_processes` instead.",
"OperatingSystem.NumberOfProcesses",
nil,
nil,
)
c.processesLimit = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "processes_limit"),
"Deprecated: Use `windows_system_process_limit` instead.",
"OperatingSystem.MaxNumberOfProcesses",
nil,
nil,
)
c.processMemoryLimitBytes = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "process_memory_limit_bytes"),
"Deprecated: Use `windows_memory_process_memory_limit_bytes` instead.",
"OperatingSystem.MaxProcessMemorySize",
nil,
nil,
)
c.users = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "users"),
"Deprecated: Use `count(windows_logon_logon_type)` instead.",
"OperatingSystem.NumberOfUsers",
nil,
nil,
)
c.virtualMemoryBytes = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "virtual_memory_bytes"),
"Deprecated: Use `windows_memory_commit_limit` instead.",
"OperatingSystem.TotalVirtualMemorySize",
nil,
nil,
)
c.visibleMemoryBytes = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "visible_memory_bytes"),
"Deprecated: Use `windows_memory_physical_total_bytes` instead.",
"OperatingSystem.TotalVisibleMemorySize",
nil,
nil,
)
c.virtualMemoryFreeBytes = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "virtual_memory_free_bytes"),
"Deprecated: Use `windows_memory_commit_limit - windows_memory_committed_bytes` instead.",
"OperatingSystem.FreeVirtualMemory",
nil,
nil,
)
return nil
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
errs := make([]error, 0, 5)
c.collect(ch)
if err := c.collectHostname(ch); err != nil {
logger.Error("failed collecting os metrics",
slog.Any("err", err),
)
errs = append(errs, err)
}
if err := c.collectLoggedInUserCount(ch); err != nil {
logger.Error("failed collecting os user count metrics",
slog.Any("err", err),
)
errs = append(errs, err)
}
if err := c.collectMemory(ch); err != nil {
logger.Error("failed collecting os memory metrics",
slog.Any("err", err),
)
errs = append(errs, err)
}
if err := c.collectTime(ch); err != nil {
logger.Error("failed collecting os time metrics",
slog.Any("err", err),
)
errs = append(errs, err)
}
if err := c.collectPaging(ctx, logger, ch); err != nil {
logger.Error("failed collecting os paging metrics",
slog.Any("err", err),
)
errs = append(errs, err)
}
return errors.Join(errs...)
}
func (c *Collector) collectLoggedInUserCount(ch chan<- prometheus.Metric) error {
workstationInfo, err := netapi32.GetWorkstationInfo()
if err != nil {
func (c *Collector) Collect(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
if err := c.collect(ctx, ch); err != nil {
_ = level.Error(c.logger).Log("msg", "failed collecting os metrics", "err", err)
return err
}
ch <- prometheus.MustNewConstMetric(
c.users,
prometheus.GaugeValue,
float64(workstationInfo.LoggedOnUsers),
)
return nil
}
func (c *Collector) collectHostname(ch chan<- prometheus.Metric) error {
hostname, err := sysinfoapi.GetComputerName(sysinfoapi.ComputerNameDNSHostname)
if err != nil {
return err
}
domain, err := sysinfoapi.GetComputerName(sysinfoapi.ComputerNameDNSDomain)
if err != nil {
return err
}
fqdn, err := sysinfoapi.GetComputerName(sysinfoapi.ComputerNameDNSFullyQualified)
if err != nil {
return err
}
ch <- prometheus.MustNewConstMetric(
c.hostname,
prometheus.GaugeValue,
1.0,
hostname,
domain,
fqdn,
)
return nil
// Win32_OperatingSystem docs:
// - https://msdn.microsoft.com/en-us/library/aa394239 - Win32_OperatingSystem class.
type Win32_OperatingSystem struct {
Caption string
FreePhysicalMemory uint64
FreeSpaceInPagingFiles uint64
FreeVirtualMemory uint64
LocalDateTime time.Time
MaxNumberOfProcesses uint32
MaxProcessMemorySize uint64
NumberOfProcesses uint32
NumberOfUsers uint32
SizeStoredInPagingFiles uint64
TotalVirtualMemorySize uint64
TotalVisibleMemorySize uint64
Version string
}
func (c *Collector) collectTime(ch chan<- prometheus.Metric) error {
func (c *Collector) collect(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
nwgi, err := netapi32.GetWorkstationInfo()
if err != nil {
return err
}
gmse, err := sysinfoapi.GlobalMemoryStatusEx()
if err != nil {
return err
}
currentTime := time.Now()
timeZoneInfo, err := kernel32.GetDynamicTimeZoneInformation()
if err != nil {
return err
}
// timeZoneKeyName contains the english name of the timezone.
timezoneName := windows.UTF16ToString(timeZoneInfo.TimeZoneKeyName[:])
timezoneName := syscall.UTF16ToString(timeZoneInfo.TimeZoneKeyName[:])
ch <- prometheus.MustNewConstMetric(
c.time,
prometheus.GaugeValue,
float64(time.Now().Unix()),
)
ch <- prometheus.MustNewConstMetric(
c.timezone,
prometheus.GaugeValue,
1.0,
timezoneName,
)
return nil
}
func (c *Collector) collectMemory(ch chan<- prometheus.Metric) error {
memoryStatusEx, err := sysinfoapi.GlobalMemoryStatusEx()
if err != nil {
return err
}
ch <- prometheus.MustNewConstMetric(
c.physicalMemoryFreeBytes,
prometheus.GaugeValue,
float64(memoryStatusEx.AvailPhys),
)
ch <- prometheus.MustNewConstMetric(
c.virtualMemoryFreeBytes,
prometheus.GaugeValue,
float64(memoryStatusEx.AvailPageFile),
)
ch <- prometheus.MustNewConstMetric(
c.virtualMemoryBytes,
prometheus.GaugeValue,
float64(memoryStatusEx.TotalPageFile),
)
ch <- prometheus.MustNewConstMetric(
c.visibleMemoryBytes,
prometheus.GaugeValue,
float64(memoryStatusEx.TotalPhys),
)
ch <- prometheus.MustNewConstMetric(
c.processMemoryLimitBytes,
prometheus.GaugeValue,
float64(memoryStatusEx.TotalVirtual),
)
return nil
}
func (c *Collector) collectPaging(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
// Get total allocation of paging files across all disks.
memManKey, err := registry.OpenKey(registry.LOCAL_MACHINE, `SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management`, registry.QUERY_VALUE)
if err != nil {
@@ -394,43 +232,96 @@ func (c *Collector) collectPaging(ctx *types.ScrapeContext, logger *slog.Logger,
pagingFiles, _, pagingErr := memManKey.GetStringsValue("ExistingPageFiles")
var fsipf float64
for _, pagingFile := range pagingFiles {
fileString := strings.ReplaceAll(pagingFile, `\??\`, "")
file, err := os.Stat(fileString)
// For unknown reasons, Windows doesn't always create a page file. Continue collection rather than aborting.
if err != nil {
logger.Debug(fmt.Sprintf("Failed to read page file (reason: %s): %s\n", err, fileString))
_ = level.Debug(c.logger).Log("msg", fmt.Sprintf("Failed to read page file (reason: %s): %s\n", err, fileString))
} else {
fsipf += float64(file.Size())
}
}
// Get build number and product name from registry
ntKey, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE)
if err != nil {
return err
}
defer ntKey.Close()
pn, _, err := ntKey.GetStringValue("ProductName")
if err != nil {
return err
}
bn, _, err := ntKey.GetStringValue("CurrentBuildNumber")
if err != nil {
return err
}
revision, _, err := ntKey.GetIntegerValue("UBR")
if errors.Is(err, registry.ErrNotExist) {
revision = 0
} else if err != nil {
return err
}
gpi, err := psapi.GetPerformanceInfo()
if err != nil {
return err
}
pfc := make([]pagingFileCounter, 0)
if err = perflib.UnmarshalObject(ctx.PerfObjects["Paging File"], &pfc, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects["Paging File"], &pfc, c.logger); err != nil {
return err
}
// Get current page file usage.
var pfbRaw float64
for _, pageFile := range pfc {
if strings.Contains(strings.ToLower(pageFile.Name), "_total") {
continue
}
pfbRaw += pageFile.Usage
}
if pagingErr == nil {
// Subtract from total page file allocation on disk.
pfb := fsipf - (pfbRaw * float64(gpi.PageSize))
// Subtract from total page file allocation on disk.
pfb := fsipf - (pfbRaw * float64(gpi.PageSize))
ch <- prometheus.MustNewConstMetric(
c.osInformation,
prometheus.GaugeValue,
1.0,
"Microsoft "+pn, // Caption
fmt.Sprintf("%d.%d.%s", nwgi.VersionMajor, nwgi.VersionMinor, bn), // Version
strconv.FormatUint(uint64(nwgi.VersionMajor), 10), // Major Version
strconv.FormatUint(uint64(nwgi.VersionMinor), 10), // Minor Version
bn, // Build number
strconv.FormatUint(revision, 10), // Revision
)
ch <- prometheus.MustNewConstMetric(
c.physicalMemoryFreeBytes,
prometheus.GaugeValue,
float64(gmse.AvailPhys),
)
ch <- prometheus.MustNewConstMetric(
c.time,
prometheus.GaugeValue,
float64(currentTime.Unix()),
)
ch <- prometheus.MustNewConstMetric(
c.timezone,
prometheus.GaugeValue,
1.0,
timezoneName,
)
if pagingErr == nil {
ch <- prometheus.MustNewConstMetric(
c.pagingFreeBytes,
prometheus.GaugeValue,
@@ -443,23 +334,12 @@ func (c *Collector) collectPaging(ctx *types.ScrapeContext, logger *slog.Logger,
fsipf,
)
} else {
logger.Debug("Could not find HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Memory Management key. windows_os_paging_free_bytes and windows_os_paging_limit_bytes will be omitted.")
_ = level.Debug(c.logger).Log("msg", "Could not find HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Memory Management key. windows_os_paging_free_bytes and windows_os_paging_limit_bytes will be omitted.")
}
ch <- prometheus.MustNewConstMetric(
c.processes,
c.virtualMemoryFreeBytes,
prometheus.GaugeValue,
float64(gpi.ProcessCount),
)
return nil
}
func (c *Collector) collect(ch chan<- prometheus.Metric) {
ch <- prometheus.MustNewConstMetric(
c.osInformation,
prometheus.GaugeValue,
1.0,
float64(gmse.AvailPageFile),
)
// Windows has no defined limit, and is based off available resources. This currently isn't calculated by WMI and is set to default value.
@@ -470,33 +350,36 @@ func (c *Collector) collect(ch chan<- prometheus.Metric) {
prometheus.GaugeValue,
float64(4294967295),
)
}
func (c *Collector) getWindowsVersion() (string, string, string, error) {
// Get build number and product name from registry
ntKey, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE)
if err != nil {
return "", "", "", fmt.Errorf("failed to open registry key: %w", err)
}
defer ntKey.Close()
productName, _, err := ntKey.GetStringValue("ProductName")
if err != nil {
return "", "", "", err
}
buildNumber, _, err := ntKey.GetStringValue("CurrentBuildNumber")
if err != nil {
return "", "", "", err
}
revision, _, err := ntKey.GetIntegerValue("UBR")
if errors.Is(err, registry.ErrNotExist) {
revision = 0
} else if err != nil {
return "", "", "", err
}
return productName, buildNumber, strconv.FormatUint(revision, 10), nil
ch <- prometheus.MustNewConstMetric(
c.processMemoryLimitBytes,
prometheus.GaugeValue,
float64(gmse.TotalVirtual),
)
ch <- prometheus.MustNewConstMetric(
c.processes,
prometheus.GaugeValue,
float64(gpi.ProcessCount),
)
ch <- prometheus.MustNewConstMetric(
c.users,
prometheus.GaugeValue,
float64(nwgi.LoggedOnUsers),
)
ch <- prometheus.MustNewConstMetric(
c.virtualMemoryBytes,
prometheus.GaugeValue,
float64(gmse.TotalPageFile),
)
ch <- prometheus.MustNewConstMetric(
c.visibleMemoryBytes,
prometheus.GaugeValue,
float64(gmse.TotalPhys),
)
return nil
}

View File

@@ -1,181 +0,0 @@
//go:build windows
package perfdata
import (
"encoding/json"
"fmt"
"log/slog"
"maps"
"slices"
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/pkg/perfdata"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const (
Name = "perfdata"
)
type Config struct {
Objects []Object `yaml:"objects"`
}
var ConfigDefaults = Config{
Objects: make([]Object, 0),
}
// A Collector is a Prometheus collector for perfdata metrics.
type Collector struct {
config Config
}
func New(config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
if config.Objects == nil {
config.Objects = ConfigDefaults.Objects
}
c := &Collector{
config: *config,
}
return c
}
func NewWithFlags(app *kingpin.Application) *Collector {
c := &Collector{
config: ConfigDefaults,
}
var objects string
app.Flag(
"collector.perfdata.objects",
"Objects of performance data to observe. See docs for more information on how to use this flag. By default, no objects are observed.",
).Default("").StringVar(&objects)
app.Action(func(*kingpin.ParseContext) error {
if objects == "" {
return nil
}
if err := json.Unmarshal([]byte(objects), &c.config.Objects); err != nil {
return fmt.Errorf("failed to parse objects: %w", err)
}
return nil
})
return c
}
func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
return []string{}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
for _, object := range c.config.Objects {
object.collector.Close()
}
return nil
}
func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error {
logger.Warn("The perfdata collector is in an experimental state! The configuration may change in future. Please report any issues.")
for i, object := range c.config.Objects {
collector, err := perfdata.NewCollector(object.Object, object.Instances, slices.Sorted(maps.Keys(object.Counters)))
if err != nil {
return fmt.Errorf("failed to create pdh collector: %w", err)
}
if object.InstanceLabel == "" {
c.config.Objects[i].InstanceLabel = "instance"
}
c.config.Objects[i].collector = collector
}
return nil
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
if err := c.collect(ch); err != nil {
logger.Error("failed collecting performance data metrics",
slog.Any("err", err),
)
return err
}
return nil
}
func (c *Collector) collect(ch chan<- prometheus.Metric) error {
for _, object := range c.config.Objects {
data, err := object.collector.Collect()
if err != nil {
return fmt.Errorf("failed to collect data: %w", err)
}
for instance, counters := range data {
for counter, value := range counters {
var labels prometheus.Labels
if instance != perfdata.EmptyInstance {
labels = prometheus.Labels{object.InstanceLabel: instance}
}
metricType := value.Type
if val, ok := object.Counters[counter]; ok {
switch val.Type {
case "counter":
metricType = prometheus.CounterValue
case "gauge":
metricType = prometheus.GaugeValue
}
}
ch <- prometheus.MustNewConstMetric(
prometheus.NewDesc(
sanitizeMetricName(fmt.Sprintf("%s_perfdata_%s_%s", types.Namespace, object.Object, counter)),
fmt.Sprintf("Performance data for \\%s\\%s", object.Object, counter),
nil,
labels,
),
metricType,
value.FirstValue,
)
}
}
}
return nil
}
func sanitizeMetricName(name string) string {
replacer := strings.NewReplacer(
".", "",
"%", "",
"/", "_",
" ", "_",
"-", "_",
)
return strings.Trim(replacer.Replace(strings.ToLower(name)), "_")
}

View File

@@ -1,87 +0,0 @@
//go:build windows
package perfdata_test
import (
"fmt"
"io"
"log/slog"
"net/http"
"net/http/httptest"
"regexp"
"testing"
"github.com/prometheus-community/windows_exporter/pkg/collector/perfdata"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
type collectorAdapter struct {
perfdata.Collector
}
// Describe implements the prometheus.Collector interface.
func (a collectorAdapter) Describe(_ chan<- *prometheus.Desc) {}
// Collect implements the prometheus.Collector interface.
func (a collectorAdapter) Collect(ch chan<- prometheus.Metric) {
logger := slog.New(slog.NewTextHandler(io.Discard, nil))
if err := a.Collector.Collect(nil, logger, ch); err != nil {
panic(fmt.Sprintf("failed to update collector: %v", err))
}
}
func TestCollector(t *testing.T) {
t.Parallel()
for _, tc := range []struct {
object string
instances []string
counters map[string]perfdata.Counter
expectedMetrics *regexp.Regexp
}{
{
object: "Memory",
instances: nil,
counters: map[string]perfdata.Counter{"Available Bytes": {Type: "gauge"}},
expectedMetrics: regexp.MustCompile(`^# HELP windows_perfdata_memory_available_bytes Performance data for \\\\Memory\\\\Available Bytes\s*# TYPE windows_perfdata_memory_available_bytes gauge\s*windows_perfdata_memory_available_bytes \d`),
},
{
object: "Process",
instances: []string{"*"},
counters: map[string]perfdata.Counter{"Thread Count": {Type: "counter"}},
expectedMetrics: regexp.MustCompile(`^# HELP windows_perfdata_process_thread_count Performance data for \\\\Process\\\\Thread Count\s*# TYPE windows_perfdata_process_thread_count counter\s*windows_perfdata_process_thread_count\{instance=".+"} \d`),
},
} {
t.Run(tc.object, func(t *testing.T) {
t.Parallel()
perfDataCollector := perfdata.New(&perfdata.Config{
Objects: []perfdata.Object{
{
Object: tc.object,
Instances: tc.instances,
Counters: tc.counters,
},
},
})
logger := slog.New(slog.NewTextHandler(io.Discard, nil))
err := perfDataCollector.Build(logger, nil)
require.NoError(t, err)
registry := prometheus.NewRegistry()
registry.MustRegister(collectorAdapter{*perfDataCollector})
rw := httptest.NewRecorder()
promhttp.HandlerFor(registry, promhttp.HandlerOpts{ErrorHandling: promhttp.ContinueOnError}).ServeHTTP(rw, &http.Request{})
got := rw.Body.String()
assert.NotEmpty(t, got)
assert.Regexp(t, tc.expectedMetrics, got)
})
}
}

View File

@@ -1,18 +0,0 @@
//go:build windows
package perfdata_test
import (
"testing"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/pkg/collector/perfdata"
"github.com/prometheus-community/windows_exporter/pkg/testutils"
)
func BenchmarkCollector(b *testing.B) {
perfDataObjects := `[{"object":"Processor Information","instances":["*"],"counters":{"*": {}}}]`
kingpin.CommandLine.GetArg("collector.perfdata.objects").StringVar(&perfDataObjects)
testutils.FuncBenchmarkCollector(b, perfdata.Name, perfdata.NewWithFlags)
}

View File

@@ -1,18 +0,0 @@
package perfdata
import (
"github.com/prometheus-community/windows_exporter/pkg/perfdata"
)
type Object struct {
Object string `json:"object" yaml:"object"`
Instances []string `json:"instances" yaml:"instances"`
Counters map[string]Counter `json:"counters" yaml:"counters"`
InstanceLabel string `json:"instance_label" yaml:"instance_label"` //nolint:tagliatelle
collector *perfdata.Collector
}
type Counter struct {
Type string `json:"type" yaml:"type"`
}

View File

@@ -4,15 +4,15 @@ package physical_disk
import (
"fmt"
"log/slog"
"regexp"
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/perflib"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "physical_disk"
@@ -30,6 +30,7 @@ var ConfigDefaults = Config{
// A Collector is a Prometheus Collector for perflib PhysicalDisk metrics.
type Collector struct {
config Config
logger log.Logger
idleTime *prometheus.Desc
readBytesTotal *prometheus.Desc
@@ -45,7 +46,7 @@ type Collector struct {
writesTotal *prometheus.Desc
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -62,6 +63,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -105,15 +108,19 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{"PhysicalDisk"}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build() error {
c.requestsQueued = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "requests_queued"),
"The number of requests queued to the disk (PhysicalDisk.CurrentDiskQueueLength)",
@@ -203,16 +210,11 @@ func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
if err := c.collect(ctx, logger, ch); err != nil {
logger.Error("failed collecting physical_disk metrics",
slog.Any("err", err),
)
func (c *Collector) Collect(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
if err := c.collect(ctx, ch); err != nil {
_ = level.Error(c.logger).Log("msg", "failed collecting physical_disk metrics", "err", err)
return err
}
return nil
}
@@ -235,12 +237,9 @@ type PhysicalDisk struct {
AvgDiskSecPerTransfer float64 `perflib:"Avg. Disk sec/Transfer"`
}
func (c *Collector) collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) collect(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
var dst []PhysicalDisk
if err := perflib.UnmarshalObject(ctx.PerfObjects["PhysicalDisk"], &dst, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects["PhysicalDisk"], &dst, c.logger); err != nil {
return err
}

View File

@@ -3,16 +3,16 @@
package printer
import (
"errors"
"fmt"
"log/slog"
"regexp"
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus-community/windows_exporter/pkg/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "printer"
@@ -39,15 +39,15 @@ var ConfigDefaults = Config{
}
type Collector struct {
config Config
wmiClient *wmi.Client
config Config
logger log.Logger
printerStatus *prometheus.Desc
printerJobStatus *prometheus.Desc
printerJobCount *prometheus.Desc
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -64,6 +64,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -103,17 +105,15 @@ func NewWithFlags(app *kingpin.Application) *Collector {
return c
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
return errors.New("wmiClient or SWbemServicesClient is nil")
}
c.wmiClient = wmiClient
func (c *Collector) Build() error {
c.printerJobStatus = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "job_status"),
"A counter of printer jobs by status",
@@ -138,9 +138,7 @@ func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
func (c *Collector) GetName() string { return Name }
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
return []string{"Printer"}, nil
}
func (c *Collector) GetPerfCounter() ([]string, error) { return []string{"Printer"}, nil }
type wmiPrinter struct {
Name string
@@ -154,21 +152,14 @@ type wmiPrintJob struct {
Status string
}
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) Collect(_ *types.ScrapeContext, ch chan<- prometheus.Metric) error {
if err := c.collectPrinterStatus(ch); err != nil {
logger.Error("failed to collect printer status metrics",
slog.Any("err", err),
)
_ = level.Error(c.logger).Log("msg", "failed to collect printer status metrics", "err", err)
return err
}
if err := c.collectPrinterJobStatus(ch); err != nil {
logger.Error("failed to collect printer job status metrics",
slog.Any("err", err),
)
_ = level.Error(c.logger).Log("msg", "failed to collect printer job status metrics", "err", err)
return err
}
@@ -177,7 +168,9 @@ func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan
func (c *Collector) collectPrinterStatus(ch chan<- prometheus.Metric) error {
var printers []wmiPrinter
if err := c.wmiClient.Query("SELECT * FROM win32_Printer", &printers); err != nil {
q := wmi.QueryAllForClass(&printers, "win32_Printer", c.logger)
if err := wmi.Query(q, &printers); err != nil {
return err
}
@@ -215,7 +208,9 @@ func (c *Collector) collectPrinterStatus(ch chan<- prometheus.Metric) error {
func (c *Collector) collectPrinterJobStatus(ch chan<- prometheus.Metric) error {
var printJobs []wmiPrintJob
if err := c.wmiClient.Query("SELECT * FROM win32_PrintJob", &printJobs); err != nil {
q := wmi.QueryAllForClass(&printJobs, "win32_PrintJob", c.logger)
if err := wmi.Query(q, &printJobs); err != nil {
return err
}

View File

@@ -5,17 +5,18 @@ package process
import (
"errors"
"fmt"
"log/slog"
"regexp"
"strconv"
"strings"
"unsafe"
"syscall"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/perflib"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus-community/windows_exporter/pkg/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
"golang.org/x/sys/windows"
)
@@ -25,21 +26,22 @@ type Config struct {
ProcessInclude *regexp.Regexp `yaml:"process_include"`
ProcessExclude *regexp.Regexp `yaml:"process_exclude"`
EnableWorkerProcess bool `yaml:"enable_iis_worker_process"` //nolint:tagliatelle
EnableReportOwner bool `yaml:"enable_report_owner"`
}
var ConfigDefaults = Config{
ProcessInclude: types.RegExpAny,
ProcessExclude: types.RegExpEmpty,
EnableWorkerProcess: false,
EnableReportOwner: false,
}
type Collector struct {
config Config
wmiClient *wmi.Client
config Config
logger log.Logger
lookupCache map[string]string
info *prometheus.Desc
cpuTimeTotal *prometheus.Desc
handleCount *prometheus.Desc
ioBytesTotal *prometheus.Desc
@@ -57,7 +59,7 @@ type Collector struct {
workingSetPrivate *prometheus.Desc
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -74,6 +76,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -99,6 +103,11 @@ func NewWithFlags(app *kingpin.Application) *Collector {
"Enable IIS worker process name queries. May cause the collector to leak memory.",
).Default(strconv.FormatBool(c.config.EnableWorkerProcess)).BoolVar(&c.config.EnableWorkerProcess)
app.Flag(
"collector.process.report-owner",
"Enable reporting of process owner.",
).Default(strconv.FormatBool(c.config.EnableReportOwner)).BoolVar(&c.config.EnableReportOwner)
app.Action(func(*kingpin.ParseContext) error {
var err error
@@ -122,122 +131,116 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{"Process"}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(logger *slog.Logger, wmiClient *wmi.Client) error {
logger = logger.With(slog.String("collector", Name))
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
return errors.New("wmiClient or SWbemServicesClient is nil")
}
c.wmiClient = wmiClient
func (c *Collector) Build() error {
if c.config.ProcessInclude.String() == "^(?:.*)$" && c.config.ProcessExclude.String() == "^(?:)$" {
logger.Warn("No filters specified for process collector. This will generate a very large number of metrics!")
_ = level.Warn(c.logger).Log("msg", "No filters specified for process collector. This will generate a very large number of metrics!")
}
c.info = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "info"),
"Process information.",
[]string{"process", "process_id", "creating_process_id", "process_group_id", "owner", "cmdline"},
nil,
)
commonLabels := make([]string, 0)
if c.config.EnableReportOwner {
commonLabels = []string{"owner"}
}
c.startTime = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "start_time"),
"Time of process start.",
[]string{"process", "process_id"},
append(commonLabels, "process", "process_id", "creating_process_id"),
nil,
)
c.cpuTimeTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "cpu_time_total"),
"Returns elapsed time that all of the threads of this process used the processor to execute instructions by mode (privileged, user).",
[]string{"process", "process_id", "mode"},
append(commonLabels, "process", "process_id", "creating_process_id", "mode"),
nil,
)
c.handleCount = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "handles"),
"Total number of handles the process has open. This number is the sum of the handles currently open by each thread in the process.",
[]string{"process", "process_id"},
append(commonLabels, "process", "process_id", "creating_process_id"),
nil,
)
c.ioBytesTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "io_bytes_total"),
"Bytes issued to I/O operations in different modes (read, write, other).",
[]string{"process", "process_id", "mode"},
append(commonLabels, "process", "process_id", "creating_process_id", "mode"),
nil,
)
c.ioOperationsTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "io_operations_total"),
"I/O operations issued in different modes (read, write, other).",
[]string{"process", "process_id", "mode"},
append(commonLabels, "process", "process_id", "creating_process_id", "mode"),
nil,
)
c.pageFaultsTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "page_faults_total"),
"Page faults by the threads executing in this process.",
[]string{"process", "process_id"},
append(commonLabels, "process", "process_id", "creating_process_id"),
nil,
)
c.pageFileBytes = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "page_file_bytes"),
"Current number of bytes this process has used in the paging file(s).",
[]string{"process", "process_id"},
append(commonLabels, "process", "process_id", "creating_process_id"),
nil,
)
c.poolBytes = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "pool_bytes"),
"Pool Bytes is the last observed number of bytes in the paged or nonpaged pool.",
[]string{"process", "process_id", "pool"},
append(commonLabels, "process", "process_id", "creating_process_id", "pool"),
nil,
)
c.priorityBase = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "priority_base"),
"Current base priority of this process. Threads within a process can raise and lower their own base priority relative to the process base priority of the process.",
[]string{"process", "process_id"},
append(commonLabels, "process", "process_id", "creating_process_id"),
nil,
)
c.privateBytes = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "private_bytes"),
"Current number of bytes this process has allocated that cannot be shared with other processes.",
[]string{"process", "process_id"},
append(commonLabels, "process", "process_id", "creating_process_id"),
nil,
)
c.threadCount = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "threads"),
"Number of threads currently active in this process.",
[]string{"process", "process_id"},
append(commonLabels, "process", "process_id", "creating_process_id"),
nil,
)
c.virtualBytes = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "virtual_bytes"),
"Current size, in bytes, of the virtual address space that the process is using.",
[]string{"process", "process_id"},
append(commonLabels, "process", "process_id", "creating_process_id"),
nil,
)
c.workingSetPrivate = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "working_set_private_bytes"),
"Size of the working set, in bytes, that is use for this process only and not shared nor shareable by other processes.",
[]string{"process", "process_id"},
append(commonLabels, "process", "process_id", "creating_process_id"),
nil,
)
c.workingSetPeak = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "working_set_peak_bytes"),
"Maximum size, in bytes, of the Working Set of this process at any point in time. The Working Set is the set of memory pages touched recently by the threads in the process.",
[]string{"process", "process_id"},
append(commonLabels, "process", "process_id", "creating_process_id"),
nil,
)
c.workingSet = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "working_set_bytes"),
"Maximum number of bytes in the working set of this process at any point in time. The working set is the set of memory pages touched recently by the threads in the process.",
[]string{"process", "process_id"},
append(commonLabels, "process", "process_id", "creating_process_id"),
nil,
)
@@ -283,24 +286,23 @@ type WorkerProcess struct {
ProcessId uint64
}
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) Collect(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
data := make([]perflibProcess, 0)
err := perflib.UnmarshalObject(ctx.PerfObjects["Process"], &data, logger)
err := perflib.UnmarshalObject(ctx.PerfObjects["Process"], &data, c.logger)
if err != nil {
return err
}
var workerProcesses []WorkerProcess
if c.config.EnableWorkerProcess {
if err := c.wmiClient.Query("SELECT * FROM WorkerProcess", &workerProcesses, nil, "root\\WebAdministration"); err != nil {
logger.Debug("Could not query WebAdministration namespace for IIS worker processes",
slog.Any("err", err),
)
queryWorkerProcess := wmi.QueryAllForClass(&workerProcesses, "WorkerProcess", c.logger)
if err := wmi.QueryNamespace(queryWorkerProcess, &workerProcesses, "root\\WebAdministration"); err != nil {
_ = level.Debug(c.logger).Log("msg", "Could not query WebAdministration namespace for IIS worker processes", "err", err)
}
}
var owner string
for _, process := range data {
if process.Name == "_Total" ||
c.config.ProcessExclude.MatchString(process.Name) ||
@@ -317,172 +319,169 @@ func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch ch
for _, wp := range workerProcesses {
if wp.ProcessId == uint64(process.IDProcess) {
processName = strings.Join([]string{processName, wp.AppPoolName}, "_")
break
}
}
}
cmdLine, processOwner, processGroupID, err := c.getProcessInformation(logger, uint32(process.IDProcess))
if err != nil {
logger.Debug("Failed to get process information",
slog.String("pid", pid),
slog.Any("err", err),
)
labels := make([]string, 0, 4)
if c.config.EnableReportOwner {
owner, err = c.getProcessOwner(int(process.IDProcess))
if err != nil {
owner = "unknown"
}
labels = []string{owner}
}
ch <- prometheus.MustNewConstMetric(
c.info,
prometheus.GaugeValue,
1.0,
processName, pid, parentPID, strconv.Itoa(int(processGroupID)), processOwner, cmdLine,
)
labels = append(labels, processName, pid, parentPID)
ch <- prometheus.MustNewConstMetric(
c.startTime,
prometheus.GaugeValue,
process.ElapsedTime,
processName, pid,
labels...,
)
ch <- prometheus.MustNewConstMetric(
c.handleCount,
prometheus.GaugeValue,
process.HandleCount,
processName, pid,
labels...,
)
ch <- prometheus.MustNewConstMetric(
c.cpuTimeTotal,
prometheus.CounterValue,
process.PercentPrivilegedTime,
processName, pid, "privileged",
append(labels, "privileged")...,
)
ch <- prometheus.MustNewConstMetric(
c.cpuTimeTotal,
prometheus.CounterValue,
process.PercentUserTime,
processName, pid, "user",
append(labels, "user")...,
)
ch <- prometheus.MustNewConstMetric(
c.ioBytesTotal,
prometheus.CounterValue,
process.IOOtherBytesPerSec,
processName, pid, "other",
append(labels, "other")...,
)
ch <- prometheus.MustNewConstMetric(
c.ioOperationsTotal,
prometheus.CounterValue,
process.IOOtherOperationsPerSec,
processName, pid, "other",
append(labels, "other")...,
)
ch <- prometheus.MustNewConstMetric(
c.ioBytesTotal,
prometheus.CounterValue,
process.IOReadBytesPerSec,
processName, pid, "read",
append(labels, "read")...,
)
ch <- prometheus.MustNewConstMetric(
c.ioOperationsTotal,
prometheus.CounterValue,
process.IOReadOperationsPerSec,
processName, pid, "read",
append(labels, "read")...,
)
ch <- prometheus.MustNewConstMetric(
c.ioBytesTotal,
prometheus.CounterValue,
process.IOWriteBytesPerSec,
processName, pid, "write",
append(labels, "write")...,
)
ch <- prometheus.MustNewConstMetric(
c.ioOperationsTotal,
prometheus.CounterValue,
process.IOWriteOperationsPerSec,
processName, pid, "write",
append(labels, "write")...,
)
ch <- prometheus.MustNewConstMetric(
c.pageFaultsTotal,
prometheus.CounterValue,
process.PageFaultsPerSec,
processName, pid,
labels...,
)
ch <- prometheus.MustNewConstMetric(
c.pageFileBytes,
prometheus.GaugeValue,
process.PageFileBytes,
processName, pid,
labels...,
)
ch <- prometheus.MustNewConstMetric(
c.poolBytes,
prometheus.GaugeValue,
process.PoolNonPagedBytes,
processName, pid, "nonpaged",
append(labels, "nonpaged")...,
)
ch <- prometheus.MustNewConstMetric(
c.poolBytes,
prometheus.GaugeValue,
process.PoolPagedBytes,
processName, pid, "paged",
append(labels, "paged")...,
)
ch <- prometheus.MustNewConstMetric(
c.priorityBase,
prometheus.GaugeValue,
process.PriorityBase,
processName, pid,
labels...,
)
ch <- prometheus.MustNewConstMetric(
c.privateBytes,
prometheus.GaugeValue,
process.PrivateBytes,
processName, pid,
labels...,
)
ch <- prometheus.MustNewConstMetric(
c.threadCount,
prometheus.GaugeValue,
process.ThreadCount,
processName, pid,
labels...,
)
ch <- prometheus.MustNewConstMetric(
c.virtualBytes,
prometheus.GaugeValue,
process.VirtualBytes,
processName, pid,
labels...,
)
ch <- prometheus.MustNewConstMetric(
c.workingSetPrivate,
prometheus.GaugeValue,
process.WorkingSetPrivate,
processName, pid,
labels...,
)
ch <- prometheus.MustNewConstMetric(
c.workingSetPeak,
prometheus.GaugeValue,
process.WorkingSetPeak,
processName, pid,
labels...,
)
ch <- prometheus.MustNewConstMetric(
c.workingSet,
prometheus.GaugeValue,
process.WorkingSet,
processName, pid,
labels...,
)
}
@@ -490,156 +489,39 @@ func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch ch
}
// ref: https://github.com/microsoft/hcsshim/blob/8beabacfc2d21767a07c20f8dd5f9f3932dbf305/internal/uvm/stats.go#L25
func (c *Collector) getProcessInformation(logger *slog.Logger, pid uint32) (string, string, uint32, error) {
if pid == 0 {
return "", "", 0, nil
func (c *Collector) getProcessOwner(pid int) (string, error) {
p, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, uint32(pid))
if errors.Is(err, syscall.Errno(0x57)) { // invalid parameter, for PIDs that don't exist
return "", errors.New("process not found")
}
hProcess, vmReadAccess, err := c.openProcess(pid)
if err != nil {
if errors.Is(err, windows.ERROR_ACCESS_DENIED) {
return "", "", 0, nil
}
return "", "", 0, err
return "", fmt.Errorf("OpenProcess: %w", err)
}
defer func(hProcess windows.Handle) {
if err := windows.CloseHandle(hProcess); err != nil {
logger.Warn("CloseHandle failed",
slog.Any("err", err),
)
}
}(hProcess)
defer windows.Close(p)
owner, err := c.getProcessOwner(logger, hProcess)
if err != nil {
return "", "", 0, err
}
var (
cmdLine string
processGroupID uint32
)
if vmReadAccess {
cmdLine, processGroupID, err = c.getExtendedProcessInformation(hProcess)
if err != nil {
return "", owner, processGroupID, err
}
}
return cmdLine, owner, processGroupID, nil
}
func (c *Collector) getExtendedProcessInformation(hProcess windows.Handle) (string, uint32, error) {
// Get the process environment block (PEB) address
var pbi windows.PROCESS_BASIC_INFORMATION
retLen := uint32(unsafe.Sizeof(pbi))
if err := windows.NtQueryInformationProcess(hProcess, windows.ProcessBasicInformation, unsafe.Pointer(&pbi), retLen, &retLen); err != nil {
return "", 0, fmt.Errorf("failed to query process basic information: %w", err)
}
peb := windows.PEB{}
err := windows.ReadProcessMemory(hProcess,
uintptr(unsafe.Pointer(pbi.PebBaseAddress)),
(*byte)(unsafe.Pointer(&peb)),
unsafe.Sizeof(peb),
nil,
)
if err != nil {
return "", 0, fmt.Errorf("failed to read process memory: %w", err)
}
processParameters := windows.RTL_USER_PROCESS_PARAMETERS{}
err = windows.ReadProcessMemory(hProcess,
uintptr(unsafe.Pointer(peb.ProcessParameters)),
(*byte)(unsafe.Pointer(&processParameters)),
unsafe.Sizeof(processParameters),
nil,
)
if err != nil {
return "", 0, fmt.Errorf("failed to read process memory: %w", err)
}
cmdLineUTF16 := make([]uint16, processParameters.CommandLine.Length)
err = windows.ReadProcessMemory(hProcess,
uintptr(unsafe.Pointer(processParameters.CommandLine.Buffer)),
(*byte)(unsafe.Pointer(&cmdLineUTF16[0])),
uintptr(processParameters.CommandLine.Length),
nil,
)
if err != nil {
return "", processParameters.ProcessGroupId, fmt.Errorf("failed to read process memory: %w", err)
}
return strings.TrimSpace(windows.UTF16ToString(cmdLineUTF16)), processParameters.ProcessGroupId, nil
}
func (c *Collector) getProcessOwner(logger *slog.Logger, hProcess windows.Handle) (string, error) {
var tok windows.Token
if err := windows.OpenProcessToken(hProcess, windows.TOKEN_QUERY, &tok); err != nil {
if errors.Is(err, windows.ERROR_ACCESS_DENIED) {
return "", nil
}
return "", fmt.Errorf("failed to open process token: %w", err)
if err = windows.OpenProcessToken(p, windows.TOKEN_QUERY, &tok); err != nil {
return "", fmt.Errorf("OpenProcessToken: %w", err)
}
defer func(tok windows.Token) {
if err := tok.Close(); err != nil {
logger.Warn("Token close failed",
slog.Any("err", err),
)
}
}(tok)
tokenUser, err := tok.GetTokenUser()
if err != nil {
return "", fmt.Errorf("failed to get token user: %w", err)
return "", fmt.Errorf("GetTokenUser: %w", err)
}
sid := tokenUser.User.Sid.String()
owner, ok := c.lookupCache[sid]
if !ok {
account, domain, _, err := tokenUser.User.Sid.LookupAccount("")
if err != nil {
owner = sid
} else {
owner = fmt.Sprintf(`%s\%s`, account, domain)
}
c.lookupCache[sid] = owner
if owner, ok := c.lookupCache[sid]; ok {
return owner, nil
}
return owner, nil
}
func (c *Collector) openProcess(pid uint32) (windows.Handle, bool, error) {
// Open the process with QUERY_INFORMATION and VM_READ permissions
hProcess, err := windows.OpenProcess(windows.PROCESS_QUERY_INFORMATION|windows.PROCESS_VM_READ, false, pid)
if err == nil {
return hProcess, true, nil
}
if !errors.Is(err, windows.ERROR_ACCESS_DENIED) {
return 0, false, fmt.Errorf("failed to open process: %w", err)
}
if errors.Is(err, windows.Errno(0x57)) { // invalid parameter, for PIDs that don't exist
return 0, false, errors.New("process not found")
}
hProcess, err = windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, pid)
account, domain, _, err := tokenUser.User.Sid.LookupAccount("")
if err != nil {
return 0, false, fmt.Errorf("failed to open process with limited permissions: %w", err)
c.lookupCache[sid] = sid
} else {
c.lookupCache[sid] = fmt.Sprintf(`%s\%s`, account, domain)
}
return hProcess, false, nil
return c.lookupCache[sid], nil
}

View File

@@ -4,71 +4,48 @@ package collector
import (
"fmt"
"log/slog"
"sync"
"sync/atomic"
"time"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus/client_golang/prometheus"
)
// Interface guard.
var _ prometheus.Collector = (*Prometheus)(nil)
// Prometheus implements prometheus.Collector for a set of Windows MetricCollectors.
// Prometheus implements prometheus.Collector for a set of Windows Collectors.
type Prometheus struct {
maxScrapeDuration time.Duration
logger *slog.Logger
metricCollectors *MetricCollectors
collectors *Collectors
logger log.Logger
// Base metrics returned by Prometheus
scrapeDurationDesc *prometheus.Desc
collectorScrapeDurationDesc *prometheus.Desc
collectorScrapeSuccessDesc *prometheus.Desc
collectorScrapeTimeoutDesc *prometheus.Desc
snapshotDuration *prometheus.Desc
scrapeDurationDesc *prometheus.Desc
scrapeSuccessDesc *prometheus.Desc
scrapeTimeoutDesc *prometheus.Desc
snapshotDuration *prometheus.Desc
}
type collectorStatus struct {
name string
statusCode collectorStatusCode
}
type collectorStatusCode int
const (
pending collectorStatusCode = iota
success
failed
)
// NewPrometheusCollector returns a new Prometheus where the set of MetricCollectors must
// NewPrometheus returns a new Prometheus where the set of Collectors must
// return metrics within the given timeout.
func (c *MetricCollectors) NewPrometheusCollector(timeout time.Duration, logger *slog.Logger) *Prometheus {
func NewPrometheus(timeout time.Duration, cs *Collectors, logger log.Logger) *Prometheus {
return &Prometheus{
maxScrapeDuration: timeout,
metricCollectors: c,
collectors: cs,
logger: logger,
scrapeDurationDesc: prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, "exporter", "scrape_duration_seconds"),
"windows_exporter: Total scrape duration.",
nil,
nil,
),
collectorScrapeDurationDesc: prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, "exporter", "collector_duration_seconds"),
"windows_exporter: Duration of a collection.",
[]string{"collector"},
nil,
),
collectorScrapeSuccessDesc: prometheus.NewDesc(
scrapeSuccessDesc: prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, "exporter", "collector_success"),
"windows_exporter: Whether the collector was successful.",
[]string{"collector"},
nil,
),
collectorScrapeTimeoutDesc: prometheus.NewDesc(
scrapeTimeoutDesc: prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, "exporter", "collector_timeout"),
"windows_exporter: Whether the collector timed out.",
[]string{"collector"},
@@ -83,159 +60,132 @@ func (c *MetricCollectors) NewPrometheusCollector(timeout time.Duration, logger
}
}
func (p *Prometheus) Describe(_ chan<- *prometheus.Desc) {}
// Describe sends all the descriptors of the Collectors included to
// the provided channel.
func (coll *Prometheus) Describe(ch chan<- *prometheus.Desc) {
ch <- coll.scrapeDurationDesc
ch <- coll.scrapeSuccessDesc
}
// Collect sends the collected metrics from each of the MetricCollectors to
type collectorOutcome int
const (
pending collectorOutcome = iota
success
failed
)
// Collect sends the collected metrics from each of the Collectors to
// prometheus.
func (p *Prometheus) Collect(ch chan<- prometheus.Metric) {
func (coll *Prometheus) Collect(ch chan<- prometheus.Metric) {
t := time.Now()
// Scrape Performance Counters for all collectors
scrapeContext, err := p.metricCollectors.PrepareScrapeContext()
scrapeContext, err := coll.collectors.PrepareScrapeContext()
ch <- prometheus.MustNewConstMetric(
p.snapshotDuration,
coll.snapshotDuration,
prometheus.GaugeValue,
time.Since(t).Seconds(),
)
if err != nil {
ch <- prometheus.NewInvalidMetric(p.collectorScrapeSuccessDesc, fmt.Errorf("failed to prepare scrape: %w", err))
ch <- prometheus.NewInvalidMetric(coll.scrapeSuccessDesc, fmt.Errorf("failed to prepare scrape: %w", err))
return
}
// WaitGroup to wait for all collectors to finish
wg := sync.WaitGroup{}
wg.Add(len(p.metricCollectors.Collectors))
// Using a channel to collect the status of each collector
// A channel is safe to use concurrently while a map is not
collectorStatusCh := make(chan collectorStatus, len(p.metricCollectors.Collectors))
// Execute all collectors concurrently
// timeout handling is done in the execute function
for name, metricsCollector := range p.metricCollectors.Collectors {
go func(name string, metricsCollector Collector) {
defer wg.Done()
collectorStatusCh <- collectorStatus{
name: name,
statusCode: p.execute(name, metricsCollector, scrapeContext, ch),
}
}(name, metricsCollector)
wg.Add(len(coll.collectors.collectors))
collectorOutcomes := make(map[string]collectorOutcome)
for name := range coll.collectors.collectors {
collectorOutcomes[name] = pending
}
// Wait for all collectors to finish
wg.Wait()
// Close the channel since we are done writing to it
close(collectorStatusCh)
for status := range collectorStatusCh {
var successValue, timeoutValue float64
if status.statusCode == pending {
timeoutValue = 1.0
metricsBuffer := make(chan prometheus.Metric)
l := sync.Mutex{}
finished := false
go func() {
for m := range metricsBuffer {
l.Lock()
if !finished {
ch <- m
}
l.Unlock()
}
}()
if status.statusCode == success {
for name, c := range coll.collectors.collectors {
go func(name string, c Collector) {
defer wg.Done()
outcome := coll.execute(name, c, scrapeContext, metricsBuffer)
l.Lock()
if !finished {
collectorOutcomes[name] = outcome
}
l.Unlock()
}(name, c)
}
allDone := make(chan struct{})
go func() {
wg.Wait()
close(allDone)
close(metricsBuffer)
}()
// Wait until either all Collectors finish, or timeout expires
select {
case <-allDone:
case <-time.After(coll.maxScrapeDuration):
}
l.Lock()
finished = true
remainingCollectorNames := make([]string, 0)
for name, outcome := range collectorOutcomes {
var successValue, timeoutValue float64
if outcome == pending {
timeoutValue = 1.0
remainingCollectorNames = append(remainingCollectorNames, name)
}
if outcome == success {
successValue = 1.0
}
ch <- prometheus.MustNewConstMetric(
p.collectorScrapeSuccessDesc,
coll.scrapeSuccessDesc,
prometheus.GaugeValue,
successValue,
status.name,
name,
)
ch <- prometheus.MustNewConstMetric(
p.collectorScrapeTimeoutDesc,
coll.scrapeTimeoutDesc,
prometheus.GaugeValue,
timeoutValue,
status.name,
name,
)
}
ch <- prometheus.MustNewConstMetric(
p.scrapeDurationDesc,
prometheus.GaugeValue,
time.Since(t).Seconds(),
)
if len(remainingCollectorNames) > 0 {
_ = level.Warn(coll.logger).Log("msg", fmt.Sprintf("Collection timed out, still waiting for %v", remainingCollectorNames))
}
l.Unlock()
}
func (p *Prometheus) execute(name string, c Collector, ctx *types.ScrapeContext, ch chan<- prometheus.Metric) collectorStatusCode {
var (
err error
duration time.Duration
timeout atomic.Bool
)
// bufCh is a buffer channel to store the metrics
// This is needed because once timeout is reached, the prometheus registry channel is closed.
bufCh := make(chan prometheus.Metric, 10)
errCh := make(chan error, 1)
// Execute the collector
go func() {
errCh <- c.Collect(ctx, p.logger, bufCh)
close(bufCh)
}()
go func() {
defer func() {
// This prevents a panic from race-condition when closing the ch channel too early.
_ = recover()
}()
// Pass metrics to the prometheus registry
// If timeout is reached, the channel is closed.
// This will cause a panic if we try to write to it.
for m := range bufCh {
if !timeout.Load() {
ch <- m
}
}
}()
func (coll *Prometheus) execute(name string, c Collector, ctx *types.ScrapeContext, ch chan<- prometheus.Metric) collectorOutcome {
t := time.Now()
// Wait for the collector to finish or timeout
select {
case err = <-errCh:
duration = time.Since(t)
ch <- prometheus.MustNewConstMetric(
p.collectorScrapeDurationDesc,
prometheus.GaugeValue,
duration.Seconds(),
name,
)
case <-time.After(p.maxScrapeDuration):
timeout.Store(true)
duration = time.Since(t)
ch <- prometheus.MustNewConstMetric(
p.collectorScrapeDurationDesc,
prometheus.GaugeValue,
duration.Seconds(),
name,
)
p.logger.Warn(fmt.Sprintf("collector %s timeouted after %s", name, p.maxScrapeDuration))
return pending
}
err := c.Collect(ctx, ch)
duration := time.Since(t).Seconds()
ch <- prometheus.MustNewConstMetric(
coll.scrapeDurationDesc,
prometheus.GaugeValue,
duration,
name,
)
if err != nil {
p.logger.Error(fmt.Sprintf("collector %s failed after %s", name, p.maxScrapeDuration),
slog.Any("err", err),
)
_ = level.Error(coll.logger).Log("msg", fmt.Sprintf("collector %s failed after %fs", name, duration), "err", err)
return failed
}
p.logger.Error(fmt.Sprintf("collector %s succeeded after %s", name, p.maxScrapeDuration))
_ = level.Debug(coll.logger).Log("msg", fmt.Sprintf("collector %s succeeded after %fs.", name, duration))
return success
}

View File

@@ -3,15 +3,15 @@
package remote_fx
import (
"log/slog"
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/perflib"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus-community/windows_exporter/pkg/utils"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "remote_fx"
@@ -27,6 +27,7 @@ var ConfigDefaults = Config{}
// https://wutils.com/wmi/root/cimv2/win32_perfrawdata_counters_remotefxgraphics/
type Collector struct {
config Config
logger log.Logger
// net
baseTCPRTT *prometheus.Desc
@@ -53,7 +54,7 @@ type Collector struct {
sourceFramesPerSecond *prometheus.Desc
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -62,6 +63,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -73,15 +76,19 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{"RemoteFX Network", "RemoteFX Graphics"}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(*slog.Logger, *wmi.Client) error {
func (c *Collector) Build() error {
// net
c.baseTCPRTT = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "net_base_tcp_rtt_seconds"),
@@ -205,30 +212,20 @@ func (c *Collector) Build(*slog.Logger, *wmi.Client) error {
[]string{"session_name"},
nil,
)
return nil
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
if err := c.collectRemoteFXNetworkCount(ctx, logger, ch); err != nil {
logger.Error("failed collecting terminal services session count metrics",
slog.Any("err", err),
)
func (c *Collector) Collect(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
if err := c.collectRemoteFXNetworkCount(ctx, ch); err != nil {
_ = level.Error(c.logger).Log("msg", "failed collecting terminal services session count metrics", "err", err)
return err
}
if err := c.collectRemoteFXGraphicsCounters(ctx, logger, ch); err != nil {
logger.Error("failed collecting terminal services session count metrics",
slog.Any("err", err),
)
if err := c.collectRemoteFXGraphicsCounters(ctx, ch); err != nil {
_ = level.Error(c.logger).Log("msg", "failed collecting terminal services session count metrics", "err", err)
return err
}
return nil
}
@@ -249,11 +246,9 @@ type perflibRemoteFxNetwork struct {
RetransmissionRate float64 `perflib:"Percentage of packets that have been retransmitted"`
}
func (c *Collector) collectRemoteFXNetworkCount(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) collectRemoteFXNetworkCount(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
dst := make([]perflibRemoteFxNetwork, 0)
err := perflib.UnmarshalObject(ctx.PerfObjects["RemoteFX Network"], &dst, logger)
err := perflib.UnmarshalObject(ctx.PerfObjects["RemoteFX Network"], &dst, c.logger)
if err != nil {
return err
}
@@ -345,7 +340,6 @@ func (c *Collector) collectRemoteFXNetworkCount(ctx *types.ScrapeContext, logger
normalizeSessionName(d.Name),
)
}
return nil
}
@@ -362,11 +356,9 @@ type perflibRemoteFxGraphics struct {
SourceFramesPerSecond float64 `perflib:"Source Frames/Second"`
}
func (c *Collector) collectRemoteFXGraphicsCounters(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) collectRemoteFXGraphicsCounters(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
dst := make([]perflibRemoteFxGraphics, 0)
err := perflib.UnmarshalObject(ctx.PerfObjects["RemoteFX Graphics"], &dst, logger)
err := perflib.UnmarshalObject(ctx.PerfObjects["RemoteFX Graphics"], &dst, c.logger)
if err != nil {
return err
}

View File

@@ -5,17 +5,17 @@ package scheduled_task
import (
"errors"
"fmt"
"log/slog"
"regexp"
"runtime"
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/go-ole/go-ole"
"github.com/go-ole/go-ole/oleutil"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "scheduled_task"
@@ -32,6 +32,7 @@ var ConfigDefaults = Config{
type Collector struct {
config Config
logger log.Logger
lastResult *prometheus.Desc
missedRuns *prometheus.Desc
@@ -68,7 +69,7 @@ type ScheduledTask struct {
type ScheduledTasks []ScheduledTask
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -85,6 +86,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -128,15 +131,19 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build() error {
c.lastResult = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "last_result"),
"The result that was returned the last time the registered task was run",
@@ -161,13 +168,9 @@ func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
return nil
}
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) Collect(_ *types.ScrapeContext, ch chan<- prometheus.Metric) error {
if err := c.collect(ch); err != nil {
logger.Error("failed collecting user metrics",
slog.Any("err", err),
)
_ = level.Error(c.logger).Log("msg", "failed collecting user metrics", "err", err)
return err
}
@@ -266,12 +269,10 @@ func getScheduledTasks() (ScheduledTasks, error) {
defer taskSchedulerObj.Release()
taskServiceObj := taskSchedulerObj.MustQueryInterface(ole.IID_IDispatch)
_, err = oleutil.CallMethod(taskServiceObj, "Connect")
if err != nil {
return scheduledTasks, err
}
defer taskServiceObj.Release()
res, err := oleutil.CallMethod(taskServiceObj, "GetFolder", `\`)
@@ -329,7 +330,6 @@ func fetchTasksRecursively(folder *ole.IDispatch, scheduledTasks *ScheduledTasks
err = oleutil.ForEach(subFolders, func(v *ole.VARIANT) error {
subFolder := v.ToIDispatch()
defer subFolder.Release()
return fetchTasksRecursively(subFolder, scheduledTasks)
})
@@ -343,7 +343,6 @@ func parseTask(task *ole.IDispatch) (ScheduledTask, error) {
if err != nil {
return scheduledTask, err
}
defer func() {
if tempErr := taskNameVar.Clear(); tempErr != nil {
err = tempErr
@@ -354,7 +353,6 @@ func parseTask(task *ole.IDispatch) (ScheduledTask, error) {
if err != nil {
return scheduledTask, err
}
defer func() {
if tempErr := taskPathVar.Clear(); tempErr != nil {
err = tempErr
@@ -365,7 +363,6 @@ func parseTask(task *ole.IDispatch) (ScheduledTask, error) {
if err != nil {
return scheduledTask, err
}
defer func() {
if tempErr := taskEnabledVar.Clear(); tempErr != nil {
err = tempErr
@@ -376,7 +373,6 @@ func parseTask(task *ole.IDispatch) (ScheduledTask, error) {
if err != nil {
return scheduledTask, err
}
defer func() {
if tempErr := taskStateVar.Clear(); tempErr != nil {
err = tempErr
@@ -387,7 +383,6 @@ func parseTask(task *ole.IDispatch) (ScheduledTask, error) {
if err != nil {
return scheduledTask, err
}
defer func() {
if tempErr := taskNumberOfMissedRunsVar.Clear(); tempErr != nil {
err = tempErr
@@ -398,7 +393,6 @@ func parseTask(task *ole.IDispatch) (ScheduledTask, error) {
if err != nil {
return scheduledTask, err
}
defer func() {
if tempErr := taskLastTaskResultVar.Clear(); tempErr != nil {
err = tempErr
@@ -407,11 +401,9 @@ func parseTask(task *ole.IDispatch) (ScheduledTask, error) {
scheduledTask.Name = taskNameVar.ToString()
scheduledTask.Path = strings.ReplaceAll(taskPathVar.ToString(), "\\", "/")
if val, ok := taskEnabledVar.Value().(bool); ok {
scheduledTask.Enabled = val
}
scheduledTask.State = TaskState(taskStateVar.Val)
scheduledTask.MissedRunsCount = float64(taskNumberOfMissedRunsVar.Val)
scheduledTask.LastTaskResult = TaskResult(taskLastTaskResultVar.Val)

View File

@@ -5,15 +5,18 @@ package service
import (
"errors"
"fmt"
"log/slog"
"regexp"
"strconv"
"strings"
"syscall"
"unsafe"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus-community/windows_exporter/pkg/utils"
"github.com/prometheus-community/windows_exporter/pkg/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/svc/mgr"
)
@@ -21,313 +24,395 @@ import (
const Name = "service"
type Config struct {
ServiceInclude *regexp.Regexp `yaml:"service_include"`
ServiceExclude *regexp.Regexp `yaml:"service_exclude"`
ServiceWhereClause string `yaml:"service_where_clause"`
UseAPI bool `yaml:"use_api"`
V2 bool `yaml:"v2"`
}
var ConfigDefaults = Config{
ServiceInclude: types.RegExpAny,
ServiceExclude: types.RegExpEmpty,
ServiceWhereClause: "",
UseAPI: false,
V2: false,
}
// A Collector is a Prometheus Collector for service metrics.
// A Collector is a Prometheus Collector for WMI Win32_Service metrics.
type Collector struct {
config Config
logger log.Logger
state *prometheus.Desc
processID *prometheus.Desc
info *prometheus.Desc
startMode *prometheus.Desc
serviceWhereClause *string
useAPI *bool
v2 *bool
serviceManagerHandle *mgr.Mgr
Information *prometheus.Desc
State *prometheus.Desc
StartMode *prometheus.Desc
Status *prometheus.Desc
StateV2 *prometheus.Desc
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
if config.ServiceExclude == nil {
config.ServiceExclude = ConfigDefaults.ServiceExclude
}
if config.ServiceInclude == nil {
config.ServiceInclude = ConfigDefaults.ServiceInclude
}
c := &Collector{
config: *config,
serviceWhereClause: &config.ServiceWhereClause,
useAPI: &config.UseAPI,
v2: &config.V2,
}
c.SetLogger(logger)
return c
}
func NewWithFlags(app *kingpin.Application) *Collector {
c := &Collector{
config: ConfigDefaults,
return &Collector{
serviceWhereClause: app.Flag(
"collector.service.services-where",
"WQL 'where' clause to use in WMI metrics query. Limits the response to the services you specify and reduces the size of the response.",
).Default(ConfigDefaults.ServiceWhereClause).String(),
useAPI: app.Flag(
"collector.service.use-api",
"Use API calls to collect service data instead of WMI. Flag 'collector.service.services-where' won't be effective.",
).Default(strconv.FormatBool(ConfigDefaults.UseAPI)).Bool(),
v2: app.Flag(
"collector.service.v2",
"Enable V2 service collector. This collector can services state much more efficiently, can't provide general service information.",
).Default(strconv.FormatBool(ConfigDefaults.V2)).Bool(),
}
var serviceExclude, serviceInclude string
app.Flag(
"collector.service.exclude",
"Regexp of service to exclude. Service name (not the display name!) must both match include and not match exclude to be included.",
).Default(c.config.ServiceExclude.String()).StringVar(&serviceExclude)
app.Flag(
"collector.service.include",
"Regexp of service to include. Process name (not the display name!) must both match include and not match exclude to be included.",
).Default(c.config.ServiceInclude.String()).StringVar(&serviceInclude)
app.Action(func(*kingpin.ParseContext) error {
var err error
c.config.ServiceExclude, err = regexp.Compile(fmt.Sprintf("^(?:%s)$", serviceExclude))
if err != nil {
return fmt.Errorf("collector.process.exclude: %w", err)
}
c.config.ServiceInclude, err = regexp.Compile(fmt.Sprintf("^(?:%s)$", serviceInclude))
if err != nil {
return fmt.Errorf("collector.process.include: %w", err)
}
return nil
})
return c
}
func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{}, nil
}
func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) Close() error {
return nil
}
if c.config.ServiceInclude.String() == "^(?:.*)$" && c.config.ServiceExclude.String() == "^(?:)$" {
logger.Warn("No filters specified for service collector. This will generate a very large number of metrics!")
func (c *Collector) Build() error {
if utils.IsEmpty(c.serviceWhereClause) {
_ = level.Warn(c.logger).Log("msg", "No where-clause specified for service collector. This will generate a very large number of metrics!")
}
if *c.useAPI {
_ = level.Warn(c.logger).Log("msg", "API collection is enabled.")
}
c.info = prometheus.NewDesc(
c.Information = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "info"),
"A metric with a constant '1' value labeled with service information",
[]string{"name", "display_name", "run_as", "path_name"},
[]string{"name", "display_name", "process_id", "run_as"},
nil,
)
c.state = prometheus.NewDesc(
c.State = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "state"),
"The state of the service (State)",
[]string{"name", "status"},
[]string{"name", "state"},
nil,
)
c.startMode = prometheus.NewDesc(
c.StartMode = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "start_mode"),
"The start mode of the service (StartMode)",
[]string{"name", "start_mode"},
nil,
)
c.processID = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "process"),
"Process of started service. The value is the creation time of the process as a unix timestamp.",
[]string{"name", "process_id"},
c.Status = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "status"),
"The status of the service (Status)",
[]string{"name", "status"},
nil,
)
c.StateV2 = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "state"),
"The state of the service (State)",
[]string{"name", "display_name", "status"},
nil,
)
// EnumServiceStatusEx requires only SC_MANAGER_ENUM_SERVICE.
handle, err := windows.OpenSCManager(nil, nil, windows.SC_MANAGER_ENUMERATE_SERVICE)
if err != nil {
return fmt.Errorf("failed to open scm: %w", err)
}
c.serviceManagerHandle = &mgr.Mgr{Handle: handle}
return nil
}
func (c *Collector) Close(logger *slog.Logger) error {
if err := c.serviceManagerHandle.Disconnect(); err != nil {
logger.Warn("Failed to disconnect from scm",
slog.Any("err", err),
)
}
return nil
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) Collect(_ *types.ScrapeContext, ch chan<- prometheus.Metric) error {
var err error
if err := c.collect(logger, ch); err != nil {
logger.Error("failed collecting API service metrics:",
slog.Any("err", err),
)
return fmt.Errorf("failed collecting API service metrics: %w", err)
switch {
case *c.useAPI:
if err = c.collectAPI(ch); err != nil {
_ = level.Error(c.logger).Log("msg", "failed collecting API service metrics:", "err", err)
}
case *c.v2:
if err = c.collectAPIV2(ch); err != nil {
_ = level.Error(c.logger).Log("msg", "failed collecting API service metrics:", "err", err)
}
default:
if err = c.collectWMI(ch); err != nil {
_ = level.Error(c.logger).Log("msg", "failed collecting WMI service metrics:", "err", err)
}
}
return err
}
// Win32_Service docs:
// - https://msdn.microsoft.com/en-us/library/aa394418(v=vs.85).aspx
type Win32_Service struct {
DisplayName string
Name string
ProcessId uint32
State string
Status string
StartMode string
StartName *string
}
var (
allStates = []string{
"stopped",
"start pending",
"stop pending",
"running",
"continue pending",
"pause pending",
"paused",
"unknown",
}
apiStateValues = map[uint32]string{
windows.SERVICE_CONTINUE_PENDING: "continue pending",
windows.SERVICE_PAUSE_PENDING: "pause pending",
windows.SERVICE_PAUSED: "paused",
windows.SERVICE_RUNNING: "running",
windows.SERVICE_START_PENDING: "start pending",
windows.SERVICE_STOP_PENDING: "stop pending",
windows.SERVICE_STOPPED: "stopped",
}
allStartModes = []string{
"boot",
"system",
"auto",
"manual",
"disabled",
}
apiStartModeValues = map[uint32]string{
windows.SERVICE_AUTO_START: "auto",
windows.SERVICE_BOOT_START: "boot",
windows.SERVICE_DEMAND_START: "manual",
windows.SERVICE_DISABLED: "disabled",
windows.SERVICE_SYSTEM_START: "system",
}
allStatuses = []string{
"ok",
"error",
"degraded",
"unknown",
"pred fail",
"starting",
"stopping",
"service",
"stressed",
"nonrecover",
"no contact",
"lost comm",
}
)
func (c *Collector) collectWMI(ch chan<- prometheus.Metric) error {
var dst []Win32_Service
q := wmi.QueryAllWhere(&dst, *c.serviceWhereClause, c.logger) //nolint:staticcheck
if err := wmi.Query(q, &dst); err != nil {
return err
}
for _, service := range dst {
pid := strconv.FormatUint(uint64(service.ProcessId), 10)
runAs := ""
if service.StartName != nil {
runAs = *service.StartName
}
ch <- prometheus.MustNewConstMetric(
c.Information,
prometheus.GaugeValue,
1.0,
strings.ToLower(service.Name),
service.DisplayName,
pid,
runAs,
)
for _, state := range allStates {
isCurrentState := 0.0
if state == strings.ToLower(service.State) {
isCurrentState = 1.0
}
ch <- prometheus.MustNewConstMetric(
c.State,
prometheus.GaugeValue,
isCurrentState,
strings.ToLower(service.Name),
state,
)
}
for _, startMode := range allStartModes {
isCurrentStartMode := 0.0
if startMode == strings.ToLower(service.StartMode) {
isCurrentStartMode = 1.0
}
ch <- prometheus.MustNewConstMetric(
c.StartMode,
prometheus.GaugeValue,
isCurrentStartMode,
strings.ToLower(service.Name),
startMode,
)
}
for _, status := range allStatuses {
isCurrentStatus := 0.0
if status == strings.ToLower(service.Status) {
isCurrentStatus = 1.0
}
ch <- prometheus.MustNewConstMetric(
c.Status,
prometheus.GaugeValue,
isCurrentStatus,
strings.ToLower(service.Name),
status,
)
}
}
return nil
}
func (c *Collector) collect(logger *slog.Logger, ch chan<- prometheus.Metric) error {
services, err := c.queryAllServices()
func (c *Collector) collectAPI(ch chan<- prometheus.Metric) error {
svcmgrConnection, err := mgr.Connect()
if err != nil {
logger.Warn("Failed to query services",
slog.Any("err", err),
)
return err
}
defer svcmgrConnection.Disconnect() //nolint:errcheck
// List All Services from the Services Manager.
serviceList, err := svcmgrConnection.ListServices()
if err != nil {
return err
}
// Iterate through the Services List.
for _, service := range serviceList {
(func() {
// Get UTF16 service name.
serviceName, err := syscall.UTF16PtrFromString(service)
if err != nil {
_ = level.Warn(c.logger).Log("msg", fmt.Sprintf("Service %s get name error: %#v", service, err))
return
}
// Open connection for service handler.
serviceHandle, err := windows.OpenService(svcmgrConnection.Handle, serviceName, windows.GENERIC_READ)
if err != nil {
_ = level.Warn(c.logger).Log("msg", fmt.Sprintf("Open service %s error: %#v", service, err))
return
}
// Create handle for each service.
serviceManager := &mgr.Service{Name: service, Handle: serviceHandle}
defer serviceManager.Close()
// Get Service Configuration.
serviceConfig, err := serviceManager.Config()
if err != nil {
_ = level.Warn(c.logger).Log("msg", fmt.Sprintf("Get service %s config error: %#v", service, err))
return
}
// Get Service Current Status.
serviceStatus, err := serviceManager.Query()
if err != nil {
_ = level.Warn(c.logger).Log("msg", fmt.Sprintf("Get service %s status error: %#v", service, err))
return
}
pid := strconv.FormatUint(uint64(serviceStatus.ProcessId), 10)
ch <- prometheus.MustNewConstMetric(
c.Information,
prometheus.GaugeValue,
1.0,
strings.ToLower(service),
serviceConfig.DisplayName,
pid,
serviceConfig.ServiceStartName,
)
for _, state := range apiStateValues {
isCurrentState := 0.0
if state == apiStateValues[uint32(serviceStatus.State)] {
isCurrentState = 1.0
}
ch <- prometheus.MustNewConstMetric(
c.State,
prometheus.GaugeValue,
isCurrentState,
strings.ToLower(service),
state,
)
}
for _, startMode := range apiStartModeValues {
isCurrentStartMode := 0.0
if startMode == apiStartModeValues[serviceConfig.StartType] {
isCurrentStartMode = 1.0
}
ch <- prometheus.MustNewConstMetric(
c.StartMode,
prometheus.GaugeValue,
isCurrentStartMode,
strings.ToLower(service),
startMode,
)
}
})()
}
return nil
}
func (c *Collector) collectAPIV2(ch chan<- prometheus.Metric) error {
services, err := c.queryAllServiceStates()
if err != nil {
_ = level.Warn(c.logger).Log("msg", "Failed to query services", "err", err)
return err
}
if services == nil {
logger.Warn("No services queried")
_ = level.Warn(c.logger).Log("msg", "No services queried")
return nil
}
// Iterate through the Services List.
for _, service := range services {
serviceName := windows.UTF16PtrToString(service.ServiceName)
if c.config.ServiceExclude.MatchString(serviceName) ||
!c.config.ServiceInclude.MatchString(serviceName) {
continue
}
var isCurrentState float64
if err := c.collectService(ch, logger, service); err != nil {
logger.Warn("failed collecting service info",
slog.Any("err", err),
slog.String("service", windows.UTF16PtrToString(service.ServiceName)),
)
}
}
return nil
}
var apiStateValues = map[uint32]string{
windows.SERVICE_CONTINUE_PENDING: "continue pending",
windows.SERVICE_PAUSE_PENDING: "pause pending",
windows.SERVICE_PAUSED: "paused",
windows.SERVICE_RUNNING: "running",
windows.SERVICE_START_PENDING: "start pending",
windows.SERVICE_STOP_PENDING: "stop pending",
windows.SERVICE_STOPPED: "stopped",
}
var apiStartModeValues = map[uint32]string{
windows.SERVICE_AUTO_START: "auto",
windows.SERVICE_BOOT_START: "boot",
windows.SERVICE_DEMAND_START: "manual",
windows.SERVICE_DISABLED: "disabled",
windows.SERVICE_SYSTEM_START: "system",
}
func (c *Collector) collectService(ch chan<- prometheus.Metric, logger *slog.Logger, service windows.ENUM_SERVICE_STATUS_PROCESS) error {
// Open connection for service handler.
serviceHandle, err := windows.OpenService(c.serviceManagerHandle.Handle, service.ServiceName, windows.SERVICE_QUERY_CONFIG)
if err != nil {
return fmt.Errorf("failed to open service: %w", err)
}
serviceNameString := windows.UTF16PtrToString(service.ServiceName)
// Create handle for each service.
serviceManager := &mgr.Service{Name: serviceNameString, Handle: serviceHandle}
defer func(serviceManager *mgr.Service) {
if err := serviceManager.Close(); err != nil {
logger.Warn("failed to close service handle",
slog.Any("err", err),
slog.String("service", serviceNameString),
)
}
}(serviceManager)
// Get Service Configuration.
serviceConfig, err := serviceManager.Config()
if err != nil {
if !errors.Is(err, windows.ERROR_FILE_NOT_FOUND) && !errors.Is(err, windows.ERROR_MUI_FILE_NOT_FOUND) {
return fmt.Errorf("failed to get service configuration: %w", err)
}
logger.Debug("failed collecting service",
slog.Any("err", err),
slog.String("service", serviceNameString),
)
}
ch <- prometheus.MustNewConstMetric(
c.info,
prometheus.GaugeValue,
1.0,
serviceNameString,
serviceConfig.DisplayName,
serviceConfig.ServiceStartName,
serviceConfig.BinaryPathName,
)
var (
isCurrentStartMode float64
isCurrentState float64
)
for _, startMode := range apiStartModeValues {
isCurrentStartMode = 0.0
if startMode == apiStartModeValues[serviceConfig.StartType] {
isCurrentStartMode = 1.0
}
ch <- prometheus.MustNewConstMetric(
c.startMode,
prometheus.GaugeValue,
isCurrentStartMode,
serviceNameString,
startMode,
)
}
for state, stateValue := range apiStateValues {
isCurrentState = 0.0
if state == service.ServiceStatusProcess.CurrentState {
isCurrentState = 1.0
}
ch <- prometheus.MustNewConstMetric(
c.state,
prometheus.GaugeValue,
isCurrentState,
serviceNameString,
stateValue,
)
}
processID := strconv.FormatUint(uint64(service.ServiceStatusProcess.ProcessId), 10)
if processID != "0" { //nolint: nestif
processStartTime, err := getProcessStartTime(logger, service.ServiceStatusProcess.ProcessId)
if err != nil {
if errors.Is(err, windows.ERROR_ACCESS_DENIED) {
logger.Debug("failed to get process start time",
slog.String("service", serviceNameString),
slog.Any("err", err),
)
} else {
logger.Warn("failed to get process start time",
slog.String("service", serviceNameString),
slog.Any("err", err),
)
for _, svc := range services {
for state, stateValue := range apiStateValues {
isCurrentState = 0.0
if state == svc.ServiceStatusProcess.CurrentState {
isCurrentState = 1.0
}
} else {
ch <- prometheus.MustNewConstMetric(
c.processID,
c.StateV2,
prometheus.GaugeValue,
float64(processStartTime/1_000_000_000),
serviceNameString,
processID,
isCurrentState,
windows.UTF16PtrToString(svc.ServiceName),
windows.UTF16PtrToString(svc.DisplayName),
stateValue,
)
}
}
@@ -335,82 +420,56 @@ func (c *Collector) collectService(ch chan<- prometheus.Metric, logger *slog.Log
return nil
}
// queryAllServices returns all service states of the current Windows system
// queryAllServiceStates returns all service states of the current Windows system
// This is realized by ask Service Manager directly.
func (c *Collector) queryAllServices() ([]windows.ENUM_SERVICE_STATUS_PROCESS, error) {
var (
bytesNeeded uint32
servicesReturned uint32
resumeHandle uint32
)
if err := windows.EnumServicesStatusEx(
c.serviceManagerHandle.Handle,
windows.SC_STATUS_PROCESS_INFO,
windows.SERVICE_WIN32,
windows.SERVICE_STATE_ALL,
nil,
0,
&bytesNeeded,
&servicesReturned,
&resumeHandle,
nil,
); !errors.Is(err, windows.ERROR_MORE_DATA) {
return nil, fmt.Errorf("could not fetch buffer size for EnumServicesStatusEx: %w", err)
//
// Unless explicitly stated otherwise all files in this repository are licensed
// under the Apache License Version 2.0.
// This product includes software developed at Datadog (https://www.datadoghq.com/).
// Copyright 2016-present Datadog, Inc.
//
// Source: https://github.com/DataDog/datadog-agent/blob/afbd8b6c87939c92610c654cb07fdfd439e4fb27/pkg/util/winutil/scmmonitor.go#L61-L96
func (c *Collector) queryAllServiceStates() ([]windows.ENUM_SERVICE_STATUS_PROCESS, error) {
// EnumServiceStatusEx requires only SC_MANAGER_ENUM_SERVICE.
h, err := windows.OpenSCManager(nil, nil, windows.SC_MANAGER_ENUMERATE_SERVICE)
if err != nil {
return nil, fmt.Errorf("failed to open scm: %w", err)
}
buf := make([]byte, bytesNeeded)
if err := windows.EnumServicesStatusEx(
c.serviceManagerHandle.Handle,
windows.SC_STATUS_PROCESS_INFO,
windows.SERVICE_WIN32,
windows.SERVICE_STATE_ALL,
&buf[0],
bytesNeeded,
&bytesNeeded,
&servicesReturned,
&resumeHandle,
nil,
); err != nil {
return nil, fmt.Errorf("could not query windows service list: %w", err)
m := &mgr.Mgr{Handle: h}
defer func() {
if err := m.Disconnect(); err != nil {
_ = level.Warn(c.logger).Log("msg", "Failed to disconnect from scm", "err", err)
}
}()
var bytesNeeded, servicesReturned uint32
var buf []byte
for {
var p *byte
if len(buf) > 0 {
p = &buf[0]
}
err = windows.EnumServicesStatusEx(m.Handle, windows.SC_ENUM_PROCESS_INFO,
windows.SERVICE_WIN32, windows.SERVICE_STATE_ALL,
p, uint32(len(buf)), &bytesNeeded, &servicesReturned, nil, nil)
if err == nil {
break
}
if !errors.Is(err, windows.ERROR_MORE_DATA) {
return nil, fmt.Errorf("failed to enum services %w", err)
}
if bytesNeeded <= uint32(len(buf)) {
return nil, err
}
buf = make([]byte, bytesNeeded)
}
if servicesReturned == 0 {
return nil, nil
}
services := unsafe.Slice((*windows.ENUM_SERVICE_STATUS_PROCESS)(unsafe.Pointer(&buf[0])), int(servicesReturned))
services := unsafe.Slice((*windows.ENUM_SERVICE_STATUS_PROCESS)(unsafe.Pointer(&buf[0])), servicesReturned)
return services, nil
}
func getProcessStartTime(logger *slog.Logger, pid uint32) (uint64, error) {
handle, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, pid)
if err != nil {
return 0, fmt.Errorf("failed to open process %w", err)
}
defer func(handle windows.Handle) {
err := windows.CloseHandle(handle)
if err != nil {
logger.Warn("failed to close process handle",
slog.Any("err", err),
)
}
}(handle)
var creation windows.Filetime
var exit windows.Filetime
var krn windows.Filetime
var user windows.Filetime
err = windows.GetProcessTimes(handle, &creation, &exit, &krn, &user)
if err != nil {
return 0, fmt.Errorf("failed to get process times %w", err)
}
return uint64(creation.Nanoseconds()), nil
}

View File

@@ -3,14 +3,14 @@
package smb
import (
"log/slog"
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/perflib"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "smb"
@@ -21,12 +21,13 @@ var ConfigDefaults = Config{}
type Collector struct {
config Config
logger log.Logger
treeConnectCount *prometheus.Desc
currentOpenFileCount *prometheus.Desc
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -35,6 +36,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -46,17 +49,21 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{
"SMB Server Shares",
}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build() error {
// desc creates a new prometheus description
desc := func(metricName string, description string, labels ...string) *prometheus.Desc {
return prometheus.NewDesc(
@@ -74,12 +81,9 @@ func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
}
// Collect collects smb metrics and sends them to prometheus.
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
if err := c.collectServerShares(ctx, logger, ch); err != nil {
logger.Error("failed to collect server share metrics",
slog.Any("err", err),
)
func (c *Collector) Collect(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
if err := c.collectServerShares(ctx, ch); err != nil {
_ = level.Error(c.logger).Log("msg", "failed to collect server share metrics", "err", err)
return err
}
@@ -95,15 +99,11 @@ type perflibServerShares struct {
TreeConnectCount float64 `perflib:"Tree Connect Count"`
}
func (c *Collector) collectServerShares(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) collectServerShares(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
var data []perflibServerShares
if err := perflib.UnmarshalObject(ctx.PerfObjects["SMB Server Shares"], &data, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects["SMB Server Shares"], &data, c.logger); err != nil {
return err
}
for _, instance := range data {
labelName := c.toLabelName(instance.Name)
if !strings.HasSuffix(labelName, "_total") {
@@ -122,7 +122,6 @@ func (c *Collector) collectServerShares(ctx *types.ScrapeContext, logger *slog.L
instance.TreeConnectCount,
)
}
return nil
}
@@ -130,6 +129,5 @@ func (c *Collector) collectServerShares(ctx *types.ScrapeContext, logger *slog.L
func (c *Collector) toLabelName(name string) string {
s := strings.ReplaceAll(strings.Join(strings.Fields(strings.ToLower(name)), "_"), ".", "_")
s = strings.ReplaceAll(s, "__", "_")
return s
}

View File

@@ -3,14 +3,14 @@
package smbclient
import (
"log/slog"
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/perflib"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const (
@@ -23,6 +23,7 @@ var ConfigDefaults = Config{}
type Collector struct {
config Config
logger log.Logger
readBytesTotal *prometheus.Desc
readBytesTransmittedViaSMBDirectTotal *prometheus.Desc
@@ -48,7 +49,7 @@ type Collector struct {
requestSecs *prometheus.Desc
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -57,6 +58,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -68,17 +71,21 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{
"SMB Client Shares",
}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build() error {
// desc creates a new prometheus description
desc := func(metricName string, description string, labels []string) *prometheus.Desc {
return prometheus.NewDesc(
@@ -178,13 +185,9 @@ func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
}
// Collect collects smb client metrics and sends them to prometheus.
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
if err := c.collectClientShares(ctx, logger, ch); err != nil {
logger.Error("Error in ClientShares",
slog.Any("err", err),
)
func (c *Collector) Collect(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
if err := c.collectClientShares(ctx, ch); err != nil {
_ = level.Error(c.logger).Log("msg", "Error in ClientShares", "err", err)
return err
}
@@ -218,15 +221,11 @@ type perflibClientShares struct {
WriteRequestsPerSec float64 `perflib:"Write Requests/sec"`
}
func (c *Collector) collectClientShares(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) collectClientShares(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
var data []perflibClientShares
if err := perflib.UnmarshalObject(ctx.PerfObjects["SMB Client Shares"], &data, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects["SMB Client Shares"], &data, c.logger); err != nil {
return err
}
for _, instance := range data {
if instance.Name == "_Total" {
continue
@@ -385,6 +384,5 @@ func (c *Collector) collectClientShares(ctx *types.ScrapeContext, logger *slog.L
serverValue, shareValue,
)
}
return nil
}

View File

@@ -4,14 +4,14 @@ package smtp
import (
"fmt"
"log/slog"
"regexp"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/perflib"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "smtp"
@@ -28,6 +28,7 @@ var ConfigDefaults = Config{
type Collector struct {
config Config
logger log.Logger
badMailedMessagesBadPickupFileTotal *prometheus.Desc
badMailedMessagesGeneralFailureTotal *prometheus.Desc
@@ -73,7 +74,7 @@ type Collector struct {
routingTableLookupsTotal *prometheus.Desc
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -90,6 +91,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -133,18 +136,20 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{"SMTP Server"}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error {
logger = logger.With(slog.String("collector", Name))
logger.Info("smtp collector is in an experimental state! Metrics for this collector have not been tested.")
func (c *Collector) Build() error {
_ = level.Info(c.logger).Log("msg", "smtp collector is in an experimental state! Metrics for this collector have not been tested.")
c.badMailedMessagesBadPickupFileTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "badmailed_messages_bad_pickup_file_total"),
@@ -404,16 +409,11 @@ func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error {
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
if err := c.collect(ctx, logger, ch); err != nil {
logger.Error("failed collecting smtp metrics",
slog.Any("err", err),
)
func (c *Collector) Collect(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
if err := c.collect(ctx, ch); err != nil {
_ = level.Error(c.logger).Log("msg", "failed collecting smtp metrics", "err", err)
return err
}
return nil
}
@@ -465,12 +465,9 @@ type PerflibSMTPServer struct {
RoutingTableLookupsTotal float64 `perflib:"Routing Table Lookups Total"`
}
func (c *Collector) collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) collect(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
var dst []PerflibSMTPServer
if err := perflib.UnmarshalObject(ctx.PerfObjects["SMTP Server"], &dst, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects["SMTP Server"], &dst, c.logger); err != nil {
return err
}
@@ -768,6 +765,5 @@ func (c *Collector) collect(ctx *types.ScrapeContext, logger *slog.Logger, ch ch
server.Name,
)
}
return nil
}

View File

@@ -3,14 +3,12 @@
package system
import (
"errors"
"log/slog"
"github.com/alecthomas/kingpin/v2"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/perflib"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "system"
@@ -22,18 +20,17 @@ var ConfigDefaults = Config{}
// A Collector is a Prometheus Collector for WMI metrics.
type Collector struct {
config Config
logger log.Logger
contextSwitchesTotal *prometheus.Desc
exceptionDispatchesTotal *prometheus.Desc
processorQueueLength *prometheus.Desc
processes *prometheus.Desc
processesLimit *prometheus.Desc
systemCallsTotal *prometheus.Desc
systemUpTime *prometheus.Desc
threads *prometheus.Desc
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -42,6 +39,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -53,15 +52,19 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{"System"}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Build() error {
c.contextSwitchesTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "context_switches_total"),
"Total number of context switches (WMI source: PerfOS_System.ContextSwitchesPersec)",
@@ -74,19 +77,6 @@ func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
nil,
nil,
)
c.processes = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "processes"),
"Current number of processes (WMI source: PerfOS_System.Processes)",
nil,
nil,
)
c.processesLimit = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "processes_limit"),
"Maximum number of processes.",
nil,
nil,
)
c.processorQueueLength = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "processor_queue_length"),
"Length of processor queue (WMI source: PerfOS_System.ProcessorQueueLength)",
@@ -111,22 +101,16 @@ func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
nil,
nil,
)
return nil
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
if err := c.collect(ctx, logger, ch); err != nil {
logger.Error("failed collecting system metrics",
slog.Any("err", err),
)
func (c *Collector) Collect(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
if err := c.collect(ctx, ch); err != nil {
_ = level.Error(c.logger).Log("msg", "failed collecting system metrics", "err", err)
return err
}
return nil
}
@@ -138,23 +122,15 @@ type system struct {
ProcessorQueueLength float64 `perflib:"Processor Queue Length"`
SystemCallsPersec float64 `perflib:"System Calls/sec"`
SystemUpTime float64 `perflib:"System Up Time"`
Processes float64 `perflib:"Processes"`
Threads float64 `perflib:"Threads"`
}
func (c *Collector) collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) collect(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
var dst []system
if err := perflib.UnmarshalObject(ctx.PerfObjects["System"], &dst, logger); err != nil {
if err := perflib.UnmarshalObject(ctx.PerfObjects["System"], &dst, c.logger); err != nil {
return err
}
if len(dst) == 0 {
return errors.New("no data returned from Performance Counter")
}
ch <- prometheus.MustNewConstMetric(
c.contextSwitchesTotal,
prometheus.CounterValue,
@@ -170,11 +146,6 @@ func (c *Collector) collect(ctx *types.ScrapeContext, logger *slog.Logger, ch ch
prometheus.GaugeValue,
dst[0].ProcessorQueueLength,
)
ch <- prometheus.MustNewConstMetric(
c.processes,
prometheus.GaugeValue,
dst[0].Processes,
)
ch <- prometheus.MustNewConstMetric(
c.systemCallsTotal,
prometheus.CounterValue,
@@ -190,15 +161,5 @@ func (c *Collector) collect(ctx *types.ScrapeContext, logger *slog.Logger, ch ch
prometheus.GaugeValue,
dst[0].Threads,
)
// Windows has no defined limit, and is based off available resources. This currently isn't calculated by WMI and is set to default value.
// https://techcommunity.microsoft.com/t5/windows-blog-archive/pushing-the-limits-of-windows-processes-and-threads/ba-p/723824
// https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-operatingsystem
ch <- prometheus.MustNewConstMetric(
c.processesLimit,
prometheus.GaugeValue,
float64(4294967295),
)
return nil
}

View File

@@ -1,16 +0,0 @@
package tcp
// Win32_PerfRawData_Tcpip_TCPv4 docs
// - https://msdn.microsoft.com/en-us/library/aa394341(v=vs.85).aspx
// The TCPv6 performance object uses the same fields.
const (
ConnectionFailures = "Connection Failures"
ConnectionsActive = "Connections Active"
ConnectionsEstablished = "Connections Established"
ConnectionsPassive = "Connections Passive"
ConnectionsReset = "Connections Reset"
SegmentsPersec = "Segments/sec"
SegmentsReceivedPersec = "Segments Received/sec"
SegmentsRetransmittedPersec = "Segments Retransmitted/sec"
SegmentsSentPersec = "Segments Sent/sec"
)

View File

@@ -3,14 +3,12 @@
package tcp
import (
"fmt"
"log/slog"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/pkg/perfdata"
"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus-community/windows_exporter/pkg/perflib"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "tcp"
@@ -22,9 +20,7 @@ var ConfigDefaults = Config{}
// A Collector is a Prometheus Collector for WMI Win32_PerfRawData_Tcpip_TCPv{4,6} metrics.
type Collector struct {
config Config
perfDataCollector4 *perfdata.Collector
perfDataCollector6 *perfdata.Collector
logger log.Logger
connectionFailures *prometheus.Desc
connectionsActive *prometheus.Desc
@@ -37,7 +33,7 @@ type Collector struct {
segmentsSentTotal *prometheus.Desc
}
func New(config *Config) *Collector {
func New(logger log.Logger, config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
@@ -46,6 +42,8 @@ func New(config *Config) *Collector {
config: *config,
}
c.SetLogger(logger)
return c
}
@@ -57,39 +55,19 @@ func (c *Collector) GetName() string {
return Name
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
return []string{}, nil
func (c *Collector) SetLogger(logger log.Logger) {
c.logger = log.With(logger, "collector", Name)
}
func (c *Collector) Close(_ *slog.Logger) error {
func (c *Collector) GetPerfCounter() ([]string, error) {
return []string{"TCPv4"}, nil
}
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
counters := []string{
ConnectionFailures,
ConnectionsActive,
ConnectionsEstablished,
ConnectionsPassive,
ConnectionsReset,
SegmentsPersec,
SegmentsReceivedPersec,
SegmentsRetransmittedPersec,
SegmentsSentPersec,
}
var err error
c.perfDataCollector4, err = perfdata.NewCollector("TCPv4", nil, counters)
if err != nil {
return fmt.Errorf("failed to create TCPv4 collector: %w", err)
}
c.perfDataCollector6, err = perfdata.NewCollector("TCPv6", nil, counters)
if err != nil {
return fmt.Errorf("failed to create TCPv6 collector: %w", err)
}
func (c *Collector) Build() error {
c.connectionFailures = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "connection_failures_total"),
"(TCP.ConnectionFailures)",
@@ -144,96 +122,109 @@ func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
[]string{"af"},
nil,
)
return nil
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
if err := c.collect(ch); err != nil {
logger.Error("failed collecting tcp metrics",
slog.Any("err", err),
)
func (c *Collector) Collect(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
if err := c.collect(ctx, ch); err != nil {
_ = level.Error(c.logger).Log("msg", "failed collecting tcp metrics", "err", err)
return err
}
return nil
}
func writeTCPCounters(metrics map[string]perfdata.CounterValues, labels []string, c *Collector, ch chan<- prometheus.Metric) {
// Win32_PerfRawData_Tcpip_TCPv4 docs
// - https://msdn.microsoft.com/en-us/library/aa394341(v=vs.85).aspx
// The TCPv6 performance object uses the same fields.
type tcp struct {
ConnectionFailures float64 `perflib:"Connection Failures"`
ConnectionsActive float64 `perflib:"Connections Active"`
ConnectionsEstablished float64 `perflib:"Connections Established"`
ConnectionsPassive float64 `perflib:"Connections Passive"`
ConnectionsReset float64 `perflib:"Connections Reset"`
SegmentsPersec float64 `perflib:"Segments/sec"`
SegmentsReceivedPersec float64 `perflib:"Segments Received/sec"`
SegmentsRetransmittedPersec float64 `perflib:"Segments Retransmitted/sec"`
SegmentsSentPersec float64 `perflib:"Segments Sent/sec"`
}
func writeTCPCounters(metrics tcp, labels []string, c *Collector, ch chan<- prometheus.Metric) {
ch <- prometheus.MustNewConstMetric(
c.connectionFailures,
prometheus.CounterValue,
metrics[ConnectionFailures].FirstValue,
metrics.ConnectionFailures,
labels...,
)
ch <- prometheus.MustNewConstMetric(
c.connectionsActive,
prometheus.CounterValue,
metrics[ConnectionsActive].FirstValue,
metrics.ConnectionsActive,
labels...,
)
ch <- prometheus.MustNewConstMetric(
c.connectionsEstablished,
prometheus.GaugeValue,
metrics[ConnectionsEstablished].FirstValue,
metrics.ConnectionsEstablished,
labels...,
)
ch <- prometheus.MustNewConstMetric(
c.connectionsPassive,
prometheus.CounterValue,
metrics[ConnectionsPassive].FirstValue,
metrics.ConnectionsPassive,
labels...,
)
ch <- prometheus.MustNewConstMetric(
c.connectionsReset,
prometheus.CounterValue,
metrics[ConnectionsReset].FirstValue,
metrics.ConnectionsReset,
labels...,
)
ch <- prometheus.MustNewConstMetric(
c.segmentsTotal,
prometheus.CounterValue,
metrics[SegmentsPersec].FirstValue,
metrics.SegmentsPersec,
labels...,
)
ch <- prometheus.MustNewConstMetric(
c.segmentsReceivedTotal,
prometheus.CounterValue,
metrics[SegmentsReceivedPersec].FirstValue,
metrics.SegmentsReceivedPersec,
labels...,
)
ch <- prometheus.MustNewConstMetric(
c.segmentsRetransmittedTotal,
prometheus.CounterValue,
metrics[SegmentsRetransmittedPersec].FirstValue,
metrics.SegmentsRetransmittedPersec,
labels...,
)
ch <- prometheus.MustNewConstMetric(
c.segmentsSentTotal,
prometheus.CounterValue,
metrics[SegmentsSentPersec].FirstValue,
metrics.SegmentsSentPersec,
labels...,
)
}
func (c *Collector) collect(ch chan<- prometheus.Metric) error {
data, err := c.perfDataCollector4.Collect()
if err != nil {
return fmt.Errorf("failed to collect TCPv4 metrics: %w", err)
func (c *Collector) collect(ctx *types.ScrapeContext, ch chan<- prometheus.Metric) error {
var dst []tcp
// TCPv4 counters
if err := perflib.UnmarshalObject(ctx.PerfObjects["TCPv4"], &dst, c.logger); err != nil {
return err
}
if len(dst) != 0 {
writeTCPCounters(dst[0], []string{"ipv4"}, c, ch)
}
writeTCPCounters(data[perfdata.EmptyInstance], []string{"ipv4"}, c, ch)
data, err = c.perfDataCollector6.Collect()
if err != nil {
return fmt.Errorf("failed to collect TCPv6 metrics: %w", err)
// TCPv6 counters
if err := perflib.UnmarshalObject(ctx.PerfObjects["TCPv6"], &dst, c.logger); err != nil {
return err
}
if len(dst) != 0 {
writeTCPCounters(dst[0], []string{"ipv6"}, c, ch)
}
writeTCPCounters(data[perfdata.EmptyInstance], []string{"ipv6"}, c, ch)
return nil
}

Some files were not shown because too many files have changed in this diff Show More