Compare commits

...

5 Commits

Author SHA1 Message Date
Jan-Otto Kröpke
6097432d87 chore: remove replace (#1628) 2024-09-14 21:44:08 +02:00
Jan-Otto Kröpke
5f36a81613 cpu: Fetch performance counter via PDH.dll via feature toggle. (off by default) (#1627) 2024-09-13 23:10:57 +02:00
Jan-Otto Kröpke
2e4ba9984d perfdata: fix incorrect collector log lines (#1626) 2024-09-13 22:12:10 +02:00
Jan-Otto Kröpke
fb275a06fe service: fix label name in windows_service_state (#1625) 2024-09-12 19:35:12 +02:00
Jan-Otto Kröpke
064ea74b6f installer: fix arm64 msi installer (#1623) 2024-09-12 15:02:21 +02:00
16 changed files with 317 additions and 73 deletions

View File

@@ -26,6 +26,9 @@ jobs:
runs-on: windows-2022
steps:
- uses: actions/checkout@v4
with:
fetch-depth: '0'
- uses: actions/setup-go@v5
with:
go-version-file: 'go.mod'
@@ -88,12 +91,6 @@ jobs:
Get-ChildItem -Path output
- name: Upload Artifacts
uses: actions/upload-artifact@v4
with:
name: windows_exporter_binaries
path: output\windows_exporter-*.exe
- name: Build Release Artifacts
run: |
$ErrorActionPreference = "Stop"
@@ -109,6 +106,14 @@ jobs:
promu checksum output\
- name: Upload Artifacts
uses: actions/upload-artifact@v4
with:
name: windows_exporter_binaries
path: |
output\windows_exporter-*.exe
output\windows_exporter-*.msi
- name: Build Docker Artifacts
run: make build-all
env:

View File

@@ -45,9 +45,6 @@ linters-settings:
# Support string case: `camel`, `pascal`, `kebab`, `snake`, `upperSnake`, `goCamel`, `goPascal`, `goKebab`, `goSnake`, `upper`, `lower`, `header`
json: camel
yaml: snake
gomoddirectives:
replace-allow-list:
- github.com/prometheus/common # https://github.com/prometheus/common/pull/694
forbidigo:
forbid:
- "^(fmt\\.Print(|f|ln)|print|println)$"

View File

@@ -110,47 +110,55 @@ The configuration file
The following parameters are available:
| Name | Description |
|----------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `ENABLED_COLLECTORS` | As the `--collectors.enabled` flag, provide a comma-separated list of enabled collectors |
| `LISTEN_ADDR` | The IP address to bind to. Defaults to an empty string. (any local address) |
| `LISTEN_PORT` | The port to bind to. Defaults to `9182`. |
| `METRICS_PATH` | The path at which to serve metrics. Defaults to `/metrics` |
| `TEXTFILE_DIRS` | Use the `--collector.textfile.directories` flag to specify one or more directories, separated by commas, where the collector should read text files containing metrics |
| `REMOTE_ADDR` | Allows setting comma separated remote IP addresses for the Windows Firewall exception (allow list). Defaults to an empty string (any remote address). |
| `EXTRA_FLAGS` | Allows passing full CLI flags. Defaults to an empty string. |
| `ADDLOCAL` | Enables features within the windows_exporter installer. Supported values: `FirewallException` |
| `REMOVE` | Disables features within the windows_exporter installer. Supported values: `FirewallException` |
| Name | Description |
|----------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `ENABLED_COLLECTORS` | As the `--collectors.enabled` flag, provide a comma-separated list of enabled collectors |
| `CONFIG_FILE` | Use the `--config.file` flag to specify a config file. If empty, no config file will be set. The special value `config.yaml` set the path to the config.yaml at install dir | |
| `LISTEN_ADDR` | The IP address to bind to. Defaults to an empty string. (any local address) |
| `LISTEN_PORT` | The port to bind to. Defaults to `9182`. |
| `METRICS_PATH` | The path at which to serve metrics. Defaults to `/metrics` |
| `TEXTFILE_DIRS` | Use the `--collector.textfile.directories` flag to specify one or more directories, separated by commas, where the collector should read text files containing metrics |
| `REMOTE_ADDR` | Allows setting comma separated remote IP addresses for the Windows Firewall exception (allow list). Defaults to an empty string (any remote address). |
| `EXTRA_FLAGS` | Allows passing full CLI flags. Defaults to an empty string. For `--collectors.enabled` and `--config.file`, use the specialized properties `ENABLED_COLLECTORS` and `CONFIG_FILE` |
| `ADDLOCAL` | Enables features within the windows_exporter installer. Supported values: `FirewallException` |
| `REMOVE` | Disables features within the windows_exporter installer. Supported values: `FirewallException` |
Parameters are sent to the installer via `msiexec`. Example invocations:
Parameters are sent to the installer via `msiexec`.
On PowerShell, the `--%` should be passed before defining properties.
Example invocations:
```powershell
msiexec /i <path-to-msi-file> ENABLED_COLLECTORS=os,iis LISTEN_PORT=5000
msiexec /i <path-to-msi-file> --% ENABLED_COLLECTORS=os,iis LISTEN_PORT=5000
```
Example service collector with a custom query.
```powershell
msiexec /i <path-to-msi-file> ENABLED_COLLECTORS=os,service --% EXTRA_FLAGS="--collector.service.services-where ""Name LIKE 'sql%'"""
msiexec /i <path-to-msi-file> --% ENABLED_COLLECTORS=os,service EXTRA_FLAGS="--collectors.exchange.enabled=""ADAccessProcesses"""
```
Define a config file.
```powershell
msiexec /i <path-to-msi-file> --% CONFIG_FILE="D:\config.yaml"
```
On some older versions of Windows,
you may need to surround parameter values with double quotes to get the installation command parsing properly:
```powershell
msiexec /i C:\Users\Administrator\Downloads\windows_exporter.msi ENABLED_COLLECTORS="ad,iis,logon,memory,process,tcp,textfile,thermalzone" TEXTFILE_DIRS="C:\custom_metrics\"
msiexec /i C:\Users\Administrator\Downloads\windows_exporter.msi --% ENABLED_COLLECTORS="ad,iis,logon,memory,process,tcp,textfile,thermalzone" TEXTFILE_DIRS="C:\custom_metrics\"
```
To install the exporter with creating a firewall exception, use the following command:
```powershell
msiexec /i <path-to-msi-file> ADDLOCAL=FirewallException
msiexec /i <path-to-msi-file> --% ADDLOCAL=FirewallException
```
Powershell versions 7.3 and above require [PSNativeCommandArgumentPassing](https://learn.microsoft.com/en-us/powershell/scripting/learn/experimental-features?view=powershell-7.3) to be set to `Legacy` when using `--% EXTRA_FLAGS`:
PowerShell versions 7.3 and above require [PSNativeCommandArgumentPassing](https://learn.microsoft.com/en-us/powershell/scripting/learn/experimental-features?view=powershell-7.3) to be set to `Legacy` when using `--% EXTRA_FLAGS`:
```powershell
$PSNativeCommandArgumentPassing = 'Legacy'
msiexec /i <path-to-msi-file> ENABLED_COLLECTORS=os,service --% EXTRA_FLAGS="--collector.service.services-where ""Name LIKE 'sql%'"""
msiexec /i <path-to-msi-file> ENABLED_COLLECTORS=os,service --% EXTRA_FLAGS="--collectors.exchange.enabled=""ADAccessProcesses"""
```
## Docker Implementation

View File

@@ -72,20 +72,20 @@ windows_service_start_mode{name="dcsvc",start_mode="manual"} 1
windows_service_start_mode{name="dcsvc",start_mode="system"} 0
# HELP windows_service_state The state of the service (State)
# TYPE windows_service_state gauge
windows_service_state{name="Themes",status="continue pending"} 0
windows_service_state{name="Themes",status="pause pending"} 0
windows_service_state{name="Themes",status="paused"} 0
windows_service_state{name="Themes",status="running"} 1
windows_service_state{name="Themes",status="start pending"} 0
windows_service_state{name="Themes",status="stop pending"} 0
windows_service_state{name="Themes",status="stopped"} 0
windows_service_state{name="dcsvc",status="continue pending"} 0
windows_service_state{name="dcsvc",status="pause pending"} 0
windows_service_state{name="dcsvc",status="paused"} 0
windows_service_state{name="dcsvc",status="running"} 0
windows_service_state{name="dcsvc",status="start pending"} 0
windows_service_state{name="dcsvc",status="stop pending"} 0
windows_service_state{name="dcsvc",status="stopped"} 1
windows_service_state{name="Themes",state="continue pending"} 0
windows_service_state{name="Themes",state="pause pending"} 0
windows_service_state{name="Themes",state="paused"} 0
windows_service_state{name="Themes",state="running"} 1
windows_service_state{name="Themes",state="start pending"} 0
windows_service_state{name="Themes",state="stop pending"} 0
windows_service_state{name="Themes",state="stopped"} 0
windows_service_state{name="dcsvc",state="continue pending"} 0
windows_service_state{name="dcsvc",state="pause pending"} 0
windows_service_state{name="dcsvc",state="paused"} 0
windows_service_state{name="dcsvc",state="running"} 0
windows_service_state{name="dcsvc",state="start pending"} 0
windows_service_state{name="dcsvc",state="stop pending"} 0
windows_service_state{name="dcsvc",state="stopped"} 1
```
## Useful queries

View File

@@ -217,6 +217,10 @@ 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())

7
go.mod
View File

@@ -11,7 +11,7 @@ require (
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.20.3
github.com/prometheus/client_model v0.6.1
github.com/prometheus/common v0.59.1
github.com/prometheus/common v0.59.2-0.20240914165825-40d6251261f7
github.com/prometheus/exporter-toolkit v0.13.0
github.com/stretchr/testify v1.9.0
github.com/yusufpapurcu/wmi v1.2.4
@@ -46,10 +46,7 @@ require (
golang.org/x/sync v0.8.0 // indirect
golang.org/x/text v0.18.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
google.golang.org/grpc v1.66.0 // indirect
google.golang.org/grpc v1.66.2 // indirect
google.golang.org/protobuf v1.34.2 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
// https://github.com/prometheus/common/pull/694
replace github.com/prometheus/common v0.59.1 => github.com/jkroepke/prometheus-common v0.0.0-20240907211841-5f9af24b97ad

8
go.sum
View File

@@ -59,8 +59,6 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jkroepke/prometheus-common v0.0.0-20240907211841-5f9af24b97ad h1:sFDfDs4nDXjES8PdrFPiXeYt8dtaxn10M/Ebxe4IuiI=
github.com/jkroepke/prometheus-common v0.0.0-20240907211841-5f9af24b97ad/go.mod h1:GpWM7dewqmVYcd7SmRaiWVe9SSqjf0UrwnYnpEZNuT0=
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
@@ -88,6 +86,8 @@ github.com/prometheus/client_golang v1.20.3/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/j
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/common v0.59.2-0.20240914165825-40d6251261f7 h1:DT3yUDP5XHbLdx1oY1xmEn0i/Ze6R2tLO3Q8QBBObEk=
github.com/prometheus/common v0.59.2-0.20240914165825-40d6251261f7/go.mod h1:GpWM7dewqmVYcd7SmRaiWVe9SSqjf0UrwnYnpEZNuT0=
github.com/prometheus/exporter-toolkit v0.13.0 h1:lmA0Q+8IaXgmFRKw09RldZmZdnvu9wwcDLIXGmTPw1c=
github.com/prometheus/exporter-toolkit v0.13.0/go.mod h1:2uop99EZl80KdXhv/MxVI2181fMcwlsumFOqBecGkG0=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
@@ -168,8 +168,8 @@ google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyac
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.66.0 h1:DibZuoBznOxbDQxRINckZcUvnCEvrW9pcWIE2yF9r1c=
google.golang.org/grpc v1.66.0/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
google.golang.org/grpc v1.66.2 h1:3QdXkuq3Bkh7w+ywLdLvM56cmGvQHUMZpiCzt6Rqaoo=
google.golang.org/grpc v1.66.2/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=

View File

@@ -13,7 +13,7 @@
Start="auto"
Type="ownProcess"
Vital="yes"
Arguments="--log.file eventlog --config.file=&quot;[APPLICATIONFOLDER]config.yaml&quot; [CollectorsFlag] [ListenFlag] [MetricsPathFlag] [TextfileDirsFlag] [ExtraFlags]">
Arguments="--log.file eventlog [ConfigFile_NonDefault][ConfigFile_Default] [CollectorsFlag] [ListenFlag] [MetricsPathFlag] [TextfileDirsFlag] [ExtraFlags]">
<util:ServiceConfig
ResetPeriodInDays="1"
FirstFailureActionType="restart"

View File

@@ -3,10 +3,10 @@
<?ifndef Version?>
<?error Version must be defined?>
<?endif?>
<?if $(sys.BUILDARCH)=x64 ?>
<?define PlatformProgramFiles = "ProgramFiles64Folder" ?>
<?else?>
<?error Platform $(var.Platform) is not supported?>
<?if $(sys.BUILDARCH) = x64 or $(sys.BUILDARCH) = arm64 ?>
<?define PlatformProgramFilesFolder = "ProgramFiles64Folder" ?>
<?else ?>
<?define PlatformProgramFilesFolder = "ProgramFilesFolder" ?>
<?endif?>
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"
@@ -67,12 +67,16 @@
<Media Id="1" Cabinet="windows_exporter.cab" EmbedCab="yes" />
<MajorUpgrade Schedule="afterInstallInitialize" DowngradeErrorMessage="A later version of [ProductName] is already installed. Setup will now exit." AllowSameVersionUpgrades="yes" />
<Property Id="ENABLED_COLLECTORS" Secure="yes" Value="[defaults]" />
<Property Id="ENABLED_COLLECTORS" Secure="yes" />
<SetProperty Id="CollectorsFlag" After="InstallFiles" Sequence="execute" Value="--collectors.enabled [ENABLED_COLLECTORS]" Condition="ENABLED_COLLECTORS" />
<Property Id="EXTRA_FLAGS" Secure="yes" />
<SetProperty Id="ExtraFlags" After="InstallFiles" Sequence="execute" Value="[EXTRA_FLAGS]" Condition="EXTRA_FLAGS" />
<Property Id="CONFIG_FILE" Secure="yes" Value="config.yaml" />
<SetProperty Id="ConfigFile_NonDefault" After="InstallFiles" Sequence="execute" Value="--config.file=&quot;[CONFIG_FILE]&quot;" Condition="CONFIG_FILE AND CONFIG_FILE&lt;&gt;&quot;config.yaml&quot;" />
<SetProperty Id="ConfigFile_Default" After="InstallFiles" Sequence="execute" Value="--config.file=&quot;[APPLICATIONFOLDER]config.yaml&quot;" Condition="CONFIG_FILE=&quot;config.yaml&quot;" />
<Property Id="LISTEN_PORT" Secure="yes" Value="9182" />
<SetProperty Id="ListenFlag" After="InstallFiles" Sequence="execute" Value="--web.listen-address [LISTEN_ADDR]:[LISTEN_PORT]" Condition="LISTEN_ADDR&lt;&gt;&quot;&quot; OR LISTEN_PORT&lt;&gt;9182" />
@@ -132,15 +136,19 @@
<!-- Edit box for property input -->
<!-- cpu,cs,logical_disk,physical_disk,net,os,service,system -->
<Control Id="PropertyEdit_ENABLED_COLLECTORS_Title1" Type="Text" X="25" Y="55" Width="300" Height="15" Transparent="yes" NoPrefix="yes" Text="Comma-separated list of collectors to use. Use '[\[]defaults[\]]' as a placeholder for all" />
<Control Id="PropertyEdit_ENABLED_COLLECTORS_Title2" Type="Text" X="25" Y="65" Width="300" Height="15" Transparent="yes" NoPrefix="yes" Text="the collectors enabled by default." />
<Control Id="PropertyEdit_ENABLED_COLLECTORS_Title2" Type="Text" X="25" Y="65" Width="300" Height="15" Transparent="yes" NoPrefix="yes" Text="the collectors enabled by default. If value is empty, the exporter default will be used." />
<Control Id="PropertyEdit_ENABLED_COLLECTORS" Type="Edit" X="24" Y="77" Width="300" Height="18" Property="ENABLED_COLLECTORS" Text="[ENABLED_COLLECTORS]" Indirect="no" />
<Control Id="PropertyEdit_EXTRA_FLAGS_Title" Type="Text" X="25" Y="100" Width="210" Height="15" Transparent="yes" NoPrefix="yes" Text="Additional command line flags" />
<Control Id="PropertyEdit_EXTRA_FLAGS_Title" Type="Text" X="25" Y="100" Width="300" Height="15" Transparent="yes" NoPrefix="yes" Text="Additional command line flags" />
<Control Id="PropertyEdit_EXTRA_FLAGS" Type="Edit" X="24" Y="112" Width="300" Height="18" Property="EXTRA_FLAGS" Text="[EXTRA_FLAGS]" Indirect="no" />
<Control Id="PropertyEdit_LISTEN_PORT_Title" Type="Text" X="25" Y="135" Width="210" Height="15" Transparent="yes" NoPrefix="yes" Text="Port to listen" />
<Control Id="PropertyEdit_LISTEN_PORT_Title" Type="Text" X="25" Y="135" Width="300" Height="15" Transparent="yes" NoPrefix="yes" Text="Port to listen" />
<Control Id="PropertyEdit_LISTEN_PORT" Type="Edit" X="24" Y="147" Width="300" Height="18" Property="LISTEN_PORT" Text="[LISTEN_PORT]" Indirect="no" />
<Control Id="PropertyEdit_CONFIG_FILE_Title1" Type="Text" X="25" Y="170" Width="300" Height="15" Transparent="yes" NoPrefix="yes" Text="Path to config file. If empty, no config will be used. If set to 'config.yaml', " />
<Control Id="PropertyEdit_CONFIG_FILE_Title2" Type="Text" X="25" Y="180" Width="300" Height="15" Transparent="yes" NoPrefix="yes" Text="then the config.yaml at the install path will be used." />
<Control Id="PropertyEdit_CONFIG_FILE" Type="Edit" X="24" Y="192" Width="300" Height="18" Property="CONFIG_FILE" Text="[CONFIG_FILE]" Indirect="no" />
<Control Id="Next" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" Text="!(loc.WixUINext)">
<Publish Event="NewDialog" Value="VerifyReadyDlg" />
</Control>

View File

@@ -0,0 +1,28 @@
package cpu
// Processor performance counters.
const (
C1TimeSeconds = "% C1 Time"
C2TimeSeconds = "% C2 Time"
C3TimeSeconds = "% C3 Time"
C1TransitionsTotal = "C1 Transitions/sec"
C2TransitionsTotal = "C2 Transitions/sec"
C3TransitionsTotal = "C3 Transitions/sec"
ClockInterruptsTotal = "Clock Interrupts/sec"
DPCsQueuedTotal = "DPCs Queued/sec"
DPCTimeSeconds = "% DPC Time"
IdleBreakEventsTotal = "Idle Break Events/sec"
IdleTimeSeconds = "% Idle Time"
InterruptsTotal = "Interrupts/sec"
InterruptTimeSeconds = "% Interrupt Time"
ParkingStatus = "Parking Status"
PerformanceLimitPercent = "% Performance Limit"
PriorityTimeSeconds = "% Priority Time"
PrivilegedTimeSeconds = "% Privileged Time"
PrivilegedUtilitySeconds = "% Privileged Utility"
ProcessorFrequencyMHz = "Processor Frequency"
ProcessorPerformance = "% Processor Performance"
ProcessorTimeSeconds = "% Processor Time"
ProcessorUtilityRate = "% Processor Utility"
UserTimeSeconds = "% User Time"
)

View File

@@ -3,12 +3,15 @@
package cpu
import (
"fmt"
"log/slog"
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/pkg/perfdata"
"github.com/prometheus-community/windows_exporter/pkg/perflib"
"github.com/prometheus-community/windows_exporter/pkg/types"
"github.com/prometheus-community/windows_exporter/pkg/utils"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
@@ -22,6 +25,8 @@ var ConfigDefaults = Config{}
type Collector struct {
config Config
perfDataCollector *perfdata.Collector
logicalProcessors *prometheus.Desc
cStateSecondsTotal *prometheus.Desc
timeTotal *prometheus.Desc
@@ -59,6 +64,10 @@ func (c *Collector) GetName() string {
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
if utils.PDHEnabled() {
return []string{}, nil
}
return []string{"Processor Information"}, nil
}
@@ -67,6 +76,41 @@ func (c *Collector) Close(_ *slog.Logger) error {
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
if utils.PDHEnabled() {
counters := []string{
C1TimeSeconds,
C2TimeSeconds,
C3TimeSeconds,
C1TransitionsTotal,
C2TransitionsTotal,
C3TransitionsTotal,
ClockInterruptsTotal,
DPCsQueuedTotal,
DPCTimeSeconds,
IdleBreakEventsTotal,
IdleTimeSeconds,
InterruptsTotal,
InterruptTimeSeconds,
ParkingStatus,
PerformanceLimitPercent,
PriorityTimeSeconds,
PrivilegedTimeSeconds,
PrivilegedUtilitySeconds,
ProcessorFrequencyMHz,
ProcessorPerformance,
ProcessorTimeSeconds,
ProcessorUtilityRate,
UserTimeSeconds,
}
var err error
c.perfDataCollector, err = perfdata.NewCollector("Processor Information", []string{"*"}, counters)
if err != nil {
return fmt.Errorf("failed to create Processor Information collector: %w", err)
}
}
c.logicalProcessors = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "logical_processor"),
"Total number of logical processors",
@@ -184,7 +228,11 @@ func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
return c.CollectFull(ctx, logger, ch)
if utils.PDHEnabled() {
return c.collectPDH(ch)
}
return c.collectFull(ctx, logger, ch)
}
type perflibProcessorInformation struct {
@@ -216,8 +264,7 @@ type perflibProcessorInformation struct {
UserTimeSeconds float64 `perflib:"% User Time"`
}
func (c *Collector) CollectFull(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
func (c *Collector) collectFull(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
data := make([]perflibProcessorInformation, 0)
err := perflib.UnmarshalObject(ctx.PerfObjects["Processor Information"], &data, logger)
@@ -364,3 +411,143 @@ func (c *Collector) CollectFull(ctx *types.ScrapeContext, logger *slog.Logger, c
return nil
}
func (c *Collector) collectPDH(ch chan<- prometheus.Metric) error {
data, err := c.perfDataCollector.Collect()
if err != nil {
return fmt.Errorf("failed to collect Processor Information metrics: %w", err)
}
var coreCount float64
for core, coreData := range data {
coreCount++
ch <- prometheus.MustNewConstMetric(
c.cStateSecondsTotal,
prometheus.CounterValue,
coreData[C1TimeSeconds].FirstValue,
core, "c1",
)
ch <- prometheus.MustNewConstMetric(
c.cStateSecondsTotal,
prometheus.CounterValue,
coreData[C2TimeSeconds].FirstValue,
core, "c2",
)
ch <- prometheus.MustNewConstMetric(
c.cStateSecondsTotal,
prometheus.CounterValue,
coreData[C3TimeSeconds].FirstValue,
core, "c3",
)
ch <- prometheus.MustNewConstMetric(
c.timeTotal,
prometheus.CounterValue,
coreData[IdleTimeSeconds].FirstValue,
core, "idle",
)
ch <- prometheus.MustNewConstMetric(
c.timeTotal,
prometheus.CounterValue,
coreData[InterruptTimeSeconds].FirstValue,
core, "interrupt",
)
ch <- prometheus.MustNewConstMetric(
c.timeTotal,
prometheus.CounterValue,
coreData[DPCTimeSeconds].FirstValue,
core, "dpc",
)
ch <- prometheus.MustNewConstMetric(
c.timeTotal,
prometheus.CounterValue,
coreData[PrivilegedTimeSeconds].FirstValue,
core, "privileged",
)
ch <- prometheus.MustNewConstMetric(
c.timeTotal,
prometheus.CounterValue,
coreData[UserTimeSeconds].FirstValue,
core, "user",
)
ch <- prometheus.MustNewConstMetric(
c.interruptsTotal,
prometheus.CounterValue,
coreData[InterruptsTotal].FirstValue,
core,
)
ch <- prometheus.MustNewConstMetric(
c.dpcsTotal,
prometheus.CounterValue,
coreData[DPCsQueuedTotal].FirstValue,
core,
)
ch <- prometheus.MustNewConstMetric(
c.clockInterruptsTotal,
prometheus.CounterValue,
coreData[ClockInterruptsTotal].FirstValue,
core,
)
ch <- prometheus.MustNewConstMetric(
c.idleBreakEventsTotal,
prometheus.CounterValue,
coreData[IdleBreakEventsTotal].FirstValue,
core,
)
ch <- prometheus.MustNewConstMetric(
c.parkingStatus,
prometheus.GaugeValue,
coreData[ParkingStatus].FirstValue,
core,
)
ch <- prometheus.MustNewConstMetric(
c.processorFrequencyMHz,
prometheus.GaugeValue,
coreData[ProcessorFrequencyMHz].FirstValue,
core,
)
ch <- prometheus.MustNewConstMetric(
c.processorPerformance,
prometheus.CounterValue,
coreData[ProcessorPerformance].FirstValue,
core,
)
ch <- prometheus.MustNewConstMetric(
c.processorMPerf,
prometheus.CounterValue,
coreData[ProcessorPerformance].SecondValue,
core,
)
ch <- prometheus.MustNewConstMetric(
c.processorRTC,
prometheus.CounterValue,
coreData[ProcessorUtilityRate].SecondValue,
core,
)
ch <- prometheus.MustNewConstMetric(
c.processorUtility,
prometheus.CounterValue,
coreData[ProcessorUtilityRate].FirstValue,
core,
)
ch <- prometheus.MustNewConstMetric(
c.processorPrivilegedUtility,
prometheus.CounterValue,
coreData[PrivilegedUtilitySeconds].FirstValue,
core,
)
}
ch <- prometheus.MustNewConstMetric(
c.logicalProcessors,
prometheus.GaugeValue,
coreCount,
)
return nil
}

View File

@@ -228,14 +228,14 @@ func (p *Prometheus) execute(name string, c Collector, ctx *types.ScrapeContext,
}
if err != nil {
p.logger.Error(fmt.Sprintf("collector %s failed after %s", name, p.maxScrapeDuration),
p.logger.Error(fmt.Sprintf("collector %s failed after %s", name, duration),
slog.Any("err", err),
)
return failed
}
p.logger.Error(fmt.Sprintf("collector %s succeeded after %s", name, p.maxScrapeDuration))
p.logger.Info(fmt.Sprintf("collector %s succeeded after %s", name, duration))
return success
}

View File

@@ -122,7 +122,7 @@ func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error {
c.state = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "state"),
"The state of the service (State)",
[]string{"name", "status"},
[]string{"name", "state"},
nil,
)
c.startMode = prometheus.NewDesc(

View File

@@ -186,6 +186,7 @@ func (c *Collector) Collect() (map[string]map[string]CounterValues, error) {
// This is a workaround for the issue with the elapsed time counter type.
// Source: https://github.com/prometheus-community/windows_exporter/pull/335/files#diff-d5d2528f559ba2648c2866aec34b1eaa5c094dedb52bd0ff22aa5eb83226bd8dR76-R83
// Ref: https://learn.microsoft.com/en-us/windows/win32/perfctrs/calculating-counter-values
switch counter.Type {
case PERF_ELAPSED_TIME:

View File

@@ -3,6 +3,7 @@
package utils
import (
"os"
"strings"
"github.com/prometheus-community/windows_exporter/pkg/types"
@@ -26,3 +27,11 @@ 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
}

View File

@@ -298,13 +298,13 @@ windows_service_start_mode{name="Themes",start_mode="manual"} 0
windows_service_start_mode{name="Themes",start_mode="system"} 0
# HELP windows_service_state The state of the service (State)
# TYPE windows_service_state gauge
windows_service_state{name="Themes",status="continue pending"} 0
windows_service_state{name="Themes",status="pause pending"} 0
windows_service_state{name="Themes",status="paused"} 0
windows_service_state{name="Themes",status="running"} 1
windows_service_state{name="Themes",status="start pending"} 0
windows_service_state{name="Themes",status="stop pending"} 0
windows_service_state{name="Themes",status="stopped"} 0
windows_service_state{name="Themes",state="continue pending"} 0
windows_service_state{name="Themes",state="pause pending"} 0
windows_service_state{name="Themes",state="paused"} 0
windows_service_state{name="Themes",state="running"} 1
windows_service_state{name="Themes",state="start pending"} 0
windows_service_state{name="Themes",state="stop pending"} 0
windows_service_state{name="Themes",state="stopped"} 0
# HELP windows_system_context_switches_total Total number of context switches (WMI source: PerfOS_System.ContextSwitchesPersec)
# TYPE windows_system_context_switches_total counter
# HELP windows_system_exception_dispatches_total Total number of exceptions dispatched (WMI source: PerfOS_System.ExceptionDispatchesPersec)