mirror of
https://github.com/prometheus-community/windows_exporter.git
synced 2026-03-02 00:26:35 +00:00
process: Use registry collector for V1 data (#1814)
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
This commit is contained in:
@@ -1,52 +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 process
|
||||
|
||||
const (
|
||||
percentProcessorTime = "% Processor Time"
|
||||
percentPrivilegedTime = "% Privileged Time"
|
||||
percentUserTime = "% User Time"
|
||||
creatingProcessID = "Creating Process ID"
|
||||
elapsedTime = "Elapsed Time"
|
||||
handleCount = "Handle Count"
|
||||
ioDataBytesPerSec = "IO Data Bytes/sec"
|
||||
ioDataOperationsPerSec = "IO Data Operations/sec"
|
||||
ioOtherBytesPerSec = "IO Other Bytes/sec"
|
||||
ioOtherOperationsPerSec = "IO Other Operations/sec"
|
||||
ioReadBytesPerSec = "IO Read Bytes/sec"
|
||||
ioReadOperationsPerSec = "IO Read Operations/sec"
|
||||
ioWriteBytesPerSec = "IO Write Bytes/sec"
|
||||
ioWriteOperationsPerSec = "IO Write Operations/sec"
|
||||
pageFaultsPerSec = "Page Faults/sec"
|
||||
pageFileBytesPeak = "Page File Bytes Peak"
|
||||
pageFileBytes = "Page File Bytes"
|
||||
poolNonPagedBytes = "Pool Nonpaged Bytes"
|
||||
poolPagedBytes = "Pool Paged Bytes"
|
||||
priorityBase = "Priority Base"
|
||||
privateBytes = "Private Bytes"
|
||||
threadCount = "Thread Count"
|
||||
virtualBytesPeak = "Virtual Bytes Peak"
|
||||
virtualBytes = "Virtual Bytes"
|
||||
workingSetPrivate = "Working Set - Private"
|
||||
workingSetPeak = "Working Set Peak"
|
||||
workingSet = "Working Set"
|
||||
|
||||
// Process V1.
|
||||
idProcess = "ID Process"
|
||||
|
||||
// Process V2.
|
||||
processID = "Process ID"
|
||||
)
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"regexp"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@@ -28,7 +27,8 @@ import (
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/prometheus-community/windows_exporter/internal/mi"
|
||||
"github.com/prometheus-community/windows_exporter/internal/perfdata"
|
||||
"github.com/prometheus-community/windows_exporter/internal/pdh"
|
||||
"github.com/prometheus-community/windows_exporter/internal/pdh/registry"
|
||||
"github.com/prometheus-community/windows_exporter/internal/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"golang.org/x/sys/windows"
|
||||
@@ -57,12 +57,14 @@ type Collector struct {
|
||||
miSession *mi.Session
|
||||
workerProcessMIQueryQuery mi.Query
|
||||
|
||||
perfDataCollector *perfdata.Collector
|
||||
collectorVersion int
|
||||
|
||||
collectorV1
|
||||
collectorV2
|
||||
|
||||
lookupCache sync.Map
|
||||
|
||||
workerCh chan processWorkerRequest
|
||||
mu sync.RWMutex
|
||||
mu sync.RWMutex
|
||||
|
||||
info *prometheus.Desc
|
||||
cpuTimeTotal *prometheus.Desc
|
||||
@@ -82,14 +84,6 @@ type Collector struct {
|
||||
workingSetPrivate *prometheus.Desc
|
||||
}
|
||||
|
||||
type processWorkerRequest struct {
|
||||
ch chan<- prometheus.Metric
|
||||
name string
|
||||
performanceCounterValues map[string]perfdata.CounterValue
|
||||
waitGroup *sync.WaitGroup
|
||||
workerProcesses []WorkerProcess
|
||||
}
|
||||
|
||||
func New(config *Config) *Collector {
|
||||
if config == nil {
|
||||
config = &ConfigDefaults
|
||||
@@ -159,9 +153,8 @@ func (c *Collector) Close() error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
c.perfDataCollector.Close()
|
||||
|
||||
close(c.workerCh)
|
||||
c.closeV1()
|
||||
c.closeV2()
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -181,56 +174,35 @@ func (c *Collector) Build(logger *slog.Logger, miSession *mi.Session) error {
|
||||
c.workerProcessMIQueryQuery = miQuery
|
||||
c.miSession = miSession
|
||||
|
||||
counters := []string{
|
||||
processID,
|
||||
percentProcessorTime,
|
||||
percentPrivilegedTime,
|
||||
percentUserTime,
|
||||
creatingProcessID,
|
||||
elapsedTime,
|
||||
handleCount,
|
||||
ioDataBytesPerSec,
|
||||
ioDataOperationsPerSec,
|
||||
ioOtherBytesPerSec,
|
||||
ioOtherOperationsPerSec,
|
||||
ioReadBytesPerSec,
|
||||
ioReadOperationsPerSec,
|
||||
ioWriteBytesPerSec,
|
||||
ioWriteOperationsPerSec,
|
||||
pageFaultsPerSec,
|
||||
pageFileBytesPeak,
|
||||
pageFileBytes,
|
||||
poolNonPagedBytes,
|
||||
poolPagedBytes,
|
||||
priorityBase,
|
||||
privateBytes,
|
||||
threadCount,
|
||||
virtualBytesPeak,
|
||||
virtualBytes,
|
||||
workingSetPrivate,
|
||||
workingSetPeak,
|
||||
workingSet,
|
||||
}
|
||||
c.collectorVersion = 2
|
||||
c.perfDataCollectorV2, err = pdh.NewCollector[perfDataCounterValuesV2]("Process V2", pdh.InstancesAll)
|
||||
|
||||
c.perfDataCollector, err = perfdata.NewCollector("Process V2", perfdata.InstancesAll, counters)
|
||||
if errors.Is(err, perfdata.NewPdhError(perfdata.PdhCstatusNoObject)) {
|
||||
counters[0] = idProcess
|
||||
|
||||
c.perfDataCollector, err = perfdata.NewCollector("Process", perfdata.InstancesAll, counters)
|
||||
if errors.Is(err, pdh.NewPdhError(pdh.CstatusNoObject)) {
|
||||
c.collectorVersion = 1
|
||||
c.perfDataCollectorV1, err = registry.NewCollector[perfDataCounterValuesV1]("Process", pdh.InstancesAll)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create Process collector: %w", err)
|
||||
}
|
||||
|
||||
c.workerCh = make(chan processWorkerRequest, 32)
|
||||
if c.collectorVersion == 1 {
|
||||
c.workerChV1 = make(chan processWorkerRequestV1, 32)
|
||||
|
||||
for range 4 {
|
||||
go c.collectWorkerV1()
|
||||
}
|
||||
} else {
|
||||
c.workerChV2 = make(chan processWorkerRequestV2, 32)
|
||||
|
||||
for range 4 {
|
||||
go c.collectWorkerV2()
|
||||
}
|
||||
}
|
||||
|
||||
c.mu = sync.RWMutex{}
|
||||
c.lookupCache = sync.Map{}
|
||||
|
||||
for range 4 {
|
||||
go c.collectWorker()
|
||||
}
|
||||
|
||||
if c.config.ProcessInclude.String() == "^(?:.*)$" && c.config.ProcessExclude.String() == "^(?:)$" {
|
||||
logger.Warn("No filters specified for process collector. This will generate a very large number of metrics!")
|
||||
}
|
||||
@@ -336,21 +308,7 @@ func (c *Collector) Build(logger *slog.Logger, miSession *mi.Session) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type WorkerProcess struct {
|
||||
AppPoolName string `mi:"AppPoolName"`
|
||||
ProcessId uint64 `mi:"ProcessId"`
|
||||
}
|
||||
|
||||
func (c *Collector) Collect(ch chan<- prometheus.Metric) error {
|
||||
perfData, err := c.perfDataCollector.Collect()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to collect metrics: %w", err)
|
||||
}
|
||||
|
||||
if len(perfData) == 0 {
|
||||
return errors.New("perflib query returned empty result set")
|
||||
}
|
||||
|
||||
var workerProcesses []WorkerProcess
|
||||
if c.config.EnableWorkerProcess {
|
||||
if err := c.miSession.Query(&workerProcesses, mi.NamespaceRootWebAdministration, c.workerProcessMIQueryQuery); err != nil {
|
||||
@@ -358,239 +316,11 @@ func (c *Collector) Collect(ch chan<- prometheus.Metric) error {
|
||||
}
|
||||
}
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
|
||||
for name, process := range perfData {
|
||||
// Duplicate processes are suffixed #, and an index number. Remove those.
|
||||
name, _, _ = strings.Cut(name, ":") // Process V2
|
||||
name, _, _ = strings.Cut(name, "#") // Process
|
||||
|
||||
if c.config.ProcessExclude.MatchString(name) || !c.config.ProcessInclude.MatchString(name) {
|
||||
continue
|
||||
}
|
||||
|
||||
wg.Add(1)
|
||||
|
||||
c.workerCh <- processWorkerRequest{
|
||||
ch: ch,
|
||||
name: name,
|
||||
performanceCounterValues: process,
|
||||
workerProcesses: workerProcesses,
|
||||
waitGroup: wg,
|
||||
}
|
||||
if c.collectorVersion == 1 {
|
||||
return c.collectV1(ch, workerProcesses)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) collectWorker() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
c.logger.Error("Worker panic",
|
||||
slog.Any("panic", r),
|
||||
slog.String("stack", string(debug.Stack())),
|
||||
)
|
||||
|
||||
// Restart the collectWorker
|
||||
go c.collectWorker()
|
||||
}
|
||||
}()
|
||||
|
||||
for req := range c.workerCh {
|
||||
(func() {
|
||||
defer req.waitGroup.Done()
|
||||
|
||||
ch := req.ch
|
||||
name := req.name
|
||||
process := req.performanceCounterValues
|
||||
|
||||
var pid uint64
|
||||
|
||||
if v, ok := process[processID]; ok {
|
||||
pid = uint64(v.FirstValue)
|
||||
} else if v, ok = process[idProcess]; ok {
|
||||
pid = uint64(v.FirstValue)
|
||||
}
|
||||
|
||||
parentPID := strconv.FormatUint(uint64(process[creatingProcessID].FirstValue), 10)
|
||||
|
||||
if c.config.EnableWorkerProcess {
|
||||
for _, wp := range req.workerProcesses {
|
||||
if wp.ProcessId == pid {
|
||||
name = strings.Join([]string{name, wp.AppPoolName}, "_")
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cmdLine, processOwner, processGroupID, err := c.getProcessInformation(uint32(pid))
|
||||
if err != nil {
|
||||
c.logger.Debug("Failed to get process information",
|
||||
slog.Uint64("pid", pid),
|
||||
slog.Any("err", err),
|
||||
)
|
||||
}
|
||||
|
||||
pidString := strconv.FormatUint(pid, 10)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.info,
|
||||
prometheus.GaugeValue,
|
||||
1.0,
|
||||
name, pidString, parentPID, strconv.Itoa(int(processGroupID)), processOwner, cmdLine,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.startTime,
|
||||
prometheus.GaugeValue,
|
||||
process[elapsedTime].FirstValue,
|
||||
name, pidString,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.handleCount,
|
||||
prometheus.GaugeValue,
|
||||
process[handleCount].FirstValue,
|
||||
name, pidString,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.cpuTimeTotal,
|
||||
prometheus.CounterValue,
|
||||
process[percentPrivilegedTime].FirstValue,
|
||||
name, pidString, "privileged",
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.cpuTimeTotal,
|
||||
prometheus.CounterValue,
|
||||
process[percentUserTime].FirstValue,
|
||||
name, pidString, "user",
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.ioBytesTotal,
|
||||
prometheus.CounterValue,
|
||||
process[ioOtherBytesPerSec].FirstValue,
|
||||
name, pidString, "other",
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.ioOperationsTotal,
|
||||
prometheus.CounterValue,
|
||||
process[ioOtherOperationsPerSec].FirstValue,
|
||||
name, pidString, "other",
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.ioBytesTotal,
|
||||
prometheus.CounterValue,
|
||||
process[ioReadBytesPerSec].FirstValue,
|
||||
name, pidString, "read",
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.ioOperationsTotal,
|
||||
prometheus.CounterValue,
|
||||
process[ioReadOperationsPerSec].FirstValue,
|
||||
name, pidString, "read",
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.ioBytesTotal,
|
||||
prometheus.CounterValue,
|
||||
process[ioWriteBytesPerSec].FirstValue,
|
||||
name, pidString, "write",
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.ioOperationsTotal,
|
||||
prometheus.CounterValue,
|
||||
process[ioWriteOperationsPerSec].FirstValue,
|
||||
name, pidString, "write",
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.pageFaultsTotal,
|
||||
prometheus.CounterValue,
|
||||
process[pageFaultsPerSec].FirstValue,
|
||||
name, pidString,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.pageFileBytes,
|
||||
prometheus.GaugeValue,
|
||||
process[pageFileBytes].FirstValue,
|
||||
name, pidString,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.poolBytes,
|
||||
prometheus.GaugeValue,
|
||||
process[poolNonPagedBytes].FirstValue,
|
||||
name, pidString, "nonpaged",
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.poolBytes,
|
||||
prometheus.GaugeValue,
|
||||
process[poolPagedBytes].FirstValue,
|
||||
name, pidString, "paged",
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.priorityBase,
|
||||
prometheus.GaugeValue,
|
||||
process[priorityBase].FirstValue,
|
||||
name, pidString,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.privateBytes,
|
||||
prometheus.GaugeValue,
|
||||
process[privateBytes].FirstValue,
|
||||
name, pidString,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.threadCount,
|
||||
prometheus.GaugeValue,
|
||||
process[threadCount].FirstValue,
|
||||
name, pidString,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.virtualBytes,
|
||||
prometheus.GaugeValue,
|
||||
process[virtualBytes].FirstValue,
|
||||
name, pidString,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.workingSetPrivate,
|
||||
prometheus.GaugeValue,
|
||||
process[workingSetPrivate].FirstValue,
|
||||
name, pidString,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.workingSetPeak,
|
||||
prometheus.GaugeValue,
|
||||
process[workingSetPeak].FirstValue,
|
||||
name, pidString,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.workingSet,
|
||||
prometheus.GaugeValue,
|
||||
process[workingSet].FirstValue,
|
||||
name, pidString,
|
||||
)
|
||||
})()
|
||||
}
|
||||
return c.collectV2(ch, workerProcesses)
|
||||
}
|
||||
|
||||
// ref: https://github.com/microsoft/hcsshim/blob/8beabacfc2d21767a07c20f8dd5f9f3932dbf305/internal/uvm/stats.go#L25
|
||||
|
||||
@@ -26,9 +26,10 @@ import (
|
||||
func BenchmarkProcessCollector(b *testing.B) {
|
||||
// PrinterInclude is not set in testing context (kingpin flags not parsed), causing the collector to skip all processes.
|
||||
localProcessInclude := ".+"
|
||||
kingpin.CommandLine.GetArg("collector.process.include").StringVar(&localProcessInclude)
|
||||
// No context name required as collector source is WMI
|
||||
testutils.FuncBenchmarkCollector(b, process.Name, process.NewWithFlags)
|
||||
testutils.FuncBenchmarkCollector(b, process.Name, process.NewWithFlags, func(app *kingpin.Application) {
|
||||
app.GetFlag("collector.process.include").StringVar(&localProcessInclude)
|
||||
})
|
||||
}
|
||||
|
||||
func TestCollector(t *testing.T) {
|
||||
|
||||
285
internal/collector/process/process_v1.go
Normal file
285
internal/collector/process/process_v1.go
Normal file
@@ -0,0 +1,285 @@
|
||||
// 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 process
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/prometheus-community/windows_exporter/internal/pdh/registry"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
type collectorV1 struct {
|
||||
perfDataCollectorV1 *registry.Collector
|
||||
perfDataObjectV1 []perfDataCounterValuesV1
|
||||
workerChV1 chan processWorkerRequestV1
|
||||
}
|
||||
|
||||
type processWorkerRequestV1 struct {
|
||||
ch chan<- prometheus.Metric
|
||||
name string
|
||||
performanceCounterValues perfDataCounterValuesV1
|
||||
waitGroup *sync.WaitGroup
|
||||
workerProcesses []WorkerProcess
|
||||
}
|
||||
|
||||
func (c *Collector) closeV1() {
|
||||
c.perfDataCollectorV1.Close()
|
||||
|
||||
if c.workerChV1 != nil {
|
||||
close(c.workerChV1)
|
||||
c.workerChV1 = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Collector) collectV1(ch chan<- prometheus.Metric, workerProcesses []WorkerProcess) error {
|
||||
err := c.perfDataCollectorV1.Collect(&c.perfDataObjectV1)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to collect metrics: %w", err)
|
||||
}
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
|
||||
for _, process := range c.perfDataObjectV1 {
|
||||
// Duplicate processes are suffixed #, and an index number. Remove those.
|
||||
name, _, _ := strings.Cut(process.Name, ":") // Process V1
|
||||
|
||||
if c.config.ProcessExclude.MatchString(name) || !c.config.ProcessInclude.MatchString(name) {
|
||||
continue
|
||||
}
|
||||
|
||||
wg.Add(1)
|
||||
|
||||
c.workerChV1 <- processWorkerRequestV1{
|
||||
ch: ch,
|
||||
name: name,
|
||||
performanceCounterValues: process,
|
||||
workerProcesses: workerProcesses,
|
||||
waitGroup: wg,
|
||||
}
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) collectWorkerV1() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
c.logger.Error("Worker panic",
|
||||
slog.Any("panic", r),
|
||||
slog.String("stack", string(debug.Stack())),
|
||||
)
|
||||
|
||||
// Restart the collectWorker
|
||||
go c.collectWorkerV1()
|
||||
}
|
||||
}()
|
||||
|
||||
for req := range c.workerChV1 {
|
||||
(func() {
|
||||
defer req.waitGroup.Done()
|
||||
|
||||
ch := req.ch
|
||||
name := req.name
|
||||
data := req.performanceCounterValues
|
||||
|
||||
pid := uint64(data.IdProcess)
|
||||
parentPID := strconv.FormatUint(uint64(data.CreatingProcessID), 10)
|
||||
|
||||
if c.config.EnableWorkerProcess {
|
||||
for _, wp := range req.workerProcesses {
|
||||
if wp.ProcessId == pid {
|
||||
name = strings.Join([]string{name, wp.AppPoolName}, "_")
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cmdLine, processOwner, processGroupID, err := c.getProcessInformation(uint32(pid))
|
||||
if err != nil {
|
||||
slog.LogAttrs(context.Background(), slog.LevelDebug, "Failed to get process information",
|
||||
slog.Uint64("pid", pid),
|
||||
slog.Any("err", err),
|
||||
)
|
||||
}
|
||||
|
||||
pidString := strconv.FormatUint(pid, 10)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.info,
|
||||
prometheus.GaugeValue,
|
||||
1.0,
|
||||
name, pidString, parentPID, strconv.Itoa(int(processGroupID)), processOwner, cmdLine,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.startTime,
|
||||
prometheus.GaugeValue,
|
||||
data.ElapsedTime,
|
||||
name, pidString,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.handleCount,
|
||||
prometheus.GaugeValue,
|
||||
data.HandleCount,
|
||||
name, pidString,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.cpuTimeTotal,
|
||||
prometheus.CounterValue,
|
||||
data.PercentPrivilegedTime,
|
||||
name, pidString, "privileged",
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.cpuTimeTotal,
|
||||
prometheus.CounterValue,
|
||||
data.PercentUserTime,
|
||||
name, pidString, "user",
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.ioBytesTotal,
|
||||
prometheus.CounterValue,
|
||||
data.IoOtherBytesPerSec,
|
||||
name, pidString, "other",
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.ioOperationsTotal,
|
||||
prometheus.CounterValue,
|
||||
data.IoOtherOperationsPerSec,
|
||||
name, pidString, "other",
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.ioBytesTotal,
|
||||
prometheus.CounterValue,
|
||||
data.IoReadBytesPerSec,
|
||||
name, pidString, "read",
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.ioOperationsTotal,
|
||||
prometheus.CounterValue,
|
||||
data.IoReadOperationsPerSec,
|
||||
name, pidString, "read",
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.ioBytesTotal,
|
||||
prometheus.CounterValue,
|
||||
data.IoWriteBytesPerSec,
|
||||
name, pidString, "write",
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.ioOperationsTotal,
|
||||
prometheus.CounterValue,
|
||||
data.IoWriteOperationsPerSec,
|
||||
name, pidString, "write",
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.pageFaultsTotal,
|
||||
prometheus.CounterValue,
|
||||
data.PageFaultsPerSec,
|
||||
name, pidString,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.pageFileBytes,
|
||||
prometheus.GaugeValue,
|
||||
data.PageFileBytes,
|
||||
name, pidString,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.poolBytes,
|
||||
prometheus.GaugeValue,
|
||||
data.PoolNonPagedBytes,
|
||||
name, pidString, "nonpaged",
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.poolBytes,
|
||||
prometheus.GaugeValue,
|
||||
data.PoolPagedBytes,
|
||||
name, pidString, "paged",
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.priorityBase,
|
||||
prometheus.GaugeValue,
|
||||
data.PriorityBase,
|
||||
name, pidString,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.privateBytes,
|
||||
prometheus.GaugeValue,
|
||||
data.PrivateBytes,
|
||||
name, pidString,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.threadCount,
|
||||
prometheus.GaugeValue,
|
||||
data.ThreadCount,
|
||||
name, pidString,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.virtualBytes,
|
||||
prometheus.GaugeValue,
|
||||
data.VirtualBytes,
|
||||
name, pidString,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.workingSetPrivate,
|
||||
prometheus.GaugeValue,
|
||||
data.WorkingSetPrivate,
|
||||
name, pidString,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.workingSetPeak,
|
||||
prometheus.GaugeValue,
|
||||
data.WorkingSetPeak,
|
||||
name, pidString,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.workingSet,
|
||||
prometheus.GaugeValue,
|
||||
data.WorkingSet,
|
||||
name, pidString,
|
||||
)
|
||||
})()
|
||||
}
|
||||
}
|
||||
285
internal/collector/process/process_v2.go
Normal file
285
internal/collector/process/process_v2.go
Normal file
@@ -0,0 +1,285 @@
|
||||
// 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 process
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"runtime/debug"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/prometheus-community/windows_exporter/internal/pdh"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
type collectorV2 struct {
|
||||
perfDataCollectorV2 *pdh.Collector
|
||||
perfDataObjectV2 []perfDataCounterValuesV2
|
||||
workerChV2 chan processWorkerRequestV2
|
||||
}
|
||||
|
||||
type processWorkerRequestV2 struct {
|
||||
ch chan<- prometheus.Metric
|
||||
name string
|
||||
performanceCounterValues perfDataCounterValuesV2
|
||||
waitGroup *sync.WaitGroup
|
||||
workerProcesses []WorkerProcess
|
||||
}
|
||||
|
||||
func (c *Collector) closeV2() {
|
||||
c.perfDataCollectorV2.Close()
|
||||
|
||||
if c.workerChV2 != nil {
|
||||
close(c.workerChV2)
|
||||
c.workerChV2 = nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Collector) collectV2(ch chan<- prometheus.Metric, workerProcesses []WorkerProcess) error {
|
||||
err := c.perfDataCollectorV2.Collect(&c.perfDataObjectV2)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to collect metrics: %w", err)
|
||||
}
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
|
||||
for _, process := range c.perfDataObjectV2 {
|
||||
// Duplicate processes are suffixed #, and an index number. Remove those.
|
||||
name, _, _ := strings.Cut(process.Name, ":") // Process V2
|
||||
|
||||
if c.config.ProcessExclude.MatchString(name) || !c.config.ProcessInclude.MatchString(name) {
|
||||
continue
|
||||
}
|
||||
|
||||
wg.Add(1)
|
||||
|
||||
c.workerChV2 <- processWorkerRequestV2{
|
||||
ch: ch,
|
||||
name: name,
|
||||
performanceCounterValues: process,
|
||||
workerProcesses: workerProcesses,
|
||||
waitGroup: wg,
|
||||
}
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) collectWorkerV2() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
c.logger.Error("Worker panic",
|
||||
slog.Any("panic", r),
|
||||
slog.String("stack", string(debug.Stack())),
|
||||
)
|
||||
|
||||
// Restart the collectWorker
|
||||
go c.collectWorkerV2()
|
||||
}
|
||||
}()
|
||||
|
||||
for req := range c.workerChV2 {
|
||||
(func() {
|
||||
defer req.waitGroup.Done()
|
||||
|
||||
ch := req.ch
|
||||
name := req.name
|
||||
data := req.performanceCounterValues
|
||||
|
||||
pid := uint64(data.ProcessID)
|
||||
parentPID := strconv.FormatUint(uint64(data.CreatingProcessID), 10)
|
||||
|
||||
if c.config.EnableWorkerProcess {
|
||||
for _, wp := range req.workerProcesses {
|
||||
if wp.ProcessId == pid {
|
||||
name = strings.Join([]string{name, wp.AppPoolName}, "_")
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cmdLine, processOwner, processGroupID, err := c.getProcessInformation(uint32(pid))
|
||||
if err != nil {
|
||||
slog.LogAttrs(context.Background(), slog.LevelDebug, "Failed to get process information",
|
||||
slog.Uint64("pid", pid),
|
||||
slog.Any("err", err),
|
||||
)
|
||||
}
|
||||
|
||||
pidString := strconv.FormatUint(pid, 10)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.info,
|
||||
prometheus.GaugeValue,
|
||||
1.0,
|
||||
name, pidString, parentPID, strconv.Itoa(int(processGroupID)), processOwner, cmdLine,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.startTime,
|
||||
prometheus.GaugeValue,
|
||||
data.ElapsedTime,
|
||||
name, pidString,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.handleCount,
|
||||
prometheus.GaugeValue,
|
||||
data.HandleCount,
|
||||
name, pidString,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.cpuTimeTotal,
|
||||
prometheus.CounterValue,
|
||||
data.PercentPrivilegedTime,
|
||||
name, pidString, "privileged",
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.cpuTimeTotal,
|
||||
prometheus.CounterValue,
|
||||
data.PercentUserTime,
|
||||
name, pidString, "user",
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.ioBytesTotal,
|
||||
prometheus.CounterValue,
|
||||
data.IoOtherBytesPerSec,
|
||||
name, pidString, "other",
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.ioOperationsTotal,
|
||||
prometheus.CounterValue,
|
||||
data.IoOtherOperationsPerSec,
|
||||
name, pidString, "other",
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.ioBytesTotal,
|
||||
prometheus.CounterValue,
|
||||
data.IoReadBytesPerSec,
|
||||
name, pidString, "read",
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.ioOperationsTotal,
|
||||
prometheus.CounterValue,
|
||||
data.IoReadOperationsPerSec,
|
||||
name, pidString, "read",
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.ioBytesTotal,
|
||||
prometheus.CounterValue,
|
||||
data.IoWriteBytesPerSec,
|
||||
name, pidString, "write",
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.ioOperationsTotal,
|
||||
prometheus.CounterValue,
|
||||
data.IoWriteOperationsPerSec,
|
||||
name, pidString, "write",
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.pageFaultsTotal,
|
||||
prometheus.CounterValue,
|
||||
data.PageFaultsPerSec,
|
||||
name, pidString,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.pageFileBytes,
|
||||
prometheus.GaugeValue,
|
||||
data.PageFileBytes,
|
||||
name, pidString,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.poolBytes,
|
||||
prometheus.GaugeValue,
|
||||
data.PoolNonPagedBytes,
|
||||
name, pidString, "nonpaged",
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.poolBytes,
|
||||
prometheus.GaugeValue,
|
||||
data.PoolPagedBytes,
|
||||
name, pidString, "paged",
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.priorityBase,
|
||||
prometheus.GaugeValue,
|
||||
data.PriorityBase,
|
||||
name, pidString,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.privateBytes,
|
||||
prometheus.GaugeValue,
|
||||
data.PrivateBytes,
|
||||
name, pidString,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.threadCount,
|
||||
prometheus.GaugeValue,
|
||||
data.ThreadCount,
|
||||
name, pidString,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.virtualBytes,
|
||||
prometheus.GaugeValue,
|
||||
data.VirtualBytes,
|
||||
name, pidString,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.workingSetPrivate,
|
||||
prometheus.GaugeValue,
|
||||
data.WorkingSetPrivate,
|
||||
name, pidString,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.workingSetPeak,
|
||||
prometheus.GaugeValue,
|
||||
data.WorkingSetPeak,
|
||||
name, pidString,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.workingSet,
|
||||
prometheus.GaugeValue,
|
||||
data.WorkingSet,
|
||||
name, pidString,
|
||||
)
|
||||
})()
|
||||
}
|
||||
}
|
||||
87
internal/collector/process/types.go
Normal file
87
internal/collector/process/types.go
Normal file
@@ -0,0 +1,87 @@
|
||||
// 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 process
|
||||
|
||||
type WorkerProcess struct {
|
||||
AppPoolName string `mi:"AppPoolName"`
|
||||
ProcessId uint64 `mi:"ProcessId"`
|
||||
}
|
||||
|
||||
type perfDataCounterValuesV1 struct {
|
||||
Name string
|
||||
|
||||
PercentProcessorTime float64 `perfdata:"% Processor Time"`
|
||||
PercentPrivilegedTime float64 `perfdata:"% Privileged Time"`
|
||||
PercentUserTime float64 `perfdata:"% User Time"`
|
||||
CreatingProcessID float64 `perfdata:"Creating Process ID"`
|
||||
ElapsedTime float64 `perfdata:"Elapsed Time"`
|
||||
HandleCount float64 `perfdata:"Handle Count"`
|
||||
IoDataBytesPerSec float64 `perfdata:"IO Data Bytes/sec"`
|
||||
IoDataOperationsPerSec float64 `perfdata:"IO Data Operations/sec"`
|
||||
IoOtherBytesPerSec float64 `perfdata:"IO Other Bytes/sec"`
|
||||
IoOtherOperationsPerSec float64 `perfdata:"IO Other Operations/sec"`
|
||||
IoReadBytesPerSec float64 `perfdata:"IO Read Bytes/sec"`
|
||||
IoReadOperationsPerSec float64 `perfdata:"IO Read Operations/sec"`
|
||||
IoWriteBytesPerSec float64 `perfdata:"IO Write Bytes/sec"`
|
||||
IoWriteOperationsPerSec float64 `perfdata:"IO Write Operations/sec"`
|
||||
PageFaultsPerSec float64 `perfdata:"Page Faults/sec"`
|
||||
PageFileBytesPeak float64 `perfdata:"Page File Bytes Peak"`
|
||||
PageFileBytes float64 `perfdata:"Page File Bytes"`
|
||||
PoolNonPagedBytes float64 `perfdata:"Pool Nonpaged Bytes"`
|
||||
PoolPagedBytes float64 `perfdata:"Pool Paged Bytes"`
|
||||
PriorityBase float64 `perfdata:"Priority Base"`
|
||||
PrivateBytes float64 `perfdata:"Private Bytes"`
|
||||
ThreadCount float64 `perfdata:"Thread Count"`
|
||||
VirtualBytesPeak float64 `perfdata:"Virtual Bytes Peak"`
|
||||
VirtualBytes float64 `perfdata:"Virtual Bytes"`
|
||||
WorkingSetPrivate float64 `perfdata:"Working Set - Private"`
|
||||
WorkingSetPeak float64 `perfdata:"Working Set Peak"`
|
||||
WorkingSet float64 `perfdata:"Working Set"`
|
||||
IdProcess float64 `perfdata:"ID Process"`
|
||||
}
|
||||
|
||||
type perfDataCounterValuesV2 struct {
|
||||
Name string
|
||||
|
||||
PercentProcessorTime float64 `perfdata:"% Processor Time"`
|
||||
PercentPrivilegedTime float64 `perfdata:"% Privileged Time"`
|
||||
PercentUserTime float64 `perfdata:"% User Time"`
|
||||
CreatingProcessID float64 `perfdata:"Creating Process ID"`
|
||||
ElapsedTime float64 `perfdata:"Elapsed Time"`
|
||||
HandleCount float64 `perfdata:"Handle Count"`
|
||||
IoDataBytesPerSec float64 `perfdata:"IO Data Bytes/sec"`
|
||||
IoDataOperationsPerSec float64 `perfdata:"IO Data Operations/sec"`
|
||||
IoOtherBytesPerSec float64 `perfdata:"IO Other Bytes/sec"`
|
||||
IoOtherOperationsPerSec float64 `perfdata:"IO Other Operations/sec"`
|
||||
IoReadBytesPerSec float64 `perfdata:"IO Read Bytes/sec"`
|
||||
IoReadOperationsPerSec float64 `perfdata:"IO Read Operations/sec"`
|
||||
IoWriteBytesPerSec float64 `perfdata:"IO Write Bytes/sec"`
|
||||
IoWriteOperationsPerSec float64 `perfdata:"IO Write Operations/sec"`
|
||||
PageFaultsPerSec float64 `perfdata:"Page Faults/sec"`
|
||||
PageFileBytesPeak float64 `perfdata:"Page File Bytes Peak"`
|
||||
PageFileBytes float64 `perfdata:"Page File Bytes"`
|
||||
PoolNonPagedBytes float64 `perfdata:"Pool Nonpaged Bytes"`
|
||||
PoolPagedBytes float64 `perfdata:"Pool Paged Bytes"`
|
||||
PriorityBase float64 `perfdata:"Priority Base"`
|
||||
PrivateBytes float64 `perfdata:"Private Bytes"`
|
||||
ThreadCount float64 `perfdata:"Thread Count"`
|
||||
VirtualBytesPeak float64 `perfdata:"Virtual Bytes Peak"`
|
||||
VirtualBytes float64 `perfdata:"Virtual Bytes"`
|
||||
WorkingSetPrivate float64 `perfdata:"Working Set - Private"`
|
||||
WorkingSetPeak float64 `perfdata:"Working Set Peak"`
|
||||
WorkingSet float64 `perfdata:"Working Set"`
|
||||
ProcessID float64 `perfdata:"Process ID"`
|
||||
}
|
||||
Reference in New Issue
Block a user