mirror of
https://github.com/prometheus-community/windows_exporter.git
synced 2026-02-08 05:56:37 +00:00
303 lines
6.9 KiB
Go
303 lines
6.9 KiB
Go
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
// Copyright 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"
|
|
"time"
|
|
|
|
"github.com/prometheus-community/windows_exporter/internal/mi"
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
)
|
|
|
|
type processWorkerRequest struct {
|
|
ch chan<- prometheus.Metric
|
|
name string
|
|
performanceCounterValues perfDataCounterValues
|
|
waitGroup *sync.WaitGroup
|
|
workerProcesses []WorkerProcess
|
|
}
|
|
|
|
func (c *Collector) collect(ch chan<- prometheus.Metric) error {
|
|
err := c.perfDataCollector.Collect(&c.perfDataObject)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to collect metrics: %w", err)
|
|
}
|
|
|
|
var workerProcesses []WorkerProcess
|
|
if c.config.EnableWorkerProcess {
|
|
if err := c.miSession.Query(&workerProcesses, mi.NamespaceRootWebAdministration, c.workerProcessMIQueryQuery); err != nil {
|
|
return fmt.Errorf("WMI query failed: %w", err)
|
|
}
|
|
}
|
|
|
|
wg := &sync.WaitGroup{}
|
|
|
|
for _, process := range c.perfDataObject {
|
|
// Duplicate processes are suffixed #, and an index number. Remove those.
|
|
name, _, _ := strings.Cut(process.Name, ":") // Process V2
|
|
|
|
// Duplicate processes are suffixed #, and an index number. Remove those.
|
|
name, _, _ = strings.Cut(name, "#") // Process V1
|
|
|
|
if c.config.ProcessExclude.MatchString(name) || !c.config.ProcessInclude.MatchString(name) {
|
|
continue
|
|
}
|
|
|
|
if process.ProcessID == 0 && name != "Idle" {
|
|
c.logger.LogAttrs(context.Background(), slog.LevelDebug, "Skipping process with PID 0",
|
|
slog.String("name", name),
|
|
slog.String("process_name", process.Name),
|
|
slog.Any("process", fmt.Sprintf("%+v", process)),
|
|
)
|
|
|
|
continue
|
|
}
|
|
|
|
wg.Add(1)
|
|
|
|
c.workerCh <- processWorkerRequest{
|
|
ch: ch,
|
|
name: name,
|
|
performanceCounterValues: process,
|
|
workerProcesses: workerProcesses,
|
|
waitGroup: wg,
|
|
}
|
|
}
|
|
|
|
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
|
|
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,
|
|
)
|
|
|
|
startTime := float64(time.Now().UnixMicro())/1e6 - data.ElapsedTime
|
|
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.startTimeOld,
|
|
prometheus.GaugeValue,
|
|
startTime,
|
|
name, pidString,
|
|
)
|
|
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.startTime,
|
|
prometheus.GaugeValue,
|
|
startTime,
|
|
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,
|
|
)
|
|
})()
|
|
}
|
|
}
|