Compare commits

...

12 Commits

Author SHA1 Message Date
Jan-Otto Kröpke
7a9a4e5831 pagefile: BREAKING: move paging metrics from os to dedicated collector (click PR for more information) (#1735)
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
2024-11-14 22:39:59 +01:00
Jan-Otto Kröpke
df8513ab8e Update README.md
Signed-off-by: Jan-Otto Kröpke <github@jkroepke.de>
2024-11-14 22:32:25 +01:00
Jan-Otto Kröpke
1956330ac4 physical_disk: refactor collector (#1734)
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
2024-11-14 22:28:28 +01:00
Jan-Otto Kröpke
baa4dc16ae chore: disable quay.io push, since no credentials are available. (#1733)
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
2024-11-14 20:14:30 +01:00
Jan-Otto Kröpke
d13d726453 chore: Switch to hostprocess base image and add support for Windows Server 2025 on Kubernetes (click PR number for more information) (#1731)
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
2024-11-14 20:01:15 +01:00
Jan-Otto Kröpke
31bcf42473 system: refactor collector (#1730)
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
2024-11-14 00:06:22 +01:00
Jan-Otto Kröpke
f332361723 terminal_services: refactor collector (#1729) 2024-11-13 21:38:31 +01:00
Jan-Otto Kröpke
b4f50c542c time: refactor collector (#1728) 2024-11-13 19:26:47 +01:00
Jan-Otto Kröpke
b53f18bcc6 vmware: refactor collector (#1727) 2024-11-12 23:16:22 +01:00
Jan-Otto Kröpke
eeb7955f5e udp: Added UDP collector (#1725) 2024-11-11 17:17:19 +01:00
dependabot[bot]
55181f5bac chore(deps): bump golang.org/x/sys from 0.26.0 to 0.27.0 (#1724)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-11-11 13:01:20 +01:00
Jan-Otto Kröpke
e6c9253f15 feat: add perfcounter.engine CLI option as alternative to WINDOWS_EXPORTER_PERF_COUNTERS_ENGINE env (#1723) 2024-11-09 23:41:59 +01:00
67 changed files with 1413 additions and 746 deletions

View File

@@ -37,7 +37,7 @@ jobs:
- name: check
run: |
PR_TITLE_PREFIX=$(echo "$PR_TITLE" | cut -d':' -f1)
if [[ -d "internal/collector/$PR_TITLE_PREFIX" ]] || [[ -d "internal/$PR_TITLE_PREFIX" ]] || [[ -d "pkg/$PR_TITLE_PREFIX" ]] || [[ -d "$PR_TITLE_PREFIX" ]] || [[ "$PR_TITLE_PREFIX" == "docs" ]] || [[ "$PR_TITLE_PREFIX" == "ci" ]] || [[ "$PR_TITLE_PREFIX" == "revert" ]] || [[ "$PR_TITLE_PREFIX" == "fix" ]] || [[ "$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 "internal/collector/$PR_TITLE_PREFIX" ]] || [[ -d "internal/$PR_TITLE_PREFIX" ]] || [[ -d "pkg/$PR_TITLE_PREFIX" ]] || [[ -d "$PR_TITLE_PREFIX" ]] || [[ "$PR_TITLE_PREFIX" == "docs" ]] || [[ "$PR_TITLE_PREFIX" == "ci" ]] || [[ "$PR_TITLE_PREFIX" == "revert" ]] || [[ "$PR_TITLE_PREFIX" == "fix" ]] || [[ "$PR_TITLE_PREFIX" == "feat" ]] || [[ "$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
exit 0
fi

View File

@@ -6,6 +6,7 @@ on:
branches:
- master
pull_request:
workflow_dispatch:
release:
types:
- published
@@ -16,10 +17,7 @@ permissions:
packages: write
env:
VERSION_PROMU: '0.14.0'
VERSION_CONTAINERD: '1.7.21'
VERSION_BUILDKIT: '0.15.2'
VERSION_BUILDX: '0.16.2'
VERSION_PROMU: '0.17.0'
jobs:
build:
@@ -33,27 +31,6 @@ jobs:
with:
go-version-file: 'go.mod'
# 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
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
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
- uses: docker/setup-buildx-action@v3
with:
driver: remote
endpoint: npipe:////./pipe/buildkitd
- name: Install WiX
run: dotnet tool install --global wix
@@ -114,16 +91,27 @@ jobs:
output\windows_exporter-*.exe
output\windows_exporter-*.msi
- name: Build Docker Artifacts
run: make build-all
- name: Release
if: startsWith(github.ref, 'refs/tags/')
env:
VERSION: >-
${{
startsWith(github.ref, 'refs/tags/') && 'latest' ||
(
github.event_name == 'pull_request' && format('pr-{0}', github.event.number) || github.ref_name
)
}}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
$TagName = $env:GITHUB_REF -replace 'refs/tags/', ''
Get-ChildItem -Path output\* -Include @('windows_exporter*.msi', 'windows_exporter*.exe', 'sha256sums.txt') | Foreach-Object {gh release upload $TagName $_}
docker:
name: Build docker images
runs-on: ubuntu-latest
needs:
- build
steps:
- uses: actions/checkout@v4
with:
fetch-depth: '0'
- name: Download Artifacts
uses: actions/download-artifact@v4
with:
name: windows_exporter_binaries
- name: Login to Docker Hub
if: ${{ github.event_name != 'pull_request' }}
@@ -137,8 +125,8 @@ jobs:
# uses: docker/login-action@v3
# with:
# registry: quay.io
# username: 'robot'
# password: ${{ secrets.QUAY_IO_API_TOKEN }}
# username: ${{ secrets.QUAY_USER }}
# password: ${{ secrets.QUAY_PASS }}
- name: Login to GitHub container registry
if: ${{ github.event_name != 'pull_request' }}
@@ -148,19 +136,32 @@ jobs:
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Push Latest image
if: ${{ github.event_name != 'pull_request' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION: ${{ startsWith(github.ref, 'refs/tags/') && 'latest' || github.ref_name }}
run: |
make push-all
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: |
ghcr.io/prometheus-community/windows-exporter
docker.io/prometheuscommunity/windows-exporter
# quay.io/prometheuscommunity/windows-exporter
tags: |
type=semver,pattern={{version}}
type=ref,event=branch
type=ref,event=pr
labels: |
org.opencontainers.image.title=windows_exporter
org.opencontainers.image.description=A Prometheus exporter for Windows machines.
org.opencontainers.image.vendor=The Prometheus Community
org.opencontainers.image.licenses=MIT
- name: Release
if: startsWith(github.ref, 'refs/tags/')
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
$TagName = $env:GITHUB_REF -replace 'refs/tags/', ''
Get-ChildItem -Path output\* -Include @('windows_exporter*.msi', 'windows_exporter*.exe', 'sha256sums.txt') | Foreach-Object {gh release upload $TagName $_}
make push-all
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
platforms: windows/amd64

View File

@@ -1,9 +1,13 @@
# Note this image doesn't really matter for hostprocess but it is good to build per OS version
# the files in the image are copied to $env:CONTAINER_SANDBOX_MOUNT_POINT on the host
# but the file system is the Host NOT the container
ARG BASE="mcr.microsoft.com/windows/nanoserver:ltsc2022"
# mcr.microsoft.com/oss/kubernetes/windows-host-process-containers-base-image:v1.0.0
# Using this image as a base for HostProcess containers has a few advantages over using other base images for Windows containers including:
# - Smaller image size
# - OS compatibility (works on any Windows version that supports containers)
# This image MUST be built with docker buildx build (buildx) command on a Linux system.
# Ref: https://github.com/microsoft/windows-host-process-containers-base-image
ARG BASE="mcr.microsoft.com/oss/kubernetes/windows-host-process-containers-base-image:v1.0.0"
FROM $BASE
ENV PATH="C:\Windows\system32;C:\Windows;"
COPY output/amd64/windows_exporter.exe /windows_exporter.exe
COPY windows_exporter*-amd64.exe /windows_exporter.exe
ENTRYPOINT ["windows_exporter.exe"]

View File

@@ -1,22 +0,0 @@
# Build this Docker Image on any platform with
# docker buildx build -t a --platform=windows/amd64 .
ARG BASE=mcr.microsoft.com/oss/kubernetes/windows-host-process-containers-base-image:v1.0.0
FROM --platform=$BUILDPLATFORM golang:1.21 as builder
ARG TARGETARCH
ARG TARGETOS
# Get dependencies
WORKDIR /w
COPY go.mod go.sum ./
RUN go mod download
RUN go install github.com/prometheus/promu@latest
# Build windows_exporter
COPY . ./
RUN GOOS=$TARGETOS GOARCH=$TARGETARCH make windows_exporter.exe
FROM $BASE
COPY --from=builder /w/windows_exporter.exe /windows_exporter.exe
ENTRYPOINT ["windows_exporter.exe"]

View File

@@ -36,7 +36,7 @@ lint:
.PHONY: e2e-test
e2e-test: windows_exporter.exe
pwsh -NonInteractive -ExecutionPolicy Bypass -File .\tools\end-to-end-test.ps1
powershell -NonInteractive -ExecutionPolicy Bypass -File .\tools\end-to-end-test.ps1
.PHONY: promtool
promtool: windows_exporter.exe
@@ -64,31 +64,19 @@ build-hostprocess:
sub-build-%:
$(MAKE) OS=$* build-image
build-all: $(addprefix sub-build-,$(ALL_OS)) build-hostprocess
push:
set -x; \
for docker_repo in ${DOCKER_REPO}; do \
for osversion in ${ALL_OS}; do \
$(DOCKER) tag local/$(DOCKER_IMAGE_NAME):$(VERSION)-$${osversion} $${docker_repo}/$(DOCKER_IMAGE_NAME):$(VERSION)-$${osversion}; \
$(DOCKER) push $${docker_repo}/$(DOCKER_IMAGE_NAME):$(VERSION)-$${osversion}; \
$(DOCKER) manifest create --amend $${docker_repo}/$(DOCKER_IMAGE_NAME):$(VERSION) $${docker_repo}/$(DOCKER_IMAGE_NAME):$(VERSION)-$${osversion}; \
full_version=`$(DOCKER) manifest inspect $(BASE_IMAGE):$${osversion} | grep "os.version" | head -n 1 | awk -F\" '{print $$4}'` || true; \
$(DOCKER) manifest annotate --os windows --arch amd64 --os-version $${full_version} $${docker_repo}/$(DOCKER_IMAGE_NAME):$(VERSION) $${docker_repo}/$(DOCKER_IMAGE_NAME):$(VERSION)-$${osversion}; \
done; \
$(DOCKER) manifest push --purge $${docker_repo}/$(DOCKER_IMAGE_NAME):$(VERSION); \
build-all: crossbuild
@for docker_repo in ${DOCKER_REPO}; do \
echo $(DOCKER) buildx build -f Dockerfile -t $${docker_repo}/$(DOCKER_IMAGE_NAME):$(VERSION) .; \
done
# We can't load the image into the local docker store, so we have to build and push it in one go
push-hostprocess:
set -x; \
for docker_repo in ${DOCKER_REPO}; do \
$(DOCKER) buildx build --push --build-arg=BASE=mcr.microsoft.com/oss/kubernetes/windows-host-process-containers-base-image:v1.0.0 -f Dockerfile -t $${docker_repo}/$(DOCKER_IMAGE_NAME):$(VERSION)-hostprocess .; \
push:
@for docker_repo in ${DOCKER_REPO}; do \
echo $(DOCKER) buildx build --push -f Dockerfile -t $${docker_repo}/$(DOCKER_IMAGE_NAME):$(VERSION) .; \
done
.PHONY: push-all
push-all: build-all
$(MAKE) DOCKER_REPO="$(ALL_DOCKER_REPOS)" push # push-hostprocess - disabled until it works on Windows
$(MAKE) DOCKER_REPO="$(ALL_DOCKER_REPOS)" push
# Mandatory target for container description sync action
.PHONY: docker-repo-name

View File

@@ -1,6 +1,12 @@
# windows_exporter
![Build Status](https://github.com/prometheus-community/windows_exporter/workflows/windows_exporter%20CI/CD/badge.svg)
[![CI](https://github.com/prometheus-community/windows_exporter/actions/workflows/release.yml/badge.svg)](https://github.com/prometheus-community/windows_exporter)
[![Linting](https://github.com/prometheus-community/windows_exporter/actions/workflows/lint.yml/badge.svg)](https://github.com/prometheus-community/windows_exporter)
[![GitHub license](https://img.shields.io/github/license/prometheus-community/windows_exporter)](https://github.com/prometheus-community/windows_exporter/blob/master/LICENSE.txt)
[![Current Release](https://img.shields.io/github/release/prometheus-community/windows_exporter.svg?logo=github)](https://github.com/prometheus-community/windows_exporter/releases/latest)
[![GitHub Repo stars](https://img.shields.io/github/stars/prometheus-community/windows_exporter?style=flat&logo=github)](https://github.com/prometheus-community/windows_exporter/stargazers)
[![GitHub all releases](https://img.shields.io/github/downloads/prometheus-community/windows_exporter/total?logo=github)](https://github.com/prometheus-community/windows_exporter/releases/latest)
[![Go Report Card](https://goreportcard.com/badge/github.com/prometheus-community/windows_exporter)](https://goreportcard.com/report/github.com/prometheus-community/windows_exporter)
A Prometheus exporter for Windows machines.
@@ -35,6 +41,7 @@ Name | Description | Enabled by default
[netframework](docs/collector.netframework.md) | .NET Framework metrics |
[net](docs/collector.net.md) | Network interface I/O | &#10003;
[os](docs/collector.os.md) | OS metrics (memory, processes, users) | &#10003;
[pagefile](docs/collector.pagefile.md) | pagefile metrics |
[perfdata](docs/collector.perfdata.md) | Custom perfdata metrics |
[physical_disk](docs/collector.physical_disk.md) | physical disk metrics | &#10003;
[printer](docs/collector.printer.md) | Printer metrics |
@@ -51,6 +58,7 @@ Name | Description | Enabled by default
[textfile](docs/collector.textfile.md) | Read prometheus metrics from a text file |
[thermalzone](docs/collector.thermalzone.md) | Thermal information |
[time](docs/collector.time.md) | Windows Time Service |
[udp](docs/collector.udp.md) | UDP connections |
[update](docs/collector.update.md) | Windows Update Service |
[vmware](docs/collector.vmware.md) | Performance counters installed by the Vmware Guest agent |

View File

@@ -41,5 +41,6 @@ This directory contains documentation of the collectors in the windows_exporter,
- [`textfile`](collector.textfile.md)
- [`thermalzone`](collector.thermalzone.md)
- [`time`](collector.time.md)
- [`udp`](collector.udp.md)
- [`update`](collector.update.md)
- [`vmware`](collector.vmware.md)

View File

@@ -14,13 +14,11 @@ 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_hostname` | Labelled system hostname information as provided by ComputerSystem.DNSHostName and ComputerSystem.Domain | gauge | `domain`, `fqdn`, `hostname` |
| `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` |
### Example metric
```
@@ -36,4 +34,4 @@ windows_os_info{build_number="19045",major_version="10",minor_version="0",produc
_This collector does not yet have useful queries, we would appreciate your help adding them!_
## Alerting examples
_This collector does not yet have alerting examples, we would appreciate your help adding them!_
_This collector does not yet have alerting examples, we would appreciate your help adding them!_

View File

@@ -0,0 +1,38 @@
# pagefile collector
The pagefile collector exposes metrics about the pagefile usage
|||
-|-
Metric name prefix | `pagefile`
Classes | [`Win32_OperatingSystem`](https://msdn.microsoft.com/en-us/library/aa394239)
Enabled by default? | Yes
## Flags
None
## Metrics
| Name | Description | Type | Labels |
|--------------------------------|-----------------------------------------------------------------------------------------------------------------------------|-------|--------|
| `windows_pagefile_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 | `file` |
| `windows_pagefile_limit_bytes` | Number of bytes that can be stored in the operating system paging files. 0 (zero) indicates that there are no paging files | gauge | `file` |
### Example metric
```
# HELP windows_pagefile_free_bytes OperatingSystem.FreeSpaceInPagingFiles
# TYPE windows_pagefile_free_bytes gauge
windows_pagefile_free_bytes{file="C:\\pagefile.sys"} 6.025797632e+09
# HELP windows_pagefile_limit_bytes OperatingSystem.SizeStoredInPagingFiles
# TYPE windows_pagefile_limit_bytes gauge
windows_pagefile_limit_bytes{file="C:\\pagefile.sys"} 6.442450944e+09
```
## Useful queries
_This collector does not yet have useful queries, 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

@@ -35,7 +35,7 @@ metrics.
Enables IIS process name queries. IIS process names are combined with their app pool name to form the `process` label.
Disabled by default, and can be enabled with `--collector.process.iis=true`.
Disabled by default, and can be enabled with `--collector.process.iis`. NOTE: Just plain parameter without `true`.
### Example
@@ -44,7 +44,7 @@ Note that multiple processes with the same name will be disambiguated by
Windows by adding a number suffix, such as `firefox#2`. Your [regexp](https://en.wikipedia.org/wiki/Regular_expression) must take
these suffixes into consideration.
:warning: The regexp is case-sensitive, so `--collector.process.include="FIREFOX.*"` will **NOT** match a process named `firefox` .
:warning: The regexp is case-sensitive, so `--collector.process.include="FIREFOX.*"` will **NOT** match a process named `firefox` .
To specify multiple names, use the pipe `|` character:
```

View File

@@ -1,12 +1,11 @@
# tcp collector
The tcp collector exposes metrics about the TCP/IPv4 network stack.
The tcp collector exposes metrics about the TCP network stack.
|||
-|-
Metric name prefix | `tcp`
Data source | Perflib
Classes | [`Win32_PerfRawData_Tcpip_TCPv4`](https://msdn.microsoft.com/en-us/library/aa394341(v=vs.85).aspx), Win32_PerfRawData_Tcpip_TCPv6
Enabled by default? | No
## Flags
@@ -15,18 +14,18 @@ None
## Metrics
Name | Description | Type | Labels
-----|-------------|------|-------
`windows_tcp_connection_failures_total` | Number of times TCP connections have made a direct transition to the CLOSED state from the SYN-SENT state or the SYN-RCVD state, plus the number of times TCP connections have made a direct transition from the SYN-RCVD state to the LISTEN state | counter | af
`windows_tcp_connections_active_total` | Number of times TCP connections have made a direct transition from the CLOSED state to the SYN-SENT state.| counter | af
`windows_tcp_connections_established` | Number of TCP connections for which the current state is either ESTABLISHED or CLOSE-WAIT. | gauge | af
`windows_tcp_connections_passive_total` | Number of times TCP connections have made a direct transition from the LISTEN state to the SYN-RCVD state. | counter | af
`windows_tcp_connections_reset_total` | Connections Reset is the number of times TCP connections have made a direct transition to the CLOSED state from either the ESTABLISHED state or the CLOSE-WAIT state. | counter | af
`windows_tcp_segments_total` | Total segments sent or received using the TCP protocol | counter | af
`windows_tcp_segments_received_total` | Total segments received, including those received in error. This count includes segments received on currently established connections | counter | af
`windows_tcp_segments_retransmitted_total` | Total segments retransmitted. That is, segments transmitted that contain one or more previously transmitted bytes | counter | af
`windows_tcp_segments_sent_total` | Total segments sent, including those on current connections, but excluding those containing *only* retransmitted bytes | counter | af
`windows_tcp_connections_state_count` | Number of TCP connections by state among: CLOSED, LISTENING, SYN_SENT, SYN_RECEIVED, ESTABLISHED, FIN_WAIT1, FIN_WAIT2, CLOSE_WAIT, CLOSING, LAST_ACK, TIME_WAIT, DELETE_TCB | gauge | af
| Name | Description | Type | Labels |
|--------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|--------|
| `windows_tcp_connection_failures_total` | Number of times TCP connections have made a direct transition to the CLOSED state from the SYN-SENT state or the SYN-RCVD state, plus the number of times TCP connections have made a direct transition from the SYN-RCVD state to the LISTEN state | counter | af |
| `windows_tcp_connections_active_total` | Number of times TCP connections have made a direct transition from the CLOSED state to the SYN-SENT state. | counter | af |
| `windows_tcp_connections_established` | Number of TCP connections for which the current state is either ESTABLISHED or CLOSE-WAIT. | gauge | af |
| `windows_tcp_connections_passive_total` | Number of times TCP connections have made a direct transition from the LISTEN state to the SYN-RCVD state. | counter | af |
| `windows_tcp_connections_reset_total` | Connections Reset is the number of times TCP connections have made a direct transition to the CLOSED state from either the ESTABLISHED state or the CLOSE-WAIT state. | counter | af |
| `windows_tcp_segments_total` | Total segments sent or received using the TCP protocol | counter | af |
| `windows_tcp_segments_received_total` | Total segments received, including those received in error. This count includes segments received on currently established connections | counter | af |
| `windows_tcp_segments_retransmitted_total` | Total segments retransmitted. That is, segments transmitted that contain one or more previously transmitted bytes | counter | af |
| `windows_tcp_segments_sent_total` | Total segments sent, including those on current connections, but excluding those containing *only* retransmitted bytes | counter | af |
| `windows_tcp_connections_state_count` | Number of TCP connections by state among: CLOSED, LISTENING, SYN_SENT, SYN_RECEIVED, ESTABLISHED, FIN_WAIT1, FIN_WAIT2, CLOSE_WAIT, CLOSING, LAST_ACK, TIME_WAIT, DELETE_TCB | gauge | af |
### Example metric
_This collector does not yet have explained examples, we would appreciate your help adding them!_

View File

@@ -5,15 +5,19 @@ If the Windows Time Service is stopped after collection has started, collector m
Please note the Time Service perflib counters are only available on [Windows Server 2016 or newer](https://docs.microsoft.com/en-us/windows-server/networking/windows-time-service/windows-server-2016-improvements).
| | |
|---------------------|---------|
| Metric name prefix | `time` |
| Data source | Perflib |
| Enabled by default? | No |
| | |
|---------------------|--------|
| Metric name prefix | `time` |
| Data source | PDH |
| Enabled by default? | No |
## Flags
None
### `--collectors.time.enabled`
Comma-separated list of collectors to use, for example: `--collectors.time.enabled=ntp,system_time`.
Matching is case-sensitive.
## Metrics

31
docs/collector.udp.md Normal file
View File

@@ -0,0 +1,31 @@
# udp collector
The udp collector exposes metrics about the UDP network stack.
|||
-|-
Metric name prefix | `udp`
Data source | Perflib
Enabled by default? | No
## Flags
None
## Metrics
| Name | Description | Type | Labels |
|-----------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------|---------|--------|
| `windows_udp_datagram_datagram_no_port_total` | Number of received UDP datagrams for which there was no application at the destination port | counter | af |
| `windows_udp_datagram_received_errors_total` | Number of received UDP datagrams that could not be delivered for reasons other than the lack of an application at the destination port | counter | af |
| `windows_udp_datagram_received_total` | Number of UDP datagrams segments received | counter | af |
| `windows_udp_datagram_sent_total` | Number of UDP datagrams segments sent | counter | af |
### 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

@@ -2,11 +2,11 @@
The vmware collector exposes metrics about a VMware guest VM
|||
-|-
Metric name prefix | `vmware`
Classes | `Win32_PerfRawData_vmGuestLib_VMem`, `Win32_PerfRawData_vmGuestLib_VCPU`
Enabled by default? | No
| | |
|---------------------|----------------------|
| Metric name prefix | `vmware` |
| Source | Performance counters |
| Enabled by default? | No |
## Flags
@@ -14,27 +14,27 @@ None
## Metrics
Name | Description | Type | Labels
-----|-------------|------|-------
`windows_vmware_mem_active_bytes` | _Not yet documented_ | gauge | None
`windows_vmware_mem_ballooned_bytes` | _Not yet documented_ | gauge | None
`windows_vmware_mem_limit_bytes` | _Not yet documented_ | gauge | None
`windows_vmware_mem_mapped_bytes` | _Not yet documented_ | gauge | None
`windows_vmware_mem_overhead_bytes` | _Not yet documented_ | gauge | None
`windows_vmware_mem_reservation_bytes` | _Not yet documented_ | gauge | None
`windows_vmware_mem_shared_bytes` | _Not yet documented_ | gauge | None
`windows_vmware_mem_shared_saved_bytes` | _Not yet documented_ | gauge | None
`windows_vmware_mem_shares` | _Not yet documented_ | gauge | None
`windows_vmware_mem_swapped_bytes` | _Not yet documented_ | gauge | None
`windows_vmware_mem_target_size_bytes` | _Not yet documented_ | gauge | None
`windows_vmware_mem_used_bytes` | _Not yet documented_ | gauge | None
`windows_vmware_cpu_limit_mhz` | _Not yet documented_ | gauge | None
`windows_vmware_cpu_reservation_mhz` | _Not yet documented_ | gauge | None
`windows_vmware_cpu_shares` | _Not yet documented_ | gauge | None
`windows_vmware_cpu_stolen_seconds_total` | _Not yet documented_ | counter | None
`windows_vmware_cpu_time_seconds_total` | _Not yet documented_ | counter | None
`windows_vmware_effective_vm_speed_mhz` | _Not yet documented_ | gauge | None
`windows_vmware_host_processor_speed_mhz` | _Not yet documented_ | gauge | None
| Name | Description | Type | Labels |
|---------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------|--------|
| `windows_vmware_mem_active_bytes` | The estimated amount of memory the virtual machine is actively using. | gauge | None |
| `windows_vmware_mem_ballooned_bytes` | The amount of memory that has been reclaimed from this virtual machine via the VMware Memory Balloon mechanism. | gauge | None |
| `windows_vmware_mem_limit_bytes` | The maximum amount of memory that is allowed to the virtual machine. Assigning a Memory Limit ensures that this virtual machine never consumes more than a certain amount of the allowed memory. By limiting the amount of memory consumed, a portion of this shared resource is allowed to other virtual machines. | gauge | None |
| `windows_vmware_mem_mapped_bytes` | The mapped memory size of this virtual machine. This is the current total amount of guest memory that is backed by physical memory. Note that this number may include pages of memory shared between multiple virtual machines and thus may be an overestimate of the amount of physical host memory consumed by this virtual machine. | gauge | None |
| `windows_vmware_mem_overhead_bytes` | The amount of overhead memory associated with this virtual machine consumed on the host system. | gauge | None |
| `windows_vmware_mem_reservation_bytes` | The minimum amount of memory that is guaranteed to the virtual machine. Assigning a Memory Reservation ensures that even as other virtual machines on the same host consume memory, there is still a certain minimum amount for this virtual machine. | gauge | None |
| `windows_vmware_mem_shared_bytes` | The amount of physical memory associated with this virtual machine that is copy-on-write (COW) shared on the host. | gauge | None |
| `windows_vmware_mem_shared_saved_bytes` | The estimated amount of physical memory on the host saved from copy-on-write (COW) shared guest physical memory. | gauge | None |
| `windows_vmware_mem_shares` | The number of memory shares allocated to the virtual machine. | gauge | None |
| `windows_vmware_mem_swapped_bytes` | The amount of memory associated with this virtual machine that has been swapped by ESX. | gauge | None |
| `windows_vmware_mem_target_size_bytes` | Memory Target Size | gauge | None |
| `windows_vmware_mem_used_bytes` | The estimated amount of physical host memory currently consumed for this virtual machines physical memory. | gauge | None |
| `windows_vmware_cpu_limit_mhz` | The maximum processing power in MHz allowed to the virtual machine. Assigning a CPU Limit ensures that this virtual machine never consumes more than a certain amount of the available processor power. By limiting the amount of processing power consumed, a portion of the processing power becomes available to other virtual machines. | gauge | None |
| `windows_vmware_cpu_reservation_mhz` | The minimum processing power in MHz available to the virtual machine. Assigning a CPU Reservation ensures that even as other virtual machines on the same host consume shared processing power, there is still a certain minimum amount for this virtual machine. | gauge | None |
| `windows_vmware_cpu_shares` | The number of CPU shares allocated to the virtual machine. | gauge | None |
| `windows_vmware_cpu_stolen_seconds_total` | The time that the VM was runnable but not scheduled to run | counter | None |
| `windows_vmware_cpu_time_seconds_total` | Current load of the VMs virtual processor | counter | None |
| `windows_vmware_cpu_effective_vm_speed_mhz` | The effective speed of the VMs virtual CPU | gauge | None |
| `windows_vmware_host_processor_speed_mhz` | Host Processor speed | gauge | None |
### Example metric
_This collector does not yet have explained examples, we would appreciate your help adding them!_

View File

@@ -28,6 +28,7 @@ import (
"github.com/prometheus-community/windows_exporter/internal/httphandler"
"github.com/prometheus-community/windows_exporter/internal/log"
"github.com/prometheus-community/windows_exporter/internal/log/flag"
"github.com/prometheus-community/windows_exporter/internal/toggle"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus-community/windows_exporter/pkg/collector"
@@ -96,6 +97,11 @@ func run() int {
"process.priority",
"Priority of the exporter process. Higher priorities may improve exporter responsiveness during periods of system load. Can be one of [\"realtime\", \"high\", \"abovenormal\", \"normal\", \"belownormal\", \"low\"]",
).Default("normal").String()
togglePDH = app.Flag(
"perfcounter.engine",
"EXPERIMENTAL: Performance counter engine to use. Can be one of \"pdh\", \"registry\". PDH is in experimental state. This flag will be removed in 0.31.",
).Default("registry").String()
)
logConfig := &log.Config{}
@@ -173,6 +179,12 @@ func run() int {
logger.Debug("Logging has Started")
if v, ok := os.LookupEnv("WINDOWS_EXPORTER_PERF_COUNTERS_ENGINE"); ok && v == "pdh" || *togglePDH == "pdh" {
logger.Info("Using performance data helper from PHD.dll for performance counter collection. This is in experimental state.")
toggle.PHDEnabled = true
}
if *printCollectors {
printCollectorsToStdout()
@@ -215,10 +227,6 @@ func run() int {
logger.Info("Enabled collectors: " + strings.Join(enabledCollectorList, ", "))
if utils.PDHEnabled() {
logger.Info("Using performance data helper from PHD.dll for performance counter collection. This is in experimental state.")
}
mux := http.NewServeMux()
mux.Handle("GET /health", httphandler.NewHealthHandler())
mux.Handle("GET /version", httphandler.NewVersionHandler())

2
go.mod
View File

@@ -14,7 +14,7 @@ require (
github.com/prometheus/common v0.60.1
github.com/prometheus/exporter-toolkit v0.13.1
github.com/stretchr/testify v1.9.0
golang.org/x/sys v0.26.0
golang.org/x/sys v0.27.0
gopkg.in/yaml.v3 v3.0.1
)

4
go.sum
View File

@@ -160,8 +160,8 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/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.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.27.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.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=

View File

@@ -11,6 +11,7 @@ import (
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/toggle"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
@@ -63,7 +64,7 @@ func (c *Collector) GetName() string {
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
if utils.PDHEnabled() {
if toggle.IsPDHEnabled() {
return []string{}, nil
}
@@ -75,7 +76,7 @@ func (c *Collector) Close(_ *slog.Logger) error {
}
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
if utils.PDHEnabled() {
if toggle.IsPDHEnabled() {
counters := []string{
requestsPerSecond,
requestProcessingTime,
@@ -183,7 +184,7 @@ func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
}
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
if utils.PDHEnabled() {
if toggle.IsPDHEnabled() {
return c.collectPDH(ch)
}

View File

@@ -14,8 +14,8 @@ import (
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/toggle"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
)
@@ -96,7 +96,7 @@ func (c *Collector) GetName() string {
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
if utils.PDHEnabled() {
if toggle.IsPDHEnabled() {
return []string{}, nil
}
@@ -108,7 +108,7 @@ func (c *Collector) Close(_ *slog.Logger) error {
}
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
if utils.PDHEnabled() {
if toggle.IsPDHEnabled() {
counters := []string{
adLoginConnectionFailures,
certificateAuthentications,
@@ -426,7 +426,7 @@ func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
}
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
if utils.PDHEnabled() {
if toggle.IsPDHEnabled() {
return c.collectPDH(ch)
}

View File

@@ -12,8 +12,8 @@ import (
"github.com/prometheus-community/windows_exporter/internal/perfdata"
"github.com/prometheus-community/windows_exporter/internal/perfdata/perftypes"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/toggle"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
)
@@ -81,7 +81,7 @@ func (c *Collector) GetName() string {
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
if utils.PDHEnabled() {
if toggle.IsPDHEnabled() {
return []string{}, nil
}
@@ -93,7 +93,7 @@ func (c *Collector) Close(_ *slog.Logger) error {
}
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
if utils.PDHEnabled() {
if toggle.IsPDHEnabled() {
counters := []string{
asyncCopyReadsTotal,
asyncDataMapsTotal,
@@ -314,7 +314,7 @@ func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
// Collect implements the Collector interface.
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
if utils.PDHEnabled() {
if toggle.IsPDHEnabled() {
return c.collectPDH(ch)
}

View File

@@ -11,6 +11,7 @@ import (
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/toggle"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
@@ -67,7 +68,7 @@ func (c *Collector) GetName() string {
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
if utils.PDHEnabled() {
if toggle.IsPDHEnabled() {
return []string{}, nil
}
@@ -79,7 +80,7 @@ func (c *Collector) Close(_ *slog.Logger) error {
}
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
if utils.PDHEnabled() {
if toggle.IsPDHEnabled() {
counters := []string{
c1TimeSeconds,
c2TimeSeconds,
@@ -108,7 +109,7 @@ func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
var err error
c.perfDataCollector, err = perfdata.NewCollector(perfdata.V1, "Processor Information", perfdata.AllInstances, counters)
c.perfDataCollector, err = perfdata.NewCollector(perfdata.V2, "Processor Information", perfdata.AllInstances, counters)
if err != nil {
return fmt.Errorf("failed to create Processor Information collector: %w", err)
}
@@ -231,7 +232,7 @@ func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
}
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
if utils.PDHEnabled() {
if toggle.IsPDHEnabled() {
return c.collectPDH(ch)
}

View File

@@ -13,8 +13,8 @@ import (
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/toggle"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
)
@@ -148,7 +148,7 @@ func (c *Collector) GetName() string {
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
if utils.PDHEnabled() {
if toggle.IsPDHEnabled() {
return []string{}, nil
}
@@ -173,7 +173,7 @@ func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error {
logger.Info("dfsr collector is in an experimental state! Metrics for this collector have not been tested.")
//nolint:nestif
if utils.PDHEnabled() {
if toggle.IsPDHEnabled() {
var err error
if slices.Contains(c.config.CollectorsEnabled, "connection") {
@@ -567,7 +567,7 @@ 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 {
if utils.PDHEnabled() {
if toggle.IsPDHEnabled() {
return c.collectPDH(ch)
}

View File

@@ -12,8 +12,8 @@ import (
"github.com/prometheus-community/windows_exporter/internal/perfdata"
"github.com/prometheus-community/windows_exporter/internal/perfdata/perftypes"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/toggle"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
)
@@ -77,7 +77,7 @@ func (c *Collector) GetName() string {
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
if utils.PDHEnabled() {
if toggle.IsPDHEnabled() {
return []string{}, nil
}
@@ -89,7 +89,7 @@ func (c *Collector) Close(_ *slog.Logger) error {
}
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
if utils.PDHEnabled() {
if toggle.IsPDHEnabled() {
counters := []string{
acksTotal,
activeQueueLength,
@@ -281,7 +281,7 @@ func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
}
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
if utils.PDHEnabled() {
if toggle.IsPDHEnabled() {
return c.collectPDH(ch)
}

View File

@@ -12,8 +12,8 @@ import (
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
"github.com/prometheus-community/windows_exporter/internal/toggle"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
)
@@ -185,7 +185,7 @@ func (c *Collector) GetName() string {
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
if utils.PDHEnabled() {
if toggle.IsPDHEnabled() {
return []string{}, nil
}
@@ -208,7 +208,7 @@ func (c *Collector) Close(_ *slog.Logger) error {
}
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
if utils.PDHEnabled() {
if toggle.IsPDHEnabled() {
collectorFuncs := map[string]func() error{
adAccessProcesses: c.buildADAccessProcesses,
transportQueues: c.buildTransportQueues,
@@ -283,7 +283,7 @@ func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
// Collect collects exchange metrics and sends them to prometheus.
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
if utils.PDHEnabled() {
if toggle.IsPDHEnabled() {
return c.collectPDH(ch)
}

View File

@@ -16,7 +16,7 @@ const (
percentDiskWriteTime = "% Disk Write Time"
percentFreeSpace = "% Free Space"
percentIdleTime = "% Idle Time"
SplitIOPerSec = "Split IO/Sec"
splitIOPerSec = "Split IO/Sec"
)
// Win32_PerfRawData_PerfDisk_LogicalDisk docs:

View File

@@ -4,7 +4,6 @@ package logical_disk
import (
"encoding/binary"
"errors"
"fmt"
"log/slog"
"regexp"
@@ -17,8 +16,8 @@ import (
"github.com/prometheus-community/windows_exporter/internal/perfdata"
"github.com/prometheus-community/windows_exporter/internal/perfdata/perftypes"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/toggle"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
"golang.org/x/sys/windows"
)
@@ -130,7 +129,7 @@ func (c *Collector) GetName() string {
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
if utils.PDHEnabled() {
if toggle.IsPDHEnabled() {
return []string{}, nil
}
@@ -142,7 +141,7 @@ func (c *Collector) Close(_ *slog.Logger) error {
}
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
if utils.PDHEnabled() {
if toggle.IsPDHEnabled() {
counters := []string{
currentDiskQueueLength,
avgDiskReadQueueLength,
@@ -156,7 +155,7 @@ func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
percentFreeSpace,
freeSpace,
percentIdleTime,
SplitIOPerSec,
splitIOPerSec,
avgDiskSecPerRead,
avgDiskSecPerWrite,
avgDiskSecPerTransfer,
@@ -302,7 +301,7 @@ func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
if utils.PDHEnabled() {
if toggle.IsPDHEnabled() {
return c.collectPDH(logger, ch)
}
@@ -329,10 +328,6 @@ func (c *Collector) collectPDH(logger *slog.Logger, ch chan<- prometheus.Metric)
return fmt.Errorf("failed to collect LogicalDisk metrics: %w", err)
}
if len(perfData) == 0 {
return errors.New("perflib query for LogicalDisk returned empty result set")
}
for name, volume := range perfData {
if name == "_Total" ||
c.config.VolumeExclude.MatchString(name) ||
@@ -453,7 +448,7 @@ func (c *Collector) collectPDH(logger *slog.Logger, ch chan<- prometheus.Metric)
ch <- prometheus.MustNewConstMetric(
c.splitIOs,
prometheus.CounterValue,
volume[SplitIOPerSec].FirstValue,
volume[splitIOPerSec].FirstValue,
name,
)

View File

@@ -16,8 +16,8 @@ import (
"github.com/prometheus-community/windows_exporter/internal/perfdata"
"github.com/prometheus-community/windows_exporter/internal/perfdata/perftypes"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/toggle"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
)
@@ -94,7 +94,7 @@ func (c *Collector) GetName() string {
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
if utils.PDHEnabled() {
if toggle.IsPDHEnabled() {
return []string{}, nil
}
@@ -106,7 +106,7 @@ func (c *Collector) Close(_ *slog.Logger) error {
}
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
if utils.PDHEnabled() {
if toggle.IsPDHEnabled() {
counters := []string{
availableBytes,
availableKBytes,
@@ -386,7 +386,7 @@ func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch ch
errs := make([]error, 0, 2)
var err error
if utils.PDHEnabled() {
if toggle.IsPDHEnabled() {
err = c.collectPDH(ch)
} else {
err = c.collectPerformanceData(ctx, logger, ch)

View File

@@ -16,8 +16,8 @@ import (
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/toggle"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
"golang.org/x/sys/windows"
)
@@ -138,7 +138,7 @@ func (c *Collector) GetName() string {
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
if utils.PDHEnabled() {
if toggle.IsPDHEnabled() {
return []string{}, nil
}
@@ -150,7 +150,7 @@ func (c *Collector) Close(_ *slog.Logger) error {
}
func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error {
if utils.PDHEnabled() {
if toggle.IsPDHEnabled() {
counters := []string{
BytesReceivedPerSec,
BytesSentPerSec,
@@ -283,7 +283,7 @@ func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch ch
if slices.Contains(c.config.CollectorsEnabled, "metrics") {
var err error
if utils.PDHEnabled() {
if toggle.IsPDHEnabled() {
err = c.collectPDH(ch)
} else {
err = c.collect(ctx, logger, ch)

View File

@@ -34,9 +34,14 @@ var ConfigDefaults = Config{}
type Collector struct {
config Config
hostname *prometheus.Desc
osInformation *prometheus.Desc
pagingFreeBytes *prometheus.Desc
hostname *prometheus.Desc
osInformation *prometheus.Desc
// pagingFreeBytes
// Deprecated: Use windows_paging_free_bytes instead.
pagingFreeBytes *prometheus.Desc
// pagingLimitBytes
// Deprecated: Use windows_paging_total_bytes instead.
pagingLimitBytes *prometheus.Desc
// users
@@ -151,13 +156,13 @@ func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error {
)
c.pagingLimitBytes = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "paging_limit_bytes"),
"OperatingSystem.SizeStoredInPagingFiles",
"Deprecated: Use windows_pagefile_limit_bytes instead.",
nil,
nil,
)
c.pagingFreeBytes = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "paging_free_bytes"),
"OperatingSystem.FreeSpaceInPagingFiles",
"Deprecated: Use windows_pagefile_free_bytes instead.",
nil,
nil,
)

View File

@@ -0,0 +1,5 @@
package pagefile
const (
usage = "% Usage"
)

View File

@@ -0,0 +1,136 @@
//go:build windows
package pagefile
import (
"fmt"
"log/slog"
"os"
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/headers/psapi"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
)
const Name = "pagefile"
type Config struct{}
var ConfigDefaults = Config{}
// A Collector is a Prometheus Collector for WMI metrics.
type Collector struct {
config Config
perfDataCollector perfdata.Collector
pagingFreeBytes *prometheus.Desc
pagingLimitBytes *prometheus.Desc
}
func New(config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
c := &Collector{
config: *config,
}
return c
}
func NewWithFlags(_ *kingpin.Application) *Collector {
return &Collector{}
}
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 {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
counters := []string{
usage,
}
var err error
c.perfDataCollector, err = perfdata.NewCollector(perfdata.V2, "Paging File", perfdata.AllInstances, counters)
if err != nil {
return fmt.Errorf("failed to create Paging File collector: %w", err)
}
c.pagingLimitBytes = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "limit_bytes"),
"Number of bytes that can be stored in the operating system paging files. 0 (zero) indicates that there are no paging files",
[]string{"file"},
nil,
)
c.pagingFreeBytes = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "free_bytes"),
"Number of bytes that can be mapped into the operating system paging files without causing any other pages to be swapped out",
[]string{"file"},
nil,
)
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 {
return c.collectPaging(ch)
}
func (c *Collector) collectPaging(ch chan<- prometheus.Metric) error {
data, err := c.perfDataCollector.Collect()
if err != nil {
return fmt.Errorf("failed to collect Paging File metrics: %w", err)
}
gpi, err := psapi.GetPerformanceInfo()
if err != nil {
return err
}
for fileName, pageFile := range data {
fileString := strings.ReplaceAll(fileName, `\??\`, "")
file, err := os.Stat(fileString)
var fileSize float64
// For unknown reasons, Windows doesn't always create a page file. Continue collection rather than aborting.
if err == nil {
fileSize = float64(file.Size())
}
ch <- prometheus.MustNewConstMetric(
c.pagingFreeBytes,
prometheus.GaugeValue,
fileSize-(pageFile[usage].FirstValue*float64(gpi.PageSize)),
fileString,
)
ch <- prometheus.MustNewConstMetric(
c.pagingLimitBytes,
prometheus.GaugeValue,
fileSize,
fileString,
)
}
return nil
}

View File

@@ -0,0 +1,16 @@
package pagefile_test
import (
"testing"
"github.com/prometheus-community/windows_exporter/internal/collector/pagefile"
"github.com/prometheus-community/windows_exporter/internal/testutils"
)
func BenchmarkCollector(b *testing.B) {
testutils.FuncBenchmarkCollector(b, pagefile.Name, pagefile.NewWithFlags)
}
func TestCollector(t *testing.T) {
testutils.TestCollector(t, pagefile.New, nil)
}

View File

@@ -0,0 +1,16 @@
package physical_disk
const (
CurrentDiskQueueLength = "Current Disk Queue Length"
DiskReadBytesPerSec = "Disk Read Bytes/sec"
DiskReadsPerSec = "Disk Reads/sec"
DiskWriteBytesPerSec = "Disk Write Bytes/sec"
DiskWritesPerSec = "Disk Writes/sec"
PercentDiskReadTime = "% Disk Read Time"
PercentDiskWriteTime = "% Disk Write Time"
PercentIdleTime = "% Idle Time"
SplitIOPerSec = "Split IO/Sec"
AvgDiskSecPerRead = "Avg. Disk sec/Read"
AvgDiskSecPerWrite = "Avg. Disk sec/Write"
AvgDiskSecPerTransfer = "Avg. Disk sec/Transfer"
)

View File

@@ -10,8 +10,8 @@ import (
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
"github.com/prometheus-community/windows_exporter/internal/perfdata/perftypes"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
)
@@ -32,6 +32,8 @@ var ConfigDefaults = Config{
type Collector struct {
config Config
perfDataCollector perfdata.Collector
idleTime *prometheus.Desc
readBytesTotal *prometheus.Desc
readLatency *prometheus.Desc
@@ -107,7 +109,7 @@ func (c *Collector) GetName() string {
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
return []string{"PhysicalDisk"}, nil
return []string{}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
@@ -115,6 +117,28 @@ func (c *Collector) Close(_ *slog.Logger) error {
}
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
counters := []string{
CurrentDiskQueueLength,
DiskReadBytesPerSec,
DiskReadsPerSec,
DiskWriteBytesPerSec,
DiskWritesPerSec,
PercentDiskReadTime,
PercentDiskWriteTime,
PercentIdleTime,
SplitIOPerSec,
AvgDiskSecPerRead,
AvgDiskSecPerWrite,
AvgDiskSecPerTransfer,
}
var err error
c.perfDataCollector, err = perfdata.NewCollector(perfdata.V2, "PhysicalDisk", perfdata.AllInstances, counters)
if err != nil {
return fmt.Errorf("failed to create PhysicalDisk collector: %w", err)
}
c.requestsQueued = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "requests_queued"),
"The number of requests queued to the disk (PhysicalDisk.CurrentDiskQueueLength)",
@@ -204,139 +228,111 @@ func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) 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),
)
return err
func (c *Collector) Collect(_ *types.ScrapeContext, _ *slog.Logger, ch chan<- prometheus.Metric) error {
if err := c.collect(ch); err != nil {
return fmt.Errorf("failed collecting physical_disk metrics: %w", err)
}
return nil
}
// PhysicalDisk
// Win32_PerfRawData_PerfDisk_PhysicalDisk docs:
// - https://docs.microsoft.com/en-us/previous-versions/aa394308(v=vs.85) - Win32_PerfRawData_PerfDisk_PhysicalDisk class.
type PhysicalDisk struct {
Name string
CurrentDiskQueueLength float64 `perflib:"Current Disk Queue Length"`
DiskReadBytesPerSec float64 `perflib:"Disk Read Bytes/sec"`
DiskReadsPerSec float64 `perflib:"Disk Reads/sec"`
DiskWriteBytesPerSec float64 `perflib:"Disk Write Bytes/sec"`
DiskWritesPerSec float64 `perflib:"Disk Writes/sec"`
PercentDiskReadTime float64 `perflib:"% Disk Read Time"`
PercentDiskWriteTime float64 `perflib:"% Disk Write Time"`
PercentIdleTime float64 `perflib:"% Idle Time"`
SplitIOPerSec float64 `perflib:"Split IO/Sec"`
AvgDiskSecPerRead float64 `perflib:"Avg. Disk sec/Read"`
AvgDiskSecPerWrite float64 `perflib:"Avg. Disk sec/Write"`
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))
var dst []PhysicalDisk
if err := v1.UnmarshalObject(ctx.PerfObjects["PhysicalDisk"], &dst, logger); err != nil {
return err
func (c *Collector) collect(ch chan<- prometheus.Metric) error {
perfData, err := c.perfDataCollector.Collect()
if err != nil {
return fmt.Errorf("failed to collect PhysicalDisk metrics: %w", err)
}
for _, disk := range dst {
if disk.Name == "_Total" ||
c.config.DiskExclude.MatchString(disk.Name) ||
!c.config.DiskInclude.MatchString(disk.Name) {
for name, disk := range perfData {
if c.config.DiskExclude.MatchString(name) ||
!c.config.DiskInclude.MatchString(name) {
continue
}
// Parse physical disk number from disk.Name. Mountpoint information is
// sometimes included, e.g. "1 C:".
disk_number, _, _ := strings.Cut(disk.Name, " ")
disk_number, _, _ := strings.Cut(name, " ")
ch <- prometheus.MustNewConstMetric(
c.requestsQueued,
prometheus.GaugeValue,
disk.CurrentDiskQueueLength,
disk[CurrentDiskQueueLength].FirstValue,
disk_number,
)
ch <- prometheus.MustNewConstMetric(
c.readBytesTotal,
prometheus.CounterValue,
disk.DiskReadBytesPerSec,
disk[DiskReadBytesPerSec].FirstValue,
disk_number,
)
ch <- prometheus.MustNewConstMetric(
c.readsTotal,
prometheus.CounterValue,
disk.DiskReadsPerSec,
disk[DiskReadsPerSec].FirstValue,
disk_number,
)
ch <- prometheus.MustNewConstMetric(
c.writeBytesTotal,
prometheus.CounterValue,
disk.DiskWriteBytesPerSec,
disk[DiskWriteBytesPerSec].FirstValue,
disk_number,
)
ch <- prometheus.MustNewConstMetric(
c.writesTotal,
prometheus.CounterValue,
disk.DiskWritesPerSec,
disk[DiskWritesPerSec].FirstValue,
disk_number,
)
ch <- prometheus.MustNewConstMetric(
c.readTime,
prometheus.CounterValue,
disk.PercentDiskReadTime,
disk[PercentDiskReadTime].FirstValue,
disk_number,
)
ch <- prometheus.MustNewConstMetric(
c.writeTime,
prometheus.CounterValue,
disk.PercentDiskWriteTime,
disk[PercentDiskWriteTime].FirstValue,
disk_number,
)
ch <- prometheus.MustNewConstMetric(
c.idleTime,
prometheus.CounterValue,
disk.PercentIdleTime,
disk[PercentIdleTime].FirstValue,
disk_number,
)
ch <- prometheus.MustNewConstMetric(
c.splitIOs,
prometheus.CounterValue,
disk.SplitIOPerSec,
disk[SplitIOPerSec].FirstValue,
disk_number,
)
ch <- prometheus.MustNewConstMetric(
c.readLatency,
prometheus.CounterValue,
disk.AvgDiskSecPerRead*perftypes.TicksToSecondScaleFactor,
disk[AvgDiskSecPerRead].FirstValue*perftypes.TicksToSecondScaleFactor,
disk_number,
)
ch <- prometheus.MustNewConstMetric(
c.writeLatency,
prometheus.CounterValue,
disk.AvgDiskSecPerWrite*perftypes.TicksToSecondScaleFactor,
disk[AvgDiskSecPerWrite].FirstValue*perftypes.TicksToSecondScaleFactor,
disk_number,
)
ch <- prometheus.MustNewConstMetric(
c.readWriteLatency,
prometheus.CounterValue,
disk.AvgDiskSecPerTransfer*perftypes.TicksToSecondScaleFactor,
disk[AvgDiskSecPerTransfer].FirstValue*perftypes.TicksToSecondScaleFactor,
disk_number,
)
}

View File

@@ -5,6 +5,7 @@ import (
"github.com/prometheus-community/windows_exporter/internal/collector/physical_disk"
"github.com/prometheus-community/windows_exporter/internal/testutils"
"github.com/prometheus-community/windows_exporter/internal/types"
)
func BenchmarkCollector(b *testing.B) {
@@ -12,7 +13,7 @@ func BenchmarkCollector(b *testing.B) {
}
func TestCollector(t *testing.T) {
t.Skip()
testutils.TestCollector(t, physical_disk.New, nil)
testutils.TestCollector(t, physical_disk.New, &physical_disk.Config{
DiskInclude: types.RegExpAny,
})
}

View File

@@ -16,8 +16,8 @@ import (
"github.com/prometheus-community/windows_exporter/internal/perfdata"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
v2 "github.com/prometheus-community/windows_exporter/internal/perfdata/v2"
"github.com/prometheus-community/windows_exporter/internal/toggle"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
"golang.org/x/sys/windows"
)
@@ -130,7 +130,7 @@ func (c *Collector) GetName() string {
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
if utils.PDHEnabled() {
if toggle.IsPDHEnabled() {
return []string{}, nil
}
@@ -156,7 +156,7 @@ func (c *Collector) Build(logger *slog.Logger, miSession *mi.Session) error {
c.workerProcessMIQueryQuery = miQuery
c.miSession = miSession
if utils.PDHEnabled() {
if toggle.IsPDHEnabled() {
counters := []string{
processID,
percentProcessorTime,
@@ -315,7 +315,7 @@ type WorkerProcess struct {
}
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
if utils.PDHEnabled() {
if toggle.IsPDHEnabled() {
return c.collectPDH(logger, ch)
}

View File

@@ -10,3 +10,7 @@ import (
func BenchmarkCollector(b *testing.B) {
testutils.FuncBenchmarkCollector(b, service.Name, service.NewWithFlags)
}
func TestCollector(t *testing.T) {
testutils.TestCollector(t, service.New, nil)
}

View File

@@ -0,0 +1,11 @@
package system
const (
ContextSwitchesPersec = "Context Switches/sec"
ExceptionDispatchesPersec = "Exception Dispatches/sec"
ProcessorQueueLength = "Processor Queue Length"
SystemCallsPersec = "System Calls/sec"
SystemUpTime = "System Up Time"
Processes = "Processes"
Threads = "Threads"
)

View File

@@ -4,11 +4,13 @@ package system
import (
"errors"
"fmt"
"log/slog"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
"github.com/prometheus-community/windows_exporter/internal/perfdata/perftypes"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
)
@@ -23,6 +25,8 @@ var ConfigDefaults = Config{}
type Collector struct {
config Config
perfDataCollector perfdata.Collector
contextSwitchesTotal *prometheus.Desc
exceptionDispatchesTotal *prometheus.Desc
processorQueueLength *prometheus.Desc
@@ -54,7 +58,7 @@ func (c *Collector) GetName() string {
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
return []string{"System"}, nil
return []string{}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
@@ -62,6 +66,23 @@ func (c *Collector) Close(_ *slog.Logger) error {
}
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
counters := []string{
ContextSwitchesPersec,
ExceptionDispatchesPersec,
ProcessorQueueLength,
SystemCallsPersec,
SystemUpTime,
Processes,
Threads,
}
var err error
c.perfDataCollector, err = perfdata.NewCollector(perfdata.V2, "System", nil, counters)
if err != nil {
return fmt.Errorf("failed to create System collector: %w", err)
}
c.contextSwitchesTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "context_switches_total"),
"Total number of context switches (WMI source: PerfOS_System.ContextSwitchesPersec)",
@@ -117,78 +138,59 @@ func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) 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 system metrics",
slog.Any("err", err),
)
return err
func (c *Collector) Collect(_ *types.ScrapeContext, _ *slog.Logger, ch chan<- prometheus.Metric) error {
if err := c.collect(ch); err != nil {
return fmt.Errorf("failed collecting system metrics: %w", err)
}
return nil
}
// Win32_PerfRawData_PerfOS_System docs:
// - https://web.archive.org/web/20050830140516/http://msdn.microsoft.com/library/en-us/wmisdk/wmi/win32_perfrawdata_perfos_system.asp
type system struct {
ContextSwitchesPersec float64 `perflib:"Context Switches/sec"`
ExceptionDispatchesPersec float64 `perflib:"Exception Dispatches/sec"`
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))
var dst []system
if err := v1.UnmarshalObject(ctx.PerfObjects["System"], &dst, logger); err != nil {
return err
func (c *Collector) collect(ch chan<- prometheus.Metric) error {
perfData, err := c.perfDataCollector.Collect()
if err != nil {
return fmt.Errorf("failed to collect System metrics: %w", err)
}
if len(dst) == 0 {
return errors.New("no data returned from Performance Counter")
data, ok := perfData[perftypes.EmptyInstance]
if !ok {
return errors.New("query for System returned empty result set")
}
ch <- prometheus.MustNewConstMetric(
c.contextSwitchesTotal,
prometheus.CounterValue,
dst[0].ContextSwitchesPersec,
data[ContextSwitchesPersec].FirstValue,
)
ch <- prometheus.MustNewConstMetric(
c.exceptionDispatchesTotal,
prometheus.CounterValue,
dst[0].ExceptionDispatchesPersec,
data[ExceptionDispatchesPersec].FirstValue,
)
ch <- prometheus.MustNewConstMetric(
c.processorQueueLength,
prometheus.GaugeValue,
dst[0].ProcessorQueueLength,
data[ProcessorQueueLength].FirstValue,
)
ch <- prometheus.MustNewConstMetric(
c.processes,
prometheus.GaugeValue,
dst[0].Processes,
data[Processes].FirstValue,
)
ch <- prometheus.MustNewConstMetric(
c.systemCallsTotal,
prometheus.CounterValue,
dst[0].SystemCallsPersec,
data[SystemCallsPersec].FirstValue,
)
ch <- prometheus.MustNewConstMetric(
c.systemUpTime,
prometheus.GaugeValue,
dst[0].SystemUpTime,
data[SystemUpTime].FirstValue,
)
ch <- prometheus.MustNewConstMetric(
c.threads,
prometheus.GaugeValue,
dst[0].Threads,
data[Threads].FirstValue,
)
// Windows has no defined limit, and is based off available resources. This currently isn't calculated by WMI and is set to default value.

View File

@@ -3,6 +3,7 @@
package tcp
import (
"errors"
"fmt"
"log/slog"
"slices"
@@ -97,6 +98,11 @@ func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
}
func (c *Collector) Close(_ *slog.Logger) error {
if slices.Contains(c.config.CollectorsEnabled, "metrics") {
c.perfDataCollector4.Close()
c.perfDataCollector6.Close()
}
return nil
}
@@ -115,12 +121,12 @@ func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
var err error
c.perfDataCollector4, err = perfdata.NewCollector(perfdata.V1, "TCPv4", nil, counters)
c.perfDataCollector4, err = perfdata.NewCollector(perfdata.V2, "TCPv4", nil, counters)
if err != nil {
return fmt.Errorf("failed to create TCPv4 collector: %w", err)
}
c.perfDataCollector6, err = perfdata.NewCollector(perfdata.V1, "TCPv6", nil, counters)
c.perfDataCollector6, err = perfdata.NewCollector(perfdata.V2, "TCPv6", nil, counters)
if err != nil {
return fmt.Errorf("failed to create TCPv6 collector: %w", err)
}
@@ -190,30 +196,22 @@ func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) 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, _ *slog.Logger, ch chan<- prometheus.Metric) error {
errs := make([]error, 0, 2)
if slices.Contains(c.config.CollectorsEnabled, "metrics") {
if err := c.collect(ch); err != nil {
logger.Error("failed collecting tcp metrics",
slog.Any("err", err),
)
return err
errs = append(errs, fmt.Errorf("failed collecting tcp metrics: %w", err))
}
}
if slices.Contains(c.config.CollectorsEnabled, "connections_state") {
if err := c.collectConnectionsState(ch); err != nil {
logger.Error("failed collecting tcp connection state metrics",
slog.Any("err", err),
)
return err
errs = append(errs, fmt.Errorf("failed collecting tcp connection state metrics: %w", err))
}
}
return nil
return errors.Join(errs...)
}
func (c *Collector) collect(ch chan<- prometheus.Metric) error {

View File

@@ -0,0 +1,23 @@
package terminal_services
const (
handleCount = "Handle Count"
pageFaultsPersec = "Page Faults/sec"
pageFileBytes = "Page File Bytes"
pageFileBytesPeak = "Page File Bytes Peak"
percentPrivilegedTime = "% Privileged Time"
percentProcessorTime = "% Processor Time"
percentUserTime = "% User Time"
poolNonpagedBytes = "Pool Nonpaged Bytes"
poolPagedBytes = "Pool Paged Bytes"
privateBytes = "Private Bytes"
threadCount = "Thread Count"
virtualBytes = "Virtual Bytes"
virtualBytesPeak = "Virtual Bytes Peak"
workingSet = "Working Set"
workingSetPeak = "Working Set Peak"
successfulConnections = "Successful Connections"
pendingConnections = "Pending Connections"
failedConnections = "Failed Connections"
)

View File

@@ -12,7 +12,8 @@ import (
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/headers/wtsapi32"
"github.com/prometheus-community/windows_exporter/internal/mi"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
"github.com/prometheus-community/windows_exporter/internal/perfdata/perftypes"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
@@ -32,7 +33,7 @@ type Win32_ServerFeature struct {
ID uint32
}
func isConnectionBrokerServer(logger *slog.Logger, miSession *mi.Session) bool {
func isConnectionBrokerServer(miSession *mi.Session) bool {
var dst []Win32_ServerFeature
if err := miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * FROM Win32_ServerFeature"))); err != nil {
return false
@@ -44,8 +45,6 @@ func isConnectionBrokerServer(logger *slog.Logger, miSession *mi.Session) bool {
}
}
logger.Debug("host is not a connection broker skipping Connection Broker performance metrics.")
return false
}
@@ -58,6 +57,9 @@ type Collector struct {
connectionBrokerEnabled bool
perfDataCollectorTerminalServicesSession perfdata.Collector
perfDataCollectorBroker perfdata.Collector
hServer windows.Handle
sessionInfo *prometheus.Desc
@@ -98,10 +100,7 @@ func (c *Collector) GetName() string {
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
return []string{
"Terminal Services Session",
"Remote Desktop Connection Broker Counterset",
}, nil
return []string{}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
@@ -110,6 +109,12 @@ func (c *Collector) Close(_ *slog.Logger) error {
return fmt.Errorf("failed to close WTS server: %w", err)
}
c.perfDataCollectorTerminalServicesSession.Close()
if c.connectionBrokerEnabled {
c.perfDataCollectorBroker.Close()
}
return nil
}
@@ -120,7 +125,49 @@ func (c *Collector) Build(logger *slog.Logger, miSession *mi.Session) error {
logger = logger.With(slog.String("collector", Name))
c.connectionBrokerEnabled = isConnectionBrokerServer(logger, miSession)
counters := []string{
handleCount,
pageFaultsPersec,
pageFileBytes,
pageFileBytesPeak,
percentPrivilegedTime,
percentProcessorTime,
percentUserTime,
poolNonpagedBytes,
poolPagedBytes,
privateBytes,
threadCount,
virtualBytes,
virtualBytesPeak,
workingSet,
workingSetPeak,
}
var err error
c.perfDataCollectorTerminalServicesSession, err = perfdata.NewCollector(perfdata.V2, "Terminal Services Session", perfdata.AllInstances, counters)
if err != nil {
return fmt.Errorf("failed to create Terminal Services Session collector: %w", err)
}
c.connectionBrokerEnabled = isConnectionBrokerServer(miSession)
if c.connectionBrokerEnabled {
counters = []string{
successfulConnections,
pendingConnections,
failedConnections,
}
var err error
c.perfDataCollectorBroker, err = perfdata.NewCollector(perfdata.V2, "Remote Desktop Connection Broker Counterset", perfdata.AllInstances, counters)
if err != nil {
return fmt.Errorf("failed to create Remote Desktop Connection Broker Counterset collector: %w", err)
}
} else {
logger.Debug("host is not a connection broker skipping Connection Broker performance metrics.")
}
c.sessionInfo = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "session_info"),
@@ -213,8 +260,6 @@ func (c *Collector) Build(logger *slog.Logger, miSession *mi.Session) error {
nil,
)
var err error
c.hServer, err = wtsapi32.WTSOpenServer("")
if err != nil {
return fmt.Errorf("failed to open WTS server: %w", err)
@@ -225,71 +270,40 @@ func (c *Collector) Build(logger *slog.Logger, miSession *mi.Session) 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 {
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
if err := c.collectWTSSessions(logger, ch); err != nil {
logger.Error("failed collecting terminal services session infos",
slog.Any("err", err),
)
return err
errs := make([]error, 0, 3)
if err := c.collectWTSSessions(logger, ch); err != nil {
errs = append(errs, fmt.Errorf("failed collecting terminal services session infos: %w", err))
}
if err := c.collectTSSessionCounters(ctx, logger, ch); err != nil {
logger.Error("failed collecting terminal services session count metrics",
slog.Any("err", err),
)
return err
if err := c.collectTSSessionCounters(ch); err != nil {
errs = append(errs, fmt.Errorf("failed collecting terminal services session count metrics: %w", err))
}
// only collect CollectionBrokerPerformance if host is a Connection Broker
if c.connectionBrokerEnabled {
if err := c.collectCollectionBrokerPerformanceCounter(ctx, logger, ch); err != nil {
logger.Error("failed collecting Connection Broker performance metrics",
slog.Any("err", err),
)
return err
if err := c.collectCollectionBrokerPerformanceCounter(ch); err != nil {
errs = append(errs, fmt.Errorf("failed collecting Connection Broker performance metrics: %w", err))
}
}
return nil
return errors.Join(errs...)
}
type perflibTerminalServicesSession struct {
Name string
HandleCount float64 `perflib:"Handle Count"`
PageFaultsPersec float64 `perflib:"Page Faults/sec"`
PageFileBytes float64 `perflib:"Page File Bytes"`
PageFileBytesPeak float64 `perflib:"Page File Bytes Peak"`
PercentPrivilegedTime float64 `perflib:"% Privileged Time"`
PercentProcessorTime float64 `perflib:"% Processor Time"`
PercentUserTime float64 `perflib:"% User Time"`
PoolNonpagedBytes float64 `perflib:"Pool Nonpaged Bytes"`
PoolPagedBytes float64 `perflib:"Pool Paged Bytes"`
PrivateBytes float64 `perflib:"Private Bytes"`
ThreadCount float64 `perflib:"Thread Count"`
VirtualBytes float64 `perflib:"Virtual Bytes"`
VirtualBytesPeak float64 `perflib:"Virtual Bytes Peak"`
WorkingSet float64 `perflib:"Working Set"`
WorkingSetPeak float64 `perflib:"Working Set Peak"`
}
func (c *Collector) collectTSSessionCounters(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
dst := make([]perflibTerminalServicesSession, 0)
err := v1.UnmarshalObject(ctx.PerfObjects["Terminal Services Session"], &dst, logger)
func (c *Collector) collectTSSessionCounters(ch chan<- prometheus.Metric) error {
perfData, err := c.perfDataCollectorTerminalServicesSession.Collect()
if err != nil {
return err
return fmt.Errorf("failed to collect Terminal Services Session metrics: %w", err)
}
names := make(map[string]bool)
for _, d := range dst {
for name, data := range perfData {
// only connect metrics for remote named sessions
n := strings.ToLower(d.Name)
n := strings.ToLower(name)
if n == "" || n == "services" || n == "console" {
continue
}
@@ -303,138 +317,130 @@ func (c *Collector) collectTSSessionCounters(ctx *types.ScrapeContext, logger *s
ch <- prometheus.MustNewConstMetric(
c.handleCount,
prometheus.GaugeValue,
d.HandleCount,
d.Name,
data[handleCount].FirstValue,
name,
)
ch <- prometheus.MustNewConstMetric(
c.pageFaultsPerSec,
prometheus.CounterValue,
d.PageFaultsPersec,
d.Name,
data[pageFaultsPersec].FirstValue,
name,
)
ch <- prometheus.MustNewConstMetric(
c.pageFileBytes,
prometheus.GaugeValue,
d.PageFileBytes,
d.Name,
data[pageFileBytes].FirstValue,
name,
)
ch <- prometheus.MustNewConstMetric(
c.pageFileBytesPeak,
prometheus.GaugeValue,
d.PageFileBytesPeak,
d.Name,
data[pageFileBytesPeak].FirstValue,
name,
)
ch <- prometheus.MustNewConstMetric(
c.percentCPUTime,
prometheus.CounterValue,
d.PercentPrivilegedTime,
d.Name,
data[percentPrivilegedTime].FirstValue,
name,
"privileged",
)
ch <- prometheus.MustNewConstMetric(
c.percentCPUTime,
prometheus.CounterValue,
d.PercentProcessorTime,
d.Name,
data[percentProcessorTime].FirstValue,
name,
"processor",
)
ch <- prometheus.MustNewConstMetric(
c.percentCPUTime,
prometheus.CounterValue,
d.PercentUserTime,
d.Name,
data[percentUserTime].FirstValue,
name,
"user",
)
ch <- prometheus.MustNewConstMetric(
c.poolNonPagedBytes,
prometheus.GaugeValue,
d.PoolNonpagedBytes,
d.Name,
data[poolNonpagedBytes].FirstValue,
name,
)
ch <- prometheus.MustNewConstMetric(
c.poolPagedBytes,
prometheus.GaugeValue,
d.PoolPagedBytes,
d.Name,
data[poolPagedBytes].FirstValue,
name,
)
ch <- prometheus.MustNewConstMetric(
c.privateBytes,
prometheus.GaugeValue,
d.PrivateBytes,
d.Name,
data[privateBytes].FirstValue,
name,
)
ch <- prometheus.MustNewConstMetric(
c.threadCount,
prometheus.GaugeValue,
d.ThreadCount,
d.Name,
data[threadCount].FirstValue,
name,
)
ch <- prometheus.MustNewConstMetric(
c.virtualBytes,
prometheus.GaugeValue,
d.VirtualBytes,
d.Name,
data[virtualBytes].FirstValue,
name,
)
ch <- prometheus.MustNewConstMetric(
c.virtualBytesPeak,
prometheus.GaugeValue,
d.VirtualBytesPeak,
d.Name,
data[virtualBytesPeak].FirstValue,
name,
)
ch <- prometheus.MustNewConstMetric(
c.workingSet,
prometheus.GaugeValue,
d.WorkingSet,
d.Name,
data[workingSet].FirstValue,
name,
)
ch <- prometheus.MustNewConstMetric(
c.workingSetPeak,
prometheus.GaugeValue,
d.WorkingSetPeak,
d.Name,
data[workingSetPeak].FirstValue,
name,
)
}
return nil
}
type perflibRemoteDesktopConnectionBrokerCounterset struct {
SuccessfulConnections float64 `perflib:"Successful Connections"`
PendingConnections float64 `perflib:"Pending Connections"`
FailedConnections float64 `perflib:"Failed Connections"`
}
func (c *Collector) collectCollectionBrokerPerformanceCounter(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
dst := make([]perflibRemoteDesktopConnectionBrokerCounterset, 0)
err := v1.UnmarshalObject(ctx.PerfObjects["Remote Desktop Connection Broker Counterset"], &dst, logger)
func (c *Collector) collectCollectionBrokerPerformanceCounter(ch chan<- prometheus.Metric) error {
perfData, err := c.perfDataCollectorBroker.Collect()
if err != nil {
return err
return fmt.Errorf("failed to collect Remote Desktop Connection Broker Counterset metrics: %w", err)
}
if len(dst) == 0 {
return errors.New("WMI query returned empty result set")
data, ok := perfData[perftypes.EmptyInstance]
if !ok {
return errors.New("query for Remote Desktop Connection Broker Counterset returned empty result set")
}
ch <- prometheus.MustNewConstMetric(
c.connectionBrokerPerformance,
prometheus.CounterValue,
dst[0].SuccessfulConnections,
data[successfulConnections].FirstValue,
"Successful",
)
ch <- prometheus.MustNewConstMetric(
c.connectionBrokerPerformance,
prometheus.CounterValue,
dst[0].PendingConnections,
data[pendingConnections].FirstValue,
"Pending",
)
ch <- prometheus.MustNewConstMetric(
c.connectionBrokerPerformance,
prometheus.CounterValue,
dst[0].FailedConnections,
data[failedConnections].FirstValue,
"Failed",
)
@@ -448,6 +454,12 @@ func (c *Collector) collectWTSSessions(logger *slog.Logger, ch chan<- prometheus
}
for _, session := range sessions {
// only connect metrics for remote named sessions
n := strings.ReplaceAll(session.SessionName, "#", " ")
if n == "" || n == "Services" || n == "Console" {
continue
}
userName := session.UserName
if session.DomainName != "" {
userName = fmt.Sprintf("%s\\%s", session.DomainName, session.UserName)
@@ -458,12 +470,11 @@ func (c *Collector) collectWTSSessions(logger *slog.Logger, ch chan<- prometheus
if session.State == stateID {
isState = 1.0
}
ch <- prometheus.MustNewConstMetric(
c.sessionInfo,
prometheus.GaugeValue,
isState,
strings.ReplaceAll(session.SessionName, "#", " "),
n,
userName,
session.HostName,
stateName,

View File

@@ -0,0 +1,10 @@
package time
const (
ClockFrequencyAdjustmentPPBTotal = "Clock Frequency Adjustment (ppb)"
ComputedTimeOffset = "Computed Time Offset"
NTPClientTimeSourceCount = "NTP Client Time Source Count"
NTPRoundTripDelay = "NTP Roundtrip Delay"
NTPServerIncomingRequestsTotal = "NTP Server Incoming Requests"
NTPServerOutgoingResponsesTotal = "NTP Server Outgoing Responses"
)

View File

@@ -4,28 +4,46 @@ package time
import (
"errors"
"fmt"
"log/slog"
"slices"
"strings"
"time"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/headers/kernel32"
"github.com/prometheus-community/windows_exporter/internal/mi"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
"github.com/prometheus-community/windows_exporter/internal/perfdata/perftypes"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
"golang.org/x/sys/windows"
)
const Name = "time"
const (
Name = "time"
type Config struct{}
collectorSystemTime = "system_time"
collectorNTP = "ntp"
)
var ConfigDefaults = Config{}
type Config struct {
CollectorsEnabled []string `yaml:"collectors_enabled"`
}
var ConfigDefaults = Config{
CollectorsEnabled: []string{
collectorSystemTime,
collectorNTP,
},
}
// Collector is a Prometheus Collector for Perflib counter metrics.
type Collector struct {
config Config
perfDataCollector perfdata.Collector
currentTime *prometheus.Desc
timezone *prometheus.Desc
clockFrequencyAdjustmentPPBTotal *prometheus.Desc
@@ -41,6 +59,10 @@ func New(config *Config) *Collector {
config = &ConfigDefaults
}
if config.CollectorsEnabled == nil {
config.CollectorsEnabled = ConfigDefaults.CollectorsEnabled
}
c := &Collector{
config: *config,
}
@@ -48,8 +70,26 @@ func New(config *Config) *Collector {
return c
}
func NewWithFlags(_ *kingpin.Application) *Collector {
return &Collector{}
func NewWithFlags(app *kingpin.Application) *Collector {
c := &Collector{
config: ConfigDefaults,
}
c.config.CollectorsEnabled = make([]string, 0)
var collectorsEnabled string
app.Flag(
"collector.time.enabled",
"Comma-separated list of collectors to use. Defaults to all, if not specified. ntp may not available on all systems.",
).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 {
@@ -57,14 +97,40 @@ func (c *Collector) GetName() string {
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
return []string{"Windows Time Service"}, nil
return []string{}, nil
}
func (c *Collector) Close(_ *slog.Logger) error {
if slices.Contains(c.config.CollectorsEnabled, collectorNTP) {
c.perfDataCollector.Close()
}
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
for _, collector := range c.config.CollectorsEnabled {
if !slices.Contains([]string{collectorSystemTime, collectorNTP}, collector) {
return fmt.Errorf("unknown collector: %s", collector)
}
}
counters := []string{
ClockFrequencyAdjustmentPPBTotal,
ComputedTimeOffset,
NTPClientTimeSourceCount,
NTPRoundTripDelay,
NTPServerIncomingRequestsTotal,
NTPServerOutgoingResponsesTotal,
}
var err error
c.perfDataCollector, err = perfdata.NewCollector(perfdata.V2, "Windows Time Service", nil, counters)
if err != nil {
return fmt.Errorf("failed to create Windows Time Service collector: %w", err)
}
c.currentTime = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "current_timestamp_seconds"),
"OperatingSystem.LocalDateTime",
@@ -119,40 +185,24 @@ func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) 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))
func (c *Collector) Collect(_ *types.ScrapeContext, _ *slog.Logger, ch chan<- prometheus.Metric) error {
errs := make([]error, 0, 2)
if err := c.collectTime(ch); err != nil {
logger.Error("failed collecting time metrics",
slog.Any("err", err),
)
errs = append(errs, err)
if slices.Contains(c.config.CollectorsEnabled, collectorSystemTime) {
if err := c.collectTime(ch); err != nil {
errs = append(errs, fmt.Errorf("failed collecting time metrics: %w", err))
}
}
if err := c.collectNTP(ctx, logger, ch); err != nil {
logger.Error("failed collecting time ntp metrics",
slog.Any("err", err),
)
errs = append(errs, err)
if slices.Contains(c.config.CollectorsEnabled, collectorNTP) {
if err := c.collectNTP(ch); err != nil {
errs = append(errs, fmt.Errorf("failed collecting time ntp metrics: %w", err))
}
}
return errors.Join(errs...)
}
// Perflib "Windows Time Service".
type windowsTime struct {
ClockFrequencyAdjustmentPPBTotal float64 `perflib:"Clock Frequency Adjustment (ppb)"`
ComputedTimeOffset float64 `perflib:"Computed Time Offset"`
NTPClientTimeSourceCount float64 `perflib:"NTP Client Time Source Count"`
NTPRoundTripDelay float64 `perflib:"NTP Roundtrip Delay"`
NTPServerIncomingRequestsTotal float64 `perflib:"NTP Server Incoming Requests"`
NTPServerOutgoingResponsesTotal float64 `perflib:"NTP Server Outgoing Responses"`
}
func (c *Collector) collectTime(ch chan<- prometheus.Metric) error {
ch <- prometheus.MustNewConstMetric(
c.currentTime,
@@ -178,48 +228,46 @@ func (c *Collector) collectTime(ch chan<- prometheus.Metric) error {
return nil
}
func (c *Collector) collectNTP(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
var dst []windowsTime // Single-instance class, array is required but will have single entry.
if err := v1.UnmarshalObject(ctx.PerfObjects["Windows Time Service"], &dst, logger); err != nil {
return err
func (c *Collector) collectNTP(ch chan<- prometheus.Metric) error {
perfData, err := c.perfDataCollector.Collect()
if err != nil {
return fmt.Errorf("failed to collect VM Memory metrics: %w", err)
}
if len(dst) == 0 {
return errors.New("no data returned for Windows Time Service")
data, ok := perfData[perftypes.EmptyInstance]
if !ok {
return errors.New("query for Windows Time Service returned empty result set")
}
ch <- prometheus.MustNewConstMetric(
c.clockFrequencyAdjustmentPPBTotal,
prometheus.CounterValue,
dst[0].ClockFrequencyAdjustmentPPBTotal,
data[ClockFrequencyAdjustmentPPBTotal].FirstValue,
)
ch <- prometheus.MustNewConstMetric(
c.computedTimeOffset,
prometheus.GaugeValue,
dst[0].ComputedTimeOffset/1000000, // microseconds -> seconds
data[ComputedTimeOffset].FirstValue/1000000, // microseconds -> seconds
)
ch <- prometheus.MustNewConstMetric(
c.ntpClientTimeSourceCount,
prometheus.GaugeValue,
dst[0].NTPClientTimeSourceCount,
data[NTPClientTimeSourceCount].FirstValue,
)
ch <- prometheus.MustNewConstMetric(
c.ntpRoundTripDelay,
prometheus.GaugeValue,
dst[0].NTPRoundTripDelay/1000000, // microseconds -> seconds
data[NTPRoundTripDelay].FirstValue/1000000, // microseconds -> seconds
)
ch <- prometheus.MustNewConstMetric(
c.ntpServerIncomingRequestsTotal,
prometheus.CounterValue,
dst[0].NTPServerIncomingRequestsTotal,
data[NTPServerIncomingRequestsTotal].FirstValue,
)
ch <- prometheus.MustNewConstMetric(
c.ntpServerOutgoingResponsesTotal,
prometheus.CounterValue,
dst[0].NTPServerOutgoingResponsesTotal,
data[NTPServerOutgoingResponsesTotal].FirstValue,
)
return nil

View File

@@ -10,3 +10,7 @@ import (
func BenchmarkCollector(b *testing.B) {
testutils.FuncBenchmarkCollector(b, time.Name, time.NewWithFlags)
}
func TestCollector(t *testing.T) {
testutils.TestCollector(t, time.New, nil)
}

View File

@@ -0,0 +1,15 @@
package udp
// The TCPv6 performance object uses the same fields.
// https://learn.microsoft.com/en-us/dotnet/api/system.net.networkinformation.tcpstate?view=net-8.0.
const (
datagramsNoPortPerSec = "Datagrams No Port/sec"
datagramsReceivedPerSec = "Datagrams Received/sec"
datagramsReceivedErrors = "Datagrams Received Errors"
datagramsSentPerSec = "Datagrams Sent/sec"
)
// Datagrams No Port/sec is the rate of received UDP datagrams for which there was no application at the destination port.
// Datagrams Received Errors is the number of received UDP datagrams that could not be delivered for reasons other than the lack of an application at the destination port.
// Datagrams Received/sec is the rate at which UDP datagrams are delivered to UDP users.
// Datagrams Sent/sec is the rate at which UDP datagrams are sent from the entity.

View File

@@ -0,0 +1,168 @@
//go:build windows
package udp
import (
"fmt"
"log/slog"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
"github.com/prometheus-community/windows_exporter/internal/perfdata/perftypes"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
)
const Name = "udp"
type Config struct{}
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
datagramsNoPortTotal *prometheus.Desc
datagramsReceivedTotal *prometheus.Desc
datagramsReceivedErrorsTotal *prometheus.Desc
datagramsSentTotal *prometheus.Desc
}
func New(config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
c := &Collector{
config: *config,
}
return c
}
func NewWithFlags(_ *kingpin.Application) *Collector {
c := &Collector{
config: ConfigDefaults,
}
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 {
c.perfDataCollector4.Close()
c.perfDataCollector6.Close()
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
counters := []string{
datagramsNoPortPerSec,
datagramsReceivedPerSec,
datagramsReceivedErrors,
datagramsSentPerSec,
}
var err error
c.perfDataCollector4, err = perfdata.NewCollector(perfdata.V2, "UDPv4", nil, counters)
if err != nil {
return fmt.Errorf("failed to create UDPv4 collector: %w", err)
}
c.perfDataCollector6, err = perfdata.NewCollector(perfdata.V2, "UDPv6", nil, counters)
if err != nil {
return fmt.Errorf("failed to create UDPv6 collector: %w", err)
}
c.datagramsNoPortTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "datagram_no_port_total"),
"Number of received UDP datagrams for which there was no application at the destination port",
[]string{"af"},
nil,
)
c.datagramsReceivedTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "datagram_received_total"),
"UDP datagrams are delivered to UDP users",
[]string{"af"},
nil,
)
c.datagramsReceivedErrorsTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "datagram_received_errors_total"),
"Number of received UDP datagrams that could not be delivered for reasons other than the lack of an application at the destination port",
[]string{"af"},
nil,
)
c.datagramsSentTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "datagram_sent_total"),
"UDP datagrams are sent from the entity",
[]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, _ *slog.Logger, ch chan<- prometheus.Metric) error {
return c.collect(ch)
}
func (c *Collector) collect(ch chan<- prometheus.Metric) error {
data, err := c.perfDataCollector4.Collect()
if err != nil {
return fmt.Errorf("failed to collect UDPv4 metrics: %w", err)
}
c.writeUDPCounters(ch, data[perftypes.EmptyInstance], []string{"ipv4"})
data, err = c.perfDataCollector6.Collect()
if err != nil {
return fmt.Errorf("failed to collect UDPv6 metrics: %w", err)
}
c.writeUDPCounters(ch, data[perftypes.EmptyInstance], []string{"ipv6"})
return nil
}
func (c *Collector) writeUDPCounters(ch chan<- prometheus.Metric, metrics map[string]perftypes.CounterValues, labels []string) {
ch <- prometheus.MustNewConstMetric(
c.datagramsNoPortTotal,
prometheus.CounterValue,
metrics[datagramsNoPortPerSec].FirstValue,
labels...,
)
ch <- prometheus.MustNewConstMetric(
c.datagramsReceivedErrorsTotal,
prometheus.CounterValue,
metrics[datagramsReceivedErrors].FirstValue,
labels...,
)
ch <- prometheus.MustNewConstMetric(
c.datagramsReceivedTotal,
prometheus.GaugeValue,
metrics[datagramsReceivedPerSec].FirstValue,
labels...,
)
ch <- prometheus.MustNewConstMetric(
c.datagramsSentTotal,
prometheus.CounterValue,
metrics[datagramsSentPerSec].FirstValue,
labels...,
)
}

View File

@@ -0,0 +1,16 @@
package udp_test
import (
"testing"
"github.com/prometheus-community/windows_exporter/internal/collector/udp"
"github.com/prometheus-community/windows_exporter/internal/testutils"
)
func BenchmarkCollector(b *testing.B) {
testutils.FuncBenchmarkCollector(b, udp.Name, udp.NewWithFlags)
}
func TestCollector(t *testing.T) {
testutils.TestCollector(t, udp.New, nil)
}

View File

@@ -0,0 +1,24 @@
package vmware
const (
couEffectiveVMSpeedMHz = "Effective VM Speed in MHz" // \VM Processor(*)\Effective VM Speed in MHz
cpuHostProcessorSpeedMHz = "Host processor speed in MHz" // \VM Processor(*)\Host processor speed in MHz
cpuLimitMHz = "Limit in MHz" // \VM Processor(*)\Limit in MHz
cpuReservationMHz = "Reservation in MHz" // \VM Processor(*)\Reservation in MHz
cpuShares = "Shares" // \VM Processor(*)\Shares
cpuStolenMs = "CPU stolen time" // \VM Processor(*)\CPU stolen time
cpuTimePercents = "% Processor Time" // \VM Processor(*)\% Processor Time
memActiveMB = "MemActiveMB" // \VM Memory\Memory Active in MB
memBalloonedMB = "MemBalloonedMB" // \VM Memory\Memory Ballooned in MB
memLimitMB = "MemLimitMB" // \VM Memory\Memory Limit in MB
memMappedMB = "MemMappedMB" // \VM Memory\Memory Mapped in MB
memOverheadMB = "MemOverheadMB" // \VM Memory\Memory Overhead in MB
memReservationMB = "MemReservationMB" // \VM Memory\Memory Reservation in MB
memSharedMB = "MemSharedMB" // \VM Memory\Memory Shared in MB
memSharedSavedMB = "MemSharedSavedMB" // \VM Memory\Memory Shared Saved in MB
memShares = "MemShares" // \VM Memory\Memory Shares
memSwappedMB = "MemSwappedMB" // \VM Memory\Memory Swapped in MB
memTargetSizeMB = "MemTargetSizeMB" // \VM Memory\Memory Target Size
memUsedMB = "MemUsedMB" // \VM Memory\Memory Used in MB
)

View File

@@ -9,8 +9,10 @@ import (
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
"github.com/prometheus-community/windows_exporter/internal/perfdata/perftypes"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
)
@@ -22,10 +24,9 @@ var ConfigDefaults = Config{}
// A Collector is a Prometheus Collector for WMI Win32_PerfRawData_vmGuestLib_VMem/Win32_PerfRawData_vmGuestLib_VCPU metrics.
type Collector struct {
config Config
miSession *mi.Session
miQueryCPU mi.Query
miQueryMem mi.Query
config Config
perfDataCollectorCPU perfdata.Collector
perfDataCollectorMemory perfdata.Collector
memActive *prometheus.Desc
memBallooned *prometheus.Desc
@@ -40,13 +41,13 @@ type Collector struct {
memTargetSize *prometheus.Desc
memUsed *prometheus.Desc
cpuLimitMHz *prometheus.Desc
cpuReservationMHz *prometheus.Desc
cpuShares *prometheus.Desc
cpuStolenTotal *prometheus.Desc
cpuTimeTotal *prometheus.Desc
effectiveVMSpeedMHz *prometheus.Desc
hostProcessorSpeedMHz *prometheus.Desc
cpuLimitMHz *prometheus.Desc
cpuReservationMHz *prometheus.Desc
cpuShares *prometheus.Desc
cpuStolenTotal *prometheus.Desc
cpuTimeTotal *prometheus.Desc
cpuEffectiveVMSpeedMHz *prometheus.Desc
hostProcessorSpeedMHz *prometheus.Desc
}
func New(config *Config) *Collector {
@@ -74,141 +75,162 @@ func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
}
func (c *Collector) Close(_ *slog.Logger) error {
c.perfDataCollectorCPU.Close()
c.perfDataCollectorMemory.Close()
return nil
}
func (c *Collector) Build(_ *slog.Logger, miSession *mi.Session) error {
if miSession == nil {
return errors.New("miSession is nil")
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
counters := []string{
cpuLimitMHz,
cpuReservationMHz,
cpuShares,
cpuStolenMs,
cpuTimePercents,
couEffectiveVMSpeedMHz,
cpuHostProcessorSpeedMHz,
}
miQuery, err := mi.NewQuery("SELECT * FROM Win32_PerfRawData_vmGuestLib_VCPU")
var err error
c.perfDataCollectorCPU, err = perfdata.NewCollector(perfdata.V2, "VM Processor", perftypes.TotalInstance, counters)
if err != nil {
return fmt.Errorf("failed to create WMI query: %w", err)
return fmt.Errorf("failed to create VM Processor collector: %w", err)
}
c.miQueryCPU = miQuery
miQuery, err = mi.NewQuery("SELECT * FROM Win32_PerfRawData_vmGuestLib_VMem")
if err != nil {
return fmt.Errorf("failed to create WMI query: %w", err)
}
c.miQueryMem = miQuery
c.miSession = miSession
c.memActive = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "mem_active_bytes"),
"(MemActiveMB)",
nil,
nil,
)
c.memBallooned = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "mem_ballooned_bytes"),
"(MemBalloonedMB)",
nil,
nil,
)
c.memLimit = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "mem_limit_bytes"),
"(MemLimitMB)",
nil,
nil,
)
c.memMapped = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "mem_mapped_bytes"),
"(MemMappedMB)",
nil,
nil,
)
c.memOverhead = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "mem_overhead_bytes"),
"(MemOverheadMB)",
nil,
nil,
)
c.memReservation = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "mem_reservation_bytes"),
"(MemReservationMB)",
nil,
nil,
)
c.memShared = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "mem_shared_bytes"),
"(MemSharedMB)",
nil,
nil,
)
c.memSharedSaved = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "mem_shared_saved_bytes"),
"(MemSharedSavedMB)",
nil,
nil,
)
c.memShares = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "mem_shares"),
"(MemShares)",
nil,
nil,
)
c.memSwapped = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "mem_swapped_bytes"),
"(MemSwappedMB)",
nil,
nil,
)
c.memTargetSize = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "mem_target_size_bytes"),
"(MemTargetSizeMB)",
nil,
nil,
)
c.memUsed = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "mem_used_bytes"),
"(MemUsedMB)",
nil,
nil,
)
c.cpuLimitMHz = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "cpu_limit_mhz"),
"(CpuLimitMHz)",
"The maximum processing power in MHz allowed to the virtual machine. Assigning a CPU Limit ensures that this virtual machine never consumes more than a certain amount of the available processor power. By limiting the amount of processing power consumed, a portion of the processing power becomes available to other virtual machines.",
nil,
nil,
)
c.cpuReservationMHz = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "cpu_reservation_mhz"),
"(CpuReservationMHz)",
"The minimum processing power in MHz available to the virtual machine. Assigning a CPU Reservation ensures that even as other virtual machines on the same host consume shared processing power, there is still a certain minimum amount for this virtual machine.",
nil,
nil,
)
c.cpuShares = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "cpu_shares"),
"(CpuShares)",
"The number of CPU shares allocated to the virtual machine.",
nil,
nil,
)
c.cpuStolenTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "cpu_stolen_seconds_total"),
"(CpuStolenMs)",
"The time that the VM was runnable but not scheduled to run.",
nil,
nil,
)
c.cpuTimeTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "cpu_time_seconds_total"),
"(CpuTimePercents)",
"Current load of the VMs virtual processor",
nil,
nil,
)
c.effectiveVMSpeedMHz = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "effective_vm_speed_mhz"),
"(EffectiveVMSpeedMHz)",
c.cpuEffectiveVMSpeedMHz = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "cpu_effective_vm_speed_mhz_total"),
"The effective speed of the VMs virtual CPU",
nil,
nil,
)
c.hostProcessorSpeedMHz = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "host_processor_speed_mhz"),
"(HostProcessorSpeedMHz)",
"Host Processor speed",
nil,
nil,
)
counters = []string{
memActiveMB,
memBalloonedMB,
memLimitMB,
memMappedMB,
memOverheadMB,
memReservationMB,
memSharedMB,
memSharedSavedMB,
memShares,
memSwappedMB,
memTargetSizeMB,
memUsedMB,
}
c.perfDataCollectorMemory, err = perfdata.NewCollector(perfdata.V2, "VM Memory", nil, counters)
if err != nil {
return fmt.Errorf("failed to create VM Memory collector: %w", err)
}
c.memActive = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "mem_active_bytes"),
"The estimated amount of memory the virtual machine is actively using.",
nil,
nil,
)
c.memBallooned = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "mem_ballooned_bytes"),
"The amount of memory that has been reclaimed from this virtual machine via the VMware Memory Balloon mechanism.",
nil,
nil,
)
c.memLimit = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "mem_limit_bytes"),
"The maximum amount of memory that is allowed to the virtual machine. Assigning a Memory Limit ensures that this virtual machine never consumes more than a certain amount of the allowed memory. By limiting the amount of memory consumed, a portion of this shared resource is allowed to other virtual machines.",
nil,
nil,
)
c.memMapped = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "mem_mapped_bytes"),
"The mapped memory size of this virtual machine. This is the current total amount of guest memory that is backed by physical memory. Note that this number may include pages of memory shared between multiple virtual machines and thus may be an overestimate of the amount of physical host memory consumed by this virtual machine.",
nil,
nil,
)
c.memOverhead = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "mem_overhead_bytes"),
"The amount of overhead memory associated with this virtual machine consumed on the host system.",
nil,
nil,
)
c.memReservation = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "mem_reservation_bytes"),
"The minimum amount of memory that is guaranteed to the virtual machine. Assigning a Memory Reservation ensures that even as other virtual machines on the same host consume memory, there is still a certain minimum amount for this virtual machine.",
nil,
nil,
)
c.memShared = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "mem_shared_bytes"),
"The amount of physical memory associated with this virtual machine that is copy-on-write (COW) shared on the host.",
nil,
nil,
)
c.memSharedSaved = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "mem_shared_saved_bytes"),
"The estimated amount of physical memory on the host saved from copy-on-write (COW) shared guest physical memory.",
nil,
nil,
)
c.memShares = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "mem_shares"),
"The number of memory shares allocated to the virtual machine.",
nil,
nil,
)
c.memSwapped = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "mem_swapped_bytes"),
"The amount of memory associated with this virtual machine that has been swapped by ESX.",
nil,
nil,
)
c.memTargetSize = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "mem_target_size_bytes"),
"Memory Target Size.",
nil,
nil,
)
c.memUsed = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "mem_used_bytes"),
"The estimated amount of physical host memory currently consumed for this virtual machines physical memory.",
nil,
nil,
)
@@ -218,192 +240,157 @@ func (c *Collector) Build(_ *slog.Logger, miSession *mi.Session) 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))
if err := c.collectMem(ch); err != nil {
logger.Error("failed collecting vmware memory metrics",
slog.Any("err", err),
)
return err
}
func (c *Collector) Collect(_ *types.ScrapeContext, _ *slog.Logger, ch chan<- prometheus.Metric) error {
errs := make([]error, 0, 2)
if err := c.collectCpu(ch); err != nil {
logger.Error("failed collecting vmware cpu metrics",
slog.Any("err", err),
)
return err
errs = append(errs, fmt.Errorf("failed collecting vmware cpu metrics: %w", err))
}
return nil
}
if err := c.collectMem(ch); err != nil {
errs = append(errs, fmt.Errorf("failed collecting vmware memory metrics: %w", err))
}
type Win32_PerfRawData_vmGuestLib_VMem struct {
MemActiveMB uint64 `mi:"MemActiveMB"`
MemBalloonedMB uint64 `mi:"MemBalloonedMB"`
MemLimitMB uint64 `mi:"MemLimitMB"`
MemMappedMB uint64 `mi:"MemMappedMB"`
MemOverheadMB uint64 `mi:"MemOverheadMB"`
MemReservationMB uint64 `mi:"MemReservationMB"`
MemSharedMB uint64 `mi:"MemSharedMB"`
MemSharedSavedMB uint64 `mi:"MemSharedSavedMB"`
MemShares uint64 `mi:"MemShares"`
MemSwappedMB uint64 `mi:"MemSwappedMB"`
MemTargetSizeMB uint64 `mi:"MemTargetSizeMB"`
MemUsedMB uint64 `mi:"MemUsedMB"`
}
type Win32_PerfRawData_vmGuestLib_VCPU struct {
CpuLimitMHz uint64 `mi:"CpuLimitMHz"`
CpuReservationMHz uint64 `mi:"CpuReservationMHz"`
CpuShares uint64 `mi:"CpuShares"`
CpuStolenMs uint64 `mi:"CpuStolenMs"`
CpuTimePercents uint64 `mi:"CpuTimePercents"`
EffectiveVMSpeedMHz uint64 `mi:"EffectiveVMSpeedMHz"`
HostProcessorSpeedMHz uint64 `mi:"HostProcessorSpeedMHz"`
return errors.Join(errs...)
}
func (c *Collector) collectMem(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_vmGuestLib_VMem
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, c.miQueryMem); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
perfData, err := c.perfDataCollectorMemory.Collect()
if err != nil {
return fmt.Errorf("failed to collect VM Memory metrics: %w", err)
}
if len(dst) == 0 {
return errors.New("WMI query returned empty result set")
data, ok := perfData[perftypes.EmptyInstance]
if !ok {
return errors.New("query for VM Memory returned empty result set")
}
ch <- prometheus.MustNewConstMetric(
c.memActive,
prometheus.GaugeValue,
mbToBytes(dst[0].MemActiveMB),
utils.MBToBytes(data[memActiveMB].FirstValue),
)
ch <- prometheus.MustNewConstMetric(
c.memBallooned,
prometheus.GaugeValue,
mbToBytes(dst[0].MemBalloonedMB),
utils.MBToBytes(data[memBalloonedMB].FirstValue),
)
ch <- prometheus.MustNewConstMetric(
c.memLimit,
prometheus.GaugeValue,
mbToBytes(dst[0].MemLimitMB),
utils.MBToBytes(data[memLimitMB].FirstValue),
)
ch <- prometheus.MustNewConstMetric(
c.memMapped,
prometheus.GaugeValue,
mbToBytes(dst[0].MemMappedMB),
utils.MBToBytes(data[memMappedMB].FirstValue),
)
ch <- prometheus.MustNewConstMetric(
c.memOverhead,
prometheus.GaugeValue,
mbToBytes(dst[0].MemOverheadMB),
utils.MBToBytes(data[memOverheadMB].FirstValue),
)
ch <- prometheus.MustNewConstMetric(
c.memReservation,
prometheus.GaugeValue,
mbToBytes(dst[0].MemReservationMB),
utils.MBToBytes(data[memReservationMB].FirstValue),
)
ch <- prometheus.MustNewConstMetric(
c.memShared,
prometheus.GaugeValue,
mbToBytes(dst[0].MemSharedMB),
utils.MBToBytes(data[memSharedMB].FirstValue),
)
ch <- prometheus.MustNewConstMetric(
c.memSharedSaved,
prometheus.GaugeValue,
mbToBytes(dst[0].MemSharedSavedMB),
utils.MBToBytes(data[memSharedSavedMB].FirstValue),
)
ch <- prometheus.MustNewConstMetric(
c.memShares,
prometheus.GaugeValue,
float64(dst[0].MemShares),
data[memShares].FirstValue,
)
ch <- prometheus.MustNewConstMetric(
c.memSwapped,
prometheus.GaugeValue,
mbToBytes(dst[0].MemSwappedMB),
utils.MBToBytes(data[memSwappedMB].FirstValue),
)
ch <- prometheus.MustNewConstMetric(
c.memTargetSize,
prometheus.GaugeValue,
mbToBytes(dst[0].MemTargetSizeMB),
utils.MBToBytes(data[memTargetSizeMB].FirstValue),
)
ch <- prometheus.MustNewConstMetric(
c.memUsed,
prometheus.GaugeValue,
mbToBytes(dst[0].MemUsedMB),
utils.MBToBytes(data[memUsedMB].FirstValue),
)
return nil
}
// mbToBytes moved to utils package
func mbToBytes(mb uint64) float64 {
return float64(mb * 1024 * 1024)
}
func (c *Collector) collectCpu(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_vmGuestLib_VCPU
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, c.miQueryCPU); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
perfData, err := c.perfDataCollectorCPU.Collect()
if err != nil {
return fmt.Errorf("failed to collect VM Memory metrics: %w", err)
}
if len(dst) == 0 {
return errors.New("WMI query returned empty result set")
data, ok := perfData["_Total"]
if !ok {
return errors.New("query for VM CPU returned empty result set")
}
ch <- prometheus.MustNewConstMetric(
c.cpuLimitMHz,
prometheus.GaugeValue,
float64(dst[0].CpuLimitMHz),
data[cpuLimitMHz].FirstValue,
)
ch <- prometheus.MustNewConstMetric(
c.cpuReservationMHz,
prometheus.GaugeValue,
float64(dst[0].CpuReservationMHz),
data[cpuReservationMHz].FirstValue,
)
ch <- prometheus.MustNewConstMetric(
c.cpuShares,
prometheus.GaugeValue,
float64(dst[0].CpuShares),
data[cpuShares].FirstValue,
)
ch <- prometheus.MustNewConstMetric(
c.cpuStolenTotal,
prometheus.CounterValue,
float64(dst[0].CpuStolenMs)*perftypes.TicksToSecondScaleFactor,
utils.MilliSecToSec(data[cpuStolenMs].FirstValue),
)
ch <- prometheus.MustNewConstMetric(
c.cpuTimeTotal,
prometheus.CounterValue,
float64(dst[0].CpuTimePercents)*perftypes.TicksToSecondScaleFactor,
utils.MilliSecToSec(data[cpuTimePercents].FirstValue),
)
ch <- prometheus.MustNewConstMetric(
c.effectiveVMSpeedMHz,
c.cpuEffectiveVMSpeedMHz,
prometheus.GaugeValue,
float64(dst[0].EffectiveVMSpeedMHz),
data[couEffectiveVMSpeedMHz].FirstValue,
)
ch <- prometheus.MustNewConstMetric(
c.hostProcessorSpeedMHz,
prometheus.GaugeValue,
float64(dst[0].HostProcessorSpeedMHz),
data[cpuHostProcessorSpeedMHz].FirstValue,
)
return nil

View File

@@ -1,6 +1,7 @@
package wtsapi32
import (
"errors"
"fmt"
"log/slog"
"unsafe"
@@ -129,7 +130,7 @@ func WTSOpenServer(server string) (windows.Handle, error) {
func WTSCloseServer(server windows.Handle) error {
r1, _, err := procWTSCloseServer.Call(uintptr(server))
if r1 != 1 {
if r1 != 1 && !errors.Is(err, windows.ERROR_SUCCESS) {
return fmt.Errorf("failed to close server: %w", err)
}
@@ -170,8 +171,7 @@ func WTSEnumerateSessionsEx(server windows.Handle, logger *slog.Logger) ([]WTSSe
if sessionInfoPointer != 0 {
defer func(class WTSTypeClass, pMemory uintptr, NumberOfEntries uint32) {
err := WTSFreeMemoryEx(class, pMemory, NumberOfEntries)
if err != nil {
if err := WTSFreeMemoryEx(class, pMemory, NumberOfEntries); err != nil {
logger.Warn("failed to free memory", "err", fmt.Errorf("WTSEnumerateSessionsEx: %w", err))
}
}(WTSTypeSessionInfoLevel1, sessionInfoPointer, count)

View File

@@ -5,8 +5,10 @@ package mi
import (
"errors"
"fmt"
"math"
"reflect"
"sync"
"time"
"unsafe"
"golang.org/x/sys/windows"
@@ -14,6 +16,10 @@ import (
// We have to registry a global callback function, since the amount of callbacks is limited.
var operationUnmarshalCallbacksInstanceResult = sync.OnceValue[uintptr](func() uintptr {
// Workaround for a deadlock issue in go.
// Ref: https://github.com/golang/go/issues/55015
go time.Sleep(time.Duration(math.MaxInt64))
return windows.NewCallback(func(
operation *Operation,
callbacks *OperationUnmarshalCallbacks,

View File

@@ -212,9 +212,16 @@ func (s *Session) QueryUnmarshal(dst any,
errs := make([]error, 0)
for err := range errCh {
if err != nil {
// We need an active go routine to prevent a
// fatal error: all goroutines are asleep - deadlock!
// ref: https://github.com/golang/go/issues/55015
// go time.Sleep(5 * time.Second)
for {
if err, ok := <-errCh; err != nil {
errs = append(errs, err)
} else if !ok {
break
}
}

View File

@@ -4,8 +4,8 @@ import "github.com/prometheus/client_golang/prometheus"
// Conversion factors.
const (
TicksToSecondScaleFactor = 1 / 1e7
WindowsEpoch = 116444736000000000
TicksToSecondScaleFactor = 1 / 1e7
WindowsEpoch int64 = 116444736000000000
)
// Based on https://github.com/leoluk/perflib_exporter/blob/master/collector/mapper.go

View File

@@ -4,6 +4,8 @@ import "github.com/prometheus/client_golang/prometheus"
const EmptyInstance = "------"
var TotalInstance = []string{"_Total"}
type CounterValues struct {
Type prometheus.ValueType
FirstValue float64

View File

@@ -5,6 +5,7 @@ package v2
import (
"errors"
"fmt"
"slices"
"strings"
"unsafe"
@@ -14,9 +15,10 @@ import (
)
type Collector struct {
object string
counters map[string]Counter
handle pdhQueryHandle
object string
counters map[string]Counter
handle pdhQueryHandle
totalCounterRequested bool
}
type Counter struct {
@@ -24,7 +26,7 @@ type Counter struct {
Desc string
Instances map[string]pdhCounterHandle
Type uint32
Frequency float64
Frequency int64
}
func NewCollector(object string, instances []string, counters []string) (*Collector, error) {
@@ -39,9 +41,10 @@ func NewCollector(object string, instances []string, counters []string) (*Collec
}
collector := &Collector{
object: object,
counters: make(map[string]Counter, len(counters)),
handle: handle,
object: object,
counters: make(map[string]Counter, len(counters)),
handle: handle,
totalCounterRequested: slices.Contains(instances, "_Total"),
}
for _, counterName := range counters {
@@ -67,30 +70,30 @@ func NewCollector(object string, instances []string, counters []string) (*Collec
counter.Instances[instance] = counterHandle
if counter.Type == 0 {
// Get the info with the current buffer size
bufLen := uint32(0)
if counter.Type != 0 {
continue
}
if ret := PdhGetCounterInfo(counterHandle, 1, &bufLen, nil); ret != PdhMoreData {
return nil, fmt.Errorf("PdhGetCounterInfo: %w", NewPdhError(ret))
}
// Get the info with the current buffer size
bufLen := uint32(0)
buf := make([]byte, bufLen)
if ret := PdhGetCounterInfo(counterHandle, 1, &bufLen, &buf[0]); ret != ErrorSuccess {
return nil, fmt.Errorf("PdhGetCounterInfo: %w", NewPdhError(ret))
}
if ret := PdhGetCounterInfo(counterHandle, 0, &bufLen, nil); ret != PdhMoreData {
return nil, fmt.Errorf("PdhGetCounterInfo: %w", NewPdhError(ret))
}
ci := (*PdhCounterInfo)(unsafe.Pointer(&buf[0]))
counter.Type = ci.DwType
counter.Desc = windows.UTF16PtrToString(ci.SzExplainText)
buf := make([]byte, bufLen)
if ret := PdhGetCounterInfo(counterHandle, 0, &bufLen, &buf[0]); ret != ErrorSuccess {
return nil, fmt.Errorf("PdhGetCounterInfo: %w", NewPdhError(ret))
}
frequency := float64(0)
ci := (*PdhCounterInfo)(unsafe.Pointer(&buf[0]))
counter.Type = ci.DwType
counter.Desc = windows.UTF16PtrToString(ci.SzExplainText)
if ret := PdhGetCounterTimeBase(counterHandle, &frequency); ret != ErrorSuccess {
if counter.Type == perftypes.PERF_ELAPSED_TIME {
if ret := PdhGetCounterTimeBase(counterHandle, &counter.Frequency); ret != ErrorSuccess {
return nil, fmt.Errorf("PdhGetCounterTimeBase: %w", NewPdhError(ret))
}
counter.Frequency = frequency
}
}
@@ -153,7 +156,7 @@ func (c *Collector) Collect() (map[string]map[string]perftypes.CounterValues, er
continue
}
items := (*[1 << 20]PdhRawCounterItem)(unsafe.Pointer(&buf[0]))[:itemCount]
items := unsafe.Slice((*PdhRawCounterItem)(unsafe.Pointer(&buf[0])), itemCount)
if data == nil {
data = make(map[string]map[string]perftypes.CounterValues, itemCount)
@@ -169,7 +172,7 @@ func (c *Collector) Collect() (map[string]map[string]perftypes.CounterValues, er
for _, item := range items {
if item.RawValue.CStatus == PdhCstatusValidData || item.RawValue.CStatus == PdhCstatusNewData {
instanceName := windows.UTF16PtrToString(item.SzName)
if strings.HasSuffix(instanceName, "_Total") {
if strings.HasSuffix(instanceName, "_Total") && !c.totalCounterRequested {
continue
}
@@ -191,14 +194,14 @@ func (c *Collector) Collect() (map[string]map[string]perftypes.CounterValues, er
switch counter.Type {
case perftypes.PERF_ELAPSED_TIME:
values.FirstValue = float64(item.RawValue.FirstValue-perftypes.WindowsEpoch) / counter.Frequency
values.SecondValue = float64(item.RawValue.SecondValue-perftypes.WindowsEpoch) / counter.Frequency
values.FirstValue = float64((item.RawValue.FirstValue - perftypes.WindowsEpoch) / counter.Frequency)
case perftypes.PERF_100NSEC_TIMER, perftypes.PERF_PRECISION_100NS_TIMER:
values.FirstValue = float64(item.RawValue.FirstValue) * perftypes.TicksToSecondScaleFactor
values.SecondValue = float64(item.RawValue.SecondValue) * perftypes.TicksToSecondScaleFactor
default:
case perftypes.PERF_AVERAGE_BULK:
values.FirstValue = float64(item.RawValue.FirstValue)
values.SecondValue = float64(item.RawValue.SecondValue)
default:
values.FirstValue = float64(item.RawValue.FirstValue)
}
data[instanceName][counter.Name] = values

View File

@@ -622,7 +622,7 @@ func PdhGetRawCounterArray(hCounter pdhCounterHandle, lpdwBufferSize *uint32, lp
//
// lpdwItemCount
// Time base that specifies the number of performance values a counter samples per second.
func PdhGetCounterTimeBase(hCounter pdhCounterHandle, pTimeBase *float64) uint32 {
func PdhGetCounterTimeBase(hCounter pdhCounterHandle, pTimeBase *int64) uint32 {
ret, _, _ := pdhPdhGetCounterTimeBase.Call(
uintptr(hCounter),
uintptr(unsafe.Pointer(pTimeBase)))

View File

@@ -11,6 +11,7 @@ import (
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/toggle"
"github.com/prometheus-community/windows_exporter/pkg/collector"
"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/require"
@@ -46,7 +47,8 @@ func FuncBenchmarkCollector[C collector.Collector](b *testing.B, name string, co
func TestCollector[C collector.Collector, V interface{}](t *testing.T, fn func(*V) C, conf *V) {
t.Helper()
t.Setenv("WINDOWS_EXPORTER_PERF_COUNTERS_ENGINE", "pdh")
toggle.PHDEnabled = true
var (
metrics []prometheus.Metric

7
internal/toggle/main.go Normal file
View File

@@ -0,0 +1,7 @@
package toggle
var PHDEnabled bool
func IsPDHEnabled() bool {
return PHDEnabled
}

View File

@@ -3,7 +3,6 @@
package utils
import (
"os"
"strings"
"github.com/prometheus-community/windows_exporter/internal/types"
@@ -27,19 +26,3 @@ func ExpandEnabledCollectors(enabled string) []string {
return result
}
func PDHEnabled() bool {
if v, ok := os.LookupEnv("WINDOWS_EXPORTER_PERF_COUNTERS_ENGINE"); ok && v == "pdh" {
return true
}
return false
}
func MIEnabled() bool {
if v, ok := os.LookupEnv("WINDOWS_EXPORTER_WMI_ENGINE"); ok && v == "mi" {
return true
}
return false
}

View File

@@ -6,6 +6,10 @@ func MilliSecToSec(t float64) float64 {
return t / 1000
}
func MBToBytes(mb float64) float64 {
return mb * 1024 * 1024
}
func BoolToFloat(b bool) float64 {
if b {
return 1.0

View File

@@ -39,6 +39,7 @@ import (
"github.com/prometheus-community/windows_exporter/internal/collector/netframework"
"github.com/prometheus-community/windows_exporter/internal/collector/nps"
"github.com/prometheus-community/windows_exporter/internal/collector/os"
"github.com/prometheus-community/windows_exporter/internal/collector/pagefile"
"github.com/prometheus-community/windows_exporter/internal/collector/perfdata"
"github.com/prometheus-community/windows_exporter/internal/collector/physical_disk"
"github.com/prometheus-community/windows_exporter/internal/collector/printer"
@@ -55,6 +56,7 @@ import (
"github.com/prometheus-community/windows_exporter/internal/collector/textfile"
"github.com/prometheus-community/windows_exporter/internal/collector/thermalzone"
"github.com/prometheus-community/windows_exporter/internal/collector/time"
"github.com/prometheus-community/windows_exporter/internal/collector/udp"
"github.com/prometheus-community/windows_exporter/internal/collector/update"
"github.com/prometheus-community/windows_exporter/internal/collector/vmware"
"github.com/prometheus-community/windows_exporter/internal/mi"
@@ -106,6 +108,7 @@ func NewWithConfig(config Config) *MetricCollectors {
collectors[netframework.Name] = netframework.New(&config.NetFramework)
collectors[nps.Name] = nps.New(&config.Nps)
collectors[os.Name] = os.New(&config.OS)
collectors[pagefile.Name] = pagefile.New(&config.Paging)
collectors[perfdata.Name] = perfdata.New(&config.PerfData)
collectors[physical_disk.Name] = physical_disk.New(&config.PhysicalDisk)
collectors[printer.Name] = printer.New(&config.Printer)
@@ -122,6 +125,7 @@ func NewWithConfig(config Config) *MetricCollectors {
collectors[textfile.Name] = textfile.New(&config.Textfile)
collectors[thermalzone.Name] = thermalzone.New(&config.ThermalZone)
collectors[time.Name] = time.New(&config.Time)
collectors[udp.Name] = udp.New(&config.UDP)
collectors[update.Name] = update.New(&config.Update)
collectors[vmware.Name] = vmware.New(&config.Vmware)

View File

@@ -29,6 +29,7 @@ import (
"github.com/prometheus-community/windows_exporter/internal/collector/netframework"
"github.com/prometheus-community/windows_exporter/internal/collector/nps"
"github.com/prometheus-community/windows_exporter/internal/collector/os"
"github.com/prometheus-community/windows_exporter/internal/collector/pagefile"
"github.com/prometheus-community/windows_exporter/internal/collector/perfdata"
"github.com/prometheus-community/windows_exporter/internal/collector/physical_disk"
"github.com/prometheus-community/windows_exporter/internal/collector/printer"
@@ -45,6 +46,7 @@ import (
"github.com/prometheus-community/windows_exporter/internal/collector/textfile"
"github.com/prometheus-community/windows_exporter/internal/collector/thermalzone"
"github.com/prometheus-community/windows_exporter/internal/collector/time"
"github.com/prometheus-community/windows_exporter/internal/collector/udp"
"github.com/prometheus-community/windows_exporter/internal/collector/update"
"github.com/prometheus-community/windows_exporter/internal/collector/vmware"
)
@@ -78,6 +80,7 @@ type Config struct {
NetFramework netframework.Config `yaml:"net_framework"`
Nps nps.Config `yaml:"nps"`
OS os.Config `yaml:"os"`
Paging pagefile.Config `yaml:"paging"`
PerfData perfdata.Config `yaml:"perf_data"`
PhysicalDisk physical_disk.Config `yaml:"physical_disk"`
Printer printer.Config `yaml:"printer"`
@@ -94,6 +97,7 @@ type Config struct {
Textfile textfile.Config `yaml:"textfile"`
ThermalZone thermalzone.Config `yaml:"thermal_zone"`
Time time.Config `yaml:"time"`
UDP udp.Config `yaml:"udp"`
Update update.Config `yaml:"update"`
Vmware vmware.Config `yaml:"vmware"`
}
@@ -130,6 +134,7 @@ var ConfigDefaults = Config{
NetFramework: netframework.ConfigDefaults,
Nps: nps.ConfigDefaults,
OS: os.ConfigDefaults,
Paging: pagefile.ConfigDefaults,
PerfData: perfdata.ConfigDefaults,
PhysicalDisk: physical_disk.ConfigDefaults,
Printer: printer.ConfigDefaults,
@@ -146,6 +151,7 @@ var ConfigDefaults = Config{
Textfile: textfile.ConfigDefaults,
ThermalZone: thermalzone.ConfigDefaults,
Time: time.ConfigDefaults,
UDP: udp.ConfigDefaults,
Update: update.ConfigDefaults,
Vmware: vmware.ConfigDefaults,
}

View File

@@ -33,6 +33,7 @@ import (
"github.com/prometheus-community/windows_exporter/internal/collector/netframework"
"github.com/prometheus-community/windows_exporter/internal/collector/nps"
"github.com/prometheus-community/windows_exporter/internal/collector/os"
"github.com/prometheus-community/windows_exporter/internal/collector/pagefile"
"github.com/prometheus-community/windows_exporter/internal/collector/perfdata"
"github.com/prometheus-community/windows_exporter/internal/collector/physical_disk"
"github.com/prometheus-community/windows_exporter/internal/collector/printer"
@@ -49,6 +50,7 @@ import (
"github.com/prometheus-community/windows_exporter/internal/collector/textfile"
"github.com/prometheus-community/windows_exporter/internal/collector/thermalzone"
"github.com/prometheus-community/windows_exporter/internal/collector/time"
"github.com/prometheus-community/windows_exporter/internal/collector/udp"
"github.com/prometheus-community/windows_exporter/internal/collector/update"
"github.com/prometheus-community/windows_exporter/internal/collector/vmware"
)
@@ -88,6 +90,7 @@ var BuildersWithFlags = map[string]BuilderWithFlags[Collector]{
netframework.Name: NewBuilderWithFlags(netframework.NewWithFlags),
nps.Name: NewBuilderWithFlags(nps.NewWithFlags),
os.Name: NewBuilderWithFlags(os.NewWithFlags),
pagefile.Name: NewBuilderWithFlags(pagefile.NewWithFlags),
perfdata.Name: NewBuilderWithFlags(perfdata.NewWithFlags),
physical_disk.Name: NewBuilderWithFlags(physical_disk.NewWithFlags),
printer.Name: NewBuilderWithFlags(printer.NewWithFlags),
@@ -104,6 +107,7 @@ var BuildersWithFlags = map[string]BuilderWithFlags[Collector]{
textfile.Name: NewBuilderWithFlags(textfile.NewWithFlags),
thermalzone.Name: NewBuilderWithFlags(thermalzone.NewWithFlags),
time.Name: NewBuilderWithFlags(time.NewWithFlags),
udp.Name: NewBuilderWithFlags(udp.NewWithFlags),
update.Name: NewBuilderWithFlags(update.NewWithFlags),
vmware.Name: NewBuilderWithFlags(vmware.NewWithFlags),
}

View File

@@ -87,8 +87,6 @@ test_alpha_total 42
# TYPE windows_cpu_interrupts_total counter
# HELP windows_cpu_logical_processor Total number of logical processors
# TYPE windows_cpu_logical_processor gauge
# HELP windows_net_nic_address_info A metric with a constant '1' value labeled with the network interface's address information.
# TYPE windows_net_nic_address_info gauge
# HELP windows_cpu_parking_status Parking Status represents whether a processor is parked or not
# TYPE windows_cpu_parking_status gauge
# HELP windows_cpu_processor_mperf_total Processor MPerf is the number of TSC ticks incremented while executing instructions
@@ -120,16 +118,22 @@ windows_exporter_collector_success{collector="cpu"} 1
windows_exporter_collector_success{collector="cpu_info"} 1
windows_exporter_collector_success{collector="cs"} 1
windows_exporter_collector_success{collector="logical_disk"} 1
windows_exporter_collector_success{collector="logon"} 1
windows_exporter_collector_success{collector="memory"} 1
windows_exporter_collector_success{collector="net"} 1
windows_exporter_collector_success{collector="os"} 1
windows_exporter_collector_success{collector="pagefile"} 1
windows_exporter_collector_success{collector="perfdata"} 1
windows_exporter_collector_success{collector="physical_disk"} 1
windows_exporter_collector_success{collector="printer"} 1
windows_exporter_collector_success{collector="process"} 1
windows_exporter_collector_success{collector="scheduled_task"} 1
windows_exporter_collector_success{collector="service"} 1
windows_exporter_collector_success{collector="system"} 1
windows_exporter_collector_success{collector="tcp"} 1
windows_exporter_collector_success{collector="textfile"} 1
windows_exporter_collector_success{collector="time"} 1
windows_exporter_collector_success{collector="udp"} 1
# HELP windows_exporter_collector_timeout windows_exporter: Whether the collector timed out.
# TYPE windows_exporter_collector_timeout gauge
windows_exporter_collector_timeout{collector="cache"} 0
@@ -137,16 +141,22 @@ windows_exporter_collector_timeout{collector="cpu"} 0
windows_exporter_collector_timeout{collector="cpu_info"} 0
windows_exporter_collector_timeout{collector="cs"} 0
windows_exporter_collector_timeout{collector="logical_disk"} 0
windows_exporter_collector_timeout{collector="logon"} 0
windows_exporter_collector_timeout{collector="memory"} 0
windows_exporter_collector_timeout{collector="net"} 0
windows_exporter_collector_timeout{collector="os"} 0
windows_exporter_collector_timeout{collector="pagefile"} 0
windows_exporter_collector_timeout{collector="perfdata"} 0
windows_exporter_collector_timeout{collector="physical_disk"} 0
windows_exporter_collector_timeout{collector="printer"} 0
windows_exporter_collector_timeout{collector="process"} 0
windows_exporter_collector_timeout{collector="scheduled_task"} 0
windows_exporter_collector_timeout{collector="service"} 0
windows_exporter_collector_timeout{collector="system"} 0
windows_exporter_collector_timeout{collector="tcp"} 0
windows_exporter_collector_timeout{collector="textfile"} 0
windows_exporter_collector_timeout{collector="time"} 0
windows_exporter_collector_timeout{collector="udp"} 0
# HELP windows_exporter_perflib_snapshot_duration_seconds Duration of perflib snapshot capture
# TYPE windows_exporter_perflib_snapshot_duration_seconds gauge
# HELP windows_exporter_scrape_duration_seconds windows_exporter: Total scrape duration.
@@ -185,6 +195,8 @@ windows_exporter_collector_timeout{collector="textfile"} 0
# TYPE windows_logical_disk_write_seconds_total counter
# HELP windows_logical_disk_writes_total The number of write operations on the disk (LogicalDisk.DiskWritesPerSec)
# TYPE windows_logical_disk_writes_total counter
# HELP windows_logon_session_logon_timestamp_seconds timestamp of the logon session in seconds.
# TYPE windows_logon_session_logon_timestamp_seconds gauge
# HELP 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 (AvailableBytes)
# TYPE windows_memory_available_bytes gauge
# HELP windows_memory_cache_bytes (CacheBytes)
@@ -229,15 +241,15 @@ windows_exporter_collector_timeout{collector="textfile"} 0
# TYPE windows_memory_standby_cache_normal_priority_bytes gauge
# HELP 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 (StandbyCacheReserveBytes)
# TYPE windows_memory_standby_cache_reserve_bytes gauge
# HELP windows_memory_swap_page_operations_total Total number of swap page read and writes (PagesPersec)
# HELP windows_memory_swap_page_operations_total Total number of swap page read and writes (PagesPerSec)
# TYPE windows_memory_swap_page_operations_total counter
# HELP windows_memory_swap_page_reads_total Number of disk page reads (a single read operation reading several pages is still only counted once) (PageReadsPersec)
# HELP windows_memory_swap_page_reads_total Number of disk page reads (a single read operation reading several pages is still only counted once) (PageReadsPerSec)
# TYPE windows_memory_swap_page_reads_total counter
# HELP windows_memory_swap_page_writes_total Number of disk page writes (a single write operation writing several pages is still only counted once) (PageWritesPersec)
# HELP windows_memory_swap_page_writes_total Number of disk page writes (a single write operation writing several pages is still only counted once) (PageWritesPerSec)
# TYPE windows_memory_swap_page_writes_total counter
# HELP 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) (PagesInputPersec)
# HELP 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) (PagesInputPerSec)
# TYPE windows_memory_swap_pages_read_total counter
# HELP 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) (PagesOutputPersec)
# HELP 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) (PagesOutputPerSec)
# TYPE windows_memory_swap_pages_written_total counter
# HELP 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 (SystemCacheResidentBytes)
# TYPE windows_memory_system_cache_resident_bytes gauge
@@ -249,11 +261,11 @@ windows_exporter_collector_timeout{collector="textfile"} 0
# TYPE windows_memory_system_driver_resident_bytes gauge
# HELP 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 (SystemDriverTotalBytes)
# TYPE windows_memory_system_driver_total_bytes gauge
# HELP 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 (TransitionFaultsPersec)
# HELP 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 (TransitionFaultsPerSec)
# TYPE windows_memory_transition_faults_total counter
# HELP 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 (TransitionPagesRePurposedPersec)
# HELP 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 (TransitionPagesRePurposedPerSec)
# TYPE windows_memory_transition_pages_repurposed_total counter
# HELP 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 (WriteCopiesPersec)
# HELP 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 (WriteCopiesPerSec)
# TYPE windows_memory_write_copies_total counter
# HELP windows_net_bytes_received_total (Network.BytesReceivedPerSec)
# TYPE windows_net_bytes_received_total counter
@@ -263,6 +275,8 @@ windows_exporter_collector_timeout{collector="textfile"} 0
# TYPE windows_net_bytes_total counter
# HELP windows_net_current_bandwidth_bytes (Network.CurrentBandwidth)
# TYPE windows_net_current_bandwidth_bytes gauge
# HELP windows_net_nic_address_info A metric with a constant '1' value labeled with the network interface's address information.
# TYPE windows_net_nic_address_info gauge
# HELP windows_net_output_queue_length_packets (Network.OutputQueueLength)
# TYPE windows_net_output_queue_length_packets gauge
# HELP windows_net_packets_outbound_discarded_total (Network.PacketsOutboundDiscarded)
@@ -285,9 +299,9 @@ windows_exporter_collector_timeout{collector="textfile"} 0
# TYPE windows_os_hostname gauge
# 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
# HELP windows_os_paging_free_bytes OperatingSystem.FreeSpaceInPagingFiles
# HELP windows_os_paging_free_bytes Deprecated: Use windows_pagefile_free_bytes instead.
# TYPE windows_os_paging_free_bytes gauge
# HELP windows_os_paging_limit_bytes OperatingSystem.SizeStoredInPagingFiles
# HELP windows_os_paging_limit_bytes Deprecated: Use windows_pagefile_limit_bytes instead.
# TYPE windows_os_paging_limit_bytes gauge
# HELP windows_os_physical_memory_free_bytes Deprecated: Use `windows_memory_physical_free_bytes` instead.
# TYPE windows_os_physical_memory_free_bytes gauge
@@ -309,6 +323,10 @@ windows_exporter_collector_timeout{collector="textfile"} 0
# TYPE windows_os_virtual_memory_free_bytes gauge
# HELP windows_os_visible_memory_bytes Deprecated: Use `windows_memory_physical_total_bytes` instead.
# TYPE windows_os_visible_memory_bytes gauge
# HELP windows_pagefile_free_bytes Number of bytes that can be mapped into the operating system paging files without causing any other pages to be swapped out
# TYPE windows_pagefile_free_bytes gauge
# HELP windows_pagefile_limit_bytes Number of bytes that can be stored in the operating system paging files. 0 (zero) indicates that there are no paging files
# TYPE windows_pagefile_limit_bytes gauge
# HELP windows_perfdata_memory_cache_faults_sec Performance data for \\Memory\\Cache Faults/sec
# TYPE windows_perfdata_memory_cache_faults_sec counter
# HELP windows_perfdata_processor_information__privileged_time Performance data for \\Processor Information\\% Privileged Time
@@ -339,16 +357,26 @@ windows_exporter_collector_timeout{collector="textfile"} 0
# TYPE windows_physical_disk_write_seconds_total counter
# HELP windows_physical_disk_writes_total The number of write operations on the disk (PhysicalDisk.DiskWritesPerSec)
# TYPE windows_physical_disk_writes_total counter
# HELP windows_printer_job_count Number of jobs processed by the printer since the last reset
# TYPE windows_printer_job_count counter
# HELP windows_printer_status Printer status
# TYPE windows_printer_status gauge
# HELP windows_scheduled_task_last_result The result that was returned the last time the registered task was run
# TYPE windows_scheduled_task_last_result gauge
windows_scheduled_task_last_result{task="/Microsoft/Windows/PLA/GAEvents"} 0
# HELP windows_scheduled_task_missed_runs The number of times the registered task missed a scheduled run
# TYPE windows_scheduled_task_missed_runs gauge
windows_scheduled_task_missed_runs{task="/Microsoft/Windows/PLA/GAEvents"} 0
# HELP windows_scheduled_task_state The current state of a scheduled task
# TYPE windows_scheduled_task_state gauge
windows_scheduled_task_state{state="disabled",task="/Microsoft/Windows/Maintenance/WinSAT"} 1
windows_scheduled_task_state{state="queued",task="/Microsoft/Windows/Maintenance/WinSAT"} 0
windows_scheduled_task_state{state="ready",task="/Microsoft/Windows/Maintenance/WinSAT"} 0
windows_scheduled_task_state{state="running",task="/Microsoft/Windows/Maintenance/WinSAT"} 0
windows_scheduled_task_state{state="unknown",task="/Microsoft/Windows/Maintenance/WinSAT"} 0
windows_scheduled_task_state{state="disabled",task="/Microsoft/Windows/PLA/GAEvents"} 0
windows_scheduled_task_state{state="queued",task="/Microsoft/Windows/PLA/GAEvents"} 0
windows_scheduled_task_state{state="ready",task="/Microsoft/Windows/PLA/GAEvents"} 0
windows_scheduled_task_state{state="running",task="/Microsoft/Windows/PLA/GAEvents"} 1
windows_scheduled_task_state{state="unknown",task="/Microsoft/Windows/PLA/GAEvents"} 0
# 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="Themes",name="Themes",path_name="C:\\WINDOWS\\System32\\svchost.exe -k netsvcs -p",run_as="LocalSystem"} 1
windows_service_info{display_name="Themes",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
# HELP windows_service_start_mode The start mode of the service (StartMode)
@@ -383,9 +411,53 @@ windows_service_state{name="Themes",state="stopped"} 0
# TYPE windows_system_system_up_time gauge
# HELP windows_system_threads Current number of threads (WMI source: PerfOS_System.Threads)
# TYPE windows_system_threads gauge
# HELP windows_tcp_connection_failures_total (TCP.ConnectionFailures)
# TYPE windows_tcp_connection_failures_total counter
# HELP windows_tcp_connections_active_total (TCP.ConnectionsActive)
# TYPE windows_tcp_connections_active_total counter
# HELP windows_tcp_connections_established (TCP.ConnectionsEstablished)
# TYPE windows_tcp_connections_established gauge
# HELP windows_tcp_connections_passive_total (TCP.ConnectionsPassive)
# TYPE windows_tcp_connections_passive_total counter
# HELP windows_tcp_connections_reset_total (TCP.ConnectionsReset)
# TYPE windows_tcp_connections_reset_total counter
# HELP windows_tcp_connections_state_count Number of TCP connections by state and address family
# TYPE windows_tcp_connections_state_count gauge
# HELP windows_tcp_segments_received_total (TCP.SegmentsReceivedTotal)
# TYPE windows_tcp_segments_received_total counter
# HELP windows_tcp_segments_retransmitted_total (TCP.SegmentsRetransmittedTotal)
# TYPE windows_tcp_segments_retransmitted_total counter
# HELP windows_tcp_segments_sent_total (TCP.SegmentsSentTotal)
# TYPE windows_tcp_segments_sent_total counter
# HELP windows_tcp_segments_total (TCP.SegmentsTotal)
# TYPE windows_tcp_segments_total counter
# HELP windows_textfile_mtime_seconds Unixtime mtime of textfiles successfully read.
# TYPE windows_textfile_mtime_seconds gauge
# HELP windows_textfile_scrape_error 1 if there was an error opening or reading a file, 0 otherwise
# TYPE windows_textfile_scrape_error gauge
windows_textfile_scrape_error 0
# HELP windows_time_clock_frequency_adjustment_ppb_total Total adjustment made to the local system clock frequency by W32Time in Parts Per Billion (PPB) units.
# TYPE windows_time_clock_frequency_adjustment_ppb_total counter
# HELP windows_time_computed_time_offset_seconds Absolute time offset between the system clock and the chosen time source, in seconds
# TYPE windows_time_computed_time_offset_seconds gauge
# HELP windows_time_current_timestamp_seconds OperatingSystem.LocalDateTime
# TYPE windows_time_current_timestamp_seconds gauge
# HELP windows_time_ntp_client_time_sources Active number of NTP Time sources being used by the client
# TYPE windows_time_ntp_client_time_sources gauge
# HELP windows_time_ntp_round_trip_delay_seconds Roundtrip delay experienced by the NTP client in receiving a response from the server for the most recent request, in seconds
# TYPE windows_time_ntp_round_trip_delay_seconds gauge
# HELP windows_time_ntp_server_incoming_requests_total Total number of requests received by NTP server
# TYPE windows_time_ntp_server_incoming_requests_total counter
# HELP windows_time_ntp_server_outgoing_responses_total Total number of requests responded to by NTP server
# TYPE windows_time_ntp_server_outgoing_responses_total counter
# HELP windows_time_timezone OperatingSystem.LocalDateTime
# TYPE windows_time_timezone gauge
# HELP windows_udp_datagram_no_port_total Number of received UDP datagrams for which there was no application at the destination port
# TYPE windows_udp_datagram_no_port_total counter
# HELP windows_udp_datagram_received_errors_total Number of received UDP datagrams that could not be delivered for reasons other than the lack of an application at the destination port
# TYPE windows_udp_datagram_received_errors_total counter
# HELP windows_udp_datagram_received_total UDP datagrams are delivered to UDP users
# TYPE windows_udp_datagram_received_total gauge
# HELP windows_udp_datagram_sent_total UDP datagrams are sent from the entity
# TYPE windows_udp_datagram_sent_total counter

View File

@@ -10,7 +10,7 @@ if (-not (Test-Path -Path '..\windows_exporter.exe')) {
Write-Output "..\windows_exporter.exe not found. Consider running \`go build\` first"
}
$temp_dir = Join-Path $env:TEMP $(New-Guid) | ForEach-Object { mkdir $_ }
$temp_dir = Join-Path $env:TEMP $([guid]::newguid()) | ForEach-Object { mkdir $_ }
# Create temporary directory for textfile collector
$textfile_dir = "$($temp_dir)/textfile"
@@ -18,14 +18,14 @@ mkdir $textfile_dir | Out-Null
Copy-Item 'e2e-textfile.prom' -Destination "$($textfile_dir)/e2e-textfile.prom"
# Omit dynamic collector information that will change after each run
$skip_re = "^(go_|windows_exporter_build_info|windows_exporter_collector_duration_seconds|windows_exporter_perflib_snapshot_duration_seconds|windows_exporter_scrape_duration_seconds|process_|windows_textfile_mtime_seconds|windows_cpu|windows_cs|windows_cache|windows_logical_disk|windows_physical_disk|windows_memory|windows_net|windows_os|windows_process|windows_service_process|windows_system|windows_perfdata|windows_textfile_mtime_seconds)"
$skip_re = "^(go_|windows_exporter_build_info|windows_exporter_collector_duration_seconds|windows_exporter_perflib_snapshot_duration_seconds|windows_exporter_scrape_duration_seconds|process_|windows_textfile_mtime_seconds|windows_cpu|windows_cs|windows_cache|windows_logon|windows_pagefile|windows_logical_disk|windows_physical_disk|windows_memory|windows_net|windows_os|windows_process|windows_service_process|windows_printer|windows_udp|windows_tcp|windows_system|windows_time|windows_session|windows_perfdata|windows_textfile_mtime_seconds)"
# Start process in background, awaiting HTTP requests.
# Use default collectors, port and address: http://localhost:9182/metrics
$exporter_proc = Start-Process `
-PassThru `
-FilePath ..\windows_exporter.exe `
-ArgumentList "--log.level=debug","--web.disable-exporter-metrics","--collectors.enabled=[defaults],cache,cpu_info,textfile,process,perfdata,scheduled_task","--collector.process.include=explorer.exe","--collector.scheduled_task.include=.*WinSAT","--collector.service.include=Themes","--collector.textfile.directories=$($textfile_dir)",@"
-ArgumentList "--log.level=debug","--web.disable-exporter-metrics","--collectors.enabled=[defaults],cpu_info,textfile,process,pagefile,perfdata,scheduled_task,tcp,udp,time,system,service,logical_disk,printer,os,net,memory,logon,cache","--collector.process.include=explorer.exe","--collector.scheduled_task.include=.*GAEvents","--collector.service.include=Themes","--collector.textfile.directories=$($textfile_dir)",@"
--collector.perfdata.objects="[{\"object\":\"Processor Information\",\"instance_label\":\"core\",\"instances\":[\"*\"],\"counters\":{\"% Processor Time\":{},\"% Privileged Time\":{}}},{\"object\":\"Memory\",\"counters\":{\"Cache Faults/sec\":{\"type\":\"counter\"}}}]"
"@ `
-WindowStyle Hidden `

View File

@@ -85,7 +85,7 @@ $script_path = $MyInvocation.MyCommand.Path
$working_dir = Split-Path $script_path
Push-Location $working_dir
$temp_dir = Join-Path $env:TEMP $(New-Guid) | ForEach-Object { mkdir $_ }
$temp_dir = Join-Path $env:TEMP $([guid]::newguid()) | ForEach-Object { mkdir $_ }
# Start process in background, awaiting HTTP requests.
# Listen on 9183/TCP, preventing conflicts with 9182/TCP used by end-to-end-test.ps1
@@ -113,13 +113,29 @@ for ($i=1; $i -le 5; $i++) {
# windows_memory_pool_nonpaged_allocs_total is wrong for years. It's not a gauge, but a counter.
$skip_re = "^([#]?\s*(HELP|TYPE)?\s*go_|windows_memory_pool_nonpaged_allocs_total)"
# Need to remove carriage returns, as promtool expects LF line endings
$output = ((Invoke-WebRequest -UseBasicParsing -URI http://127.0.0.1:9183/metrics).Content) -Split "`r?`n" | Select-String -NotMatch $skip_re | Join-String -Separator "`n"
# Join the split lines back to a single String (with LF line endings!)
$output = $output -Join "`n"
Stop-Process -Id $exporter_proc.Id
try {
# Need to remove carriage returns, as promtool expects LF line endings
$output = ((Invoke-WebRequest -UseBasicParsing -URI http://127.0.0.1:9183/metrics).Content) -Split "`r?`n" | Select-String -NotMatch $skip_re | Join-String -Separator "`n"
# $output = (((Invoke-WebRequest -UseBasicParsing -URI http://127.0.0.1:9183/metrics).Content) -Split "`r?`n" | Select-String -NotMatch $skip_re) -join "`n"
# Join the split lines back to a single String (with LF line endings!)
$output = $output -Join "`n"
Stop-Process -Id $exporter_proc.Id
} catch {
Write-Host "STDOUT"
Get-Content "$($temp_dir)/windows_exporter.log"
Write-Host "STDERR"
Get-Content "$($temp_dir)/windows_exporter_error.log"
throw $_
}
$ExitCode = Start-RawProcess -InputVar $output -CommandName promtool.exe -CommandArgs @("check metrics")
if ($ExitCode -ne 0) {
Write-Host "OUTPUT"
Write-Host $output
Write-Host "Promtool command returned exit code $($ExitCode). See output for details."
EXIT 1
}