Compare commits

..

25 Commits

Author SHA1 Message Date
Jan-Otto Kröpke
367fae95c4 mscluster: restore support for Windows Server 2016-2019 (#1882)
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
2025-02-12 21:03:24 +01:00
Jan-Otto Kröpke
96ffc3bf3f config: multiple web.listen-address args results into an error, if --config.file is defined. (#1876)
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
2025-02-12 20:30:12 +01:00
Jan-Otto Kröpke
285c4cc5ea feat: windows_exporter uses own event log source to correctly format messages. (#1873)
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
Signed-off-by: Jan-Otto Kröpke <github@jkroepke.de>
2025-02-10 18:57:31 +01:00
Jan-Otto Kröpke
f07aceb0dd cs: fix metric description (#1881)
Signed-off-by: Jan-Otto Kröpke <github@jkroepke.de>
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
2025-02-10 18:23:35 +01:00
Jan-Otto Kröpke
dcacce4577 fix: sign binaries (#1878)
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
2025-02-09 22:34:55 +01:00
Jan-Otto Kröpke
fc5b3051fa feat: sign binaries (#1875)
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
2025-02-08 20:04:37 +01:00
Jan-Otto Kröpke
1b2958a7cc fix: slow stop if run as service (#1870)
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
2025-02-07 22:26:09 +01:00
Jan-Otto Kröpke
a20e1854d1 netframework: fix MI_RESULT_INVALID_QUERY (#1862)
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
2025-02-02 09:14:56 +01:00
Jan-Otto Kröpke
fe21cb44f6 installer: add validation for EXTRA_FLAGS (#1867)
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
2025-02-02 09:14:45 +01:00
Jan-Otto Kröpke
71ec0bd6a3 process: negative values with windows_process_start_time (#1857)
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
2025-01-30 20:30:08 +01:00
dependabot[bot]
8bff623393 chore(deps): bump actions/stale from 9.0.0 to 9.1.0 (#1860) 2025-01-27 20:14:42 +01:00
dependabot[bot]
3eabd0a00c chore(deps): bump github.com/bmatcuk/doublestar/v4 from 4.8.0 to 4.8.1 (#1859) 2025-01-27 20:14:07 +01:00
Jan-Otto Kröpke
73186cde48 installer: force close application on uninstall (#1854)
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
2025-01-23 16:43:36 +01:00
Jan-Otto Kröpke
25e04fc947 service: re-use buffer for return all services (#1853)
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
2025-01-23 16:43:17 +01:00
Jan-Otto Kröpke
6b7201856c fix: process priority setting (#1852)
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
2025-01-23 14:56:49 +01:00
Jan-Otto Kröpke
608b83cfd8 config: deprecate remote http configuration (#1849)
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
2025-01-21 08:59:08 +01:00
Hamed Mansouri
40a42ca457 docs: fix typo in README.md (#1844)
Signed-off-by: Hamed Mansouri <hamed0381@gmail.com>
2025-01-21 00:47:03 +01:00
dependabot[bot]
423c8a787e chore(deps): bump golang.org/x/net from 0.32.0 to 0.33.0 (#1848)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-20 14:41:21 +00:00
dependabot[bot]
6cefbed7f7 chore(deps): bump github.com/bmatcuk/doublestar/v4 from 4.7.1 to 4.8.0 (#1846)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-01-20 15:35:43 +01:00
Jan-Otto Kröpke
5836a7dbf2 dhcp: add dhcp scope stats (#1840)
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
2025-01-20 12:03:03 +01:00
Jan-Otto Kröpke
d31ce0507c fix: Windows 11/Windows Server 2025 service compatibility (#1841)
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
2025-01-19 11:37:05 +01:00
Jan-Otto Kröpke
faa98d2708 Update LICENSE
Signed-off-by: Jan-Otto Kröpke <github@jkroepke.de>
2025-01-19 11:21:40 +01:00
Jan-Otto Kröpke
f0f3d0d96e net: fix sanitize # on the nic label for windows_net_nic_address_info (#1839)
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
2025-01-17 13:34:21 +01:00
Jan-Otto Kröpke
f73a74b678 netframework: add --collector.netframework.enabled CLI flag (#1833)
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
2025-01-15 21:23:50 +01:00
Jan-Otto Kröpke
b6f89ad92f docs: add CONTRIBUTING.md docs (#1834)
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
2025-01-15 21:23:42 +01:00
57 changed files with 1601 additions and 570 deletions

40
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@@ -0,0 +1,40 @@
<!--
Please give your PR a title in the form "area: short description". For example "cpu: reduce usage by 95%" or "docs: fix typo in installation.md".
If your PR is to fix an issue, put "Fixes #issue-number" in the description.
Don't forget!
- Please sign CNCF's Developer Certificate of Origin and sign-off your commits by adding the -s / --signoff flag to `git commit`. See https://github.com/apps/dco for more information.
- If the PR adds or changes a behaviour or fixes a bug of an exported API it would need a unit/e2e test.
- Performance improvements would need a benchmark test to prove it.
- All comments should start with a capital letter and end with a full stop.
-->
#### What this PR does / why we need it
#### Which issue this PR fixes
*(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close that issue when PR gets merged)*: fixes #
- fixes #
#### Special notes for your reviewer
#### Particularly user-facing changes
#### Checklist
Complete these before marking the PR as `ready to review`:
<!-- [Place an '[x]' (no spaces) in all applicable fields.] -->
- [ ] [DCO](https://github.com/prometheus-community/helm-charts/blob/main/CONTRIBUTING.md#sign-off-your-work) signed
- [ ] The PR title has a summary of the changes and the area they affect
- [ ] The PR body has a summary to reflect any significant (and particularly user-facing) changes introduced by this PR

View File

@@ -22,6 +22,7 @@ env:
jobs:
build:
runs-on: windows-2022
environment: build
steps:
- uses: actions/checkout@v4
with:
@@ -68,6 +69,40 @@ jobs:
Get-ChildItem -Path output
- name: Sign build artifacts
if: ${{ (github.event_name != 'pull_request' && github.repository == 'prometheus-community/windows_exporter') || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == 'prometheus-community/windows_exporter') }}
run: |
$ErrorActionPreference = "Stop"
$Version = Get-Content VERSION
$b64 = $env:CODE_SIGN_KEY
$filename = 'windows_exporter_CodeSign.pfx'
$bytes = [Convert]::FromBase64String($b64)
[IO.File]::WriteAllBytes($filename, $bytes)
$basePath = "C:\Program Files (x86)\Windows Kits\10\bin"
$latestSigntool = Get-ChildItem -Path $basePath -Directory |
Where-Object { $_.Name -match "^\d+\.\d+\.\d+\.\d+$" } |
Sort-Object { [Version]$_.Name } -Descending |
Select-Object -First 1 |
ForEach-Object { Join-Path $_.FullName "x64\signtool.exe" }
if (Test-Path $latestSigntool) {
Write-Output $latestSigntool
} else {
Write-Output "signtool.exe not found"
}
foreach($Arch in "amd64", "arm64") {
& $latestSigntool sign /v /tr "http://timestamp.digicert.com" /d "Prometheus exporter for Windows machines" /td SHA256 /fd SHA256 /a /f "windows_exporter_CodeSign.pfx" /p $env:CODE_SIGN_PASSWORD "output\windows_exporter-$Version-$Arch.exe"
}
rm windows_exporter_CodeSign.pfx
env:
CODE_SIGN_KEY: ${{ secrets.CODE_SIGN_KEY }}
CODE_SIGN_PASSWORD: ${{ secrets.CODE_SIGN_PASSWORD }}
- name: Build Release Artifacts
run: |
$ErrorActionPreference = "Stop"
@@ -83,6 +118,40 @@ jobs:
promu checksum output\
- name: Sign installer artifacts
if: ${{ (github.event_name != 'pull_request' && github.repository == 'prometheus-community/windows_exporter') || (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == 'prometheus-community/windows_exporter') }}
run: |
$ErrorActionPreference = "Stop"
$Version = Get-Content VERSION
$b64 = $env:CODE_SIGN_KEY
$filename = 'windows_exporter_CodeSign.pfx'
$bytes = [Convert]::FromBase64String($b64)
[IO.File]::WriteAllBytes($filename, $bytes)
$basePath = "C:\Program Files (x86)\Windows Kits\10\bin"
$latestSigntool = Get-ChildItem -Path $basePath -Directory |
Where-Object { $_.Name -match "^\d+\.\d+\.\d+\.\d+$" } |
Sort-Object { [Version]$_.Name } -Descending |
Select-Object -First 1 |
ForEach-Object { Join-Path $_.FullName "x64\signtool.exe" }
if (Test-Path $latestSigntool) {
Write-Output $latestSigntool
} else {
Write-Output "signtool.exe not found"
}
foreach($Arch in "amd64", "arm64") {
& $latestSigntool sign /v /tr "http://timestamp.digicert.com" /d "Prometheus exporter for Windows machines" /td SHA256 /fd SHA256 /a /f "windows_exporter_CodeSign.pfx" /p $env:CODE_SIGN_PASSWORD "output\windows_exporter-$Version-$Arch.msi"
}
rm windows_exporter_CodeSign.pfx
env:
CODE_SIGN_KEY: ${{ secrets.CODE_SIGN_KEY }}
CODE_SIGN_PASSWORD: ${{ secrets.CODE_SIGN_PASSWORD }}
- name: Upload Artifacts
uses: actions/upload-artifact@v4
with:
@@ -103,6 +172,9 @@ jobs:
runs-on: ubuntu-latest
needs:
- build
env:
DOCKER_BUILD_SUMMARY: false
DOCKER_BUILD_RECORD_UPLOAD: false
steps:
- uses: actions/checkout@v4
with:
@@ -165,3 +237,4 @@ jobs:
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
platforms: windows/amd64
annotations: ${{ steps.meta.outputs.labels }}

View File

@@ -11,7 +11,7 @@ jobs:
if: github.repository_owner == 'prometheus' || github.repository_owner == 'prometheus-community' # Don't run this workflow on forks.
runs-on: ubuntu-latest
steps:
- uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0
- uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
# opt out of defaults to avoid marking issues as stale and closing them

View File

@@ -11,7 +11,7 @@ jobs:
if: github.repository_owner == 'prometheus' || github.repository_owner == 'prometheus-community' # Don't run this workflow on forks.
runs-on: ubuntu-latest
steps:
- uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0
- uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
# opt out of defaults to avoid marking issues as stale and closing them

82
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,82 @@
# Contributing
windows_exporter uses GitHub to manage reviews of pull requests.
* If you are a new contributor see: [Steps to Contribute](#steps-to-contribute)
* If you have a trivial fix or improvement, go ahead and create a pull request,
addressing (with `@...`) a suitable maintainer of this repository (see
[MAINTAINERS.md](MAINTAINERS.md)) in the description of the pull request.
* If you plan to do something more involved, first discuss your ideas
as [github issue](https://github.com/prometheus-community/windows_exporter/issues).
This will avoid unnecessary work and surely give you and us a good deal
of inspiration. New collectors are unlikely to be accepted, since the
`performancecounter` collector is the preferred way to collect metrics.
* Relevant coding style guidelines are the [Go Code Review
Comments](https://code.google.com/p/go-wiki/wiki/CodeReviewComments)
and the _Formatting and style_ section of Peter Bourgon's [Go: Best
Practices for Production
Environments](https://peter.bourgon.org/go-in-production/#formatting-and-style).
gofmt and [golangci-lint](https://github.com/golangci/golangci-lint) are your friends.
* Be sure to sign off on the [DCO](https://github.com/probot/dco#how-it-works).
## Steps to Contribute
Should you wish to work on an issue, please claim it first by commenting on the GitHub issue that you want to work on it. This is to prevent duplicated efforts from contributors on the same issue.
For quickly compiling and testing your changes do:
```bash
# For building.
go build -o windows_exporter.exe ./cmd/windows_exporter/
./windows_exporter.exe
# For testing.
make test # Make sure all the tests pass before you commit and push :)
```
To run a collection of Go linters through [`golangci-lint`](https://github.com/golangci/golangci-lint), do:
```bash
make lint
```
If it reports an issue and you think that the warning needs to be disregarded or is a false-positive, you can add a special comment `//nolint:linter1[,linter2,...]` before the offending line. Use this sparingly though, fixing the code to comply with the linter's recommendation is in general the preferred course of action. See [this section of the golangci-lint documentation](https://golangci-lint.run/usage/false-positives/#nolint-directive) for more information.
All our issues are regularly tagged so that you can also filter down the issues involving the components you want to work on. For our labeling policy refer [the wiki page](https://github.com/prometheus/prometheus/wiki/Label-Names-and-Descriptions).
## Pull Request Checklist
* Branch from the main branch and, if needed, rebase to the current main branch before submitting your pull request. If it doesn't merge cleanly with main you may be asked to rebase your changes.
* Commits should be as small as possible, while ensuring that each commit is correct independently (i.e., each commit should compile and pass tests).
* The PR title should be of the format: `subsystem: what this PR does` (for example, `cpu: Add support for thing` or `docs: fix typo`).
* If your patch is not getting reviewed or you need a specific person to review it, you can @-reply a reviewer asking for a review in the pull request or a comment.
* Add tests relevant to the fixed bug or new feature.
## Dependency management
The Prometheus project uses [Go modules](https://golang.org/cmd/go/#hdr-Modules__module_versions__and_more) to manage dependencies on external packages.
To add or update a new dependency, use the `go get` command:
```bash
# Pick the latest tagged release.
go get example.com/some/module/pkg@latest
# Pick a specific version.
go get example.com/some/module/pkg@vX.Y.Z
```
Tidy up the `go.mod` and `go.sum` files:
```bash
# The GO111MODULE variable can be omitted when the code isn't located in GOPATH.
GO111MODULE=on go mod tidy
```
You have to commit the changes to `go.mod` and `go.sum` before submitting the pull request.

View File

@@ -1,6 +1,7 @@
The MIT License (MIT)
Copyright (c) 2016 Martin Lindhe
Copyright (c) 2021 The Prometheus Authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -88,7 +88,7 @@ windows_exporter accepts flags to configure certain behaviours. The ones configu
| `--web.listen-address` | host:port for exporter. | `:9182` |
| `--telemetry.path` | URL path for surfacing collected metrics. | `/metrics` |
| `--telemetry.max-requests` | Maximum number of concurrent requests. 0 to disable. | `5` |
| `--collectors.enabled` | Comma-separated list of collectors to use. Use `[defaults]` as a placeholder which gets expanded containing all the collectors enabled by default." | `[defaults]` |
| `--collectors.enabled` | Comma-separated list of collectors to use. Use `[defaults]` as a placeholder which gets expanded containing all the collectors enabled by default. | `[defaults]` |
| `--collectors.print` | If true, print available collectors and exit. | |
| `--scrape.timeout-margin` | Seconds to subtract from the timeout allowed by the client. Tune to allow for overhead or high loads. | `0.5` |
| `--web.config.file` | A [web config][web_config] for setting up TLS and Auth | None |
@@ -100,6 +100,9 @@ windows_exporter accepts flags to configure certain behaviours. The ones configu
The latest release can be downloaded from the [releases page](https://github.com/prometheus-community/windows_exporter/releases).
All binaries and installation packages are signed with an self-signed certificate. The public key can be found [here](https://github.com/prometheus-community/windows_exporter/blob/master/installer/codesign.cer).
Once import into the trusted root certificate store, the binaries and installation packages will be trusted.
Each release provides a .msi installer. The installer will setup the windows_exporter as a Windows service, as well as create an exception in the Windows Firewall.
If the installer is run without any parameters, the exporter will run with default settings for enabled collectors, ports, etc.

View File

@@ -0,0 +1,151 @@
// Copyright 2025 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"fmt"
"os"
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/svc"
"golang.org/x/sys/windows/svc/eventlog"
)
const serviceName = "windows_exporter"
//nolint:gochecknoglobals
var (
// exitCodeCh is a channel to send an exit code from the main function to the service manager.
// Additionally, if there is an error in the IsService var declaration,
// the exit code is sent to the service manager as well.
exitCodeCh = make(chan int, 1)
// stopCh is a channel to send a signal to the service manager that the service is stopping.
stopCh = make(chan struct{})
// serviceManagerFinishedCh is a channel to send a signal to the main function that the service manager has stopped the service.
serviceManagerFinishedCh = make(chan struct{})
)
// IsService variable declaration allows initiating time-sensitive components like registering the Windows service
// as early as possible in the startup process.
// init functions are called in the order they are declared, so this package should be imported first.
//
// Ref: https://github.com/prometheus-community/windows_exporter/issues/551#issuecomment-1220774835
//
// Declare imports on this package should be avoided where possible.
// var declaration run before init function, so it guarantees that windows_exporter respond to service manager early
// and avoid timeout.
// The order of the var declaration and init functions depends on the filename as well. The filename should be 0_service.go
// Ref: https://medium.com/@markbates/go-init-order-dafa89fcef22
//
//nolint:gochecknoglobals
var IsService = func() bool {
var err error
isService, err := svc.IsWindowsService()
if err != nil {
//nolint:gosec
_ = os.WriteFile("C:\\Program Files\\windows_exporter\\start-service.error.log", []byte(fmt.Sprintf("failed to detect service: %v", err)), 0o644)
return false
}
if !isService {
return false
}
defer func() {
go func() {
err := svc.Run(serviceName, &windowsExporterService{})
if err != nil {
_ = logToEventToLog(windows.EVENTLOG_ERROR_TYPE, fmt.Sprintf("failed to start service: %v", err))
}
serviceManagerFinishedCh <- struct{}{}
}()
}()
if err := logToEventToLog(windows.EVENTLOG_INFORMATION_TYPE, "attempting to start exporter service"); err != nil {
//nolint:gosec
_ = os.WriteFile("C:\\Program Files\\windows_exporter\\start-service.error.log", []byte(fmt.Sprintf("failed sent log to event log: %v", err)), 0o644)
exitCodeCh <- 2
}
return true
}()
type windowsExporterService struct{}
// Execute is the entry point for the Windows service manager.
func (s *windowsExporterService) Execute(_ []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (bool, uint32) {
changes <- svc.Status{State: svc.StartPending}
changes <- svc.Status{State: svc.Running, Accepts: svc.AcceptStop | svc.AcceptShutdown}
for {
select {
case exitCodeCh := <-exitCodeCh:
// Stop the service if an exit code from the main function is received.
changes <- svc.Status{State: svc.StopPending}
return true, uint32(exitCodeCh)
case c := <-r:
// Handle the service control request.
switch c.Cmd {
case svc.Interrogate:
changes <- c.CurrentStatus
case svc.Stop, svc.Shutdown:
// Stop the service if a stop or shutdown request is received.
_ = logToEventToLog(windows.EVENTLOG_INFORMATION_TYPE, "service stop received")
changes <- svc.Status{State: svc.StopPending}
// Send a signal to the main function to stop the service.
stopCh <- struct{}{}
// Wait for the main function to stop the service.
return false, uint32(<-exitCodeCh)
default:
_ = logToEventToLog(windows.EVENTLOG_ERROR_TYPE, fmt.Sprintf("unexpected control request #%d", c))
}
}
}
}
// logToEventToLog logs a message to the Windows event log.
func logToEventToLog(eType uint16, msg string) error {
eventLog, err := eventlog.Open(serviceName)
if err != nil {
return fmt.Errorf("failed to open event log: %w", err)
}
defer func(eventLog *eventlog.Log) {
_ = eventLog.Close()
}(eventLog)
switch eType {
case windows.EVENTLOG_ERROR_TYPE:
err = eventLog.Error(102, msg)
case windows.EVENTLOG_WARNING_TYPE:
err = eventLog.Warning(101, msg)
case windows.EVENTLOG_INFORMATION_TYPE:
err = eventLog.Info(100, msg)
}
if err != nil {
return fmt.Errorf("error report event: %w", err)
}
return nil
}

View File

@@ -17,12 +17,7 @@
package main
//goland:noinspection GoUnsortedImport
//nolint:gofumpt
import (
// Its important that we do these first so that we can register with the Windows service control ASAP to avoid timeouts.
"github.com/prometheus-community/windows_exporter/internal/windowsservice"
"context"
"errors"
"fmt"
@@ -55,14 +50,14 @@ func main() {
exitCode := run()
// If we are running as a service, we need to signal the service control manager that we are done.
if !windowsservice.IsService {
if !IsService {
os.Exit(exitCode)
}
windowsservice.ExitCodeCh <- exitCode
exitCodeCh <- exitCode
// Wait for the service control manager to signal that we are done.
<-windowsservice.StopCh
<-serviceManagerFinishedCh
}
func run() int {
@@ -114,7 +109,7 @@ func run() int {
logFile := &log.AllowedFile{}
_ = logFile.Set("stdout")
if windowsservice.IsService {
if IsService {
_ = logFile.Set("eventlog")
}
@@ -168,11 +163,6 @@ func run() int {
return 1
}
// NOTE: This is temporary fix for issue #1092, calling kingpin.Parse
// twice makes slices flags duplicate its value, this clean up
// the first parse before the second call.
*webConfig.WebListenAddresses = (*webConfig.WebListenAddresses)[1:]
// Parse flags once more to include those discovered in configuration file(s).
if _, err = app.Parse(os.Args[1:]); err != nil {
logger.ErrorContext(ctx, "failed to parse CLI args from YAML file",
@@ -182,6 +172,12 @@ func run() int {
return 1
}
// NOTE: This is temporary fix for issue #1092, calling kingpin.Parse
// twice makes slices flags duplicate its value, this clean up
// the first parse before the second call.
slices.Sort(*webConfig.WebListenAddresses)
*webConfig.WebListenAddresses = slices.Clip(slices.Compact(*webConfig.WebListenAddresses))
logger, err = log.New(logConfig)
if err != nil {
//nolint:sloglint // we do not have an logger yet
@@ -276,7 +272,7 @@ func run() int {
select {
case <-ctx.Done():
logger.Info("Shutting down windows_exporter via kill signal")
case <-windowsservice.StopCh:
case <-stopCh:
logger.Info("Shutting down windows_exporter via service control")
case err := <-errCh:
if err != nil {
@@ -330,7 +326,7 @@ func setPriorityWindows(logger *slog.Logger, pid int, priority string) error {
winPriority, ok := priorityStringToInt[priority]
// Only set process priority if a non-default and valid value has been set
if !ok || winPriority != windows.NORMAL_PRIORITY_CLASS {
if !ok || winPriority == windows.NORMAL_PRIORITY_CLASS {
return nil
}

View File

@@ -2,49 +2,177 @@
The dhcp collector exposes DHCP Server metrics
|||
-|-
Metric name prefix | `dhcp`
Data source | Perflib
Classes | `DHCP Server`
Enabled by default? | No
| | |
|---------------------|---------------|
| Metric name prefix | `dhcp` |
| Data source | Perflib |
| Classes | `DHCP Server` |
| Enabled by default? | No |
## Flags
None
### `--collector.dhcp.enabled`
Comma-separated list of collectors to use. Defaults to all, if not specified.
## Metrics
Name | Description | Type | Labels
-----|-------------|------|-------
`packets_received_total` | Total number of packets received by the DHCP server | counter | None
`duplicates_dropped_total` | Total number of duplicate packets received by the DHCP server | counter | None
`packets_expired_total` | Total number of packets expired in the DHCP server message queue | counter | None
`active_queue_length` | Number of packets in the processing queue of the DHCP server | gauge | None
`conflict_check_queue_length` | Number of packets in the DHCP server queue waiting on conflict detection (ping) | gauge | None
`discovers_total` | Total DHCP Discovers received by the DHCP server | counter | None
`offers_total` | Total DHCP Offers sent by the DHCP server | counter | None
`requests_total` | Total DHCP Requests received by the DHCP server | counter | None
`informs_total` | Total DHCP Informs received by the DHCP server | counter | None
`acks_total` | Total DHCP Acks sent by the DHCP server | counter | None
`nacks_total` | Total DHCP Nacks sent by the DHCP server | counter | None
`declines_total` | Total DHCP Declines received by the DHCP server | counter | None
`releases_total` | Total DHCP Releases received by the DHCP server | counter | None
`offer_queue_length` | Number of packets in the offer queue of the DHCP server | gauge | None
`denied_due_to_match_total` | Total number of DHCP requests denied, based on matches from the Deny List | gauge | None
`denied_due_to_nonmatch_total` | Total number of DHCP requests denied, based on non-matches from the Allow List | gauge | None
`failover_bndupd_sent_total` | Number of DHCP failover Binding Update messages sent | counter | None
`failover_bndupd_received_total` | Number of DHCP failover Binding Update messages received | counter | None
`failover_bndack_sent_total` | Number of DHCP failover Binding Ack messages sent | counter | None
`failover_bndack_received_total` | Number of DHCP failover Binding Ack messages received | counter | None
`failover_bndupd_pending_in_outbound_queue` | Number of pending outbound DHCP failover Binding Update messages | counter | None
`failover_transitions_communicationinterrupted_state_total` | Total number of transitions into COMMUNICATION INTERRUPTED state | counter | None
`failover_transitions_partnerdown_state_total` | Total number of transitions into PARTNER DOWN state | counter | None
`failover_transitions_recover_total` | Total number of transitions into RECOVER state | counter | None
`failover_bndupd_dropped_total` | Total number of DHCP faileover Binding Updates dropped | counter | None
| Name | Description | Type | Labels |
|--------------------------------------------------------------------------|--------------------------------------------------------------------------------|---------|-----------------------------------------------------|
| `windows_dhcp_ack_total` | Total DHCP Acks sent by the DHCP server | counter | None |
| `windows_dhcp_denied_due_to_match_total` | Total number of DHCP requests denied, based on matches from the Deny List | gauge | None |
| `windows_dhcp_denied_due_to_nonmatch_total` | Total number of DHCP requests denied, based on non-matches from the Allow List | gauge | None |
| `windows_dhcp_declines_total` | Total DHCP Declines received by the DHCP server | counter | None |
| `windows_dhcp_discovers_total` | Total DHCP Discovers received by the DHCP server | counter | None |
| `windows_dhcp_failover_bndack_received_total` | Number of DHCP failover Binding Ack messages received | counter | None |
| `windows_dhcp_failover_bndack_sent_total` | Number of DHCP failover Binding Ack messages sent | counter | None |
| `windows_dhcp_failover_bndupd_dropped_total` | Total number of DHCP failover Binding Updates dropped | counter | None |
| `windows_dhcp_failover_bndupd_received_total` | Number of DHCP failover Binding Update messages received | counter | None |
| `windows_dhcp_failover_bndupd_sent_total` | Number of DHCP failover Binding Update messages sent | counter | None |
| `windows_dhcp_failover_bndupd_pending_in_outbound_queue` | Number of pending outbound DHCP failover Binding Update messages | counter | None |
| `windows_dhcp_failover_transitions_communicationinterrupted_state_total` | Total number of transitions into COMMUNICATION INTERRUPTED state | counter | None |
| `windows_dhcp_failover_transitions_partnerdown_state_total` | Total number of transitions into PARTNER DOWN state | counter | None |
| `windows_dhcp_failover_transitions_recover_total` | Total number of transitions into RECOVER state | counter | None |
| `windows_dhcp_informs_total` | Total DHCP Informs received by the DHCP server | counter | None |
| `windows_dhcp_nacks_total` | Total DHCP Nacks sent by the DHCP server | counter | None |
| `windows_dhcp_offers_total` | Total DHCP Offers sent by the DHCP server | counter | None |
| `windows_dhcp_packets_expired_total` | Total number of packets expired in the DHCP server message queue | counter | None |
| `windows_dhcp_packets_received_total` | Total number of packets received by the DHCP server | counter | None |
| `windows_dhcp_pending_offers_total` | Total number of pending offers in the DHCP server | counter | None |
| `windows_dhcp_releases_total` | Total DHCP Releases received by the DHCP server | counter | None |
| `windows_dhcp_requests_total` | Total DHCP Requests received by the DHCP server | counter | None |
| `windows_dhcp_scope_addresses_free_on_this_server` | DHCP Scope free addresses on this server | gauge | `scope` |
| `windows_dhcp_scope_addresses_free_on_partner_server` | DHCP Scope free addresses on partner server | gauge | `scope` |
| `windows_dhcp_scope_addresses_free` | DHCP Scope free addresses | gauge | `scope` |
| `windows_dhcp_scope_addresses_in_use_on_this_server` | DHCP Scope addresses in use on this server | gauge | `scope` |
| `windows_dhcp_scope_addresses_in_use_on_partner_server` | DHCP Scope addresses in use on partner server | gauge | `scope` |
| `windows_dhcp_scope_addresses_in_use` | DHCP Scope addresses in use | gauge | `scope` |
| `windows_dhcp_scope_info` | DHCP Scope information | gauge | `name`, `superscope_name`, `superscope_id`, `scope` |
| `windows_dhcp_scope_pending_offers` | DHCP Scope pending offers | gauge | `scope` |
| `windows_dhcp_scope_reserved_address` | DHCP Scope reserved addresses | gauge | `scope` |
| `windows_dhcp_scope_state` | DHCP Scope state | gauge | `scope`, `state` |
### Example metric
_This collector does not yet have explained examples, we would appreciate your help adding them!_
```
# HELP windows_dhcp_acks_total Total DHCP Acks sent by the DHCP server (AcksTotal)
# TYPE windows_dhcp_acks_total counter
windows_dhcp_acks_total 0
# HELP windows_dhcp_active_queue_length Number of packets in the processing queue of the DHCP server (ActiveQueueLength)
# TYPE windows_dhcp_active_queue_length gauge
windows_dhcp_active_queue_length 0
# HELP windows_dhcp_conflict_check_queue_length Number of packets in the DHCP server queue waiting on conflict detection (ping). (ConflictCheckQueueLength)
# TYPE windows_dhcp_conflict_check_queue_length gauge
windows_dhcp_conflict_check_queue_length 0
# HELP windows_dhcp_declines_total Total DHCP Declines received by the DHCP server (DeclinesTotal)
# TYPE windows_dhcp_declines_total counter
windows_dhcp_declines_total 0
# HELP windows_dhcp_denied_due_to_match_total Total number of DHCP requests denied, based on matches from the Deny list (DeniedDueToMatch)
# TYPE windows_dhcp_denied_due_to_match_total counter
windows_dhcp_denied_due_to_match_total 0
# HELP windows_dhcp_denied_due_to_nonmatch_total Total number of DHCP requests denied, based on non-matches from the Allow list (DeniedDueToNonMatch)
# TYPE windows_dhcp_denied_due_to_nonmatch_total counter
windows_dhcp_denied_due_to_nonmatch_total 0
# HELP windows_dhcp_discovers_total Total DHCP Discovers received by the DHCP server (DiscoversTotal)
# TYPE windows_dhcp_discovers_total counter
windows_dhcp_discovers_total 0
# HELP windows_dhcp_duplicates_dropped_total Total number of duplicate packets received by the DHCP server (DuplicatesDroppedTotal)
# TYPE windows_dhcp_duplicates_dropped_total counter
windows_dhcp_duplicates_dropped_total 0
# HELP windows_dhcp_failover_bndack_received_total Number of DHCP fail over Binding Ack messages received (FailoverBndackReceivedTotal)
# TYPE windows_dhcp_failover_bndack_received_total counter
windows_dhcp_failover_bndack_received_total 0
# HELP windows_dhcp_failover_bndack_sent_total Number of DHCP fail over Binding Ack messages sent (FailoverBndackSentTotal)
# TYPE windows_dhcp_failover_bndack_sent_total counter
windows_dhcp_failover_bndack_sent_total 0
# HELP windows_dhcp_failover_bndupd_dropped_total Total number of DHCP fail over Binding Updates dropped (FailoverBndupdDropped)
# TYPE windows_dhcp_failover_bndupd_dropped_total counter
windows_dhcp_failover_bndupd_dropped_total 0
# HELP windows_dhcp_failover_bndupd_pending_in_outbound_queue Number of pending outbound DHCP fail over Binding Update messages (FailoverBndupdPendingOutboundQueue)
# TYPE windows_dhcp_failover_bndupd_pending_in_outbound_queue gauge
windows_dhcp_failover_bndupd_pending_in_outbound_queue 0
# HELP windows_dhcp_failover_bndupd_received_total Number of DHCP fail over Binding Update messages received (FailoverBndupdReceivedTotal)
# TYPE windows_dhcp_failover_bndupd_received_total counter
windows_dhcp_failover_bndupd_received_total 0
# HELP windows_dhcp_failover_bndupd_sent_total Number of DHCP fail over Binding Update messages sent (FailoverBndupdSentTotal)
# TYPE windows_dhcp_failover_bndupd_sent_total counter
windows_dhcp_failover_bndupd_sent_total 0
# HELP windows_dhcp_failover_transitions_communicationinterrupted_state_total Total number of transitions into COMMUNICATION INTERRUPTED state (FailoverTransitionsCommunicationinterruptedState)
# TYPE windows_dhcp_failover_transitions_communicationinterrupted_state_total counter
windows_dhcp_failover_transitions_communicationinterrupted_state_total 0
# HELP windows_dhcp_failover_transitions_partnerdown_state_total Total number of transitions into PARTNER DOWN state (FailoverTransitionsPartnerdownState)
# TYPE windows_dhcp_failover_transitions_partnerdown_state_total counter
windows_dhcp_failover_transitions_partnerdown_state_total 0
# HELP windows_dhcp_failover_transitions_recover_total Total number of transitions into RECOVER state (FailoverTransitionsRecoverState)
# TYPE windows_dhcp_failover_transitions_recover_total counter
windows_dhcp_failover_transitions_recover_total 0
# HELP windows_dhcp_informs_total Total DHCP Informs received by the DHCP server (InformsTotal)
# TYPE windows_dhcp_informs_total counter
windows_dhcp_informs_total 0
# HELP windows_dhcp_nacks_total Total DHCP Nacks sent by the DHCP server (NacksTotal)
# TYPE windows_dhcp_nacks_total counter
windows_dhcp_nacks_total 0
# HELP windows_dhcp_offer_queue_length Number of packets in the offer queue of the DHCP server (OfferQueueLength)
# TYPE windows_dhcp_offer_queue_length gauge
windows_dhcp_offer_queue_length 0
# HELP windows_dhcp_offers_total Total DHCP Offers sent by the DHCP server (OffersTotal)
# TYPE windows_dhcp_offers_total counter
windows_dhcp_offers_total 0
# HELP windows_dhcp_packets_expired_total Total number of packets expired in the DHCP server message queue (PacketsExpiredTotal)
# TYPE windows_dhcp_packets_expired_total counter
windows_dhcp_packets_expired_total 0
# HELP windows_dhcp_packets_received_total Total number of packets received by the DHCP server (PacketsReceivedTotal)
# TYPE windows_dhcp_packets_received_total counter
windows_dhcp_packets_received_total 0
# HELP windows_dhcp_releases_total Total DHCP Releases received by the DHCP server (ReleasesTotal)
# TYPE windows_dhcp_releases_total counter
windows_dhcp_releases_total 0
# HELP windows_dhcp_requests_total Total DHCP Requests received by the DHCP server (RequestsTotal)
# TYPE windows_dhcp_requests_total counter
windows_dhcp_requests_total 0
# HELP windows_dhcp_scope_addresses_free_total DHCP Scope free addresses
# TYPE windows_dhcp_scope_addresses_free_total gauge
windows_dhcp_scope_addresses_free_total{scope="10.11.12.0/25"} 0
windows_dhcp_scope_addresses_free_total{scope="172.16.0.0/24"} 0
windows_dhcp_scope_addresses_free_total{scope="192.168.0.0/24"} 231
# HELP windows_dhcp_scope_addresses_in_use_total DHCP Scope addresses in use
# TYPE windows_dhcp_scope_addresses_in_use_total gauge
windows_dhcp_scope_addresses_in_use_total{scope="10.11.12.0/25"} 0
windows_dhcp_scope_addresses_in_use_total{scope="172.16.0.0/24"} 0
windows_dhcp_scope_addresses_in_use_total{scope="192.168.0.0/24"} 0
# HELP windows_dhcp_scope_info DHCP Scope information
# TYPE windows_dhcp_scope_info gauge
windows_dhcp_scope_info{name="SUBSUPERSCOPE",scope="172.16.0.0/24",superscope_id="2",superscope_name="SUPERSCOPE"} 1
windows_dhcp_scope_info{name="TEST",scope="192.168.0.0/24",superscope_id="0",superscope_name=""} 1
windows_dhcp_scope_info{name="TEST2",scope="10.11.12.0/25",superscope_id="2",superscope_name="SUPERSCOPE"} 1
# HELP windows_dhcp_scope_pending_offers_total DHCP Scope pending offers
# TYPE windows_dhcp_scope_pending_offers_total gauge
windows_dhcp_scope_pending_offers_total{scope="10.11.12.0/25"} 0
windows_dhcp_scope_pending_offers_total{scope="172.16.0.0/24"} 0
windows_dhcp_scope_pending_offers_total{scope="192.168.0.0/24"} 0
# HELP windows_dhcp_scope_reserved_address_total DHCP Scope reserved addresses
# TYPE windows_dhcp_scope_reserved_address_total gauge
windows_dhcp_scope_reserved_address_total{scope="10.11.12.0/25"} 0
windows_dhcp_scope_reserved_address_total{scope="172.16.0.0/24"} 0
windows_dhcp_scope_reserved_address_total{scope="192.168.0.0/24"} 2
# HELP windows_dhcp_scope_state DHCP Scope state
# TYPE windows_dhcp_scope_state gauge
windows_dhcp_scope_state{scope="10.11.12.0/25",state="Disabled"} 1
windows_dhcp_scope_state{scope="10.11.12.0/25",state="DisabledSwitched"} 0
windows_dhcp_scope_state{scope="10.11.12.0/25",state="Enabled"} 0
windows_dhcp_scope_state{scope="10.11.12.0/25",state="EnabledSwitched"} 0
windows_dhcp_scope_state{scope="10.11.12.0/25",state="InvalidState"} 0
windows_dhcp_scope_state{scope="172.16.0.0/24",state="Disabled"} 1
windows_dhcp_scope_state{scope="172.16.0.0/24",state="DisabledSwitched"} 0
windows_dhcp_scope_state{scope="172.16.0.0/24",state="Enabled"} 0
windows_dhcp_scope_state{scope="172.16.0.0/24",state="EnabledSwitched"} 0
windows_dhcp_scope_state{scope="172.16.0.0/24",state="InvalidState"} 0
windows_dhcp_scope_state{scope="192.168.0.0/24",state="Disabled"} 0
windows_dhcp_scope_state{scope="192.168.0.0/24",state="DisabledSwitched"} 0
windows_dhcp_scope_state{scope="192.168.0.0/24",state="Enabled"} 1
windows_dhcp_scope_state{scope="192.168.0.0/24",state="EnabledSwitched"} 0
windows_dhcp_scope_state{scope="192.168.0.0/24",state="InvalidState"} 0
```
## Useful queries
_This collector does not yet have any useful queries added, we would appreciate your help adding them!_

View File

@@ -19,6 +19,10 @@ If given, an interface name needs to match the include regexp in order for the c
If given, an interface name needs to *not* match the exclude regexp in order for the corresponding metrics to be reported
### `--collector.net.enabled`
Comma-separated list of collectors to use. Defaults to all, if not specified.
## Metrics
Name | Description | Type | Labels

View File

@@ -10,7 +10,9 @@ The netframework collector exposes metrics about dotnet framework.
## Flags
None
### `--collector.netframework.enabled`
Comma-separated list of collectors to use. Defaults to all, if not specified.
## Metrics

29
go.mod
View File

@@ -5,16 +5,15 @@ go 1.23
require (
github.com/Microsoft/hcsshim v0.12.9
github.com/alecthomas/kingpin/v2 v2.4.0
github.com/bmatcuk/doublestar/v4 v4.7.1
github.com/bmatcuk/doublestar/v4 v4.8.1
github.com/dimchansky/utfbom v1.1.1
github.com/go-ole/go-ole v1.3.0
github.com/google/uuid v1.6.0
github.com/prometheus/client_golang v1.20.5
github.com/prometheus/client_golang v1.21.0-rc.0
github.com/prometheus/client_model v0.6.1
github.com/prometheus/common v0.61.0
github.com/prometheus/exporter-toolkit v0.13.2
github.com/prometheus/common v0.62.0
github.com/prometheus/exporter-toolkit v0.14.0
github.com/stretchr/testify v1.10.0
golang.org/x/sys v0.29.0
golang.org/x/sys v0.30.0
gopkg.in/yaml.v3 v3.0.1
)
@@ -23,7 +22,7 @@ require (
github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/containerd/cgroups/v3 v3.0.4 // indirect
github.com/containerd/cgroups/v3 v3.0.5 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/containerd/typeurl/v2 v2.2.3 // indirect
@@ -43,13 +42,13 @@ require (
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/xhit/go-str2duration/v2 v2.1.0 // indirect
go.opencensus.io v0.24.0 // indirect
golang.org/x/crypto v0.31.0 // indirect
golang.org/x/net v0.32.0 // indirect
golang.org/x/oauth2 v0.24.0 // indirect
golang.org/x/sync v0.10.0 // indirect
golang.org/x/text v0.21.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697 // indirect
google.golang.org/grpc v1.68.0 // indirect
google.golang.org/protobuf v1.35.2 // indirect
golang.org/x/crypto v0.32.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/oauth2 v0.26.0 // indirect
golang.org/x/sync v0.11.0 // indirect
golang.org/x/text v0.22.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250204164813-702378808489 // indirect
google.golang.org/grpc v1.70.0 // indirect
google.golang.org/protobuf v1.36.5 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)

58
go.sum
View File

@@ -10,15 +10,15 @@ github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b h1:mimo19zliBX/vS
github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bmatcuk/doublestar/v4 v4.7.1 h1:fdDeAqgT47acgwd9bd9HxJRDmc9UAmPpc+2m0CXv75Q=
github.com/bmatcuk/doublestar/v4 v4.7.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/bmatcuk/doublestar/v4 v4.8.1 h1:54Bopc5c2cAvhLRAzqOGCYHYyhcDHsFF4wWIR5wKP38=
github.com/bmatcuk/doublestar/v4 v4.8.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/containerd/cgroups/v3 v3.0.4 h1:2fs7l3P0Qxb1nKWuJNFiwhp2CqiKzho71DQkDrHJIo4=
github.com/containerd/cgroups/v3 v3.0.4/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins=
github.com/containerd/cgroups/v3 v3.0.5 h1:44na7Ud+VwyE7LIoJ8JTNQOa549a8543BmzaJHo6Bzo=
github.com/containerd/cgroups/v3 v3.0.5/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins=
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
@@ -66,8 +66,6 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
@@ -92,15 +90,15 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_golang v1.21.0-rc.0 h1:bR+RxBlwcr4q8hXkgSOA/J18j6n0/qH0Gb0DH+8c+RY=
github.com/prometheus/client_golang v1.21.0-rc.0/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg=
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.61.0 h1:3gv/GThfX0cV2lpO7gkTUwZru38mxevy90Bj8YFSRQQ=
github.com/prometheus/common v0.61.0/go.mod h1:zr29OCN/2BsJRaFwG8QOBr41D6kkchKbpeNH7pAjb/s=
github.com/prometheus/exporter-toolkit v0.13.2 h1:Z02fYtbqTMy2i/f+xZ+UK5jy/bl1Ex3ndzh06T/Q9DQ=
github.com/prometheus/exporter-toolkit v0.13.2/go.mod h1:tCqnfx21q6qN1KA4U3Bfb8uWzXfijIrJz3/kTIqMV7g=
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/exporter-toolkit v0.14.0 h1:NMlswfibpcZZ+H0sZBiTjrA3/aBFHkNZqE+iCj5EmRg=
github.com/prometheus/exporter-toolkit v0.14.0/go.mod h1:Gu5LnVvt7Nr/oqTBUC23WILZepW0nffNo10XdhQcwWA=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
@@ -128,8 +126,8 @@ go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
@@ -145,30 +143,30 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI=
golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs=
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE=
golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.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.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@@ -186,15 +184,15 @@ google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697 h1:LWZqQOEjDyONlF1H6afSWpAL/znlREo2tHfLoe+8LMA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250204164813-702378808489 h1:5bKytslY8ViY0Cj/ewmRtrWHW64bNF03cAatUUFCdFI=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250204164813-702378808489/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0=
google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA=
google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
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=
@@ -204,8 +202,8 @@ google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=

View File

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

BIN
installer/codesign.cer Normal file

Binary file not shown.

View File

@@ -27,10 +27,15 @@
ErrorControl="normal"
Start="auto"
Type="ownProcess"
Interactive="no"
Vital="yes"
Arguments="[ConfigFileFlag] [CollectorsFlag] [ListenFlag] [MetricsPathFlag] [TextfileDirsFlag] [ExtraFlags]">
<ServiceConfig
DelayedAutoStart="yes"
OnInstall="yes"
OnReinstall="yes" />
<util:ServiceConfig
ResetPeriodInDays="1"
ResetPeriodInDays="0"
FirstFailureActionType="restart"
SecondFailureActionType="restart"
ThirdFailureActionType="restart"
@@ -39,6 +44,12 @@
<ServiceDependency Id="wmiApSrv" />
</ServiceInstall>
<ServiceControl Id="ServiceStateControl" Name="windows_exporter" Remove="uninstall" Start="install" Stop="both"/>
<!-- The "Name" field must match the argument to eventlog.Open() -->
<util:EventSource Log="Application" Name="windows_exporter"
EventMessageFile="%SystemRoot%\System32\EventCreate.exe"
SupportsErrors="yes"
SupportsInformationals="yes"
SupportsWarnings="yes"/>
</Component>
<Component Id="CreateTextfileDirectory" Directory="textfile_inputs" Guid="d03ef58a-9cbf-4165-ad39-d143e9b27e14">
<CreateFolder />

View File

@@ -43,6 +43,9 @@
Property="OLDERVERSIONBEINGUPGRADED" />
</Upgrade>
<CustomAction Id="CheckExtraFlags"
Error="The parameter '--config.file' must not be included in EXTRA_FLAGS. Use CONFIG_FILE instead. Please remove it and try again." />
<CustomAction Id="set_maintenance" Property="MAINTENANCE" Value="true" />
<!-- Set to reinstall all features. -->
@@ -54,12 +57,12 @@
<CustomAction Id="set_reinstallmode_property"
Property="REINSTALLMODE"
Value="amus" />
<!-- START CUSTOM ACTION FOR CONFIG FILE CREATION -->
<SetProperty
Id="CreateConfigFile"
Value="&quot;[%ComSpec]&quot; /c TYPE NUL >>&quot;[ConfigFile_NonDefault][ConfigFile_Default]&quot;"
Before="CreateConfigFile"
Sequence="execute"
Condition="ConfigFile_NonDefault OR ConfigFile_Default"
/>
<CustomAction
Id="CreateConfigFile"
@@ -69,6 +72,24 @@
Return="check"
Impersonate="no"
/>
<!-- END CUSTOM ACTION FOR CONFIG FILE CREATION -->
<!-- START CUSTOM ACTION FOR KILLING THE PROCESS -->
<SetProperty
Id="KillProcess"
Value="&quot;[WindowsFolder]\System32\taskkill.exe&quot; /T /F /IM windows_exporter.exe"
Before="KillProcess"
Sequence="execute"
/>
<CustomAction
Id="KillProcess"
BinaryRef="Wix4UtilCA_$(sys.BUILDARCHSHORT)"
DllEntry="WixQuietExec"
Execute="deferred"
Return="ignore"
Impersonate="no"
/>
<!-- END CUSTOM ACTION FOR KILLING THE PROCESS -->
<InstallExecuteSequence>
<!-- Set REINSTALL=all and REINSTALLMODE=amus if the user reruns the
@@ -78,6 +99,10 @@
<Custom Action="set_reinstall_all_property" Before="set_reinstallmode_property" Condition="MAINTENANCE"/>
<Custom Action="set_reinstallmode_property" Before="LaunchConditions" Condition="MAINTENANCE"/>
<Custom Action="CreateConfigFile" Before="InstallServices" Condition="ConfigFile_NonDefault OR ConfigFile_Default" />
<Custom Action="KillProcess" Before="RemoveFiles" />
<Custom Action="CheckExtraFlags" Before="InstallInitialize"
Condition="EXTRA_FLAGS AND (EXTRA_FLAGS&gt;&lt;&quot;--config.file&quot;)" />
</InstallExecuteSequence>
<Media Id="1" Cabinet="windows_exporter.cab" EmbedCab="yes" />
@@ -90,9 +115,10 @@
<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]" Condition="CONFIG_FILE AND CONFIG_FILE&lt;&gt;&quot;config.yaml&quot;" />
<SetProperty Id="ConfigFile_Remote" After="InstallFiles" Sequence="execute" Value="[CONFIG_FILE]" Condition="CONFIG_FILE AND (CONFIG_FILE&lt;&lt;&quot;http://&quot; OR CONFIG_FILE&lt;&lt;&quot;https://&quot;)" />
<SetProperty Id="ConfigFile_NonDefault" After="InstallFiles" Sequence="execute" Value="[CONFIG_FILE]" Condition="CONFIG_FILE AND CONFIG_FILE&lt;&gt;&quot;config.yaml&quot; AND NOT (CONFIG_FILE&lt;&lt;&quot;http://&quot; OR CONFIG_FILE&lt;&lt;&quot;https://&quot;)" />
<SetProperty Id="ConfigFile_Default" After="InstallFiles" Sequence="execute" Value="[APPLICATIONFOLDER]config.yaml" Condition="CONFIG_FILE=&quot;config.yaml&quot;" />
<SetProperty Id="ConfigFileFlag" After="InstallFiles" Sequence="execute" Value="--config.file=&quot;[ConfigFile_NonDefault][ConfigFile_Default]&quot;" Condition="ConfigFile_NonDefault OR ConfigFile_Default" />
<SetProperty Id="ConfigFileFlag" After="InstallFiles" Sequence="execute" Value="--config.file=&quot;[ConfigFile_Remote][ConfigFile_NonDefault][ConfigFile_Default]&quot;" Condition="ConfigFile_Remote OR ConfigFile_NonDefault OR ConfigFile_Default" />
<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" />

View File

@@ -37,7 +37,7 @@ type Collector struct {
config Config
// physicalMemoryBytes
// Deprecated: Use windows_physical_memory_total_bytes instead
// Deprecated: Use windows_memory_physical_total_bytes instead
physicalMemoryBytes *prometheus.Desc
// logicalProcessors
// Deprecated: Use windows_cpu_logical_processor instead
@@ -85,7 +85,7 @@ func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error {
)
c.physicalMemoryBytes = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "physical_memory_bytes"),
"Deprecated: Use windows_physical_memory_total_bytes instead",
"Deprecated: Use windows_memory_physical_total_bytes instead",
nil,
nil,
)

View File

@@ -16,22 +16,39 @@
package dhcp
import (
"errors"
"fmt"
"log/slog"
"slices"
"strconv"
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/headers/dhcpsapi"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/pdh"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
)
const Name = "dhcp"
const (
Name = "dhcp"
type Config struct{}
subCollectorServerMetrics = "server_metrics"
subCollectorScopeMetrics = "scope_metrics"
)
type Config struct {
CollectorsEnabled []string `yaml:"collectors_enabled"`
}
//nolint:gochecknoglobals
var ConfigDefaults = Config{}
var ConfigDefaults = Config{
CollectorsEnabled: []string{
subCollectorServerMetrics,
subCollectorScopeMetrics,
},
}
// A Collector is a Prometheus Collector perflib DHCP metrics.
type Collector struct {
@@ -65,6 +82,17 @@ type Collector struct {
packetsReceivedTotal *prometheus.Desc
releasesTotal *prometheus.Desc
requestsTotal *prometheus.Desc
scopeInfo *prometheus.Desc
scopeState *prometheus.Desc
scopeAddressesFreeTotal *prometheus.Desc
scopeAddressesFreeOnPartnerServerTotal *prometheus.Desc
scopeAddressesFreeOnThisServerTotal *prometheus.Desc
scopeAddressesInUseTotal *prometheus.Desc
scopeAddressesInUseOnPartnerServerTotal *prometheus.Desc
scopeAddressesInUseOnThisServerTotal *prometheus.Desc
scopePendingOffersTotal *prometheus.Desc
scopeReservedAddressTotal *prometheus.Desc
}
func New(config *Config) *Collector {
@@ -72,6 +100,10 @@ func New(config *Config) *Collector {
config = &ConfigDefaults
}
if config.CollectorsEnabled == nil {
config.CollectorsEnabled = ConfigDefaults.CollectorsEnabled
}
c := &Collector{
config: *config,
}
@@ -79,8 +111,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.dhcp.enabled",
"Comma-separated list of collectors to use. Defaults to all, if not specified.",
).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 {
@@ -88,7 +138,9 @@ func (c *Collector) GetName() string {
}
func (c *Collector) Close() error {
c.perfDataCollector.Close()
if slices.Contains(c.config.CollectorsEnabled, subCollectorServerMetrics) {
c.perfDataCollector.Close()
}
return nil
}
@@ -96,166 +148,258 @@ func (c *Collector) Close() error {
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
var err error
c.perfDataCollector, err = pdh.NewCollector[perfDataCounterValues](pdh.CounterTypeRaw, "DHCP Server", nil)
if err != nil {
return fmt.Errorf("failed to create DHCP Server collector: %w", err)
if slices.Contains(c.config.CollectorsEnabled, subCollectorServerMetrics) {
c.perfDataCollector, err = pdh.NewCollector[perfDataCounterValues](pdh.CounterTypeRaw, "DHCP Server", nil)
if err != nil {
return fmt.Errorf("failed to create DHCP Server collector: %w", err)
}
c.packetsReceivedTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "packets_received_total"),
"Total number of packets received by the DHCP server (PacketsReceivedTotal)",
nil,
nil,
)
c.duplicatesDroppedTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "duplicates_dropped_total"),
"Total number of duplicate packets received by the DHCP server (DuplicatesDroppedTotal)",
nil,
nil,
)
c.packetsExpiredTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "packets_expired_total"),
"Total number of packets expired in the DHCP server message queue (PacketsExpiredTotal)",
nil,
nil,
)
c.activeQueueLength = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "active_queue_length"),
"Number of packets in the processing queue of the DHCP server (ActiveQueueLength)",
nil,
nil,
)
c.conflictCheckQueueLength = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "conflict_check_queue_length"),
"Number of packets in the DHCP server queue waiting on conflict detection (ping). (ConflictCheckQueueLength)",
nil,
nil,
)
c.discoversTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "discovers_total"),
"Total DHCP Discovers received by the DHCP server (DiscoversTotal)",
nil,
nil,
)
c.offersTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "offers_total"),
"Total DHCP Offers sent by the DHCP server (OffersTotal)",
nil,
nil,
)
c.requestsTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "requests_total"),
"Total DHCP Requests received by the DHCP server (RequestsTotal)",
nil,
nil,
)
c.informsTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "informs_total"),
"Total DHCP Informs received by the DHCP server (InformsTotal)",
nil,
nil,
)
c.acksTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "acks_total"),
"Total DHCP Acks sent by the DHCP server (AcksTotal)",
nil,
nil,
)
c.nACKsTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "nacks_total"),
"Total DHCP Nacks sent by the DHCP server (NacksTotal)",
nil,
nil,
)
c.declinesTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "declines_total"),
"Total DHCP Declines received by the DHCP server (DeclinesTotal)",
nil,
nil,
)
c.releasesTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "releases_total"),
"Total DHCP Releases received by the DHCP server (ReleasesTotal)",
nil,
nil,
)
c.offerQueueLength = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "offer_queue_length"),
"Number of packets in the offer queue of the DHCP server (OfferQueueLength)",
nil,
nil,
)
c.deniedDueToMatch = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "denied_due_to_match_total"),
"Total number of DHCP requests denied, based on matches from the Deny list (DeniedDueToMatch)",
nil,
nil,
)
c.deniedDueToNonMatch = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "denied_due_to_nonmatch_total"),
"Total number of DHCP requests denied, based on non-matches from the Allow list (DeniedDueToNonMatch)",
nil,
nil,
)
c.failoverBndUpdSentTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "failover_bndupd_sent_total"),
"Number of DHCP fail over Binding Update messages sent (FailoverBndupdSentTotal)",
nil,
nil,
)
c.failoverBndUpdReceivedTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "failover_bndupd_received_total"),
"Number of DHCP fail over Binding Update messages received (FailoverBndupdReceivedTotal)",
nil,
nil,
)
c.failoverBndAckSentTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "failover_bndack_sent_total"),
"Number of DHCP fail over Binding Ack messages sent (FailoverBndackSentTotal)",
nil,
nil,
)
c.failoverBndAckReceivedTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "failover_bndack_received_total"),
"Number of DHCP fail over Binding Ack messages received (FailoverBndackReceivedTotal)",
nil,
nil,
)
c.failoverBndUpdPendingOutboundQueue = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "failover_bndupd_pending_in_outbound_queue"),
"Number of pending outbound DHCP fail over Binding Update messages (FailoverBndupdPendingOutboundQueue)",
nil,
nil,
)
c.failoverTransitionsCommunicationInterruptedState = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "failover_transitions_communicationinterrupted_state_total"),
"Total number of transitions into COMMUNICATION INTERRUPTED state (FailoverTransitionsCommunicationinterruptedState)",
nil,
nil,
)
c.failoverTransitionsPartnerDownState = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "failover_transitions_partnerdown_state_total"),
"Total number of transitions into PARTNER DOWN state (FailoverTransitionsPartnerdownState)",
nil,
nil,
)
c.failoverTransitionsRecoverState = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "failover_transitions_recover_total"),
"Total number of transitions into RECOVER state (FailoverTransitionsRecoverState)",
nil,
nil,
)
c.failoverBndUpdDropped = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "failover_bndupd_dropped_total"),
"Total number of DHCP fail over Binding Updates dropped (FailoverBndupdDropped)",
nil,
nil,
)
}
c.packetsReceivedTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "packets_received_total"),
"Total number of packets received by the DHCP server (PacketsReceivedTotal)",
nil,
nil,
)
c.duplicatesDroppedTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "duplicates_dropped_total"),
"Total number of duplicate packets received by the DHCP server (DuplicatesDroppedTotal)",
nil,
nil,
)
c.packetsExpiredTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "packets_expired_total"),
"Total number of packets expired in the DHCP server message queue (PacketsExpiredTotal)",
nil,
nil,
)
c.activeQueueLength = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "active_queue_length"),
"Number of packets in the processing queue of the DHCP server (ActiveQueueLength)",
nil,
nil,
)
c.conflictCheckQueueLength = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "conflict_check_queue_length"),
"Number of packets in the DHCP server queue waiting on conflict detection (ping). (ConflictCheckQueueLength)",
nil,
nil,
)
c.discoversTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "discovers_total"),
"Total DHCP Discovers received by the DHCP server (DiscoversTotal)",
nil,
nil,
)
c.offersTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "offers_total"),
"Total DHCP Offers sent by the DHCP server (OffersTotal)",
nil,
nil,
)
c.requestsTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "requests_total"),
"Total DHCP Requests received by the DHCP server (RequestsTotal)",
nil,
nil,
)
c.informsTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "informs_total"),
"Total DHCP Informs received by the DHCP server (InformsTotal)",
nil,
nil,
)
c.acksTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "acks_total"),
"Total DHCP Acks sent by the DHCP server (AcksTotal)",
nil,
nil,
)
c.nACKsTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "nacks_total"),
"Total DHCP Nacks sent by the DHCP server (NacksTotal)",
nil,
nil,
)
c.declinesTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "declines_total"),
"Total DHCP Declines received by the DHCP server (DeclinesTotal)",
nil,
nil,
)
c.releasesTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "releases_total"),
"Total DHCP Releases received by the DHCP server (ReleasesTotal)",
nil,
nil,
)
c.offerQueueLength = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "offer_queue_length"),
"Number of packets in the offer queue of the DHCP server (OfferQueueLength)",
nil,
nil,
)
c.deniedDueToMatch = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "denied_due_to_match_total"),
"Total number of DHCP requests denied, based on matches from the Deny list (DeniedDueToMatch)",
nil,
nil,
)
c.deniedDueToNonMatch = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "denied_due_to_nonmatch_total"),
"Total number of DHCP requests denied, based on non-matches from the Allow list (DeniedDueToNonMatch)",
nil,
nil,
)
c.failoverBndUpdSentTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "failover_bndupd_sent_total"),
"Number of DHCP fail over Binding Update messages sent (FailoverBndupdSentTotal)",
nil,
nil,
)
c.failoverBndUpdReceivedTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "failover_bndupd_received_total"),
"Number of DHCP fail over Binding Update messages received (FailoverBndupdReceivedTotal)",
nil,
nil,
)
c.failoverBndAckSentTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "failover_bndack_sent_total"),
"Number of DHCP fail over Binding Ack messages sent (FailoverBndackSentTotal)",
nil,
nil,
)
c.failoverBndAckReceivedTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "failover_bndack_received_total"),
"Number of DHCP fail over Binding Ack messages received (FailoverBndackReceivedTotal)",
nil,
nil,
)
c.failoverBndUpdPendingOutboundQueue = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "failover_bndupd_pending_in_outbound_queue"),
"Number of pending outbound DHCP fail over Binding Update messages (FailoverBndupdPendingOutboundQueue)",
nil,
nil,
)
c.failoverTransitionsCommunicationInterruptedState = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "failover_transitions_communicationinterrupted_state_total"),
"Total number of transitions into COMMUNICATION INTERRUPTED state (FailoverTransitionsCommunicationinterruptedState)",
nil,
nil,
)
c.failoverTransitionsPartnerDownState = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "failover_transitions_partnerdown_state_total"),
"Total number of transitions into PARTNER DOWN state (FailoverTransitionsPartnerdownState)",
nil,
nil,
)
c.failoverTransitionsRecoverState = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "failover_transitions_recover_total"),
"Total number of transitions into RECOVER state (FailoverTransitionsRecoverState)",
nil,
nil,
)
c.failoverBndUpdDropped = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "failover_bndupd_dropped_total"),
"Total number of DHCP fail over Binding Updates dropped (FailoverBndupdDropped)",
nil,
nil,
)
if slices.Contains(c.config.CollectorsEnabled, subCollectorScopeMetrics) {
c.scopeInfo = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "scope_info"),
"DHCP Scope information",
[]string{"name", "superscope_name", "superscope_id", "scope"},
nil,
)
c.scopeState = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "scope_state"),
"DHCP Scope state",
[]string{"scope", "state"},
nil,
)
c.scopeAddressesFreeTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "scope_addresses_free"),
"DHCP Scope free addresses",
[]string{"scope"},
nil,
)
c.scopeAddressesFreeOnPartnerServerTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "scope_addresses_free_on_partner_server"),
"DHCP Scope free addresses on partner server",
[]string{"scope"},
nil,
)
c.scopeAddressesFreeOnThisServerTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "scope_addresses_free_on_this_server"),
"DHCP Scope free addresses on this server",
[]string{"scope"},
nil,
)
c.scopeAddressesInUseTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "scope_addresses_in_use"),
"DHCP Scope addresses in use",
[]string{"scope"},
nil,
)
c.scopeAddressesInUseOnPartnerServerTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "scope_addresses_in_use_on_partner_server"),
"DHCP Scope addresses in use on partner server",
[]string{"scope"},
nil,
)
c.scopeAddressesInUseOnThisServerTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "scope_addresses_in_use_on_this_server"),
"DHCP Scope addresses in use on this server",
[]string{"scope"},
nil,
)
c.scopePendingOffersTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "scope_pending_offers"),
"DHCP Scope pending offers",
[]string{"scope"},
nil,
)
c.scopeReservedAddressTotal = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "scope_reserved_address"),
"DHCP Scope reserved addresses",
[]string{"scope"},
nil,
)
}
return nil
}
func (c *Collector) Collect(ch chan<- prometheus.Metric) error {
var errs []error
if slices.Contains(c.config.CollectorsEnabled, subCollectorServerMetrics) {
if err := c.collectServerMetrics(ch); err != nil {
errs = append(errs, err)
}
}
if slices.Contains(c.config.CollectorsEnabled, subCollectorScopeMetrics) {
if err := c.collectScopeMetrics(ch); err != nil {
errs = append(errs, err)
}
}
return errors.Join(errs...)
}
func (c *Collector) collectServerMetrics(ch chan<- prometheus.Metric) error {
err := c.perfDataCollector.Collect(&c.perfDataObject)
if err != nil {
return fmt.Errorf("failed to collect DHCP Server metrics: %w", err)
@@ -413,3 +557,113 @@ func (c *Collector) Collect(ch chan<- prometheus.Metric) error {
return nil
}
func (c *Collector) collectScopeMetrics(ch chan<- prometheus.Metric) error {
dhcpScopes, err := dhcpsapi.GetDHCPV4ScopeStatistics()
if err != nil {
return fmt.Errorf("failed to get DHCP scopes: %w", err)
}
for _, scope := range dhcpScopes {
scopeID := scope.ScopeIPAddress.String()
ch <- prometheus.MustNewConstMetric(
c.scopeInfo,
prometheus.GaugeValue,
1,
scope.Name,
scope.SuperScopeName,
strconv.Itoa(int(scope.SuperScopeNumber)),
scopeID,
)
for state, name := range dhcpsapi.DHCP_SUBNET_STATE_NAMES {
metric := 0.0
if state == scope.State {
metric = 1.0
}
ch <- prometheus.MustNewConstMetric(
c.scopeState,
prometheus.GaugeValue,
metric,
scopeID,
name,
)
}
if scope.AddressesFree != -1 {
ch <- prometheus.MustNewConstMetric(
c.scopeAddressesFreeTotal,
prometheus.GaugeValue,
scope.AddressesFree,
scopeID,
)
}
if scope.AddressesFreeOnPartnerServer != -1 {
ch <- prometheus.MustNewConstMetric(
c.scopeAddressesFreeOnPartnerServerTotal,
prometheus.GaugeValue,
scope.AddressesFreeOnPartnerServer,
scopeID,
)
}
if scope.AddressesFreeOnThisServer != -1 {
ch <- prometheus.MustNewConstMetric(
c.scopeAddressesFreeOnThisServerTotal,
prometheus.GaugeValue,
scope.AddressesFreeOnThisServer,
scopeID,
)
}
if scope.AddressesInUse != -1 {
ch <- prometheus.MustNewConstMetric(
c.scopeAddressesInUseTotal,
prometheus.GaugeValue,
scope.AddressesInUse,
scopeID,
)
}
if scope.AddressesInUseOnPartnerServer != -1 {
ch <- prometheus.MustNewConstMetric(
c.scopeAddressesInUseOnPartnerServerTotal,
prometheus.GaugeValue,
scope.AddressesInUseOnPartnerServer,
scopeID,
)
}
if scope.AddressesInUseOnThisServer != -1 {
ch <- prometheus.MustNewConstMetric(
c.scopeAddressesInUseOnThisServerTotal,
prometheus.GaugeValue,
scope.AddressesInUseOnThisServer,
scopeID,
)
}
if scope.PendingOffers != -1 {
ch <- prometheus.MustNewConstMetric(
c.scopePendingOffersTotal,
prometheus.GaugeValue,
scope.PendingOffers,
scopeID,
)
}
if scope.ReservedAddress != -1 {
ch <- prometheus.MustNewConstMetric(
c.scopeReservedAddressTotal,
prometheus.GaugeValue,
scope.ReservedAddress,
scopeID,
)
}
}
return nil
}

View File

@@ -23,11 +23,10 @@ import (
"strings"
"sync"
"github.com/Microsoft/hcsshim/osversion"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
"golang.org/x/sys/windows"
)
const (
@@ -157,19 +156,17 @@ func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
return nil
}
version := windows.RtlGetVersion()
subCollectors := map[string]struct {
build func() error
collect func(ch chan<- prometheus.Metric) error
close func()
minBuildNumber uint32
minBuildNumber uint16
}{
subCollectorDataStore: {
build: c.buildDataStore,
collect: c.collectDataStore,
close: c.perfDataCollectorDataStore.Close,
minBuildNumber: types.BuildNumberWindowsServer2022,
minBuildNumber: osversion.LTSC2022,
},
subCollectorDynamicMemoryBalancer: {
build: c.buildDynamicMemoryBalancer,
@@ -243,6 +240,8 @@ func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
},
}
buildNumber := osversion.Build()
// Result must order, to prevent test failures.
sort.Strings(c.config.CollectorsEnabled)
@@ -253,7 +252,7 @@ func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
return fmt.Errorf("unknown collector: %s", name)
}
if version.BuildNumber < subCollectors[name].minBuildNumber {
if buildNumber < subCollectors[name].minBuildNumber {
errs = append(errs, fmt.Errorf("collector %s requires Windows Server 2022 or newer", name))
continue

View File

@@ -18,6 +18,7 @@ package mscluster
import (
"fmt"
"github.com/Microsoft/hcsshim/osversion"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
@@ -192,7 +193,14 @@ type msClusterCluster struct {
}
func (c *Collector) buildCluster() error {
clusterMIQuery, err := mi.NewQuery("SELECT * FROM MSCluster_Cluster")
buildNumber := osversion.Build()
wmiSelect := "AddEvictDelay,AdminAccessPoint,AutoAssignNodeSite,AutoBalancerLevel,AutoBalancerMode,BackupInProgress,BlockCacheSize,ClusSvcHangTimeout,ClusSvcRegroupOpeningTimeout,ClusSvcRegroupPruningTimeout,ClusSvcRegroupStageTimeout,ClusSvcRegroupTickInMilliseconds,ClusterEnforcedAntiAffinity,ClusterFunctionalLevel,ClusterGroupWaitDelay,ClusterLogLevel,ClusterLogSize,ClusterUpgradeVersion,CrossSiteDelay,CrossSiteThreshold,CrossSubnetDelay,CrossSubnetThreshold,CsvBalancer,DatabaseReadWriteMode,DefaultNetworkRole,DisableGroupPreferredOwnerRandomization,DrainOnShutdown,DynamicQuorumEnabled,EnableSharedVolumes,FixQuorum,GracePeriodEnabled,GracePeriodTimeout,GroupDependencyTimeout,HangRecoveryAction,IgnorePersistentStateOnStartup,LogResourceControls,LowerQuorumPriorityNodeId,MessageBufferLength,MinimumNeverPreemptPriority,MinimumPreemptorPriority,NetftIPSecEnabled,PlacementOptions,PlumbAllCrossSubnetRoutes,PreventQuorum,QuarantineDuration,QuarantineThreshold,QuorumArbitrationTimeMax,QuorumArbitrationTimeMin,QuorumLogFileSize,QuorumTypeValue,RequestReplyTimeout,ResiliencyDefaultPeriod,ResiliencyLevel,ResourceDllDeadlockPeriod,RootMemoryReserved,RouteHistoryLength,S2DBusTypes,S2DCacheDesiredState,S2DCacheFlashReservePercent,S2DCachePageSizeKBytes,S2DEnabled,S2DIOLatencyThreshold,S2DOptimizations,SameSubnetDelay,SameSubnetThreshold,SecurityLevel,SharedVolumeVssWriterOperationTimeout,ShutdownTimeoutInMinutes,UseClientAccessNetworksForSharedVolumes,WitnessDatabaseWriteTimeout,WitnessDynamicWeight,WitnessRestartInterval"
if buildNumber >= osversion.LTSC2022 {
wmiSelect += ",DetectManagedEvents,SecurityLevelForStorage,MaxNumberOfNodes,DetectManagedEventsThreshold,DetectedCloudPlatform"
}
clusterMIQuery, err := mi.NewQuery(fmt.Sprintf("SELECT %s FROM MSCluster_Cluster", wmiSelect))
if err != nil {
return fmt.Errorf("failed to create WMI query: %w", err)
}
@@ -852,27 +860,6 @@ func (c *Collector) collectCluster(ch chan<- prometheus.Metric) error {
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.clusterDetectedCloudPlatform,
prometheus.GaugeValue,
float64(v.DetectedCloudPlatform),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.clusterDetectManagedEvents,
prometheus.GaugeValue,
float64(v.DetectManagedEvents),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.clusterDetectManagedEventsThreshold,
prometheus.GaugeValue,
float64(v.DetectManagedEventsThreshold),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.clusterDisableGroupPreferredOwnerRandomization,
prometheus.GaugeValue,
@@ -957,13 +944,6 @@ func (c *Collector) collectCluster(ch chan<- prometheus.Metric) error {
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.clusterMaxNumberOfNodes,
prometheus.GaugeValue,
float64(v.MaxNumberOfNodes),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.clusterMessageBufferLength,
prometheus.GaugeValue,
@@ -1167,13 +1147,6 @@ func (c *Collector) collectCluster(ch chan<- prometheus.Metric) error {
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.clusterSecurityLevelForStorage,
prometheus.GaugeValue,
float64(v.SecurityLevelForStorage),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.clusterSharedVolumeVssWriterOperationTimeout,
prometheus.GaugeValue,
@@ -1215,6 +1188,43 @@ func (c *Collector) collectCluster(ch chan<- prometheus.Metric) error {
float64(v.WitnessRestartInterval),
v.Name,
)
if osversion.Build() >= osversion.LTSC2022 {
ch <- prometheus.MustNewConstMetric(
c.clusterDetectManagedEvents,
prometheus.GaugeValue,
float64(v.DetectManagedEvents),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.clusterDetectManagedEventsThreshold,
prometheus.GaugeValue,
float64(v.DetectManagedEventsThreshold),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.clusterSecurityLevelForStorage,
prometheus.GaugeValue,
float64(v.SecurityLevelForStorage),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.clusterMaxNumberOfNodes,
prometheus.GaugeValue,
float64(v.MaxNumberOfNodes),
v.Name,
)
ch <- prometheus.MustNewConstMetric(
c.clusterDetectedCloudPlatform,
prometheus.GaugeValue,
float64(v.DetectedCloudPlatform),
v.Name,
)
}
}
return nil

View File

@@ -48,7 +48,7 @@ type msClusterNetwork struct {
}
func (c *Collector) buildNetwork() error {
networkMIQuery, err := mi.NewQuery("SELECT * FROM MSCluster_Network")
networkMIQuery, err := mi.NewQuery("SELECT Characteristics,Flags,Metric,Role,State FROM MSCluster_Network")
if err != nil {
return fmt.Errorf("failed to create WMI query: %w", err)
}

View File

@@ -18,6 +18,7 @@ package mscluster
import (
"fmt"
"github.com/Microsoft/hcsshim/osversion"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
@@ -66,7 +67,14 @@ type msClusterNode struct {
}
func (c *Collector) buildNode() error {
nodeMIQuery, err := mi.NewQuery("SELECT * FROM MSCluster_Node")
buildNumber := osversion.Build()
wmiSelect := "BuildNumber,Characteristics,DynamicWeight,Flags,MajorVersion,MinorVersion,NeedsPreventQuorum,NodeDrainStatus,NodeHighestVersion,NodeLowestVersion,NodeWeight,State,StatusInformation"
if buildNumber >= osversion.LTSC2022 {
wmiSelect += ",DetectedCloudPlatform"
}
nodeMIQuery, err := mi.NewQuery(fmt.Sprintf("SELECT %s FROM MSCluster_Node", wmiSelect))
if err != nil {
return fmt.Errorf("failed to create WMI query: %w", err)
}

View File

@@ -74,7 +74,7 @@ type msClusterResource struct {
}
func (c *Collector) buildResource() error {
resourceMIQuery, err := mi.NewQuery("SELECT * FROM MSCluster_Resource")
resourceMIQuery, err := mi.NewQuery("SELECT Name,Type,OwnerGroup,OwnerNode,Characteristics,DeadlockTimeout,EmbeddedFailureAction,Flags,IsAlivePollInterval,LooksAlivePollInterval,MonitorProcessId,PendingTimeout,ResourceClass,RestartAction,RestartDelay,RestartPeriod,RestartThreshold,RetryPeriodOnFailure,State,Subclass FROM MSCluster_Resource")
if err != nil {
return fmt.Errorf("failed to create WMI query: %w", err)
}

View File

@@ -66,7 +66,7 @@ type msClusterResourceGroup struct {
}
func (c *Collector) buildResourceGroup() error {
resourceGroupMIQuery, err := mi.NewQuery("SELECT * FROM MSCluster_ResourceGroup")
resourceGroupMIQuery, err := mi.NewQuery("SELECT AutoFailbackType,Characteristics,ColdStartSetting,DefaultOwner,FailbackWindowEnd,FailbackWindowStart,FailoverPeriod,FailoverThreshold,Flags,GroupType,OwnerNode,Priority,ResiliencyPeriod,State FROM MSCluster_ResourceGroup")
if err != nil {
return fmt.Errorf("failed to create WMI query: %w", err)
}

View File

@@ -391,7 +391,7 @@ func (c *Collector) collectNICAddresses(ch chan<- prometheus.Metric) error {
return err
}
convertNicName := strings.NewReplacer("(", "[", ")", "]")
convertNicName := strings.NewReplacer("(", "[", ")", "]", "#", "_")
for _, nicAdapterAddress := range nicAdapterAddresses {
friendlyName := windows.UTF16PtrToString(nicAdapterAddress.FriendlyName)

View File

@@ -20,6 +20,7 @@ import (
"fmt"
"log/slog"
"sort"
"strings"
"sync"
"github.com/alecthomas/kingpin/v2"
@@ -143,8 +144,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.netframework.enabled",
"Comma-separated list of collectors to use. Defaults to all, if not specified.",
).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 {

View File

@@ -63,7 +63,7 @@ type Win32_PerfRawData_NETFramework_NETCLRExceptions struct {
func (c *Collector) collectClrExceptions(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_NETFramework_NETCLRExceptions
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_NETFramework_NETCLRExceptions"))); err != nil {
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * FROM Win32_PerfRawData_NETFramework_NETCLRExceptions"))); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}

View File

@@ -57,7 +57,7 @@ type Win32_PerfRawData_NETFramework_NETCLRInterop struct {
func (c *Collector) collectClrInterop(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_NETFramework_NETCLRInterop
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_NETFramework_NETCLRInterop"))); err != nil {
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * FROM Win32_PerfRawData_NETFramework_NETCLRInterop"))); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}

View File

@@ -65,7 +65,7 @@ type Win32_PerfRawData_NETFramework_NETCLRJit struct {
func (c *Collector) collectClrJIT(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_NETFramework_NETCLRJit
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_NETFramework_NETCLRJit"))); err != nil {
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * FROM Win32_PerfRawData_NETFramework_NETCLRJit"))); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}

View File

@@ -104,7 +104,7 @@ type Win32_PerfRawData_NETFramework_NETCLRLoading struct {
func (c *Collector) collectClrLoading(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_NETFramework_NETCLRLoading
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_NETFramework_NETCLRLoading"))); err != nil {
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * FROM Win32_PerfRawData_NETFramework_NETCLRLoading"))); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}

View File

@@ -86,7 +86,7 @@ type Win32_PerfRawData_NETFramework_NETCLRLocksAndThreads struct {
func (c *Collector) collectClrLocksAndThreads(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_NETFramework_NETCLRLocksAndThreads
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_NETFramework_NETCLRLocksAndThreads"))); err != nil {
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * FROM Win32_PerfRawData_NETFramework_NETCLRLocksAndThreads"))); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}

View File

@@ -135,7 +135,7 @@ type Win32_PerfRawData_NETFramework_NETCLRMemory struct {
func (c *Collector) collectClrMemory(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_NETFramework_NETCLRMemory
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_NETFramework_NETCLRMemory"))); err != nil {
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * FROM Win32_PerfRawData_NETFramework_NETCLRMemory"))); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}

View File

@@ -77,7 +77,7 @@ type Win32_PerfRawData_NETFramework_NETCLRRemoting struct {
func (c *Collector) collectClrRemoting(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_NETFramework_NETCLRRemoting
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_NETFramework_NETCLRRemoting"))); err != nil {
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * FROM Win32_PerfRawData_NETFramework_NETCLRRemoting"))); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}

View File

@@ -64,7 +64,7 @@ type Win32_PerfRawData_NETFramework_NETCLRSecurity struct {
func (c *Collector) collectClrSecurity(ch chan<- prometheus.Metric) error {
var dst []Win32_PerfRawData_NETFramework_NETCLRSecurity
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * Win32_PerfRawData_NETFramework_NETCLRSecurity"))); err != nil {
if err := c.miSession.Query(&dst, mi.NamespaceRootCIMv2, utils.Must(mi.NewQuery("SELECT * FROM Win32_PerfRawData_NETFramework_NETCLRSecurity"))); err != nil {
return fmt.Errorf("WMI query failed: %w", err)
}

View File

@@ -28,5 +28,7 @@ func BenchmarkCollector(b *testing.B) {
}
func TestCollector(t *testing.T) {
t.Skip("Skipping test as it requires WMI data")
testutils.TestCollector(t, netframework.New, nil)
}

View File

@@ -76,6 +76,8 @@ type Collector struct {
poolBytes *prometheus.Desc
priorityBase *prometheus.Desc
privateBytes *prometheus.Desc
// Deprecated: Use start_time_seconds_timestamp instead
startTimeOld *prometheus.Desc
startTime *prometheus.Desc
threadCount *prometheus.Desc
virtualBytes *prometheus.Desc
@@ -214,8 +216,15 @@ func (c *Collector) Build(logger *slog.Logger, miSession *mi.Session) error {
nil,
)
c.startTime = prometheus.NewDesc(
c.startTimeOld = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "start_time"),
"DEPRECATED: Use start_time_seconds_timestamp instead",
[]string{"process", "process_id"},
nil,
)
c.startTime = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "start_time_seconds_timestamp"),
"Time of process start.",
[]string{"process", "process_id"},
nil,

View File

@@ -141,6 +141,13 @@ func (c *Collector) collectWorkerV1() {
name, pidString,
)
ch <- prometheus.MustNewConstMetric(
c.startTimeOld,
prometheus.GaugeValue,
data.ElapsedTime,
name, pidString,
)
ch <- prometheus.MustNewConstMetric(
c.handleCount,
prometheus.GaugeValue,

View File

@@ -23,6 +23,7 @@ import (
"strconv"
"strings"
"sync"
"time"
"github.com/prometheus-community/windows_exporter/internal/pdh"
"github.com/prometheus/client_golang/prometheus"
@@ -134,10 +135,19 @@ func (c *Collector) collectWorkerV2() {
name, pidString, parentPID, strconv.Itoa(int(processGroupID)), processOwner, cmdLine,
)
startTime := float64(time.Now().Unix() - int64(data.ElapsedTime))
ch <- prometheus.MustNewConstMetric(
c.startTimeOld,
prometheus.GaugeValue,
startTime,
name, pidString,
)
ch <- prometheus.MustNewConstMetric(
c.startTime,
prometheus.GaugeValue,
data.ElapsedTime,
startTime,
name, pidString,
)

View File

@@ -64,7 +64,8 @@ type Collector struct {
// ref: https://victoriametrics.com/blog/go-sync-pool/
serviceConfigPoolBytes sync.Pool
serviceManagerHandle *mgr.Mgr
serviceManagerHandle *mgr.Mgr
queryAllServicesBuffer []byte
}
func New(config *Config) *Collector {
@@ -140,6 +141,8 @@ func (c *Collector) Build(logger *slog.Logger, _ *mi.Session) error {
},
}
c.queryAllServicesBuffer = make([]byte, 1024*100)
c.info = prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, Name, "info"),
"A metric with a constant '1' value labeled with service information",
@@ -209,7 +212,7 @@ func (c *Collector) Close() error {
func (c *Collector) Collect(ch chan<- prometheus.Metric) error {
services, err := c.queryAllServices()
if err != nil {
return fmt.Errorf("failed to query services: %w", err)
return fmt.Errorf("failed to query all services: %w", err)
}
servicesCh := make(chan windows.ENUM_SERVICE_STATUS_PROCESS, len(services))
@@ -368,16 +371,16 @@ func (c *Collector) queryAllServices() ([]windows.ENUM_SERVICE_STATUS_PROCESS, e
err error
)
buf := make([]byte, 1024*100)
for {
currentBufferSize := uint32(cap(c.queryAllServicesBuffer))
err = windows.EnumServicesStatusEx(
c.serviceManagerHandle.Handle,
windows.SC_STATUS_PROCESS_INFO,
windows.SERVICE_WIN32,
windows.SERVICE_STATE_ALL,
&buf[0],
uint32(len(buf)),
&c.queryAllServicesBuffer[0],
currentBufferSize,
&bytesNeeded,
&servicesReturned,
nil,
@@ -392,18 +395,18 @@ func (c *Collector) queryAllServices() ([]windows.ENUM_SERVICE_STATUS_PROCESS, e
return nil, err
}
if bytesNeeded <= uint32(len(buf)) {
return nil, err
if bytesNeeded <= currentBufferSize {
return nil, fmt.Errorf("windows.EnumServicesStatusEx reports buffer too small (%d), but buffer is large enough (%d)", currentBufferSize, bytesNeeded)
}
buf = make([]byte, bytesNeeded)
c.queryAllServicesBuffer = make([]byte, bytesNeeded)
}
if servicesReturned == 0 {
return []windows.ENUM_SERVICE_STATUS_PROCESS{}, nil
}
services := unsafe.Slice((*windows.ENUM_SERVICE_STATUS_PROCESS)(unsafe.Pointer(&buf[0])), int(servicesReturned))
services := unsafe.Slice((*windows.ENUM_SERVICE_STATUS_PROCESS)(unsafe.Pointer(&c.queryAllServicesBuffer[0])), int(servicesReturned))
return services, nil
}

View File

@@ -42,10 +42,14 @@ type Resolver struct {
func NewResolver(ctx context.Context, file string, logger *slog.Logger, insecureSkipVerify bool) (*Resolver, error) {
flags := map[string]string{}
var fileBytes []byte
var (
err error
fileBytes []byte
)
var err error
if strings.HasPrefix(file, "http://") || strings.HasPrefix(file, "https://") {
logger.WarnContext(ctx, "Loading configuration file from URL is deprecated and will be removed in 0.31.0. Use a local file instead.")
fileBytes, err = readFromURL(ctx, file, logger, insecureSkipVerify)
if err != nil {
return nil, err

View File

@@ -0,0 +1,214 @@
package dhcpsapi
import (
"errors"
"fmt"
"net"
"unsafe"
"golang.org/x/sys/windows"
)
//nolint:gochecknoglobals
var (
modDhcpServer = windows.NewLazySystemDLL("dhcpsapi.dll")
procDhcpGetSubnetInfo = modDhcpServer.NewProc("DhcpGetSubnetInfo")
procDhcpGetSuperScopeInfoV4 = modDhcpServer.NewProc("DhcpGetSuperScopeInfoV4")
procDhcpRpcFreeMemory = modDhcpServer.NewProc("DhcpRpcFreeMemory")
procDhcpV4EnumSubnetReservations = modDhcpServer.NewProc("DhcpV4EnumSubnetReservations")
procDhcpV4FailoverGetScopeStatistics = modDhcpServer.NewProc("DhcpV4FailoverGetScopeStatistics")
procDhcpGetMibInfoV5 = modDhcpServer.NewProc("DhcpGetMibInfoV5")
)
func GetDHCPV4ScopeStatistics() ([]DHCPV4Scope, error) {
var mibInfo *DHCP_MIB_INFO_V5
if err := dhcpGetMibInfoV5(&mibInfo); err != nil {
return nil, err
}
defer dhcpRpcFreeMemory(unsafe.Pointer(mibInfo))
subnetScopeInfos := make(map[DHCP_IP_ADDRESS]DHCP_SUBNET_MIB_INFO_V5, mibInfo.Scopes)
subnetMIBScopeInfos := unsafe.Slice(mibInfo.ScopeInfo, mibInfo.Scopes)
for _, subnetMIBScopeInfo := range subnetMIBScopeInfos {
subnetScopeInfos[subnetMIBScopeInfo.Subnet] = subnetMIBScopeInfo
}
var superScopeTable *DHCP_SUPER_SCOPE_TABLE
if err := dhcpGetSuperScopeInfoV4(&superScopeTable); err != nil {
return nil, err
} else if superScopeTable == nil {
return nil, errors.New("dhcpGetSuperScopeInfoV4 returned nil")
}
defer dhcpRpcFreeMemory(unsafe.Pointer(superScopeTable))
scopes := make([]DHCPV4Scope, 0, superScopeTable.Count)
subnets := unsafe.Slice(superScopeTable.Entries, superScopeTable.Count)
var errs []error
for _, subnet := range subnets {
if err := (func() error {
var subnetInfo *DHCP_SUBNET_INFO
err := dhcpGetSubnetInfo(subnet.SubnetAddress, &subnetInfo)
if err != nil {
return fmt.Errorf("failed to get subnet info: %w", err)
}
defer dhcpRpcFreeMemory(unsafe.Pointer(subnetInfo))
scope := DHCPV4Scope{
Name: subnetInfo.SubnetName.String(),
SuperScopeName: subnet.SuperScopeName.String(),
ScopeIPAddress: net.IPNet{IP: subnetInfo.SubnetAddress.IPv4(), Mask: subnetInfo.SubnetMask.IPv4Mask()},
SuperScopeNumber: subnet.SuperScopeNumber,
State: subnetInfo.SubnetState,
AddressesFree: -1,
AddressesFreeOnPartnerServer: -1,
AddressesFreeOnThisServer: -1,
AddressesInUse: -1,
AddressesInUseOnPartnerServer: -1,
AddressesInUseOnThisServer: -1,
PendingOffers: -1,
ReservedAddress: -1,
}
if subnetScopeInfo, ok := subnetScopeInfos[subnetInfo.SubnetAddress]; ok {
scope.AddressesInUse = float64(subnetScopeInfo.NumAddressesInUse)
scope.AddressesFree = float64(subnetScopeInfo.NumAddressesFree)
scope.PendingOffers = float64(subnetScopeInfo.NumPendingOffers)
}
subnetReservationCount, err := dhcpV4EnumSubnetReservations(subnet.SubnetAddress)
if err != nil {
return fmt.Errorf("failed to get subnet reservation count: %w", err)
} else {
scope.ReservedAddress = float64(subnetReservationCount)
}
var subnetStatistics *DHCP_FAILOVER_STATISTICS
err = dhcpV4FailoverGetScopeStatistics(subnet.SubnetAddress, &subnetStatistics)
defer dhcpRpcFreeMemory(unsafe.Pointer(subnetStatistics))
if err == nil {
scope.AddressesFree = float64(subnetStatistics.AddrFree)
scope.AddressesInUse = float64(subnetStatistics.AddrInUse)
scope.AddressesFreeOnPartnerServer = float64(subnetStatistics.PartnerAddrFree)
scope.AddressesInUseOnPartnerServer = float64(subnetStatistics.PartnerAddrInUse)
scope.AddressesFreeOnThisServer = float64(subnetStatistics.ThisAddrFree)
scope.AddressesInUseOnThisServer = float64(subnetStatistics.ThisAddrInUse)
} else if !errors.Is(err, ERROR_DHCP_FO_SCOPE_NOT_IN_RELATIONSHIP) {
return fmt.Errorf("failed to get subnet statistics: %w", err)
}
scopes = append(scopes, scope)
return nil
})(); err != nil {
errs = append(errs, err)
}
}
return scopes, errors.Join(errs...)
}
// dhcpGetSubnetInfo https://learn.microsoft.com/en-us/windows/win32/api/dhcpsapi/nf-dhcpsapi-dhcpgetsubnetinfo
func dhcpGetSubnetInfo(subnetAddress DHCP_IP_ADDRESS, subnetInfo **DHCP_SUBNET_INFO) error {
ret, _, _ := procDhcpGetSubnetInfo.Call(
0,
uintptr(subnetAddress),
uintptr(unsafe.Pointer(subnetInfo)),
)
if ret != 0 {
return fmt.Errorf("dhcpGetSubnetInfo failed with code %w", windows.Errno(ret))
}
return nil
}
// dhcpV4FailoverGetScopeStatistics https://learn.microsoft.com/en-us/windows/win32/api/dhcpsapi/nf-dhcpsapi-dhcpv4failovergetscopestatistics
func dhcpV4FailoverGetScopeStatistics(scopeId DHCP_IP_ADDRESS, stats **DHCP_FAILOVER_STATISTICS) error {
ret, _, _ := procDhcpV4FailoverGetScopeStatistics.Call(
0,
uintptr(scopeId),
uintptr(unsafe.Pointer(stats)),
)
if ret != 0 {
return fmt.Errorf("dhcpV4FailoverGetScopeStatistics failed with code %w", windows.Errno(ret))
}
return nil
}
// dhcpGetSuperScopeInfoV4 https://learn.microsoft.com/en-us/windows/win32/api/dhcpsapi/nf-dhcpsapi-dhcpgetsuperscopeinfov4
func dhcpGetSuperScopeInfoV4(superScopeTable **DHCP_SUPER_SCOPE_TABLE) error {
ret, _, _ := procDhcpGetSuperScopeInfoV4.Call(
0,
uintptr(unsafe.Pointer(superScopeTable)),
)
if ret != 0 {
return fmt.Errorf("dhcpGetSuperScopeInfoV4 failed with code %w", windows.Errno(ret))
}
return nil
}
// dhcpGetMibInfoV5 https://learn.microsoft.com/en-us/windows/win32/api/dhcpsapi/nf-dhcpsapi-dhcpgetmibinfov5
func dhcpGetMibInfoV5(mibInfo **DHCP_MIB_INFO_V5) error {
ret, _, _ := procDhcpGetMibInfoV5.Call(
0,
uintptr(unsafe.Pointer(mibInfo)),
)
if ret != 0 {
return fmt.Errorf("dhcpGetMibInfoV5 failed with code %w", windows.Errno(ret))
}
return nil
}
// dhcpV4EnumSubnetReservations https://learn.microsoft.com/en-us/windows/win32/api/dhcpsapi/nf-dhcpsapi-dhcpv4enumsubnetreservations
func dhcpV4EnumSubnetReservations(subnetAddress DHCP_IP_ADDRESS) (uint32, error) {
var (
elementsRead uint32
elementsTotal uint32
elementsInfo uintptr
resumeHandle *windows.Handle
)
ret, _, _ := procDhcpV4EnumSubnetReservations.Call(
0,
uintptr(subnetAddress),
uintptr(unsafe.Pointer(&resumeHandle)),
0,
uintptr(unsafe.Pointer(&elementsInfo)),
uintptr(unsafe.Pointer(&elementsRead)),
uintptr(unsafe.Pointer(&elementsTotal)),
)
dhcpRpcFreeMemory(unsafe.Pointer(elementsInfo))
if !errors.Is(windows.Errno(ret), windows.ERROR_MORE_DATA) && !errors.Is(windows.Errno(ret), windows.ERROR_NO_MORE_ITEMS) {
return 0, fmt.Errorf("dhcpV4EnumSubnetReservations failed with code %w", windows.Errno(ret))
}
return elementsRead + elementsTotal, nil
}
func dhcpRpcFreeMemory(pointer unsafe.Pointer) {
if uintptr(pointer) == 0 {
return
}
//nolint:dogsled
_, _, _ = procDhcpRpcFreeMemory.Call(uintptr(pointer))
}

View File

@@ -0,0 +1,24 @@
package dhcpsapi
import (
"errors"
"testing"
"github.com/stretchr/testify/require"
"golang.org/x/sys/windows"
)
func TestGetDHCPV4ScopeStatistics(t *testing.T) {
t.Parallel()
if procDhcpGetSuperScopeInfoV4.Find() != nil {
t.Skip("DhcpGetSuperScopeInfoV4 is not available")
}
_, err := GetDHCPV4ScopeStatistics()
if errors.Is(err, windows.Errno(1753)) {
t.Skip(err.Error())
}
require.NoError(t, err)
}

View File

@@ -0,0 +1,137 @@
package dhcpsapi
import (
"encoding/binary"
"net"
"github.com/prometheus-community/windows_exporter/internal/headers/win32api"
"golang.org/x/sys/windows"
)
//nolint:gochecknoglobals
var ERROR_DHCP_FO_SCOPE_NOT_IN_RELATIONSHIP = windows.Errno(20116)
type DHCPV4Scope struct {
Name string
State DHCP_SUBNET_STATE
SuperScopeName string
SuperScopeNumber uint32
ScopeIPAddress net.IPNet
AddressesFree float64
AddressesFreeOnPartnerServer float64
AddressesFreeOnThisServer float64
AddressesInUse float64
AddressesInUseOnPartnerServer float64
AddressesInUseOnThisServer float64
PendingOffers float64
ReservedAddress float64
}
type (
DHCP_IP_ADDRESS win32api.DWORD
DHCP_IP_MASK win32api.DWORD
)
func (ip DHCP_IP_ADDRESS) IPv4() net.IP {
ipBytes := make([]byte, 4)
binary.BigEndian.PutUint32(ipBytes, uint32(ip))
return ipBytes
}
func (ip DHCP_IP_MASK) IPv4Mask() net.IPMask {
ipBytes := make([]byte, 4)
binary.BigEndian.PutUint32(ipBytes, uint32(ip))
return ipBytes
}
type DHCP_SUPER_SCOPE_TABLE struct {
Count win32api.DWORD
Entries *DHCP_SUPER_SCOPE_TABLE_ENTRY
}
type DHCP_SUPER_SCOPE_TABLE_ENTRY struct {
SubnetAddress DHCP_IP_ADDRESS
SuperScopeNumber win32api.DWORD
NextInSuperScope win32api.DWORD
SuperScopeName win32api.LPWSTR
}
// DHCP_SUBNET_INFO https://learn.microsoft.com/de-de/windows/win32/api/dhcpsapi/ns-dhcpsapi-dhcp_subnet_info
type DHCP_SUBNET_INFO struct {
SubnetAddress DHCP_IP_ADDRESS
SubnetMask DHCP_IP_MASK
SubnetName win32api.LPWSTR
SubnetComment win32api.LPWSTR
PrimaryHost DHCP_HOST_INFO
SubnetState DHCP_SUBNET_STATE
}
type DHCP_HOST_INFO struct {
IpAddress DHCP_IP_ADDRESS
NetBiosName win32api.LPWSTR
HostName win32api.LPWSTR
}
// DHCP_SUBNET_STATE https://learn.microsoft.com/de-de/windows/win32/api/dhcpsapi/ne-dhcpsapi-dhcp_subnet_state
type DHCP_SUBNET_STATE uint32
const (
DhcpSubnetEnabled DHCP_SUBNET_STATE = 0
DhcpSubnetDisabled DHCP_SUBNET_STATE = 1
DhcpSubnetEnabledSwitched DHCP_SUBNET_STATE = 2
DhcpSubnetDisabledSwitched DHCP_SUBNET_STATE = 3
DhcpSubnetInvalidState DHCP_SUBNET_STATE = 4
)
//nolint:gochecknoglobals
var DHCP_SUBNET_STATE_NAMES = map[DHCP_SUBNET_STATE]string{
DhcpSubnetEnabled: "Enabled",
DhcpSubnetDisabled: "Disabled",
DhcpSubnetEnabledSwitched: "EnabledSwitched",
DhcpSubnetDisabledSwitched: "DisabledSwitched",
DhcpSubnetInvalidState: "InvalidState",
}
type DHCP_FAILOVER_STATISTICS struct {
NumAddr win32api.DWORD
AddrFree win32api.DWORD
AddrInUse win32api.DWORD
PartnerAddrFree win32api.DWORD
ThisAddrFree win32api.DWORD
PartnerAddrInUse win32api.DWORD
ThisAddrInUse win32api.DWORD
}
type DHCP_MIB_INFO_V5 struct {
Discovers win32api.DWORD
Offers win32api.DWORD
Requests win32api.DWORD
Acks win32api.DWORD
Naks win32api.DWORD
Declines win32api.DWORD
Releases win32api.DWORD
ServerStartTime win32api.DATE_TIME
QtnNumLeases win32api.DWORD
QtnPctQtnLeases win32api.DWORD
QtnProbationLeases win32api.DWORD
QtnNonQtnLeases win32api.DWORD
QtnExemptLeases win32api.DWORD
QtnCapableClients win32api.DWORD
QtnIASErrors win32api.DWORD
DelayedOffers win32api.DWORD
ScopesWithDelayedOffers win32api.DWORD
Scopes win32api.DWORD
ScopeInfo *DHCP_SUBNET_MIB_INFO_V5
}
type DHCP_SUBNET_MIB_INFO_V5 struct {
Subnet DHCP_IP_ADDRESS
NumAddressesInUse win32api.DWORD
NumAddressesFree win32api.DWORD
NumPendingOffers win32api.DWORD
}

View File

@@ -0,0 +1,15 @@
package win32api
import "golang.org/x/sys/windows"
type (
DATE_TIME = windows.Filetime
DWORD = uint32
LPWSTR struct {
*uint16
}
)
func (s LPWSTR) String() string {
return windows.UTF16PtrToString(s.uint16)
}

View File

@@ -22,7 +22,6 @@ import (
"strconv"
"time"
"github.com/google/uuid"
"github.com/prometheus-community/windows_exporter/pkg/collector"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/collectors"
@@ -82,8 +81,7 @@ func New(logger *slog.Logger, metricCollectors *collector.Collection, options *O
func (c *MetricsHTTPHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
logger := c.logger.With(
slog.Any("remote", r.RemoteAddr),
slog.Any("correlation_id", uuid.New().String()),
slog.String("remote", r.RemoteAddr),
)
scrapeTimeout := c.getScrapeTimeout(logger, r)

View File

@@ -17,50 +17,41 @@
package eventlog
import (
"bytes"
"fmt"
"io"
"regexp"
"strings"
"golang.org/x/sys/windows"
)
const (
// NeLogOemCode is a generic error log entry for OEMs to use to
// elog errors from OEM value added services.
// See: https://github.com/microsoft/win32metadata/blob/2f3c5282ce1024a712aeccd90d3aa50bf7a49e27/generation/WinSDK/RecompiledIdlHeaders/um/LMErrlog.h#L824-L845
NeLogOemCode = uint32(3299)
"golang.org/x/sys/windows/svc/eventlog"
)
// Interface guard.
var _ io.Writer = (*Writer)(nil)
var reStripTimeAndLevel = regexp.MustCompile(`^time=\S+ level=\S+ `)
type Writer struct {
handle windows.Handle
handle *eventlog.Log
}
// NewEventLogWriter returns a new Writer which writes to Windows EventLog.
func NewEventLogWriter(handle windows.Handle) *Writer {
// NewEventLogWriter returns a new Writer, which writes to Windows EventLog.
func NewEventLogWriter(handle *eventlog.Log) *Writer {
return &Writer{handle: handle}
}
func (w *Writer) Write(p []byte) (int, error) {
var eType uint16
var err error
msg := strings.TrimSpace(string(p))
eventLogMsg := reStripTimeAndLevel.ReplaceAllString(msg, "")
switch {
case bytes.Contains(p, []byte(" level=error")) || bytes.Contains(p, []byte(`"level":"error"`)):
eType = windows.EVENTLOG_ERROR_TYPE
case bytes.Contains(p, []byte(" level=warn")) || bytes.Contains(p, []byte(`"level":"warn"`)):
eType = windows.EVENTLOG_WARNING_TYPE
case strings.Contains(msg, " level=ERROR") || strings.Contains(msg, `"level":"error"`):
err = w.handle.Error(102, eventLogMsg)
case strings.Contains(msg, " level=WARN") || strings.Contains(msg, `"level":"warn"`):
err = w.handle.Warning(101, eventLogMsg)
default:
eType = windows.EVENTLOG_INFORMATION_TYPE
err = w.handle.Info(100, eventLogMsg)
}
msg, err := windows.UTF16PtrFromString(string(p))
if err != nil {
return 0, fmt.Errorf("error convert string to UTF-16: %w", err)
}
ss := []*uint16{msg, nil, nil, nil, nil, nil, nil, nil, nil}
return len(p), windows.ReportEvent(w.handle, eType, 0, NeLogOemCode, 0, 9, 0, &ss[0], nil)
return len(p), err
}

View File

@@ -24,7 +24,7 @@ import (
"github.com/prometheus-community/windows_exporter/internal/log/eventlog"
"github.com/prometheus/common/promslog"
"golang.org/x/sys/windows"
wineventlog "golang.org/x/sys/windows/svc/eventlog"
)
// AllowedFile is a settable identifier for the output file that the logger can have.
@@ -51,12 +51,12 @@ func (f *AllowedFile) Set(s string) error {
case "stderr":
f.w = os.Stderr
case "eventlog":
handle, err := windows.RegisterEventSource(nil, windows.StringToUTF16Ptr("windows_exporter"))
eventLog, err := wineventlog.Open("windows_exporter")
if err != nil {
return fmt.Errorf("failed to open event log: %w", err)
}
f.w = eventlog.NewEventLogWriter(handle)
f.w = eventlog.NewEventLogWriter(eventLog)
default:
file, err := os.OpenFile(s, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0o200)
if err != nil {

View File

@@ -138,6 +138,10 @@ func (o *OperationUnmarshalCallbacks) InstanceResult(
element, err := instance.GetElement(miTag)
if err != nil {
if errors.Is(err, MI_RESULT_NO_SUCH_PROPERTY) {
continue
}
o.errCh <- fmt.Errorf("failed to get element %s: %w", miTag, err)
return 0

View File

@@ -172,6 +172,12 @@ func NewCollectorWithReflection(resultType CounterType, object string, instances
continue
}
if bufLen == 0 {
errs = append(errs, errors.New("GetCounterInfo: buffer length is zero"))
continue
}
buf := make([]byte, bufLen)
if ret := GetCounterInfo(counterHandle, 0, &bufLen, &buf[0]); ret != ErrorSuccess {
errs = append(errs, fmt.Errorf("GetCounterInfo: %w", NewPdhError(ret)))
@@ -390,7 +396,7 @@ func (c *Collector) collectWorkerRaw() {
case PERF_ELAPSED_TIME:
dv.Index(index).
Field(counter.FieldIndexValue).
SetFloat(float64((item.RawValue.FirstValue - WindowsEpoch) / counter.Frequency))
SetFloat(float64((item.RawValue.SecondValue - item.RawValue.FirstValue) / counter.Frequency))
case PERF_100NSEC_TIMER, PERF_PRECISION_100NS_TIMER:
dv.Index(index).
Field(counter.FieldIndexValue).

View File

@@ -16,10 +16,5 @@
package types
const (
BuildNumberWindowsServer2025 uint32 = 26100
BuildNumberWindowsServer2022 uint32 = 20348
BuildNumberWindowsServer2019 uint32 = 17763
BuildNumberWindowsServer2016 uint32 = 14393
Namespace = "windows"
)

View File

@@ -1,23 +0,0 @@
// Copyright 2024 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build windows
// Package windowsservice allows initiating time-sensitive components like registering the Windows service
// as early as possible in the startup process.
// init functions are called in the order they are declared, so this package should be imported first.
// Declare imports on this package should be avoided where possible.
//
// Ref: https://github.com/prometheus-community/windows_exporter/issues/551#issuecomment-1220774835
package windowsservice

View File

@@ -1,61 +0,0 @@
// Copyright 2024 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package windowsservice
import (
"fmt"
"os"
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/svc"
)
//nolint:gochecknoglobals
var (
// IsService is true if the exporter is running as a Windows service.
IsService bool
// ExitCodeCh is a channel to send the exit code return from the [github.com/prometheus-community/windows_exporter/cmd/windows_exporter] function to the service manager.
ExitCodeCh = make(chan int)
// StopCh is a channel to send a signal to the service manager that the service is stopping.
StopCh = make(chan struct{})
)
//nolint:gochecknoinits // An init function is required to communicate with the Windows service manager early in the program.
func init() {
var err error
IsService, err = svc.IsWindowsService()
if err != nil {
if err := logToEventToLog(windows.EVENTLOG_ERROR_TYPE, fmt.Sprintf("failed to detect service: %v", err)); err != nil {
os.Exit(2)
}
os.Exit(1)
}
if !IsService {
return
}
if err := logToEventToLog(windows.EVENTLOG_INFORMATION_TYPE, "attempting to start exporter service"); err != nil {
os.Exit(2)
}
go func() {
if err := svc.Run(serviceName, &windowsExporterService{}); err != nil {
_ = logToEventToLog(windows.EVENTLOG_ERROR_TYPE, fmt.Sprintf("failed to start service: %v", err))
}
}()
}

View File

@@ -1,44 +0,0 @@
// Copyright 2024 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build windows
package windowsservice
import (
"fmt"
wineventlog "github.com/prometheus-community/windows_exporter/internal/log/eventlog"
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/svc/eventlog"
)
// logToEventToLog logs a message to the Windows event log.
func logToEventToLog(eType uint16, msg string) error {
eventLog, err := eventlog.Open("windows_exporter")
if err != nil {
return fmt.Errorf("failed to open event log: %w", err)
}
defer func(eventLog *eventlog.Log) {
_ = eventLog.Close()
}(eventLog)
p, err := windows.UTF16PtrFromString(msg)
if err != nil {
return fmt.Errorf("error convert string to UTF-16: %w", err)
}
ss := []*uint16{p, nil, nil, nil, nil, nil, nil, nil, nil}
return windows.ReportEvent(eventLog.Handle, eType, 0, wineventlog.NeLogOemCode, 0, 9, 0, &ss[0], nil)
}

View File

@@ -1,64 +0,0 @@
// Copyright 2024 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//go:build windows
package windowsservice
import (
"fmt"
"golang.org/x/sys/windows"
"golang.org/x/sys/windows/svc"
)
const (
serviceName = "windows_exporter"
)
type windowsExporterService struct{}
// Execute is the entry point for the Windows service manager.
func (s *windowsExporterService) Execute(_ []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (bool, uint32) {
changes <- svc.Status{State: svc.StartPending}
changes <- svc.Status{State: svc.Running, Accepts: svc.AcceptStop | svc.AcceptShutdown}
for {
select {
case exitCodeCh := <-ExitCodeCh:
// Stop the service if an exit code from the main function is received.
changes <- svc.Status{State: svc.StopPending}
return true, uint32(exitCodeCh)
case c := <-r:
// Handle the service control request.
switch c.Cmd {
case svc.Interrogate:
changes <- c.CurrentStatus
case svc.Stop, svc.Shutdown:
// Stop the service if a stop or shutdown request is received.
_ = logToEventToLog(windows.EVENTLOG_INFORMATION_TYPE, "service stop received")
changes <- svc.Status{State: svc.StopPending}
// Send a signal to the main function to stop the service.
StopCh <- struct{}{}
// Wait for the main function to stop the service.
return false, uint32(<-ExitCodeCh)
default:
_ = logToEventToLog(windows.EVENTLOG_ERROR_TYPE, fmt.Sprintf("unexpected control request #%d", c))
}
}
}
}

View File

@@ -105,7 +105,7 @@ test_alpha_total 42
# TYPE windows_cs_hostname gauge
# HELP windows_cs_logical_processors Deprecated: Use windows_cpu_logical_processor instead
# TYPE windows_cs_logical_processors gauge
# HELP windows_cs_physical_memory_bytes Deprecated: Use windows_physical_memory_total_bytes instead
# HELP windows_cs_physical_memory_bytes Deprecated: Use windows_memory_physical_total_bytes instead
# TYPE windows_cs_physical_memory_bytes gauge
# HELP windows_exporter_build_info A metric with a constant '1' value labeled by version, revision, branch, goversion from which windows_exporter was built, and the goos and goarch for the build.
# TYPE windows_exporter_build_info gauge