mirror of
https://github.com/prometheus-community/windows_exporter.git
synced 2026-02-08 05:56:37 +00:00
chore: release 0.29.0.rc0 (#1600)
This commit is contained in:
@@ -4,10 +4,9 @@ package ad
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log/slog"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/yusufpapurcu/wmi"
|
||||
@@ -108,15 +107,15 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, wmiClient *wmi.Client) error {
|
||||
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
|
||||
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
|
||||
return errors.New("wmiClient or SWbemServicesClient is nil")
|
||||
}
|
||||
@@ -502,12 +501,16 @@ func (c *Collector) Build(_ log.Logger, wmiClient *wmi.Client) error {
|
||||
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
if err := c.collect(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting ad metrics", "err", err)
|
||||
logger.Error("failed collecting ad metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -668,6 +671,7 @@ func (c *Collector) collect(ch chan<- prometheus.Metric) error {
|
||||
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_DirectoryServices_DirectoryServices", &dst); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(dst) == 0 {
|
||||
return errors.New("WMI query returned empty result set")
|
||||
}
|
||||
|
||||
@@ -4,11 +4,10 @@ package adcs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/perflib"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/utils"
|
||||
@@ -60,15 +59,15 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{"Certification Authority"}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
|
||||
c.requestsPerSecond = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "requests_total"),
|
||||
"Total certificate requests processed",
|
||||
@@ -151,12 +150,16 @@ func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
if err := c.collectADCSCounters(ctx, logger, ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting ADCS metrics", "err", err)
|
||||
logger.Error("failed collecting ADCS metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -177,15 +180,18 @@ type perflibADCS struct {
|
||||
SignedCertificateTimestampListProcessingTime float64 `perflib:"Signed Certificate Timestamp List processing time (ms)"`
|
||||
}
|
||||
|
||||
func (c *Collector) collectADCSCounters(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
func (c *Collector) collectADCSCounters(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
dst := make([]perflibADCS, 0)
|
||||
|
||||
if _, ok := ctx.PerfObjects["Certification Authority"]; !ok {
|
||||
return errors.New("perflib did not contain an entry for Certification Authority")
|
||||
}
|
||||
|
||||
err := perflib.UnmarshalObject(ctx.PerfObjects["Certification Authority"], &dst, logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(dst) == 0 {
|
||||
return errors.New("perflib query for Certification Authority (ADCS) returned empty result set")
|
||||
}
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
package adfs
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"math"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/perflib"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
@@ -87,15 +87,15 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{"AD FS"}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
|
||||
c.adLoginConnectionFailures = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "ad_login_connection_failures_total"),
|
||||
"Total number of connection failures to an Active Directory domain controller",
|
||||
@@ -404,9 +404,11 @@ type perflibADFS struct {
|
||||
FederationMetadataRequests float64 `perflib:"Federation Metadata Requests"`
|
||||
}
|
||||
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
var adfsData []perflibADFS
|
||||
|
||||
err := perflib.UnmarshalObject(ctx.PerfObjects["AD FS"], &adfsData, logger)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -669,5 +671,6 @@ func (c *Collector) Collect(ctx *types.ScrapeContext, logger log.Logger, ch chan
|
||||
prometheus.CounterValue,
|
||||
adfsData[0].FederationMetadataRequests,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
25
pkg/collector/cache/cache.go
vendored
25
pkg/collector/cache/cache.go
vendored
@@ -3,9 +3,9 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/perflib"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
@@ -74,15 +74,15 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{"Cache"}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
|
||||
c.asyncCopyReadsTotal = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "async_copy_reads_total"),
|
||||
"(AsyncCopyReadsTotal)",
|
||||
@@ -257,14 +257,17 @@ func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Collect implements the Collector interface.
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
if err := c.collect(ctx, logger, ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting cache metrics", "err", err)
|
||||
logger.Error("failed collecting cache metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
@@ -306,9 +309,11 @@ type perflibCache struct {
|
||||
SyncPinReadsTotal float64 `perflib:"Sync Pin Reads/sec"`
|
||||
}
|
||||
|
||||
func (c *Collector) collect(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
var dst []perflibCache // Single-instance class, array is required but will have single entry.
|
||||
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects["Cache"], &dst, logger); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -5,11 +5,11 @@ package collector
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/collector/ad"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/collector/adcs"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/collector/adfs"
|
||||
@@ -44,6 +44,7 @@ import (
|
||||
"github.com/prometheus-community/windows_exporter/pkg/collector/netframework_clrsecurity"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/collector/nps"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/collector/os"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/collector/perfdata"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/collector/physical_disk"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/collector/printer"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/collector/process"
|
||||
@@ -68,7 +69,7 @@ import (
|
||||
)
|
||||
|
||||
// NewWithFlags To be called by the exporter for collector initialization before running kingpin.Parse.
|
||||
func NewWithFlags(app *kingpin.Application) *Collectors {
|
||||
func NewWithFlags(app *kingpin.Application) *MetricCollectors {
|
||||
collectors := map[string]Collector{}
|
||||
|
||||
for name, builder := range BuildersWithFlags {
|
||||
@@ -81,8 +82,8 @@ func NewWithFlags(app *kingpin.Application) *Collectors {
|
||||
// NewWithConfig To be called by the external libraries for collector initialization without running kingpin.Parse
|
||||
//
|
||||
//goland:noinspection GoUnusedExportedFunction
|
||||
func NewWithConfig(config Config) *Collectors {
|
||||
collectors := map[string]Collector{}
|
||||
func NewWithConfig(config Config) *MetricCollectors {
|
||||
collectors := Map{}
|
||||
collectors[ad.Name] = ad.New(&config.AD)
|
||||
collectors[adcs.Name] = adcs.New(&config.ADCS)
|
||||
collectors[adfs.Name] = adfs.New(&config.ADFS)
|
||||
@@ -117,6 +118,7 @@ func NewWithConfig(config Config) *Collectors {
|
||||
collectors[netframework_clrsecurity.Name] = netframework_clrsecurity.New(&config.NetframeworkClrsecurity)
|
||||
collectors[nps.Name] = nps.New(&config.Nps)
|
||||
collectors[os.Name] = os.New(&config.Os)
|
||||
collectors[perfdata.Name] = perfdata.New(&config.PerfData)
|
||||
collectors[physical_disk.Name] = physical_disk.New(&config.PhysicalDisk)
|
||||
collectors[printer.Name] = printer.New(&config.Printer)
|
||||
collectors[process.Name] = process.New(&config.Process)
|
||||
@@ -140,16 +142,16 @@ func NewWithConfig(config Config) *Collectors {
|
||||
}
|
||||
|
||||
// New To be called by the external libraries for collector initialization.
|
||||
func New(collectors Map) *Collectors {
|
||||
return &Collectors{
|
||||
collectors: collectors,
|
||||
wmiClient: &wmi.Client{
|
||||
func New(collectors Map) *MetricCollectors {
|
||||
return &MetricCollectors{
|
||||
Collectors: collectors,
|
||||
WMIClient: &wmi.Client{
|
||||
AllowMissingFields: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Collectors) SetPerfCounterQuery(logger log.Logger) error {
|
||||
func (c *MetricCollectors) SetPerfCounterQuery(logger *slog.Logger) error {
|
||||
var (
|
||||
err error
|
||||
|
||||
@@ -157,9 +159,9 @@ func (c *Collectors) SetPerfCounterQuery(logger log.Logger) error {
|
||||
perfIndicies []string
|
||||
)
|
||||
|
||||
perfCounterDependencies := make([]string, 0, len(c.collectors))
|
||||
perfCounterDependencies := make([]string, 0, len(c.Collectors))
|
||||
|
||||
for _, collector := range c.collectors {
|
||||
for _, collector := range c.Collectors {
|
||||
perfCounterNames, err = collector.GetPerfCounter(logger)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -173,31 +175,31 @@ func (c *Collectors) SetPerfCounterQuery(logger log.Logger) error {
|
||||
perfCounterDependencies = append(perfCounterDependencies, strings.Join(perfIndicies, " "))
|
||||
}
|
||||
|
||||
c.perfCounterQuery = strings.Join(perfCounterDependencies, " ")
|
||||
c.PerfCounterQuery = strings.Join(perfCounterDependencies, " ")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Enable removes all collectors that not enabledCollectors.
|
||||
func (c *Collectors) Enable(enabledCollectors []string) {
|
||||
for name := range c.collectors {
|
||||
func (c *MetricCollectors) Enable(enabledCollectors []string) {
|
||||
for name := range c.Collectors {
|
||||
if !slices.Contains(enabledCollectors, name) {
|
||||
delete(c.collectors, name)
|
||||
delete(c.Collectors, name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build To be called by the exporter for collector initialization.
|
||||
func (c *Collectors) Build(logger log.Logger) error {
|
||||
func (c *MetricCollectors) Build(logger *slog.Logger) error {
|
||||
var err error
|
||||
|
||||
c.wmiClient.SWbemServicesClient, err = wmi.InitializeSWbemServices(c.wmiClient)
|
||||
c.WMIClient.SWbemServicesClient, err = wmi.InitializeSWbemServices(c.WMIClient)
|
||||
if err != nil {
|
||||
return fmt.Errorf("initialize SWbemServices: %w", err)
|
||||
}
|
||||
|
||||
for _, collector := range c.collectors {
|
||||
if err = collector.Build(logger, c.wmiClient); err != nil {
|
||||
for _, collector := range c.Collectors {
|
||||
if err = collector.Build(logger, c.WMIClient); err != nil {
|
||||
return fmt.Errorf("error build collector %s: %w", collector.GetName(), err)
|
||||
}
|
||||
}
|
||||
@@ -206,12 +208,12 @@ func (c *Collectors) Build(logger log.Logger) error {
|
||||
}
|
||||
|
||||
// PrepareScrapeContext creates a ScrapeContext to be used during a single scrape.
|
||||
func (c *Collectors) PrepareScrapeContext() (*types.ScrapeContext, error) {
|
||||
if c.perfCounterQuery == "" { // if perfCounterQuery is empty, no perf counters are needed.
|
||||
func (c *MetricCollectors) PrepareScrapeContext() (*types.ScrapeContext, error) {
|
||||
if c.PerfCounterQuery == "" { // if perfCounterQuery is empty, no perf counters are needed.
|
||||
return &types.ScrapeContext{}, nil
|
||||
}
|
||||
|
||||
objs, err := perflib.GetPerflibSnapshot(c.perfCounterQuery)
|
||||
objs, err := perflib.GetPerflibSnapshot(c.PerfCounterQuery)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -220,17 +222,17 @@ func (c *Collectors) PrepareScrapeContext() (*types.ScrapeContext, error) {
|
||||
}
|
||||
|
||||
// Close To be called by the exporter for collector cleanup.
|
||||
func (c *Collectors) Close() error {
|
||||
errs := make([]error, 0, len(c.collectors))
|
||||
func (c *MetricCollectors) Close(logger *slog.Logger) error {
|
||||
errs := make([]error, 0, len(c.Collectors))
|
||||
|
||||
for _, collector := range c.collectors {
|
||||
if err := collector.Close(); err != nil {
|
||||
for _, collector := range c.Collectors {
|
||||
if err := collector.Close(logger); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
if c.wmiClient != nil && c.wmiClient.SWbemServicesClient != nil {
|
||||
if err := c.wmiClient.SWbemServicesClient.Close(); err != nil {
|
||||
if c.WMIClient != nil && c.WMIClient.SWbemServicesClient != nil {
|
||||
if err := c.WMIClient.SWbemServicesClient.Close(); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import (
|
||||
"github.com/prometheus-community/windows_exporter/pkg/collector/netframework_clrsecurity"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/collector/nps"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/collector/os"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/collector/perfdata"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/collector/physical_disk"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/collector/printer"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/collector/process"
|
||||
@@ -90,6 +91,7 @@ type Config struct {
|
||||
NetframeworkClrsecurity netframework_clrsecurity.Config `yaml:"netframework_clrsecurity"`
|
||||
Nps nps.Config `yaml:"nps"`
|
||||
Os os.Config `yaml:"os"`
|
||||
PerfData perfdata.Config `yaml:"perf_data"`
|
||||
PhysicalDisk physical_disk.Config `yaml:"physical_disk"`
|
||||
Printer printer.Config `yaml:"printer"`
|
||||
Process process.Config `yaml:"process"`
|
||||
@@ -148,6 +150,7 @@ var ConfigDefaults = Config{
|
||||
NetframeworkClrsecurity: netframework_clrsecurity.ConfigDefaults,
|
||||
Nps: nps.ConfigDefaults,
|
||||
Os: os.ConfigDefaults,
|
||||
PerfData: perfdata.ConfigDefaults,
|
||||
PhysicalDisk: physical_disk.ConfigDefaults,
|
||||
Printer: printer.ConfigDefaults,
|
||||
Process: process.ConfigDefaults,
|
||||
|
||||
@@ -5,12 +5,11 @@ package container
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
"github.com/Microsoft/hcsshim"
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/perflib"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
@@ -79,15 +78,15 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
|
||||
c.containerAvailable = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "available"),
|
||||
"Available",
|
||||
@@ -202,21 +201,27 @@ func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
if err := c.collect(logger, ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting collector metrics", "err", err)
|
||||
logger.Error("failed collecting collector metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) collect(logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
func (c *Collector) collect(logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
// Types Container is passed to get the containers compute systems only
|
||||
containers, err := hcsshim.GetContainers(hcsshim.ComputeSystemQuery{Types: []string{"Container"}})
|
||||
if err != nil {
|
||||
_ = level.Error(logger).Log("msg", "Err in Getting containers", "err", err)
|
||||
logger.Error("Err in Getting containers",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -240,9 +245,16 @@ func (c *Collector) collect(logger log.Logger, ch chan<- prometheus.Metric) erro
|
||||
|
||||
if err = c.collectContainer(logger, ch, containerDetails, containerIdWithPrefix); err != nil {
|
||||
if hcsshim.IsNotExist(err) {
|
||||
_ = level.Debug(logger).Log("msg", "err in fetching container statistics", "containerId", containerDetails.ID, "err", err)
|
||||
logger.Debug("err in fetching container statistics",
|
||||
slog.String("container_id", containerDetails.ID),
|
||||
slog.Any("err", err),
|
||||
)
|
||||
} else {
|
||||
_ = level.Error(logger).Log("msg", "err in fetching container statistics", "containerId", containerDetails.ID, "err", err)
|
||||
logger.Error("err in fetching container statistics",
|
||||
slog.String("container_id", containerDetails.ID),
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
collectErrors = append(collectErrors, err)
|
||||
}
|
||||
|
||||
@@ -263,7 +275,7 @@ func (c *Collector) collect(logger log.Logger, ch chan<- prometheus.Metric) erro
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) collectContainer(logger log.Logger, ch chan<- prometheus.Metric, containerDetails hcsshim.ContainerProperties, containerIdWithPrefix string) error {
|
||||
func (c *Collector) collectContainer(logger *slog.Logger, ch chan<- prometheus.Metric, containerDetails hcsshim.ContainerProperties, containerIdWithPrefix string) error {
|
||||
container, err := hcsshim.OpenContainer(containerDetails.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error in opening container: %w", err)
|
||||
@@ -275,7 +287,9 @@ func (c *Collector) collectContainer(logger log.Logger, ch chan<- prometheus.Met
|
||||
}
|
||||
|
||||
if err := container.Close(); err != nil {
|
||||
_ = level.Error(logger).Log("err", fmt.Errorf("error in closing container: %w", err))
|
||||
logger.Error("error in closing container",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -358,22 +372,27 @@ func (c *Collector) collectContainer(logger log.Logger, ch chan<- prometheus.Met
|
||||
// With HNSv2, the network stats must be collected from hcsshim.HNSListEndpointRequest.
|
||||
// Network statistics from the container.Statistics() are providing data only, if HNSv1 is used.
|
||||
// Ref: https://github.com/prometheus-community/windows_exporter/pull/1218
|
||||
func (c *Collector) collectNetworkMetrics(logger log.Logger, ch chan<- prometheus.Metric, containerPrefixes map[string]string) error {
|
||||
func (c *Collector) collectNetworkMetrics(logger *slog.Logger, ch chan<- prometheus.Metric, containerPrefixes map[string]string) error {
|
||||
hnsEndpoints, err := hcsshim.HNSListEndpointRequest()
|
||||
if err != nil {
|
||||
_ = level.Warn(logger).Log("msg", "Failed to collect network stats for containers")
|
||||
logger.Warn("Failed to collect network stats for containers")
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if len(hnsEndpoints) == 0 {
|
||||
_ = level.Info(logger).Log("msg", "No network stats for containers to collect")
|
||||
logger.Info("No network stats for containers to collect")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, endpoint := range hnsEndpoints {
|
||||
endpointStats, err := hcsshim.GetHNSEndpointStats(endpoint.Id)
|
||||
if err != nil {
|
||||
_ = level.Warn(logger).Log("msg", "Failed to collect network stats for interface "+endpoint.Id, "err", err)
|
||||
logger.Warn("Failed to collect network stats for interface "+endpoint.Id,
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -381,7 +400,8 @@ func (c *Collector) collectNetworkMetrics(logger log.Logger, ch chan<- prometheu
|
||||
containerIdWithPrefix, ok := containerPrefixes[containerId]
|
||||
|
||||
if !ok {
|
||||
_ = level.Debug(logger).Log("msg", "Failed to collect network stats for container "+containerId)
|
||||
logger.Debug("Failed to collect network stats for container " + containerId)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -3,13 +3,12 @@
|
||||
package cpu
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/perflib"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/winversion"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/yusufpapurcu/wmi"
|
||||
)
|
||||
@@ -23,6 +22,7 @@ var ConfigDefaults = Config{}
|
||||
type Collector struct {
|
||||
config Config
|
||||
|
||||
logicalProcessors *prometheus.Desc
|
||||
cStateSecondsTotal *prometheus.Desc
|
||||
timeTotal *prometheus.Desc
|
||||
interruptsTotal *prometheus.Desc
|
||||
@@ -58,18 +58,22 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
if winversion.WindowsVersionFloat() > 6.05 {
|
||||
return []string{"Processor Information"}, nil
|
||||
}
|
||||
return []string{"Processor"}, nil
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{"Processor Information"}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
|
||||
c.logicalProcessors = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "logical_processor"),
|
||||
"Total number of logical processors",
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
|
||||
c.cStateSecondsTotal = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "cstate_seconds_total"),
|
||||
"Time spent in low-power idle state",
|
||||
@@ -95,16 +99,6 @@ func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
nil,
|
||||
)
|
||||
|
||||
// For Windows 2008 (version 6.0) or earlier we only have the "Processor"
|
||||
// class. As of Windows 2008 R2 (version 6.1) the more detailed
|
||||
// "Processor Information" set is available (although some of the counters
|
||||
// are added in later versions, so we aren't guaranteed to get all of
|
||||
// them).
|
||||
// Value 6.05 was selected to split between Windows versions.
|
||||
if winversion.WindowsVersionFloat() < 6.05 {
|
||||
return nil
|
||||
}
|
||||
|
||||
c.cStateSecondsTotal = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "cstate_seconds_total"),
|
||||
"Time spent in low-power idle state",
|
||||
@@ -187,113 +181,10 @@ func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
if winversion.WindowsVersionFloat() > 6.05 {
|
||||
return c.CollectFull(ctx, logger, ch)
|
||||
}
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
return c.CollectBasic(ctx, logger, ch)
|
||||
}
|
||||
|
||||
type perflibProcessor struct {
|
||||
Name string
|
||||
C1Transitions float64 `perflib:"C1 Transitions/sec"`
|
||||
C2Transitions float64 `perflib:"C2 Transitions/sec"`
|
||||
C3Transitions float64 `perflib:"C3 Transitions/sec"`
|
||||
DPCRate float64 `perflib:"DPC Rate"`
|
||||
DPCsQueued float64 `perflib:"DPCs Queued/sec"`
|
||||
Interrupts float64 `perflib:"Interrupts/sec"`
|
||||
PercentC1Time float64 `perflib:"% C1 Time"`
|
||||
PercentC2Time float64 `perflib:"% C2 Time"`
|
||||
PercentC3Time float64 `perflib:"% C3 Time"`
|
||||
PercentDPCTime float64 `perflib:"% DPC Time"`
|
||||
PercentIdleTime float64 `perflib:"% Idle Time"`
|
||||
PercentInterruptTime float64 `perflib:"% Interrupt Time"`
|
||||
PercentPrivilegedTime float64 `perflib:"% Privileged Time"`
|
||||
PercentProcessorTime float64 `perflib:"% Processor Time"`
|
||||
PercentUserTime float64 `perflib:"% User Time"`
|
||||
}
|
||||
|
||||
func (c *Collector) CollectBasic(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
data := make([]perflibProcessor, 0)
|
||||
err := perflib.UnmarshalObject(ctx.PerfObjects["Processor"], &data, logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, cpu := range data {
|
||||
if strings.Contains(strings.ToLower(cpu.Name), "_total") {
|
||||
continue
|
||||
}
|
||||
core := cpu.Name
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.cStateSecondsTotal,
|
||||
prometheus.CounterValue,
|
||||
cpu.PercentC1Time,
|
||||
core, "c1",
|
||||
)
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.cStateSecondsTotal,
|
||||
prometheus.CounterValue,
|
||||
cpu.PercentC2Time,
|
||||
core, "c2",
|
||||
)
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.cStateSecondsTotal,
|
||||
prometheus.CounterValue,
|
||||
cpu.PercentC3Time,
|
||||
core, "c3",
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.timeTotal,
|
||||
prometheus.CounterValue,
|
||||
cpu.PercentIdleTime,
|
||||
core, "idle",
|
||||
)
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.timeTotal,
|
||||
prometheus.CounterValue,
|
||||
cpu.PercentInterruptTime,
|
||||
core, "interrupt",
|
||||
)
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.timeTotal,
|
||||
prometheus.CounterValue,
|
||||
cpu.PercentDPCTime,
|
||||
core, "dpc",
|
||||
)
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.timeTotal,
|
||||
prometheus.CounterValue,
|
||||
cpu.PercentPrivilegedTime,
|
||||
core, "privileged",
|
||||
)
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.timeTotal,
|
||||
prometheus.CounterValue,
|
||||
cpu.PercentUserTime,
|
||||
core, "user",
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.interruptsTotal,
|
||||
prometheus.CounterValue,
|
||||
cpu.Interrupts,
|
||||
core,
|
||||
)
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.dpcsTotal,
|
||||
prometheus.CounterValue,
|
||||
cpu.DPCsQueued,
|
||||
core,
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
return c.CollectFull(ctx, logger, ch)
|
||||
}
|
||||
|
||||
type perflibProcessorInformation struct {
|
||||
@@ -325,20 +216,26 @@ type perflibProcessorInformation struct {
|
||||
UserTimeSeconds float64 `perflib:"% User Time"`
|
||||
}
|
||||
|
||||
func (c *Collector) CollectFull(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) CollectFull(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
data := make([]perflibProcessorInformation, 0)
|
||||
|
||||
err := perflib.UnmarshalObject(ctx.PerfObjects["Processor Information"], &data, logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var coreCount float64
|
||||
|
||||
for _, cpu := range data {
|
||||
if strings.Contains(strings.ToLower(cpu.Name), "_total") {
|
||||
continue
|
||||
}
|
||||
|
||||
core := cpu.Name
|
||||
|
||||
coreCount++
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.cStateSecondsTotal,
|
||||
prometheus.CounterValue,
|
||||
@@ -459,5 +356,11 @@ func (c *Collector) CollectFull(ctx *types.ScrapeContext, logger log.Logger, ch
|
||||
)
|
||||
}
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.logicalProcessors,
|
||||
prometheus.GaugeValue,
|
||||
coreCount,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -4,12 +4,11 @@ package cpu_info
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log/slog"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/yusufpapurcu/wmi"
|
||||
@@ -58,15 +57,15 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, wmiClient *wmi.Client) error {
|
||||
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
|
||||
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
|
||||
return errors.New("wmiClient or SWbemServicesClient is nil")
|
||||
}
|
||||
@@ -152,12 +151,16 @@ type win32Processor struct {
|
||||
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
if err := c.collect(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting cpu_info metrics", "err", err)
|
||||
logger.Error("failed collecting cpu_info metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -169,6 +172,7 @@ func (c *Collector) collect(ch chan<- prometheus.Metric) error {
|
||||
if err := c.wmiClient.Query("SELECT Architecture, DeviceId, Description, Family, L2CacheSize, L3CacheSize, Name, ThreadCount, NumberOfCores, NumberOfEnabledCore, NumberOfLogicalProcessors FROM Win32_Processor", &dst); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(dst) == 0 {
|
||||
return errors.New("WMI query returned empty result set")
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
package cs
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/headers/sysinfoapi"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
@@ -22,9 +22,15 @@ var ConfigDefaults = Config{}
|
||||
type Collector struct {
|
||||
config Config
|
||||
|
||||
// physicalMemoryBytes
|
||||
// Deprecated: Use windows_cpu_logical_processor instead
|
||||
physicalMemoryBytes *prometheus.Desc
|
||||
logicalProcessors *prometheus.Desc
|
||||
hostname *prometheus.Desc
|
||||
// logicalProcessors
|
||||
// Deprecated: Use windows_physical_memory_total_bytes instead
|
||||
logicalProcessors *prometheus.Desc
|
||||
// hostname
|
||||
// Deprecated: Use windows_os_hostname instead
|
||||
hostname *prometheus.Desc
|
||||
}
|
||||
|
||||
func New(config *Config) *Collector {
|
||||
@@ -47,30 +53,35 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error {
|
||||
logger.Warn("The cs collector is deprecated and will be removed in a future release. " +
|
||||
"Logical processors has been moved to cpu_info collector. " +
|
||||
"Physical memory has been moved to memory collector. " +
|
||||
"Hostname has been moved to os collector.")
|
||||
|
||||
c.logicalProcessors = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "logical_processors"),
|
||||
"ComputerSystem.NumberOfLogicalProcessors",
|
||||
"Deprecated: Use windows_cpu_logical_processor instead",
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
c.physicalMemoryBytes = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "physical_memory_bytes"),
|
||||
"ComputerSystem.TotalPhysicalMemory",
|
||||
"Deprecated: Use windows_physical_memory_total_bytes instead",
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
c.hostname = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "hostname"),
|
||||
"Labelled system hostname information as provided by ComputerSystem.DNSHostName and ComputerSystem.Domain",
|
||||
"Deprecated: Use windows_os_hostname instead",
|
||||
[]string{
|
||||
"hostname",
|
||||
"domain",
|
||||
@@ -78,17 +89,22 @@ func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
},
|
||||
nil,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
if err := c.collect(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting cs metrics", "err", err)
|
||||
logger.Error("failed collecting cs metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -118,10 +134,12 @@ func (c *Collector) collect(ch chan<- prometheus.Metric) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
domain, err := sysinfoapi.GetComputerName(sysinfoapi.ComputerNameDNSDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fqdn, err := sysinfoapi.GetComputerName(sysinfoapi.ComputerNameDNSFullyQualified)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -3,12 +3,11 @@
|
||||
package dfsr
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/perflib"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
@@ -80,13 +79,14 @@ type Collector struct {
|
||||
dfsrChildCollectors []dfsrCollectorFunc
|
||||
}
|
||||
|
||||
type dfsrCollectorFunc func(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error
|
||||
type dfsrCollectorFunc func(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error
|
||||
|
||||
// Map Perflib sources to DFSR Collector names
|
||||
// e.g, volume -> DFS Replication Service Volumes.
|
||||
func dfsrGetPerfObjectName(collector string) string {
|
||||
prefix := "DFS "
|
||||
suffix := ""
|
||||
|
||||
switch collector {
|
||||
case "connection":
|
||||
suffix = "Replication Connections"
|
||||
@@ -95,6 +95,7 @@ func dfsrGetPerfObjectName(collector string) string {
|
||||
case "volume":
|
||||
suffix = "Replication Service Volumes"
|
||||
}
|
||||
|
||||
return prefix + suffix
|
||||
}
|
||||
|
||||
@@ -137,7 +138,7 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
// Perflib sources are dynamic, depending on the enabled child collectors
|
||||
expandedChildCollectors := slices.Compact(c.config.CollectorsEnabled)
|
||||
perflibDependencies := make([]string, 0, len(expandedChildCollectors))
|
||||
@@ -149,14 +150,14 @@ func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
return perflibDependencies, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(logger log.Logger, _ *wmi.Client) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
_ = level.Info(logger).Log("msg", "dfsr collector is in an experimental state! Metrics for this collector have not been tested.")
|
||||
logger.Info("dfsr collector is in an experimental state! Metrics for this collector have not been tested.")
|
||||
|
||||
// connection
|
||||
c.connectionBandwidthSavingsUsingDFSReplicationTotal = prometheus.NewDesc(
|
||||
@@ -459,6 +460,7 @@ func (c *Collector) Build(logger log.Logger, _ *wmi.Client) error {
|
||||
// for use in Collector.Collect().
|
||||
func (c *Collector) getDFSRChildCollectors(enabledCollectors []string) []dfsrCollectorFunc {
|
||||
var dfsrCollectors []dfsrCollectorFunc
|
||||
|
||||
for _, collector := range enabledCollectors {
|
||||
switch collector {
|
||||
case "connection":
|
||||
@@ -475,14 +477,15 @@ func (c *Collector) getDFSRChildCollectors(enabledCollectors []string) []dfsrCol
|
||||
|
||||
// Collect implements the Collector interface.
|
||||
// Sends metric values for each metric to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
for _, fn := range c.dfsrChildCollectors {
|
||||
err := fn(ctx, logger, ch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -501,9 +504,11 @@ type PerflibDFSRConnection struct {
|
||||
SizeOfFilesReceivedTotal float64 `perflib:"Size of Files Received"`
|
||||
}
|
||||
|
||||
func (c *Collector) collectConnection(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) collectConnection(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
var dst []PerflibDFSRConnection
|
||||
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects["DFS Replication Connections"], &dst, logger); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -572,6 +577,7 @@ func (c *Collector) collectConnection(ctx *types.ScrapeContext, logger log.Logge
|
||||
connection.Name,
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -608,9 +614,11 @@ type perflibDFSRFolder struct {
|
||||
UpdatesDroppedTotal float64 `perflib:"Updates Dropped"`
|
||||
}
|
||||
|
||||
func (c *Collector) collectFolder(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) collectFolder(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
var dst []perflibDFSRFolder
|
||||
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects["DFS Replicated Folders"], &dst, logger); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -805,6 +813,7 @@ func (c *Collector) collectFolder(ctx *types.ScrapeContext, logger log.Logger, c
|
||||
folder.Name,
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -819,9 +828,11 @@ type perflibDFSRVolume struct {
|
||||
USNJournalUnreadPercentage float64 `perflib:"USN Journal Records Unread Percentage"`
|
||||
}
|
||||
|
||||
func (c *Collector) collectVolume(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) collectVolume(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
var dst []perflibDFSRVolume
|
||||
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects["DFS Replication Service Volumes"], &dst, logger); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -862,5 +873,6 @@ func (c *Collector) collectVolume(ctx *types.ScrapeContext, logger log.Logger, c
|
||||
volume.Name,
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
package dhcp
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/perflib"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
@@ -68,15 +69,15 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{"DHCP Server"}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
|
||||
c.packetsReceivedTotal = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "packets_received_total"),
|
||||
"Total number of packets received by the DHCP server (PacketsReceivedTotal)",
|
||||
@@ -227,6 +228,7 @@ func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -261,9 +263,11 @@ type dhcpPerf struct {
|
||||
FailoverBndupdDropped float64 `perflib:"Failover: BndUpd Dropped."`
|
||||
}
|
||||
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
var dhcpPerfs []dhcpPerf
|
||||
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects["DHCP Server"], &dhcpPerfs, logger); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -4,11 +4,10 @@ package diskdrive
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/yusufpapurcu/wmi"
|
||||
@@ -55,15 +54,15 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, wmiClient *wmi.Client) error {
|
||||
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
|
||||
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
|
||||
return errors.New("wmiClient or SWbemServicesClient is nil")
|
||||
}
|
||||
@@ -161,12 +160,16 @@ var (
|
||||
)
|
||||
|
||||
// Collect sends the metric values for each metric to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
if err := c.collect(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting disk_drive_info metrics", "err", err)
|
||||
logger.Error("failed collecting disk_drive_info metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -176,6 +179,7 @@ func (c *Collector) collect(ch chan<- prometheus.Metric) error {
|
||||
if err := c.wmiClient.Query(win32DiskQuery, &dst); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(dst) == 0 {
|
||||
return errors.New("WMI query returned empty result set")
|
||||
}
|
||||
|
||||
@@ -4,10 +4,9 @@ package dns
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log/slog"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/yusufpapurcu/wmi"
|
||||
@@ -68,15 +67,15 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, wmiClient *wmi.Client) error {
|
||||
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
|
||||
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
|
||||
return errors.New("wmiClient or SWbemServicesClient is nil")
|
||||
}
|
||||
@@ -215,17 +214,22 @@ func (c *Collector) Build(_ log.Logger, wmiClient *wmi.Client) error {
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
if err := c.collect(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting dns metrics", "err", err)
|
||||
logger.Error("failed collecting dns metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -280,6 +284,7 @@ func (c *Collector) collect(ch chan<- prometheus.Metric) error {
|
||||
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_DNS_DNS", &dst); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(dst) == 0 {
|
||||
return errors.New("WMI query returned empty result set")
|
||||
}
|
||||
|
||||
@@ -4,12 +4,11 @@ package exchange
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/perflib"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
@@ -103,6 +102,7 @@ func NewWithFlags(app *kingpin.Application) *Collector {
|
||||
c.config.CollectorsEnabled = make([]string, 0)
|
||||
|
||||
var listAllCollectors bool
|
||||
|
||||
var collectorsEnabled string
|
||||
|
||||
app.Flag(
|
||||
@@ -158,7 +158,7 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{
|
||||
"MSExchange ADAccess Processes",
|
||||
"MSExchangeTransport Queues",
|
||||
@@ -173,11 +173,11 @@ func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
|
||||
// desc creates a new prometheus description
|
||||
desc := func(metricName string, description string, labels ...string) *prometheus.Desc {
|
||||
return prometheus.NewDesc(
|
||||
@@ -231,9 +231,9 @@ func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
}
|
||||
|
||||
// Collect collects exchange metrics and sends them to prometheus.
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
collectorFuncs := map[string]func(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error{
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
collectorFuncs := map[string]func(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error{
|
||||
"ADAccessProcesses": c.collectADAccessProcesses,
|
||||
"TransportQueues": c.collectTransportQueues,
|
||||
"HttpProxy": c.collectHTTPProxy,
|
||||
@@ -248,10 +248,14 @@ func (c *Collector) Collect(ctx *types.ScrapeContext, logger log.Logger, ch chan
|
||||
|
||||
for _, collectorName := range c.config.CollectorsEnabled {
|
||||
if err := collectorFuncs[collectorName](ctx, logger, ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "Error in "+collectorName, "err", err)
|
||||
logger.Error("Error in "+collectorName,
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -266,14 +270,17 @@ type perflibADAccessProcesses struct {
|
||||
LongRunningLDAPOperationsPerMin float64 `perflib:"Long Running LDAP Operations/min"`
|
||||
}
|
||||
|
||||
func (c *Collector) collectADAccessProcesses(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) collectADAccessProcesses(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
var data []perflibADAccessProcesses
|
||||
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects["MSExchange ADAccess Processes"], &data, logger); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
labelUseCount := make(map[string]int)
|
||||
|
||||
for _, proc := range data {
|
||||
labelName := c.toLabelName(proc.Name)
|
||||
if strings.HasSuffix(labelName, "_total") {
|
||||
@@ -317,6 +324,7 @@ func (c *Collector) collectADAccessProcesses(ctx *types.ScrapeContext, logger lo
|
||||
labelName,
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -325,9 +333,11 @@ type perflibAvailabilityService struct {
|
||||
RequestsSec float64 `perflib:"Availability Requests (sec)"`
|
||||
}
|
||||
|
||||
func (c *Collector) collectAvailabilityService(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) collectAvailabilityService(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
var data []perflibAvailabilityService
|
||||
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects["MSExchange Availability Service"], &data, logger); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -339,6 +349,7 @@ func (c *Collector) collectAvailabilityService(ctx *types.ScrapeContext, logger
|
||||
availservice.RequestsSec,
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -354,9 +365,11 @@ type perflibHTTPProxy struct {
|
||||
ProxyRequestsPerSec float64 `perflib:"Proxy Requests/Sec"`
|
||||
}
|
||||
|
||||
func (c *Collector) collectHTTPProxy(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) collectHTTPProxy(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
var data []perflibHTTPProxy
|
||||
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects["MSExchange HttpProxy"], &data, logger); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -400,6 +413,7 @@ func (c *Collector) collectHTTPProxy(ctx *types.ScrapeContext, logger log.Logger
|
||||
labelName,
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -409,9 +423,11 @@ type perflibOWA struct {
|
||||
RequestsPerSec float64 `perflib:"Requests/sec"`
|
||||
}
|
||||
|
||||
func (c *Collector) collectOWA(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) collectOWA(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
var data []perflibOWA
|
||||
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects["MSExchange OWA"], &data, logger); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -428,6 +444,7 @@ func (c *Collector) collectOWA(ctx *types.ScrapeContext, logger log.Logger, ch c
|
||||
owa.RequestsPerSec,
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -438,9 +455,11 @@ type perflibActiveSync struct {
|
||||
SyncCommandsPerSec float64 `perflib:"Sync Commands/sec"`
|
||||
}
|
||||
|
||||
func (c *Collector) collectActiveSync(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) collectActiveSync(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
var data []perflibActiveSync
|
||||
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects["MSExchange ActiveSync"], &data, logger); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -462,6 +481,7 @@ func (c *Collector) collectActiveSync(ctx *types.ScrapeContext, logger log.Logge
|
||||
instance.SyncCommandsPerSec,
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -475,9 +495,11 @@ type perflibRPCClientAccess struct {
|
||||
UserCount float64 `perflib:"User Count"`
|
||||
}
|
||||
|
||||
func (c *Collector) collectRPC(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) collectRPC(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
var data []perflibRPCClientAccess
|
||||
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects["MSExchange RpcClientAccess"], &data, logger); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -532,9 +554,11 @@ type perflibTransportQueues struct {
|
||||
PoisonQueueLength float64 `perflib:"Poison Queue Length"`
|
||||
}
|
||||
|
||||
func (c *Collector) collectTransportQueues(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) collectTransportQueues(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
var data []perflibTransportQueues
|
||||
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects["MSExchangeTransport Queues"], &data, logger); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -593,6 +617,7 @@ func (c *Collector) collectTransportQueues(ctx *types.ScrapeContext, logger log.
|
||||
labelName,
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -607,9 +632,11 @@ type perflibWorkloadManagementWorkloads struct {
|
||||
IsActive float64 `perflib:"Active"`
|
||||
}
|
||||
|
||||
func (c *Collector) collectWorkloadManagementWorkloads(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) collectWorkloadManagementWorkloads(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
var data []perflibWorkloadManagementWorkloads
|
||||
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects["MSExchange WorkloadManagement Workloads"], &data, logger); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -659,12 +686,15 @@ type perflibAutodiscover struct {
|
||||
RequestsPerSec float64 `perflib:"Requests/sec"`
|
||||
}
|
||||
|
||||
func (c *Collector) collectAutoDiscover(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) collectAutoDiscover(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
var data []perflibAutodiscover
|
||||
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects["MSExchangeAutodiscover"], &data, logger); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, autodisc := range data {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.autoDiscoverRequestsPerSec,
|
||||
@@ -672,6 +702,7 @@ func (c *Collector) collectAutoDiscover(ctx *types.ScrapeContext, logger log.Log
|
||||
autodisc.RequestsPerSec,
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -680,9 +711,11 @@ type perflibMapiHttpEmsmdb struct {
|
||||
ActiveUserCount float64 `perflib:"Active User Count"`
|
||||
}
|
||||
|
||||
func (c *Collector) collectMapiHttpEmsmdb(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) collectMapiHttpEmsmdb(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
var data []perflibMapiHttpEmsmdb
|
||||
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects["MSExchange MapiHttp Emsmdb"], &data, logger); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -702,6 +735,7 @@ func (c *Collector) collectMapiHttpEmsmdb(ctx *types.ScrapeContext, logger log.L
|
||||
func (c *Collector) toLabelName(name string) string {
|
||||
s := strings.ReplaceAll(strings.Join(strings.Fields(strings.ToLower(name)), "_"), ".", "_")
|
||||
s = strings.ReplaceAll(s, "__", "_")
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,9 @@ package fsrmquota
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log/slog"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/utils"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
@@ -56,15 +55,15 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, wmiClient *wmi.Client) error {
|
||||
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
|
||||
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
|
||||
return errors.New("wmiClient or SWbemServicesClient is nil")
|
||||
}
|
||||
@@ -125,17 +124,22 @@ func (c *Collector) Build(_ log.Logger, wmiClient *wmi.Client) error {
|
||||
[]string{"path", "template"},
|
||||
nil,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
if err := c.collect(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting fsrmquota metrics", "err", err)
|
||||
logger.Error("failed collecting fsrmquota metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -158,6 +162,7 @@ type MSFT_FSRMQuota struct {
|
||||
|
||||
func (c *Collector) collect(ch chan<- prometheus.Metric) error {
|
||||
var dst []MSFT_FSRMQuota
|
||||
|
||||
var count int
|
||||
|
||||
if err := c.wmiClient.Query("SELECT * FROM MSFT_FSRMQuota", &dst, nil, "root/microsoft/windows/fsrm"); err != nil {
|
||||
@@ -225,5 +230,6 @@ func (c *Collector) collect(ch chan<- prometheus.Metric) error {
|
||||
prometheus.GaugeValue,
|
||||
float64(count),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
//go:build windows
|
||||
|
||||
package collector
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
stdlog "log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/google/uuid"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/collectors"
|
||||
"github.com/prometheus/client_golang/prometheus/collectors/version"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
)
|
||||
|
||||
func (c *Collectors) BuildServeHTTP(logger log.Logger, disableExporterMetrics bool, timeoutMargin float64) http.HandlerFunc {
|
||||
collectorFactory := func(timeout time.Duration, requestedCollectors []string) (error, *Prometheus) {
|
||||
filteredCollectors := make(map[string]Collector)
|
||||
// scrape all enabled collectors if no collector is requested
|
||||
if len(requestedCollectors) == 0 {
|
||||
filteredCollectors = c.collectors
|
||||
}
|
||||
for _, name := range requestedCollectors {
|
||||
col, exists := c.collectors[name]
|
||||
if !exists {
|
||||
return fmt.Errorf("unavailable collector: %s", name), nil
|
||||
}
|
||||
filteredCollectors[name] = col
|
||||
}
|
||||
|
||||
filtered := Collectors{
|
||||
collectors: filteredCollectors,
|
||||
perfCounterQuery: c.perfCounterQuery,
|
||||
}
|
||||
|
||||
return nil, NewPrometheus(timeout, &filtered, logger)
|
||||
}
|
||||
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
logger := log.With(logger, "remote", r.RemoteAddr, "correlation_id", uuid.New().String())
|
||||
|
||||
const defaultTimeout = 10.0
|
||||
|
||||
var timeoutSeconds float64
|
||||
if v := r.Header.Get("X-Prometheus-Scrape-Timeout-Seconds"); v != "" {
|
||||
var err error
|
||||
timeoutSeconds, err = strconv.ParseFloat(v, 64)
|
||||
if err != nil {
|
||||
_ = level.Warn(logger).Log("msg", fmt.Sprintf("Couldn't parse X-Prometheus-Scrape-Timeout-Seconds: %q. Defaulting timeout to %f", v, defaultTimeout))
|
||||
}
|
||||
}
|
||||
if timeoutSeconds == 0 {
|
||||
timeoutSeconds = defaultTimeout
|
||||
}
|
||||
timeoutSeconds -= timeoutMargin
|
||||
|
||||
reg := prometheus.NewRegistry()
|
||||
err, wc := collectorFactory(time.Duration(timeoutSeconds*float64(time.Second)), r.URL.Query()["collect[]"])
|
||||
if err != nil {
|
||||
_ = level.Warn(logger).Log("msg", "Couldn't create filtered metrics handler", "err", err)
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
_, _ = w.Write([]byte(fmt.Sprintf("Couldn't create filtered metrics handler: %s", err)))
|
||||
return
|
||||
}
|
||||
|
||||
reg.MustRegister(wc)
|
||||
if !disableExporterMetrics {
|
||||
reg.MustRegister(
|
||||
collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}),
|
||||
collectors.NewGoCollector(),
|
||||
version.NewCollector("windows_exporter"),
|
||||
)
|
||||
}
|
||||
|
||||
h := promhttp.HandlerFor(reg, promhttp.HandlerOpts{
|
||||
ErrorLog: stdlog.New(log.NewStdlibAdapter(level.Error(logger)), "", stdlog.Lshortfile),
|
||||
})
|
||||
h.ServeHTTP(w, r)
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,10 @@ package hyperv
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/yusufpapurcu/wmi"
|
||||
@@ -161,15 +160,15 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, wmiClient *wmi.Client) error {
|
||||
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
|
||||
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
|
||||
return errors.New("wmiClient or SWbemServicesClient is nil")
|
||||
}
|
||||
@@ -750,70 +749,107 @@ func (c *Collector) Build(_ log.Logger, wmiClient *wmi.Client) error {
|
||||
[]string{"vm"},
|
||||
nil,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
if err := c.collectVmHealth(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting hyperV health status metrics", "err", err)
|
||||
logger.Error("failed collecting hyperV health status metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.collectVmVid(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting hyperV pages metrics", "err", err)
|
||||
logger.Error("failed collecting hyperV pages metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.collectVmHv(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting hyperV hv status metrics", "err", err)
|
||||
logger.Error("failed collecting hyperV hv status metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.collectVmProcessor(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting hyperV processor metrics", "err", err)
|
||||
logger.Error("failed collecting hyperV processor metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.collectHostLPUsage(logger, ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting hyperV host logical processors metrics", "err", err)
|
||||
logger.Error("failed collecting hyperV host logical processors metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.collectHostCpuUsage(logger, ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting hyperV host CPU metrics", "err", err)
|
||||
logger.Error("failed collecting hyperV host CPU metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.collectVmCpuUsage(logger, ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting hyperV VM CPU metrics", "err", err)
|
||||
logger.Error("failed collecting hyperV VM CPU metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.collectVmSwitch(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting hyperV switch metrics", "err", err)
|
||||
logger.Error("failed collecting hyperV switch metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.collectVmEthernet(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting hyperV ethernet metrics", "err", err)
|
||||
logger.Error("failed collecting hyperV ethernet metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.collectVmStorage(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting hyperV virtual storage metrics", "err", err)
|
||||
logger.Error("failed collecting hyperV virtual storage metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.collectVmNetwork(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting hyperV virtual network metrics", "err", err)
|
||||
logger.Error("failed collecting hyperV virtual network metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.collectVmMemory(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting hyperV virtual memory metrics", "err", err)
|
||||
logger.Error("failed collecting hyperV virtual memory metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1086,7 +1122,7 @@ type Win32_PerfRawData_HvStats_HyperVHypervisorLogicalProcessor struct {
|
||||
PercentTotalRunTime uint
|
||||
}
|
||||
|
||||
func (c *Collector) collectHostLPUsage(logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
func (c *Collector) collectHostLPUsage(logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
var dst []Win32_PerfRawData_HvStats_HyperVHypervisorLogicalProcessor
|
||||
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_HvStats_HyperVHypervisorLogicalProcessor", &dst); err != nil {
|
||||
return err
|
||||
@@ -1096,12 +1132,15 @@ func (c *Collector) collectHostLPUsage(logger log.Logger, ch chan<- prometheus.M
|
||||
if strings.Contains(obj.Name, "_Total") {
|
||||
continue
|
||||
}
|
||||
|
||||
// The name format is Hv LP <core id>
|
||||
parts := strings.Split(obj.Name, " ")
|
||||
if len(parts) != 3 {
|
||||
_ = level.Warn(logger).Log("msg", fmt.Sprintf("Unexpected format of Name in collectHostLPUsage: %q", obj.Name))
|
||||
logger.Warn(fmt.Sprintf("Unexpected format of Name in collectHostLPUsage: %q", obj.Name))
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
coreId := parts[2]
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
@@ -1139,7 +1178,7 @@ type Win32_PerfRawData_HvStats_HyperVHypervisorRootVirtualProcessor struct {
|
||||
CPUWaitTimePerDispatch uint64
|
||||
}
|
||||
|
||||
func (c *Collector) collectHostCpuUsage(logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
func (c *Collector) collectHostCpuUsage(logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
var dst []Win32_PerfRawData_HvStats_HyperVHypervisorRootVirtualProcessor
|
||||
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_HvStats_HyperVHypervisorRootVirtualProcessor", &dst); err != nil {
|
||||
return err
|
||||
@@ -1149,12 +1188,15 @@ func (c *Collector) collectHostCpuUsage(logger log.Logger, ch chan<- prometheus.
|
||||
if strings.Contains(obj.Name, "_Total") {
|
||||
continue
|
||||
}
|
||||
|
||||
// The name format is Root VP <core id>
|
||||
parts := strings.Split(obj.Name, " ")
|
||||
if len(parts) != 3 {
|
||||
_ = level.Warn(logger).Log("msg", "Unexpected format of Name in collectHostCpuUsage: "+obj.Name)
|
||||
logger.Warn("Unexpected format of Name in collectHostCpuUsage: " + obj.Name)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
coreId := parts[2]
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
@@ -1206,7 +1248,7 @@ type Win32_PerfRawData_HvStats_HyperVHypervisorVirtualProcessor struct {
|
||||
CPUWaitTimePerDispatch uint64
|
||||
}
|
||||
|
||||
func (c *Collector) collectVmCpuUsage(logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
func (c *Collector) collectVmCpuUsage(logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
var dst []Win32_PerfRawData_HvStats_HyperVHypervisorVirtualProcessor
|
||||
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_HvStats_HyperVHypervisorVirtualProcessor", &dst); err != nil {
|
||||
return err
|
||||
@@ -1216,17 +1258,22 @@ func (c *Collector) collectVmCpuUsage(logger log.Logger, ch chan<- prometheus.Me
|
||||
if strings.Contains(obj.Name, "_Total") {
|
||||
continue
|
||||
}
|
||||
|
||||
// The name format is <VM Name>:Hv VP <vcore id>
|
||||
parts := strings.Split(obj.Name, ":")
|
||||
if len(parts) != 2 {
|
||||
_ = level.Warn(logger).Log("msg", fmt.Sprintf("Unexpected format of Name in collectVmCpuUsage: %q, expected %q. Skipping.", obj.Name, "<VM Name>:Hv VP <vcore id>"))
|
||||
logger.Warn(fmt.Sprintf("Unexpected format of Name in collectVmCpuUsage: %q, expected %q. Skipping.", obj.Name, "<VM Name>:Hv VP <vcore id>"))
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
coreParts := strings.Split(parts[1], " ")
|
||||
if len(coreParts) != 3 {
|
||||
_ = level.Warn(logger).Log("msg", fmt.Sprintf("Unexpected format of core identifier in collectVmCpuUsage: %q, expected %q. Skipping.", parts[1], "Hv VP <vcore id>"))
|
||||
logger.Warn(fmt.Sprintf("Unexpected format of core identifier in collectVmCpuUsage: %q, expected %q. Skipping.", parts[1], "Hv VP <vcore id>"))
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
vmName := parts[0]
|
||||
coreId := coreParts[2]
|
||||
|
||||
|
||||
@@ -4,13 +4,12 @@ package iis
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/perflib"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
@@ -250,7 +249,7 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{
|
||||
"Web Service",
|
||||
"APP_POOL_WAS",
|
||||
@@ -259,12 +258,12 @@ func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(logger log.Logger, _ *wmi.Client) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
c.iisVersion = getIISVersion(logger)
|
||||
|
||||
@@ -894,31 +893,44 @@ type simpleVersion struct {
|
||||
minor uint64
|
||||
}
|
||||
|
||||
func getIISVersion(logger log.Logger) simpleVersion {
|
||||
func getIISVersion(logger *slog.Logger) simpleVersion {
|
||||
k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\InetStp\`, registry.QUERY_VALUE)
|
||||
if err != nil {
|
||||
_ = level.Warn(logger).Log("msg", "Couldn't open registry to determine IIS version", "err", err)
|
||||
logger.Warn("Couldn't open registry to determine IIS version",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return simpleVersion{}
|
||||
}
|
||||
|
||||
defer func() {
|
||||
err = k.Close()
|
||||
if err != nil {
|
||||
_ = level.Warn(logger).Log("msg", "Failed to close registry key", "err", err)
|
||||
logger.Warn("Failed to close registry key",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
}
|
||||
}()
|
||||
|
||||
major, _, err := k.GetIntegerValue("MajorVersion")
|
||||
if err != nil {
|
||||
_ = level.Warn(logger).Log("msg", "Couldn't open registry to determine IIS version", "err", err)
|
||||
return simpleVersion{}
|
||||
}
|
||||
minor, _, err := k.GetIntegerValue("MinorVersion")
|
||||
if err != nil {
|
||||
_ = level.Warn(logger).Log("msg", "Couldn't open registry to determine IIS version", "err", err)
|
||||
logger.Warn("Couldn't open registry to determine IIS version",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return simpleVersion{}
|
||||
}
|
||||
|
||||
_ = level.Debug(logger).Log("msg", fmt.Sprintf("Detected IIS %d.%d\n", major, minor))
|
||||
minor, _, err := k.GetIntegerValue("MinorVersion")
|
||||
if err != nil {
|
||||
logger.Warn("Couldn't open registry to determine IIS version",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return simpleVersion{}
|
||||
}
|
||||
|
||||
logger.Debug(fmt.Sprintf("Detected IIS %d.%d\n", major, minor))
|
||||
|
||||
return simpleVersion{
|
||||
major: major,
|
||||
@@ -928,25 +940,37 @@ func getIISVersion(logger log.Logger) simpleVersion {
|
||||
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
if err := c.collectWebService(ctx, logger, ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting iis metrics", "err", err)
|
||||
logger.Error("failed collecting iis metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.collectAPP_POOL_WAS(ctx, logger, ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting iis metrics", "err", err)
|
||||
logger.Error("failed collecting iis metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.collectW3SVC_W3WP(ctx, logger, ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting iis metrics", "err", err)
|
||||
logger.Error("failed collecting iis metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.collectWebServiceCache(ctx, logger, ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting iis metrics", "err", err)
|
||||
logger.Error("failed collecting iis metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1040,12 +1064,15 @@ func dedupIISNames[V hasGetIISName](services []V) map[string]V {
|
||||
name := strings.Split(entry.getIISName(), "#")[0]
|
||||
webServiceDeDuplicated[name] = entry
|
||||
}
|
||||
|
||||
return webServiceDeDuplicated
|
||||
}
|
||||
|
||||
func (c *Collector) collectWebService(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) collectWebService(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
var webService []perflibWebService
|
||||
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects["Web Service"], &webService, logger); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1336,9 +1363,11 @@ var applicationStates = map[uint32]string{
|
||||
7: "Delete Pending",
|
||||
}
|
||||
|
||||
func (c *Collector) collectAPP_POOL_WAS(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) collectAPP_POOL_WAS(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
var APP_POOL_WAS []perflibAPP_POOL_WAS
|
||||
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects["APP_POOL_WAS"], &APP_POOL_WAS, logger); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1514,9 +1543,11 @@ type perflibW3SVC_W3WP_IIS8 struct {
|
||||
WebSocketConnectionsRejected float64 `perflib:"WebSocket Connections Rejected / Sec"`
|
||||
}
|
||||
|
||||
func (c *Collector) collectW3SVC_W3WP(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) collectW3SVC_W3WP(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
var W3SVC_W3WP []perflibW3SVC_W3WP
|
||||
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects["W3SVC_W3WP"], &W3SVC_W3WP, logger); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1526,6 +1557,7 @@ func (c *Collector) collectW3SVC_W3WP(ctx *types.ScrapeContext, logger log.Logge
|
||||
for w3Name, app := range w3svcW3WPDeduplicated {
|
||||
// Extract the apppool name from the format <PID>_<NAME>
|
||||
pid := workerProcessNameExtractor.ReplaceAllString(w3Name, "$1")
|
||||
|
||||
name := workerProcessNameExtractor.ReplaceAllString(w3Name, "$2")
|
||||
if name == "" || name == "_Total" ||
|
||||
c.config.AppExclude.MatchString(name) ||
|
||||
@@ -1784,6 +1816,7 @@ func (c *Collector) collectW3SVC_W3WP(ctx *types.ScrapeContext, logger log.Logge
|
||||
for w3Name, app := range w3svcW3WPIIS8Deduplicated {
|
||||
// Extract the apppool name from the format <PID>_<NAME>
|
||||
pid := workerProcessNameExtractor.ReplaceAllString(w3Name, "$1")
|
||||
|
||||
name := workerProcessNameExtractor.ReplaceAllString(w3Name, "$2")
|
||||
if name == "" || name == "_Total" ||
|
||||
c.config.AppExclude.MatchString(name) ||
|
||||
@@ -1912,9 +1945,11 @@ type perflibWebServiceCache struct {
|
||||
ServiceCache_OutputCacheQueriesTotal float64
|
||||
}
|
||||
|
||||
func (c *Collector) collectWebServiceCache(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) collectWebServiceCache(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
var WebServiceCache []perflibWebServiceCache
|
||||
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects["Web Service Cache"], &WebServiceCache, logger); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
package license
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/headers/slc"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
@@ -53,15 +53,15 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
|
||||
c.licenseStatus = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "status"),
|
||||
"Status of windows license",
|
||||
@@ -74,12 +74,16 @@ func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
if err := c.collect(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting license metrics", "err", err)
|
||||
logger.Error("failed collecting license metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -5,14 +5,13 @@ package logical_disk
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/perflib"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
@@ -124,15 +123,15 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{"LogicalDisk"}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
|
||||
c.information = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "info"),
|
||||
"A metric with a constant '1' value labeled with logical disk information",
|
||||
@@ -262,12 +261,16 @@ func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
if err := c.collect(ctx, logger, ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting logical_disk metrics", "err", err)
|
||||
logger.Error("failed collecting logical_disk metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -294,8 +297,9 @@ type logicalDisk struct {
|
||||
AvgDiskSecPerTransfer float64 `perflib:"Avg. Disk sec/Transfer"`
|
||||
}
|
||||
|
||||
func (c *Collector) collect(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
var (
|
||||
err error
|
||||
diskID string
|
||||
@@ -316,12 +320,16 @@ func (c *Collector) collect(ctx *types.ScrapeContext, logger log.Logger, ch chan
|
||||
|
||||
diskID, err = getDiskIDByVolume(volume.Name)
|
||||
if err != nil {
|
||||
_ = level.Warn(logger).Log("msg", "failed to get disk ID for "+volume.Name, "err", err)
|
||||
logger.Warn("failed to get disk ID for "+volume.Name,
|
||||
slog.Any("err", err),
|
||||
)
|
||||
}
|
||||
|
||||
info, err = getVolumeInfo(volume.Name)
|
||||
if err != nil {
|
||||
_ = level.Warn(logger).Log("msg", "failed to get volume information for %s"+volume.Name, "err", err)
|
||||
logger.Warn("failed to get volume information for %s"+volume.Name,
|
||||
slog.Any("err", err),
|
||||
)
|
||||
}
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
@@ -480,11 +488,13 @@ const diskExtentSize = 24
|
||||
func getDiskIDByVolume(rootDrive string) (string, error) {
|
||||
// Open a volume handle to the Disk Root.
|
||||
var err error
|
||||
|
||||
var f windows.Handle
|
||||
|
||||
// mode has to include FILE_SHARE permission to allow concurrent access to the disk.
|
||||
// use 0 as access mode to avoid admin permission.
|
||||
mode := uint32(windows.FILE_SHARE_READ | windows.FILE_SHARE_WRITE | windows.FILE_SHARE_DELETE)
|
||||
|
||||
f, err = windows.CreateFile(
|
||||
windows.StringToUTF16Ptr(`\\.\`+rootDrive),
|
||||
0, mode, nil, windows.OPEN_EXISTING, uint32(windows.FILE_ATTRIBUTE_READONLY), 0)
|
||||
@@ -498,6 +508,7 @@ func getDiskIDByVolume(rootDrive string) (string, error) {
|
||||
volumeDiskExtents := make([]byte, 16*1024)
|
||||
|
||||
var bytesReturned uint32
|
||||
|
||||
err = windows.DeviceIoControl(f, controlCode, nil, 0, &volumeDiskExtents[0], uint32(len(volumeDiskExtents)), &bytesReturned, nil)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not identify physical drive for %s: %w", rootDrive, err)
|
||||
|
||||
@@ -4,10 +4,9 @@ package logon
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log/slog"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/yusufpapurcu/wmi"
|
||||
@@ -47,15 +46,15 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, wmiClient *wmi.Client) error {
|
||||
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
|
||||
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
|
||||
return errors.New("wmiClient or SWbemServicesClient is nil")
|
||||
}
|
||||
@@ -67,17 +66,22 @@ func (c *Collector) Build(_ log.Logger, wmiClient *wmi.Client) error {
|
||||
[]string{"status"},
|
||||
nil,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
if err := c.collect(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting user metrics", "err", err)
|
||||
logger.Error("failed collecting user metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -92,6 +96,7 @@ func (c *Collector) collect(ch chan<- prometheus.Metric) error {
|
||||
if err := c.wmiClient.Query("SELECT * FROM Win32_LogonSession", &dst); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(dst) == 0 {
|
||||
return errors.New("WMI query returned empty result set")
|
||||
}
|
||||
@@ -232,5 +237,6 @@ func (c *Collector) collect(ch chan<- prometheus.Metric) error {
|
||||
float64(cachedunlock),
|
||||
"cached_unlock",
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package collector
|
||||
|
||||
import (
|
||||
"maps"
|
||||
"slices"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/collector/ad"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/collector/adcs"
|
||||
@@ -36,6 +39,7 @@ import (
|
||||
"github.com/prometheus-community/windows_exporter/pkg/collector/netframework_clrsecurity"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/collector/nps"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/collector/os"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/collector/perfdata"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/collector/physical_disk"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/collector/printer"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/collector/process"
|
||||
@@ -54,7 +58,6 @@ import (
|
||||
"github.com/prometheus-community/windows_exporter/pkg/collector/time"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/collector/vmware"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/collector/vmware_blast"
|
||||
"golang.org/x/exp/maps"
|
||||
)
|
||||
|
||||
func NewBuilderWithFlags[C Collector](fn BuilderWithFlags[C]) BuilderWithFlags[Collector] {
|
||||
@@ -98,6 +101,7 @@ var BuildersWithFlags = map[string]BuilderWithFlags[Collector]{
|
||||
netframework_clrsecurity.Name: NewBuilderWithFlags(netframework_clrsecurity.NewWithFlags),
|
||||
nps.Name: NewBuilderWithFlags(nps.NewWithFlags),
|
||||
os.Name: NewBuilderWithFlags(os.NewWithFlags),
|
||||
perfdata.Name: NewBuilderWithFlags(perfdata.NewWithFlags),
|
||||
physical_disk.Name: NewBuilderWithFlags(physical_disk.NewWithFlags),
|
||||
printer.Name: NewBuilderWithFlags(printer.NewWithFlags),
|
||||
process.Name: NewBuilderWithFlags(process.NewWithFlags),
|
||||
@@ -119,5 +123,5 @@ var BuildersWithFlags = map[string]BuilderWithFlags[Collector]{
|
||||
}
|
||||
|
||||
func Available() []string {
|
||||
return maps.Keys(BuildersWithFlags)
|
||||
return slices.Sorted(maps.Keys(BuildersWithFlags))
|
||||
}
|
||||
|
||||
@@ -6,9 +6,12 @@
|
||||
package memory
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/headers/sysinfoapi"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/perflib"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
@@ -25,6 +28,7 @@ var ConfigDefaults = Config{}
|
||||
type Collector struct {
|
||||
config Config
|
||||
|
||||
// Performance metrics
|
||||
availableBytes *prometheus.Desc
|
||||
cacheBytes *prometheus.Desc
|
||||
cacheBytesPeak *prometheus.Desc
|
||||
@@ -57,6 +61,11 @@ type Collector struct {
|
||||
transitionFaultsTotal *prometheus.Desc
|
||||
transitionPagesRepurposedTotal *prometheus.Desc
|
||||
writeCopiesTotal *prometheus.Desc
|
||||
|
||||
// Global memory status
|
||||
processMemoryLimitBytes *prometheus.Desc
|
||||
physicalMemoryTotalBytes *prometheus.Desc
|
||||
physicalMemoryFreeBytes *prometheus.Desc
|
||||
}
|
||||
|
||||
func New(config *Config) *Collector {
|
||||
@@ -79,15 +88,15 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{"Memory"}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
|
||||
c.availableBytes = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "available_bytes"),
|
||||
"The amount of physical memory immediately available for allocation to a process or for system use. It is equal to the sum of memory assigned to"+
|
||||
@@ -292,17 +301,78 @@ func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
c.processMemoryLimitBytes = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "process_memory_limit_bytes"),
|
||||
"The size of the user-mode portion of the virtual address space of the calling process, in bytes. This value depends on the type of process, the type of processor, and the configuration of the operating system.",
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
c.physicalMemoryTotalBytes = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "physical_total_bytes"),
|
||||
"The amount of actual physical memory, in bytes.",
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
c.physicalMemoryFreeBytes = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "physical_free_bytes"),
|
||||
"The amount of physical memory currently available, in bytes. This is the amount of physical memory that can be immediately reused without having to write its contents to disk first. It is the sum of the size of the standby, free, and zero lists.",
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
if err := c.collect(ctx, logger, ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting memory metrics", "err", err)
|
||||
return err
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
errs := make([]error, 0, 2)
|
||||
|
||||
if err := c.collectPerformanceData(ctx, logger, ch); err != nil {
|
||||
logger.Error("failed collecting memory metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
if err := c.collectGlobalMemoryStatus(ch); err != nil {
|
||||
logger.Error("failed collecting memory metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
func (c *Collector) collectGlobalMemoryStatus(ch chan<- prometheus.Metric) error {
|
||||
memoryStatusEx, err := sysinfoapi.GlobalMemoryStatusEx()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get memory status: %w", err)
|
||||
}
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.processMemoryLimitBytes,
|
||||
prometheus.GaugeValue,
|
||||
float64(memoryStatusEx.TotalVirtual),
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.physicalMemoryTotalBytes,
|
||||
prometheus.GaugeValue,
|
||||
float64(memoryStatusEx.TotalPhys),
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.physicalMemoryFreeBytes,
|
||||
prometheus.GaugeValue,
|
||||
float64(memoryStatusEx.AvailPhys),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -343,9 +413,11 @@ type memory struct {
|
||||
WriteCopiesPersec float64 `perflib:"Write Copies/sec"`
|
||||
}
|
||||
|
||||
func (c *Collector) collect(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) collectPerformanceData(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
var dst []memory
|
||||
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects["Memory"], &dst, logger); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -3,11 +3,11 @@ package mscluster
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/yusufpapurcu/wmi"
|
||||
@@ -213,15 +213,15 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{"Memory"}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, wmiClient *wmi.Client) error {
|
||||
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
|
||||
if len(c.config.CollectorsEnabled) == 0 {
|
||||
return nil
|
||||
}
|
||||
@@ -257,7 +257,7 @@ func (c *Collector) Build(_ log.Logger, wmiClient *wmi.Client) error {
|
||||
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, _ log.Logger, ch chan<- prometheus.Metric) error {
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, _ *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
if len(c.config.CollectorsEnabled) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -4,11 +4,10 @@ package msmq
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/utils"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
@@ -68,16 +67,16 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(logger log.Logger, wmiClient *wmi.Client) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Build(logger *slog.Logger, wmiClient *wmi.Client) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
|
||||
return errors.New("wmiClient or SWbemServicesClient is nil")
|
||||
@@ -86,7 +85,7 @@ func (c *Collector) Build(logger log.Logger, wmiClient *wmi.Client) error {
|
||||
c.wmiClient = wmiClient
|
||||
|
||||
if *c.config.QueryWhereClause == "" {
|
||||
_ = level.Warn(logger).Log("msg", "No where-clause specified for msmq collector. This will generate a very large number of metrics!")
|
||||
logger.Warn("No where-clause specified for msmq collector. This will generate a very large number of metrics!")
|
||||
}
|
||||
|
||||
c.bytesInJournalQueue = prometheus.NewDesc(
|
||||
@@ -113,17 +112,22 @@ func (c *Collector) Build(logger log.Logger, wmiClient *wmi.Client) error {
|
||||
[]string{"name"},
|
||||
nil,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
if err := c.collect(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting msmq metrics", "err", err)
|
||||
logger.Error("failed collecting msmq metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ package mssql
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
@@ -12,8 +13,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/perflib"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
@@ -46,7 +45,7 @@ var ConfigDefaults = Config{
|
||||
|
||||
type mssqlInstancesType map[string]string
|
||||
|
||||
func getMSSQLInstances(logger log.Logger) mssqlInstancesType {
|
||||
func getMSSQLInstances(logger *slog.Logger) mssqlInstancesType {
|
||||
sqlInstances := make(mssqlInstancesType)
|
||||
|
||||
// in case querying the registry fails, return the default instance
|
||||
@@ -54,21 +53,31 @@ func getMSSQLInstances(logger log.Logger) mssqlInstancesType {
|
||||
sqlDefaultInstance["MSSQLSERVER"] = ""
|
||||
|
||||
regKey := `Software\Microsoft\Microsoft SQL Server\Instance Names\SQL`
|
||||
|
||||
k, err := registry.OpenKey(registry.LOCAL_MACHINE, regKey, registry.QUERY_VALUE)
|
||||
if err != nil {
|
||||
_ = level.Warn(logger).Log("msg", "Couldn't open registry to determine SQL instances", "err", err)
|
||||
logger.Warn("Couldn't open registry to determine SQL instances",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return sqlDefaultInstance
|
||||
}
|
||||
|
||||
defer func() {
|
||||
err = k.Close()
|
||||
if err != nil {
|
||||
_ = level.Warn(logger).Log("msg", "Failed to close registry key", "err", err)
|
||||
logger.Warn("Failed to close registry key",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
}
|
||||
}()
|
||||
|
||||
instanceNames, err := k.ReadValueNames(0)
|
||||
if err != nil {
|
||||
_ = level.Warn(logger).Log("msg", "Can't ReadSubKeyNames", "err", err)
|
||||
logger.Warn("Can't ReadSubKeyNames",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return sqlDefaultInstance
|
||||
}
|
||||
|
||||
@@ -78,7 +87,7 @@ func getMSSQLInstances(logger log.Logger) mssqlInstancesType {
|
||||
}
|
||||
}
|
||||
|
||||
_ = level.Debug(logger).Log("msg", fmt.Sprintf("Detected MSSQL Instances: %#v\n", sqlInstances))
|
||||
logger.Debug(fmt.Sprintf("Detected MSSQL Instances: %#v\n", sqlInstances))
|
||||
|
||||
return sqlInstances
|
||||
}
|
||||
@@ -110,7 +119,9 @@ func mssqlGetPerfObjectName(sqlInstance string, collector string) string {
|
||||
if sqlInstance != "MSSQLSERVER" {
|
||||
prefix = "MSSQL$" + sqlInstance + ":"
|
||||
}
|
||||
|
||||
suffix := ""
|
||||
|
||||
switch collector {
|
||||
case "accessmethods":
|
||||
suffix = "Access Methods"
|
||||
@@ -137,6 +148,7 @@ func mssqlGetPerfObjectName(sqlInstance string, collector string) string {
|
||||
case "waitstats":
|
||||
suffix = "Wait Statistics"
|
||||
}
|
||||
|
||||
return prefix + suffix
|
||||
}
|
||||
|
||||
@@ -436,6 +448,7 @@ func NewWithFlags(app *kingpin.Application) *Collector {
|
||||
}
|
||||
|
||||
var listAllCollectors bool
|
||||
|
||||
var collectorsEnabled string
|
||||
|
||||
app.Flag(
|
||||
@@ -478,7 +491,7 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(logger log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(logger *slog.Logger) ([]string, error) {
|
||||
c.mssqlInstances = getMSSQLInstances(logger)
|
||||
perfCounters := make([]string, 0, len(c.mssqlInstances)*len(c.config.CollectorsEnabled))
|
||||
|
||||
@@ -491,11 +504,11 @@ func (c *Collector) GetPerfCounter(logger log.Logger) ([]string, error) {
|
||||
return perfCounters, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
|
||||
// Result must order, to prevent test failures.
|
||||
sort.Strings(c.config.CollectorsEnabled)
|
||||
|
||||
@@ -1971,24 +1984,30 @@ func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type mssqlCollectorFunc func(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric, sqlInstance string) error
|
||||
type mssqlCollectorFunc func(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric, sqlInstance string) error
|
||||
|
||||
func (c *Collector) execute(ctx *types.ScrapeContext, logger log.Logger, name string, fn mssqlCollectorFunc, ch chan<- prometheus.Metric, sqlInstance string, wg *sync.WaitGroup) {
|
||||
func (c *Collector) execute(ctx *types.ScrapeContext, logger *slog.Logger, name string, fn mssqlCollectorFunc, ch chan<- prometheus.Metric, sqlInstance string, wg *sync.WaitGroup) {
|
||||
// Reset failure counter on each scrape
|
||||
c.mssqlChildCollectorFailure = 0
|
||||
|
||||
defer wg.Done()
|
||||
|
||||
begin := time.Now()
|
||||
err := fn(ctx, logger, ch, sqlInstance)
|
||||
duration := time.Since(begin)
|
||||
|
||||
var success float64
|
||||
|
||||
if err != nil {
|
||||
_ = level.Error(logger).Log("msg", fmt.Sprintf("mssql class collector %s failed after %fs", name, duration.Seconds()), "err", err)
|
||||
logger.Error(fmt.Sprintf("mssql class collector %s failed after %fs", name, duration.Seconds()),
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
success = 0
|
||||
c.mssqlChildCollectorFailure++
|
||||
} else {
|
||||
_ = level.Debug(logger).Log("msg", fmt.Sprintf("mssql class collector %s succeeded after %fs.", name, duration.Seconds()))
|
||||
logger.Debug(fmt.Sprintf("mssql class collector %s succeeded after %fs.", name, duration.Seconds()))
|
||||
|
||||
success = 1
|
||||
}
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
@@ -2007,8 +2026,8 @@ func (c *Collector) execute(ctx *types.ScrapeContext, logger log.Logger, name st
|
||||
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
wg := sync.WaitGroup{}
|
||||
|
||||
for sqlInstance := range c.mssqlInstances {
|
||||
@@ -2016,6 +2035,7 @@ func (c *Collector) Collect(ctx *types.ScrapeContext, logger log.Logger, ch chan
|
||||
function := c.mssqlCollectors[name]
|
||||
|
||||
wg.Add(1)
|
||||
|
||||
go c.execute(ctx, logger, name, function, ch, sqlInstance, &wg)
|
||||
}
|
||||
}
|
||||
@@ -2079,9 +2099,10 @@ type mssqlAccessMethods struct {
|
||||
WorktablesFromCacheRatioBase float64 `perflib:"Worktables From Cache Base_Base"`
|
||||
}
|
||||
|
||||
func (c *Collector) collectAccessMethods(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric, sqlInstance string) error {
|
||||
func (c *Collector) collectAccessMethods(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric, sqlInstance string) error {
|
||||
var dst []mssqlAccessMethods
|
||||
_ = level.Debug(logger).Log("msg", fmt.Sprintf("mssql_accessmethods collector iterating sql instance %s.", sqlInstance))
|
||||
|
||||
logger.Debug(fmt.Sprintf("mssql_accessmethods collector iterating sql instance %s.", sqlInstance))
|
||||
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects[mssqlGetPerfObjectName(sqlInstance, "accessmethods")], &dst, logger); err != nil {
|
||||
return err
|
||||
@@ -2396,6 +2417,7 @@ func (c *Collector) collectAccessMethods(ctx *types.ScrapeContext, logger log.Lo
|
||||
sqlInstance,
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2414,9 +2436,10 @@ type mssqlAvailabilityReplica struct {
|
||||
SendstoTransportPerSec float64 `perflib:"Sends to Transport/sec"`
|
||||
}
|
||||
|
||||
func (c *Collector) collectAvailabilityReplica(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric, sqlInstance string) error {
|
||||
func (c *Collector) collectAvailabilityReplica(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric, sqlInstance string) error {
|
||||
var dst []mssqlAvailabilityReplica
|
||||
_ = level.Debug(logger).Log("msg", fmt.Sprintf("mssql_availreplica collector iterating sql instance %s.", sqlInstance))
|
||||
|
||||
logger.Debug(fmt.Sprintf("mssql_availreplica collector iterating sql instance %s.", sqlInstance))
|
||||
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects[mssqlGetPerfObjectName(sqlInstance, "availreplica")], &dst, logger); err != nil {
|
||||
return err
|
||||
@@ -2426,6 +2449,7 @@ func (c *Collector) collectAvailabilityReplica(ctx *types.ScrapeContext, logger
|
||||
if strings.ToLower(v.Name) == "_total" {
|
||||
continue
|
||||
}
|
||||
|
||||
replicaName := v.Name
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
@@ -2491,6 +2515,7 @@ func (c *Collector) collectAvailabilityReplica(ctx *types.ScrapeContext, logger
|
||||
sqlInstance, replicaName,
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2522,9 +2547,10 @@ type mssqlBufferManager struct {
|
||||
TargetPages float64 `perflib:"Target pages"`
|
||||
}
|
||||
|
||||
func (c *Collector) collectBufferManager(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric, sqlInstance string) error {
|
||||
func (c *Collector) collectBufferManager(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric, sqlInstance string) error {
|
||||
var dst []mssqlBufferManager
|
||||
_ = level.Debug(logger).Log("msg", fmt.Sprintf("mssql_bufman collector iterating sql instance %s.", sqlInstance))
|
||||
|
||||
logger.Debug(fmt.Sprintf("mssql_bufman collector iterating sql instance %s.", sqlInstance))
|
||||
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects[mssqlGetPerfObjectName(sqlInstance, "bufman")], &dst, logger); err != nil {
|
||||
return err
|
||||
@@ -2726,9 +2752,10 @@ type mssqlDatabaseReplica struct {
|
||||
TransactionDelay float64 `perflib:"Transaction Delay"`
|
||||
}
|
||||
|
||||
func (c *Collector) collectDatabaseReplica(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric, sqlInstance string) error {
|
||||
func (c *Collector) collectDatabaseReplica(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric, sqlInstance string) error {
|
||||
var dst []mssqlDatabaseReplica
|
||||
_ = level.Debug(logger).Log("msg", fmt.Sprintf("mssql_dbreplica collector iterating sql instance %s.", sqlInstance))
|
||||
|
||||
logger.Debug(fmt.Sprintf("mssql_dbreplica collector iterating sql instance %s.", sqlInstance))
|
||||
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects[mssqlGetPerfObjectName(sqlInstance, "dbreplica")], &dst, logger); err != nil {
|
||||
return err
|
||||
@@ -2738,6 +2765,7 @@ func (c *Collector) collectDatabaseReplica(ctx *types.ScrapeContext, logger log.
|
||||
if strings.ToLower(v.Name) == "_total" {
|
||||
continue
|
||||
}
|
||||
|
||||
replicaName := v.Name
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
@@ -2908,6 +2936,7 @@ func (c *Collector) collectDatabaseReplica(ctx *types.ScrapeContext, logger log.
|
||||
sqlInstance, replicaName,
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -2965,9 +2994,10 @@ type mssqlDatabases struct {
|
||||
XTPMemoryUsedKB float64 `perflib:"XTP Memory Used (KB)"`
|
||||
}
|
||||
|
||||
func (c *Collector) collectDatabases(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric, sqlInstance string) error {
|
||||
func (c *Collector) collectDatabases(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric, sqlInstance string) error {
|
||||
var dst []mssqlDatabases
|
||||
_ = level.Debug(logger).Log("msg", fmt.Sprintf("mssql_databases collector iterating sql instance %s.", sqlInstance))
|
||||
|
||||
logger.Debug(fmt.Sprintf("mssql_databases collector iterating sql instance %s.", sqlInstance))
|
||||
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects[mssqlGetPerfObjectName(sqlInstance, "databases")], &dst, logger); err != nil {
|
||||
return err
|
||||
@@ -2977,6 +3007,7 @@ func (c *Collector) collectDatabases(ctx *types.ScrapeContext, logger log.Logger
|
||||
if strings.ToLower(v.Name) == "_total" {
|
||||
continue
|
||||
}
|
||||
|
||||
dbName := v.Name
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
@@ -3315,6 +3346,7 @@ func (c *Collector) collectDatabases(ctx *types.ScrapeContext, logger log.Logger
|
||||
sqlInstance, dbName,
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3347,9 +3379,10 @@ type mssqlGeneralStatistics struct {
|
||||
UserConnections float64 `perflib:"User Connections"`
|
||||
}
|
||||
|
||||
func (c *Collector) collectGeneralStatistics(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric, sqlInstance string) error {
|
||||
func (c *Collector) collectGeneralStatistics(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric, sqlInstance string) error {
|
||||
var dst []mssqlGeneralStatistics
|
||||
_ = level.Debug(logger).Log("msg", fmt.Sprintf("mssql_genstats collector iterating sql instance %s.", sqlInstance))
|
||||
|
||||
logger.Debug(fmt.Sprintf("mssql_genstats collector iterating sql instance %s.", sqlInstance))
|
||||
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects[mssqlGetPerfObjectName(sqlInstance, "genstats")], &dst, logger); err != nil {
|
||||
return err
|
||||
@@ -3542,9 +3575,10 @@ type mssqlLocks struct {
|
||||
NumberOfDeadlocksPerSec float64 `perflib:"Number of Deadlocks/sec"`
|
||||
}
|
||||
|
||||
func (c *Collector) collectLocks(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric, sqlInstance string) error {
|
||||
func (c *Collector) collectLocks(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric, sqlInstance string) error {
|
||||
var dst []mssqlLocks
|
||||
_ = level.Debug(logger).Log("msg", fmt.Sprintf("mssql_locks collector iterating sql instance %s.", sqlInstance))
|
||||
|
||||
logger.Debug(fmt.Sprintf("mssql_locks collector iterating sql instance %s.", sqlInstance))
|
||||
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects[mssqlGetPerfObjectName(sqlInstance, "locks")], &dst, logger); err != nil {
|
||||
return err
|
||||
@@ -3554,6 +3588,7 @@ func (c *Collector) collectLocks(ctx *types.ScrapeContext, logger log.Logger, ch
|
||||
if strings.ToLower(v.Name) == "_total" {
|
||||
continue
|
||||
}
|
||||
|
||||
lockResourceName := v.Name
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
@@ -3612,6 +3647,7 @@ func (c *Collector) collectLocks(ctx *types.ScrapeContext, logger log.Logger, ch
|
||||
sqlInstance, lockResourceName,
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3640,9 +3676,10 @@ type mssqlMemoryManager struct {
|
||||
TotalServerMemoryKB float64 `perflib:"Total Server Memory (KB)"`
|
||||
}
|
||||
|
||||
func (c *Collector) collectMemoryManager(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric, sqlInstance string) error {
|
||||
func (c *Collector) collectMemoryManager(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric, sqlInstance string) error {
|
||||
var dst []mssqlMemoryManager
|
||||
_ = level.Debug(logger).Log("msg", fmt.Sprintf("mssql_memmgr collector iterating sql instance %s.", sqlInstance))
|
||||
|
||||
logger.Debug(fmt.Sprintf("mssql_memmgr collector iterating sql instance %s.", sqlInstance))
|
||||
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects[mssqlGetPerfObjectName(sqlInstance, "memmgr")], &dst, logger); err != nil {
|
||||
return err
|
||||
@@ -3809,9 +3846,10 @@ type mssqlSQLStatistics struct {
|
||||
UnsafeAutoParamsPerSec float64 `perflib:"Unsafe Auto-Params/sec"`
|
||||
}
|
||||
|
||||
func (c *Collector) collectSQLStats(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric, sqlInstance string) error {
|
||||
func (c *Collector) collectSQLStats(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric, sqlInstance string) error {
|
||||
var dst []mssqlSQLStatistics
|
||||
_ = level.Debug(logger).Log("msg", fmt.Sprintf("mssql_sqlstats collector iterating sql instance %s.", sqlInstance))
|
||||
|
||||
logger.Debug(fmt.Sprintf("mssql_sqlstats collector iterating sql instance %s.", sqlInstance))
|
||||
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects[mssqlGetPerfObjectName(sqlInstance, "sqlstats")], &dst, logger); err != nil {
|
||||
return err
|
||||
@@ -3917,9 +3955,10 @@ type mssqlWaitStatistics struct {
|
||||
WaitStatsTransactionOwnershipWaits float64 `perflib:"Transaction ownership waits"`
|
||||
}
|
||||
|
||||
func (c *Collector) collectWaitStats(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric, sqlInstance string) error {
|
||||
func (c *Collector) collectWaitStats(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric, sqlInstance string) error {
|
||||
var dst []mssqlWaitStatistics
|
||||
_ = level.Debug(logger).Log("msg", fmt.Sprintf("mssql_waitstats collector iterating sql instance %s.", sqlInstance))
|
||||
|
||||
logger.Debug(fmt.Sprintf("mssql_waitstats collector iterating sql instance %s.", sqlInstance))
|
||||
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects[mssqlGetPerfObjectName(sqlInstance, "waitstats")], &dst, logger); err != nil {
|
||||
return err
|
||||
@@ -4023,9 +4062,10 @@ type mssqlSQLErrors struct {
|
||||
|
||||
// Win32_PerfRawData_MSSQLSERVER_SQLServerErrors docs:
|
||||
// - https://docs.microsoft.com/en-us/sql/relational-databases/performance-monitor/sql-server-sql-errors-object
|
||||
func (c *Collector) collectSQLErrors(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric, sqlInstance string) error {
|
||||
func (c *Collector) collectSQLErrors(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric, sqlInstance string) error {
|
||||
var dst []mssqlSQLErrors
|
||||
_ = level.Debug(logger).Log("msg", fmt.Sprintf("mssql_sqlerrors collector iterating sql instance %s.", sqlInstance))
|
||||
|
||||
logger.Debug(fmt.Sprintf("mssql_sqlerrors collector iterating sql instance %s.", sqlInstance))
|
||||
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects[mssqlGetPerfObjectName(sqlInstance, "sqlerrors")], &dst, logger); err != nil {
|
||||
return err
|
||||
@@ -4035,6 +4075,7 @@ func (c *Collector) collectSQLErrors(ctx *types.ScrapeContext, logger log.Logger
|
||||
if strings.ToLower(v.Name) == "_total" {
|
||||
continue
|
||||
}
|
||||
|
||||
resource := v.Name
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
@@ -4066,9 +4107,10 @@ type mssqlTransactions struct {
|
||||
|
||||
// Win32_PerfRawData_MSSQLSERVER_Transactions docs:
|
||||
// - https://docs.microsoft.com/en-us/sql/relational-databases/performance-monitor/sql-server-transactions-object
|
||||
func (c *Collector) collectTransactions(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric, sqlInstance string) error {
|
||||
func (c *Collector) collectTransactions(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric, sqlInstance string) error {
|
||||
var dst []mssqlTransactions
|
||||
_ = level.Debug(logger).Log("msg", fmt.Sprintf("mssql_transactions collector iterating sql instance %s.", sqlInstance))
|
||||
|
||||
logger.Debug(fmt.Sprintf("mssql_transactions collector iterating sql instance %s.", sqlInstance))
|
||||
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects[mssqlGetPerfObjectName(sqlInstance, "transactions")], &dst, logger); err != nil {
|
||||
return err
|
||||
|
||||
@@ -4,11 +4,10 @@ package net
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"regexp"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/perflib"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
@@ -108,15 +107,15 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{"Network Interface"}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
|
||||
c.bytesReceivedTotal = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "bytes_received_total"),
|
||||
"(Network.BytesReceivedPerSec)",
|
||||
@@ -201,12 +200,16 @@ func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
if err := c.collect(ctx, logger, ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting net metrics", "err", err)
|
||||
logger.Error("failed collecting net metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -235,8 +238,9 @@ type networkInterface struct {
|
||||
CurrentBandwidth float64 `perflib:"Current Bandwidth"`
|
||||
}
|
||||
|
||||
func (c *Collector) collect(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
var dst []networkInterface
|
||||
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects["Network Interface"], &dst, logger); err != nil {
|
||||
|
||||
@@ -4,10 +4,9 @@ package netframework_clrexceptions
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log/slog"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/yusufpapurcu/wmi"
|
||||
@@ -50,15 +49,15 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, wmiClient *wmi.Client) error {
|
||||
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
|
||||
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
|
||||
return errors.New("wmiClient or SWbemServicesClient is nil")
|
||||
}
|
||||
@@ -88,17 +87,22 @@ func (c *Collector) Build(_ log.Logger, wmiClient *wmi.Client) error {
|
||||
[]string{"process"},
|
||||
nil,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
if err := c.collect(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting win32_perfrawdata_netframework_netclrexceptions metrics", "err", err)
|
||||
logger.Error("failed collecting win32_perfrawdata_netframework_netclrexceptions metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,9 @@ package netframework_clrinterop
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log/slog"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/yusufpapurcu/wmi"
|
||||
@@ -49,15 +48,15 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, wmiClient *wmi.Client) error {
|
||||
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
|
||||
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
|
||||
return errors.New("wmiClient or SWbemServicesClient is nil")
|
||||
}
|
||||
@@ -81,17 +80,22 @@ func (c *Collector) Build(_ log.Logger, wmiClient *wmi.Client) error {
|
||||
[]string{"process"},
|
||||
nil,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
if err := c.collect(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting win32_perfrawdata_netframework_netclrinterop metrics", "err", err)
|
||||
logger.Error("failed collecting win32_perfrawdata_netframework_netclrinterop metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
package netframework_clrjit
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/yusufpapurcu/wmi"
|
||||
@@ -48,15 +48,15 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
|
||||
c.numberOfMethodsJitted = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "jit_methods_total"),
|
||||
"Displays the total number of methods JIT-compiled since the application started. This counter does not include pre-JIT-compiled methods.",
|
||||
@@ -81,17 +81,22 @@ func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
[]string{"process"},
|
||||
nil,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
if err := c.collect(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting win32_perfrawdata_netframework_netclrjit metrics", "err", err)
|
||||
logger.Error("failed collecting win32_perfrawdata_netframework_netclrjit metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,9 @@ package netframework_clrloading
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log/slog"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/yusufpapurcu/wmi"
|
||||
@@ -55,15 +54,15 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, wmiClient *wmi.Client) error {
|
||||
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
|
||||
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
|
||||
return errors.New("wmiClient or SWbemServicesClient is nil")
|
||||
}
|
||||
@@ -124,17 +123,22 @@ func (c *Collector) Build(_ log.Logger, wmiClient *wmi.Client) error {
|
||||
[]string{"process"},
|
||||
nil,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
if err := c.collect(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting win32_perfrawdata_netframework_netclrloading metrics", "err", err)
|
||||
logger.Error("failed collecting win32_perfrawdata_netframework_netclrloading metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,9 @@ package netframework_clrlocksandthreads
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log/slog"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/yusufpapurcu/wmi"
|
||||
@@ -53,15 +52,15 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, wmiClient *wmi.Client) error {
|
||||
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
|
||||
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
|
||||
return errors.New("wmiClient or SWbemServicesClient is nil")
|
||||
}
|
||||
@@ -110,17 +109,22 @@ func (c *Collector) Build(_ log.Logger, wmiClient *wmi.Client) error {
|
||||
[]string{"process"},
|
||||
nil,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
if err := c.collect(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting win32_perfrawdata_netframework_netclrlocksandthreads metrics", "err", err)
|
||||
logger.Error("failed collecting win32_perfrawdata_netframework_netclrlocksandthreads metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,9 @@ package netframework_clrmemory
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log/slog"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/yusufpapurcu/wmi"
|
||||
@@ -58,15 +57,15 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, wmiClient *wmi.Client) error {
|
||||
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
|
||||
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
|
||||
return errors.New("wmiClient or SWbemServicesClient is nil")
|
||||
}
|
||||
@@ -145,17 +144,22 @@ func (c *Collector) Build(_ log.Logger, wmiClient *wmi.Client) error {
|
||||
[]string{"process"},
|
||||
nil,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
if err := c.collect(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting win32_perfrawdata_netframework_netclrmemory metrics", "err", err)
|
||||
logger.Error("failed collecting win32_perfrawdata_netframework_netclrmemory metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,9 @@ package netframework_clrremoting
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log/slog"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/yusufpapurcu/wmi"
|
||||
@@ -52,15 +51,15 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, wmiClient *wmi.Client) error {
|
||||
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
|
||||
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
|
||||
return errors.New("wmiClient or SWbemServicesClient is nil")
|
||||
}
|
||||
@@ -103,17 +102,22 @@ func (c *Collector) Build(_ log.Logger, wmiClient *wmi.Client) error {
|
||||
[]string{"process"},
|
||||
nil,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
if err := c.collect(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting win32_perfrawdata_netframework_netclrremoting metrics", "err", err)
|
||||
logger.Error("failed collecting win32_perfrawdata_netframework_netclrremoting metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -4,10 +4,9 @@ package netframework_clrsecurity
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log/slog"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/yusufpapurcu/wmi"
|
||||
@@ -50,15 +49,15 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, wmiClient *wmi.Client) error {
|
||||
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
|
||||
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
|
||||
return errors.New("wmiClient or SWbemServicesClient is nil")
|
||||
}
|
||||
@@ -88,17 +87,22 @@ func (c *Collector) Build(_ log.Logger, wmiClient *wmi.Client) error {
|
||||
[]string{"process"},
|
||||
nil,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
if err := c.collect(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting win32_perfrawdata_netframework_netclrsecurity metrics", "err", err)
|
||||
logger.Error("failed collecting win32_perfrawdata_netframework_netclrsecurity metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -3,10 +3,9 @@ package nps
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/yusufpapurcu/wmi"
|
||||
@@ -71,15 +70,15 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, wmiClient *wmi.Client) error {
|
||||
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
|
||||
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
|
||||
return errors.New("wmiClient or SWbemServicesClient is nil")
|
||||
}
|
||||
@@ -236,21 +235,26 @@ func (c *Collector) Build(_ log.Logger, wmiClient *wmi.Client) error {
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
if err := c.CollectAccept(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", fmt.Sprintf("failed collecting NPS accept data: %s", err))
|
||||
logger.Error(fmt.Sprintf("failed collecting NPS accept data: %s", err))
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.CollectAccounting(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", fmt.Sprintf("failed collecting NPS accounting data: %s", err))
|
||||
logger.Error(fmt.Sprintf("failed collecting NPS accounting data: %s", err))
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -5,15 +5,13 @@ package os
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/headers/kernel32"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/headers/netapi32"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/headers/psapi"
|
||||
@@ -22,6 +20,7 @@ import (
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/yusufpapurcu/wmi"
|
||||
"golang.org/x/sys/windows"
|
||||
"golang.org/x/sys/windows/registry"
|
||||
)
|
||||
|
||||
@@ -35,19 +34,45 @@ var ConfigDefaults = Config{}
|
||||
type Collector struct {
|
||||
config Config
|
||||
|
||||
osInformation *prometheus.Desc
|
||||
pagingFreeBytes *prometheus.Desc
|
||||
pagingLimitBytes *prometheus.Desc
|
||||
hostname *prometheus.Desc
|
||||
osInformation *prometheus.Desc
|
||||
pagingFreeBytes *prometheus.Desc
|
||||
pagingLimitBytes *prometheus.Desc
|
||||
|
||||
// users
|
||||
// Deprecated: Use windows_system_processes instead.
|
||||
processes *prometheus.Desc
|
||||
// users
|
||||
// Deprecated: Use windows_system_process_limit instead.
|
||||
processesLimit *prometheus.Desc
|
||||
|
||||
// users
|
||||
// Deprecated: Use count(windows_logon_logon_type) instead.
|
||||
users *prometheus.Desc
|
||||
|
||||
// physicalMemoryFreeBytes
|
||||
// Deprecated: Use windows_memory_physical_free_bytes instead.
|
||||
physicalMemoryFreeBytes *prometheus.Desc
|
||||
|
||||
// processMemoryLimitBytes
|
||||
// Deprecated: Use windows_memory_process_memory_limit_bytes instead.
|
||||
processMemoryLimitBytes *prometheus.Desc
|
||||
processes *prometheus.Desc
|
||||
processesLimit *prometheus.Desc
|
||||
time *prometheus.Desc
|
||||
timezone *prometheus.Desc
|
||||
users *prometheus.Desc
|
||||
virtualMemoryBytes *prometheus.Desc
|
||||
virtualMemoryFreeBytes *prometheus.Desc
|
||||
visibleMemoryBytes *prometheus.Desc
|
||||
|
||||
// time
|
||||
// Deprecated: Use windows_time_current_timestamp_seconds instead.
|
||||
time *prometheus.Desc
|
||||
// timezone
|
||||
// Deprecated: Use windows_time_timezone instead.
|
||||
timezone *prometheus.Desc
|
||||
// virtualMemoryBytes
|
||||
// Deprecated: Use windows_memory_commit_limit instead.
|
||||
virtualMemoryBytes *prometheus.Desc
|
||||
// virtualMemoryFreeBytes
|
||||
// Deprecated: Use windows_memory_commit_limit instead.
|
||||
virtualMemoryFreeBytes *prometheus.Desc
|
||||
// visibleMemoryBytes
|
||||
// Deprecated: Use windows_memory_physical_total_bytes instead.
|
||||
visibleMemoryBytes *prometheus.Desc
|
||||
}
|
||||
|
||||
type pagingFileCounter struct {
|
||||
@@ -76,19 +101,50 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{"Paging File"}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error {
|
||||
logger.Warn("The os collect holds a number of deprecated metrics and will be removed mid 2025. " +
|
||||
"See https://github.com/prometheus-community/windows_exporter/pull/1596 for more information.")
|
||||
|
||||
workstationInfo, err := netapi32.GetWorkstationInfo()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get workstation info: %w", err)
|
||||
}
|
||||
|
||||
productName, buildNumber, revision, err := c.getWindowsVersion()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get Windows version: %w", err)
|
||||
}
|
||||
|
||||
c.osInformation = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "info"),
|
||||
"OperatingSystem.Caption, OperatingSystem.Version",
|
||||
[]string{"product", "version", "major_version", "minor_version", "build_number", "revision"},
|
||||
`Contains full product name & version in labels. Note that the "major_version" for Windows 11 is \"10\"; a build number greater than 22000 represents Windows 11.`,
|
||||
nil,
|
||||
prometheus.Labels{
|
||||
"product": productName,
|
||||
"version": fmt.Sprintf("%d.%d.%s", workstationInfo.VersionMajor, workstationInfo.VersionMinor, buildNumber),
|
||||
"major_version": strconv.FormatUint(uint64(workstationInfo.VersionMajor), 10),
|
||||
"minor_version": strconv.FormatUint(uint64(workstationInfo.VersionMinor), 10),
|
||||
"build_number": buildNumber,
|
||||
"revision": revision,
|
||||
},
|
||||
)
|
||||
|
||||
c.hostname = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "hostname"),
|
||||
"Labelled system hostname information as provided by ComputerSystem.DNSHostName and ComputerSystem.Domain",
|
||||
[]string{
|
||||
"hostname",
|
||||
"domain",
|
||||
"fqdn",
|
||||
},
|
||||
nil,
|
||||
)
|
||||
c.pagingLimitBytes = prometheus.NewDesc(
|
||||
@@ -105,118 +161,228 @@ func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
)
|
||||
c.physicalMemoryFreeBytes = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "physical_memory_free_bytes"),
|
||||
"OperatingSystem.FreePhysicalMemory",
|
||||
"Deprecated: Use `windows_memory_physical_free_bytes` instead.",
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
c.time = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "time"),
|
||||
"OperatingSystem.LocalDateTime",
|
||||
"Deprecated: Use windows_time_current_timestamp_seconds instead.",
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
c.timezone = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "timezone"),
|
||||
"OperatingSystem.LocalDateTime",
|
||||
"Deprecated: Use windows_time_timezone instead.",
|
||||
[]string{"timezone"},
|
||||
nil,
|
||||
)
|
||||
c.processes = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "processes"),
|
||||
"OperatingSystem.NumberOfProcesses",
|
||||
"Deprecated: Use `windows_system_processes` instead.",
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
c.processesLimit = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "processes_limit"),
|
||||
"OperatingSystem.MaxNumberOfProcesses",
|
||||
"Deprecated: Use `windows_system_process_limit` instead.",
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
c.processMemoryLimitBytes = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "process_memory_limit_bytes"),
|
||||
"OperatingSystem.MaxProcessMemorySize",
|
||||
"Deprecated: Use `windows_memory_process_memory_limit_bytes` instead.",
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
c.users = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "users"),
|
||||
"OperatingSystem.NumberOfUsers",
|
||||
"Deprecated: Use `count(windows_logon_logon_type)` instead.",
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
c.virtualMemoryBytes = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "virtual_memory_bytes"),
|
||||
"OperatingSystem.TotalVirtualMemorySize",
|
||||
"Deprecated: Use `windows_memory_commit_limit` instead.",
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
c.visibleMemoryBytes = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "visible_memory_bytes"),
|
||||
"OperatingSystem.TotalVisibleMemorySize",
|
||||
"Deprecated: Use `windows_memory_physical_total_bytes` instead.",
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
c.virtualMemoryFreeBytes = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "virtual_memory_free_bytes"),
|
||||
"OperatingSystem.FreeVirtualMemory",
|
||||
"Deprecated: Use `windows_memory_commit_limit - windows_memory_committed_bytes` instead.",
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
if err := c.collect(ctx, logger, ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting os metrics", "err", err)
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
errs := make([]error, 0, 5)
|
||||
|
||||
c.collect(ch)
|
||||
|
||||
if err := c.collectHostname(ch); err != nil {
|
||||
logger.Error("failed collecting os metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
if err := c.collectLoggedInUserCount(ch); err != nil {
|
||||
logger.Error("failed collecting os user count metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
if err := c.collectMemory(ch); err != nil {
|
||||
logger.Error("failed collecting os memory metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
if err := c.collectTime(ch); err != nil {
|
||||
logger.Error("failed collecting os time metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
if err := c.collectPaging(ctx, logger, ch); err != nil {
|
||||
logger.Error("failed collecting os paging metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
func (c *Collector) collectLoggedInUserCount(ch chan<- prometheus.Metric) error {
|
||||
workstationInfo, err := netapi32.GetWorkstationInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.users,
|
||||
prometheus.GaugeValue,
|
||||
float64(workstationInfo.LoggedOnUsers),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Win32_OperatingSystem docs:
|
||||
// - https://msdn.microsoft.com/en-us/library/aa394239 - Win32_OperatingSystem class.
|
||||
type Win32_OperatingSystem struct {
|
||||
Caption string
|
||||
FreePhysicalMemory uint64
|
||||
FreeSpaceInPagingFiles uint64
|
||||
FreeVirtualMemory uint64
|
||||
LocalDateTime time.Time
|
||||
MaxNumberOfProcesses uint32
|
||||
MaxProcessMemorySize uint64
|
||||
NumberOfProcesses uint32
|
||||
NumberOfUsers uint32
|
||||
SizeStoredInPagingFiles uint64
|
||||
TotalVirtualMemorySize uint64
|
||||
TotalVisibleMemorySize uint64
|
||||
Version string
|
||||
func (c *Collector) collectHostname(ch chan<- prometheus.Metric) error {
|
||||
hostname, err := sysinfoapi.GetComputerName(sysinfoapi.ComputerNameDNSHostname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
domain, err := sysinfoapi.GetComputerName(sysinfoapi.ComputerNameDNSDomain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fqdn, err := sysinfoapi.GetComputerName(sysinfoapi.ComputerNameDNSFullyQualified)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.hostname,
|
||||
prometheus.GaugeValue,
|
||||
1.0,
|
||||
hostname,
|
||||
domain,
|
||||
fqdn,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) collect(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
nwgi, err := netapi32.GetWorkstationInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gmse, err := sysinfoapi.GlobalMemoryStatusEx()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
currentTime := time.Now()
|
||||
|
||||
func (c *Collector) collectTime(ch chan<- prometheus.Metric) error {
|
||||
timeZoneInfo, err := kernel32.GetDynamicTimeZoneInformation()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// timeZoneKeyName contains the english name of the timezone.
|
||||
timezoneName := syscall.UTF16ToString(timeZoneInfo.TimeZoneKeyName[:])
|
||||
timezoneName := windows.UTF16ToString(timeZoneInfo.TimeZoneKeyName[:])
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.time,
|
||||
prometheus.GaugeValue,
|
||||
float64(time.Now().Unix()),
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.timezone,
|
||||
prometheus.GaugeValue,
|
||||
1.0,
|
||||
timezoneName,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) collectMemory(ch chan<- prometheus.Metric) error {
|
||||
memoryStatusEx, err := sysinfoapi.GlobalMemoryStatusEx()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.physicalMemoryFreeBytes,
|
||||
prometheus.GaugeValue,
|
||||
float64(memoryStatusEx.AvailPhys),
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.virtualMemoryFreeBytes,
|
||||
prometheus.GaugeValue,
|
||||
float64(memoryStatusEx.AvailPageFile),
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.virtualMemoryBytes,
|
||||
prometheus.GaugeValue,
|
||||
float64(memoryStatusEx.TotalPageFile),
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.visibleMemoryBytes,
|
||||
prometheus.GaugeValue,
|
||||
float64(memoryStatusEx.TotalPhys),
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.processMemoryLimitBytes,
|
||||
prometheus.GaugeValue,
|
||||
float64(memoryStatusEx.TotalVirtual),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) collectPaging(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
// Get total allocation of paging files across all disks.
|
||||
memManKey, err := registry.OpenKey(registry.LOCAL_MACHINE, `SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management`, registry.QUERY_VALUE)
|
||||
if err != nil {
|
||||
@@ -228,96 +394,43 @@ func (c *Collector) collect(ctx *types.ScrapeContext, logger log.Logger, ch chan
|
||||
pagingFiles, _, pagingErr := memManKey.GetStringsValue("ExistingPageFiles")
|
||||
|
||||
var fsipf float64
|
||||
|
||||
for _, pagingFile := range pagingFiles {
|
||||
fileString := strings.ReplaceAll(pagingFile, `\??\`, "")
|
||||
file, err := os.Stat(fileString)
|
||||
// For unknown reasons, Windows doesn't always create a page file. Continue collection rather than aborting.
|
||||
if err != nil {
|
||||
_ = level.Debug(logger).Log("msg", fmt.Sprintf("Failed to read page file (reason: %s): %s\n", err, fileString))
|
||||
logger.Debug(fmt.Sprintf("Failed to read page file (reason: %s): %s\n", err, fileString))
|
||||
} else {
|
||||
fsipf += float64(file.Size())
|
||||
}
|
||||
}
|
||||
|
||||
// Get build number and product name from registry
|
||||
ntKey, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer ntKey.Close()
|
||||
|
||||
pn, _, err := ntKey.GetStringValue("ProductName")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bn, _, err := ntKey.GetStringValue("CurrentBuildNumber")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
revision, _, err := ntKey.GetIntegerValue("UBR")
|
||||
if errors.Is(err, registry.ErrNotExist) {
|
||||
revision = 0
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
gpi, err := psapi.GetPerformanceInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pfc := make([]pagingFileCounter, 0)
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects["Paging File"], &pfc, logger); err != nil {
|
||||
if err = perflib.UnmarshalObject(ctx.PerfObjects["Paging File"], &pfc, logger); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get current page file usage.
|
||||
var pfbRaw float64
|
||||
|
||||
for _, pageFile := range pfc {
|
||||
if strings.Contains(strings.ToLower(pageFile.Name), "_total") {
|
||||
continue
|
||||
}
|
||||
|
||||
pfbRaw += pageFile.Usage
|
||||
}
|
||||
|
||||
// Subtract from total page file allocation on disk.
|
||||
pfb := fsipf - (pfbRaw * float64(gpi.PageSize))
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.osInformation,
|
||||
prometheus.GaugeValue,
|
||||
1.0,
|
||||
"Microsoft "+pn, // Caption
|
||||
fmt.Sprintf("%d.%d.%s", nwgi.VersionMajor, nwgi.VersionMinor, bn), // Version
|
||||
strconv.FormatUint(uint64(nwgi.VersionMajor), 10), // Major Version
|
||||
strconv.FormatUint(uint64(nwgi.VersionMinor), 10), // Minor Version
|
||||
bn, // Build number
|
||||
strconv.FormatUint(revision, 10), // Revision
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.physicalMemoryFreeBytes,
|
||||
prometheus.GaugeValue,
|
||||
float64(gmse.AvailPhys),
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.time,
|
||||
prometheus.GaugeValue,
|
||||
float64(currentTime.Unix()),
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.timezone,
|
||||
prometheus.GaugeValue,
|
||||
1.0,
|
||||
timezoneName,
|
||||
)
|
||||
|
||||
if pagingErr == nil {
|
||||
// Subtract from total page file allocation on disk.
|
||||
pfb := fsipf - (pfbRaw * float64(gpi.PageSize))
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.pagingFreeBytes,
|
||||
prometheus.GaugeValue,
|
||||
@@ -330,12 +443,23 @@ func (c *Collector) collect(ctx *types.ScrapeContext, logger log.Logger, ch chan
|
||||
fsipf,
|
||||
)
|
||||
} else {
|
||||
_ = level.Debug(logger).Log("msg", "Could not find HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Memory Management key. windows_os_paging_free_bytes and windows_os_paging_limit_bytes will be omitted.")
|
||||
logger.Debug("Could not find HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Memory Management key. windows_os_paging_free_bytes and windows_os_paging_limit_bytes will be omitted.")
|
||||
}
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.virtualMemoryFreeBytes,
|
||||
c.processes,
|
||||
prometheus.GaugeValue,
|
||||
float64(gmse.AvailPageFile),
|
||||
float64(gpi.ProcessCount),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) collect(ch chan<- prometheus.Metric) {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.osInformation,
|
||||
prometheus.GaugeValue,
|
||||
1.0,
|
||||
)
|
||||
|
||||
// Windows has no defined limit, and is based off available resources. This currently isn't calculated by WMI and is set to default value.
|
||||
@@ -346,36 +470,33 @@ func (c *Collector) collect(ctx *types.ScrapeContext, logger log.Logger, ch chan
|
||||
prometheus.GaugeValue,
|
||||
float64(4294967295),
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.processMemoryLimitBytes,
|
||||
prometheus.GaugeValue,
|
||||
float64(gmse.TotalVirtual),
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.processes,
|
||||
prometheus.GaugeValue,
|
||||
float64(gpi.ProcessCount),
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.users,
|
||||
prometheus.GaugeValue,
|
||||
float64(nwgi.LoggedOnUsers),
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.virtualMemoryBytes,
|
||||
prometheus.GaugeValue,
|
||||
float64(gmse.TotalPageFile),
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.visibleMemoryBytes,
|
||||
prometheus.GaugeValue,
|
||||
float64(gmse.TotalPhys),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) getWindowsVersion() (string, string, string, error) {
|
||||
// Get build number and product name from registry
|
||||
ntKey, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE)
|
||||
if err != nil {
|
||||
return "", "", "", fmt.Errorf("failed to open registry key: %w", err)
|
||||
}
|
||||
|
||||
defer ntKey.Close()
|
||||
|
||||
productName, _, err := ntKey.GetStringValue("ProductName")
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
buildNumber, _, err := ntKey.GetStringValue("CurrentBuildNumber")
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
revision, _, err := ntKey.GetIntegerValue("UBR")
|
||||
if errors.Is(err, registry.ErrNotExist) {
|
||||
revision = 0
|
||||
} else if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
return productName, buildNumber, strconv.FormatUint(revision, 10), nil
|
||||
}
|
||||
|
||||
181
pkg/collector/perfdata/perfdata.go
Normal file
181
pkg/collector/perfdata/perfdata.go
Normal file
@@ -0,0 +1,181 @@
|
||||
//go:build windows
|
||||
|
||||
package perfdata
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"maps"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/perfdata"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/yusufpapurcu/wmi"
|
||||
)
|
||||
|
||||
const (
|
||||
Name = "perfdata"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Objects []Object `yaml:"objects"`
|
||||
}
|
||||
|
||||
var ConfigDefaults = Config{
|
||||
Objects: make([]Object, 0),
|
||||
}
|
||||
|
||||
// A Collector is a Prometheus collector for perfdata metrics.
|
||||
type Collector struct {
|
||||
config Config
|
||||
}
|
||||
|
||||
func New(config *Config) *Collector {
|
||||
if config == nil {
|
||||
config = &ConfigDefaults
|
||||
}
|
||||
|
||||
if config.Objects == nil {
|
||||
config.Objects = ConfigDefaults.Objects
|
||||
}
|
||||
|
||||
c := &Collector{
|
||||
config: *config,
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func NewWithFlags(app *kingpin.Application) *Collector {
|
||||
c := &Collector{
|
||||
config: ConfigDefaults,
|
||||
}
|
||||
|
||||
var objects string
|
||||
|
||||
app.Flag(
|
||||
"collector.perfdata.objects",
|
||||
"Objects of performance data to observe. See docs for more information on how to use this flag. By default, no objects are observed.",
|
||||
).Default("").StringVar(&objects)
|
||||
|
||||
app.Action(func(*kingpin.ParseContext) error {
|
||||
if objects == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := json.Unmarshal([]byte(objects), &c.config.Objects); err != nil {
|
||||
return fmt.Errorf("failed to parse objects: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
for _, object := range c.config.Objects {
|
||||
object.collector.Close()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error {
|
||||
logger.Warn("The perfdata collector is in an experimental state! The configuration may change in future. Please report any issues.")
|
||||
|
||||
for i, object := range c.config.Objects {
|
||||
collector, err := perfdata.NewCollector(object.Object, object.Instances, slices.Sorted(maps.Keys(object.Counters)))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create pdh collector: %w", err)
|
||||
}
|
||||
|
||||
if object.InstanceLabel == "" {
|
||||
c.config.Objects[i].InstanceLabel = "instance"
|
||||
}
|
||||
|
||||
c.config.Objects[i].collector = collector
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
if err := c.collect(ch); err != nil {
|
||||
logger.Error("failed collecting performance data metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) collect(ch chan<- prometheus.Metric) error {
|
||||
for _, object := range c.config.Objects {
|
||||
data, err := object.collector.Collect()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to collect data: %w", err)
|
||||
}
|
||||
|
||||
for instance, counters := range data {
|
||||
for counter, value := range counters {
|
||||
var labels prometheus.Labels
|
||||
if instance != perfdata.EmptyInstance {
|
||||
labels = prometheus.Labels{object.InstanceLabel: instance}
|
||||
}
|
||||
|
||||
metricType := value.Type
|
||||
|
||||
if val, ok := object.Counters[counter]; ok {
|
||||
switch val.Type {
|
||||
case "counter":
|
||||
metricType = prometheus.CounterValue
|
||||
case "gauge":
|
||||
metricType = prometheus.GaugeValue
|
||||
}
|
||||
}
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
prometheus.NewDesc(
|
||||
sanitizeMetricName(fmt.Sprintf("%s_perfdata_%s_%s", types.Namespace, object.Object, counter)),
|
||||
fmt.Sprintf("Performance data for \\%s\\%s", object.Object, counter),
|
||||
nil,
|
||||
labels,
|
||||
),
|
||||
metricType,
|
||||
value.FirstValue,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func sanitizeMetricName(name string) string {
|
||||
replacer := strings.NewReplacer(
|
||||
".", "",
|
||||
"%", "",
|
||||
"/", "_",
|
||||
" ", "_",
|
||||
"-", "_",
|
||||
)
|
||||
|
||||
return strings.Trim(replacer.Replace(strings.ToLower(name)), "_")
|
||||
}
|
||||
87
pkg/collector/perfdata/perfdata_collector_test.go
Normal file
87
pkg/collector/perfdata/perfdata_collector_test.go
Normal file
@@ -0,0 +1,87 @@
|
||||
//go:build windows
|
||||
|
||||
package perfdata_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/prometheus-community/windows_exporter/pkg/collector/perfdata"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type collectorAdapter struct {
|
||||
perfdata.Collector
|
||||
}
|
||||
|
||||
// Describe implements the prometheus.Collector interface.
|
||||
func (a collectorAdapter) Describe(_ chan<- *prometheus.Desc) {}
|
||||
|
||||
// Collect implements the prometheus.Collector interface.
|
||||
func (a collectorAdapter) Collect(ch chan<- prometheus.Metric) {
|
||||
logger := slog.New(slog.NewTextHandler(io.Discard, nil))
|
||||
|
||||
if err := a.Collector.Collect(nil, logger, ch); err != nil {
|
||||
panic(fmt.Sprintf("failed to update collector: %v", err))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCollector(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, tc := range []struct {
|
||||
object string
|
||||
instances []string
|
||||
counters map[string]perfdata.Counter
|
||||
expectedMetrics *regexp.Regexp
|
||||
}{
|
||||
{
|
||||
object: "Memory",
|
||||
instances: nil,
|
||||
counters: map[string]perfdata.Counter{"Available Bytes": {Type: "gauge"}},
|
||||
expectedMetrics: regexp.MustCompile(`^# HELP windows_perfdata_memory_available_bytes Performance data for \\\\Memory\\\\Available Bytes\s*# TYPE windows_perfdata_memory_available_bytes gauge\s*windows_perfdata_memory_available_bytes \d`),
|
||||
},
|
||||
{
|
||||
object: "Process",
|
||||
instances: []string{"*"},
|
||||
counters: map[string]perfdata.Counter{"Thread Count": {Type: "counter"}},
|
||||
expectedMetrics: regexp.MustCompile(`^# HELP windows_perfdata_process_thread_count Performance data for \\\\Process\\\\Thread Count\s*# TYPE windows_perfdata_process_thread_count counter\s*windows_perfdata_process_thread_count\{instance=".+"} \d`),
|
||||
},
|
||||
} {
|
||||
t.Run(tc.object, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
perfDataCollector := perfdata.New(&perfdata.Config{
|
||||
Objects: []perfdata.Object{
|
||||
{
|
||||
Object: tc.object,
|
||||
Instances: tc.instances,
|
||||
Counters: tc.counters,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
logger := slog.New(slog.NewTextHandler(io.Discard, nil))
|
||||
err := perfDataCollector.Build(logger, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
registry := prometheus.NewRegistry()
|
||||
registry.MustRegister(collectorAdapter{*perfDataCollector})
|
||||
|
||||
rw := httptest.NewRecorder()
|
||||
promhttp.HandlerFor(registry, promhttp.HandlerOpts{ErrorHandling: promhttp.ContinueOnError}).ServeHTTP(rw, &http.Request{})
|
||||
got := rw.Body.String()
|
||||
|
||||
assert.NotEmpty(t, got)
|
||||
assert.Regexp(t, tc.expectedMetrics, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
18
pkg/collector/perfdata/perfdata_test.go
Normal file
18
pkg/collector/perfdata/perfdata_test.go
Normal file
@@ -0,0 +1,18 @@
|
||||
//go:build windows
|
||||
|
||||
package perfdata_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/collector/perfdata"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/testutils"
|
||||
)
|
||||
|
||||
func BenchmarkCollector(b *testing.B) {
|
||||
perfDataObjects := `[{"object":"Processor Information","instances":["*"],"counters":{"*": {}}}]`
|
||||
kingpin.CommandLine.GetArg("collector.perfdata.objects").StringVar(&perfDataObjects)
|
||||
|
||||
testutils.FuncBenchmarkCollector(b, perfdata.Name, perfdata.NewWithFlags)
|
||||
}
|
||||
18
pkg/collector/perfdata/types.go
Normal file
18
pkg/collector/perfdata/types.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package perfdata
|
||||
|
||||
import (
|
||||
"github.com/prometheus-community/windows_exporter/pkg/perfdata"
|
||||
)
|
||||
|
||||
type Object struct {
|
||||
Object string `json:"object" yaml:"object"`
|
||||
Instances []string `json:"instances" yaml:"instances"`
|
||||
Counters map[string]Counter `json:"counters" yaml:"counters"`
|
||||
InstanceLabel string `json:"instance_label" yaml:"instance_label"` //nolint:tagliatelle
|
||||
|
||||
collector *perfdata.Collector
|
||||
}
|
||||
|
||||
type Counter struct {
|
||||
Type string `json:"type" yaml:"type"`
|
||||
}
|
||||
@@ -4,12 +4,11 @@ package physical_disk
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/perflib"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
@@ -106,15 +105,15 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{"PhysicalDisk"}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
|
||||
c.requestsQueued = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "requests_queued"),
|
||||
"The number of requests queued to the disk (PhysicalDisk.CurrentDiskQueueLength)",
|
||||
@@ -204,12 +203,16 @@ func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
if err := c.collect(ctx, logger, ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting physical_disk metrics", "err", err)
|
||||
logger.Error("failed collecting physical_disk metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -232,9 +235,11 @@ type PhysicalDisk struct {
|
||||
AvgDiskSecPerTransfer float64 `perflib:"Avg. Disk sec/Transfer"`
|
||||
}
|
||||
|
||||
func (c *Collector) collect(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
var dst []PhysicalDisk
|
||||
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects["PhysicalDisk"], &dst, logger); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -5,12 +5,11 @@ package printer
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/yusufpapurcu/wmi"
|
||||
@@ -104,11 +103,11 @@ func NewWithFlags(app *kingpin.Application) *Collector {
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, wmiClient *wmi.Client) error {
|
||||
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
|
||||
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
|
||||
return errors.New("wmiClient or SWbemServicesClient is nil")
|
||||
}
|
||||
@@ -139,7 +138,9 @@ func (c *Collector) Build(_ log.Logger, wmiClient *wmi.Client) error {
|
||||
|
||||
func (c *Collector) GetName() string { return Name }
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) { return []string{"Printer"}, nil }
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{"Printer"}, nil
|
||||
}
|
||||
|
||||
type wmiPrinter struct {
|
||||
Name string
|
||||
@@ -153,15 +154,21 @@ type wmiPrintJob struct {
|
||||
Status string
|
||||
}
|
||||
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
if err := c.collectPrinterStatus(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed to collect printer status metrics", "err", err)
|
||||
logger.Error("failed to collect printer status metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.collectPrinterJobStatus(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed to collect printer job status metrics", "err", err)
|
||||
logger.Error("failed to collect printer job status metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@@ -5,15 +5,13 @@ package process
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/perflib"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
@@ -124,16 +122,16 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{"Process"}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(logger log.Logger, wmiClient *wmi.Client) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Build(logger *slog.Logger, wmiClient *wmi.Client) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
|
||||
return errors.New("wmiClient or SWbemServicesClient is nil")
|
||||
@@ -142,7 +140,7 @@ func (c *Collector) Build(logger log.Logger, wmiClient *wmi.Client) error {
|
||||
c.wmiClient = wmiClient
|
||||
|
||||
if c.config.ProcessInclude.String() == "^(?:.*)$" && c.config.ProcessExclude.String() == "^(?:)$" {
|
||||
_ = level.Warn(logger).Log("msg", "No filters specified for process collector. This will generate a very large number of metrics!")
|
||||
logger.Warn("No filters specified for process collector. This will generate a very large number of metrics!")
|
||||
}
|
||||
|
||||
c.info = prometheus.NewDesc(
|
||||
@@ -285,9 +283,10 @@ type WorkerProcess struct {
|
||||
ProcessId uint64
|
||||
}
|
||||
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
data := make([]perflibProcess, 0)
|
||||
|
||||
err := perflib.UnmarshalObject(ctx.PerfObjects["Process"], &data, logger)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -296,7 +295,9 @@ func (c *Collector) Collect(ctx *types.ScrapeContext, logger log.Logger, ch chan
|
||||
var workerProcesses []WorkerProcess
|
||||
if c.config.EnableWorkerProcess {
|
||||
if err := c.wmiClient.Query("SELECT * FROM WorkerProcess", &workerProcesses, nil, "root\\WebAdministration"); err != nil {
|
||||
_ = level.Debug(logger).Log("msg", "Could not query WebAdministration namespace for IIS worker processes", "err", err)
|
||||
logger.Debug("Could not query WebAdministration namespace for IIS worker processes",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,6 +317,7 @@ func (c *Collector) Collect(ctx *types.ScrapeContext, logger log.Logger, ch chan
|
||||
for _, wp := range workerProcesses {
|
||||
if wp.ProcessId == uint64(process.IDProcess) {
|
||||
processName = strings.Join([]string{processName, wp.AppPoolName}, "_")
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -323,7 +325,10 @@ func (c *Collector) Collect(ctx *types.ScrapeContext, logger log.Logger, ch chan
|
||||
|
||||
cmdLine, processOwner, processGroupID, err := c.getProcessInformation(logger, uint32(process.IDProcess))
|
||||
if err != nil {
|
||||
_ = level.Debug(logger).Log("msg", "Failed to get process information", "pid", pid, "err", err)
|
||||
logger.Debug("Failed to get process information",
|
||||
slog.String("pid", pid),
|
||||
slog.Any("err", err),
|
||||
)
|
||||
}
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
@@ -485,7 +490,7 @@ func (c *Collector) Collect(ctx *types.ScrapeContext, logger log.Logger, ch chan
|
||||
}
|
||||
|
||||
// ref: https://github.com/microsoft/hcsshim/blob/8beabacfc2d21767a07c20f8dd5f9f3932dbf305/internal/uvm/stats.go#L25
|
||||
func (c *Collector) getProcessInformation(logger log.Logger, pid uint32) (string, string, uint32, error) {
|
||||
func (c *Collector) getProcessInformation(logger *slog.Logger, pid uint32) (string, string, uint32, error) {
|
||||
if pid == 0 {
|
||||
return "", "", 0, nil
|
||||
}
|
||||
@@ -501,7 +506,9 @@ func (c *Collector) getProcessInformation(logger log.Logger, pid uint32) (string
|
||||
|
||||
defer func(hProcess windows.Handle) {
|
||||
if err := windows.CloseHandle(hProcess); err != nil {
|
||||
_ = level.Warn(logger).Log("msg", "CloseHandle failed", "err", err)
|
||||
logger.Warn("CloseHandle failed",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
}
|
||||
}(hProcess)
|
||||
|
||||
@@ -528,12 +535,14 @@ func (c *Collector) getProcessInformation(logger log.Logger, pid uint32) (string
|
||||
func (c *Collector) getExtendedProcessInformation(hProcess windows.Handle) (string, uint32, error) {
|
||||
// Get the process environment block (PEB) address
|
||||
var pbi windows.PROCESS_BASIC_INFORMATION
|
||||
|
||||
retLen := uint32(unsafe.Sizeof(pbi))
|
||||
if err := windows.NtQueryInformationProcess(hProcess, windows.ProcessBasicInformation, unsafe.Pointer(&pbi), retLen, &retLen); err != nil {
|
||||
return "", 0, fmt.Errorf("failed to query process basic information: %w", err)
|
||||
}
|
||||
|
||||
peb := windows.PEB{}
|
||||
|
||||
err := windows.ReadProcessMemory(hProcess,
|
||||
uintptr(unsafe.Pointer(pbi.PebBaseAddress)),
|
||||
(*byte)(unsafe.Pointer(&peb)),
|
||||
@@ -545,6 +554,7 @@ func (c *Collector) getExtendedProcessInformation(hProcess windows.Handle) (stri
|
||||
}
|
||||
|
||||
processParameters := windows.RTL_USER_PROCESS_PARAMETERS{}
|
||||
|
||||
err = windows.ReadProcessMemory(hProcess,
|
||||
uintptr(unsafe.Pointer(peb.ProcessParameters)),
|
||||
(*byte)(unsafe.Pointer(&processParameters)),
|
||||
@@ -570,7 +580,7 @@ func (c *Collector) getExtendedProcessInformation(hProcess windows.Handle) (stri
|
||||
return strings.TrimSpace(windows.UTF16ToString(cmdLineUTF16)), processParameters.ProcessGroupId, nil
|
||||
}
|
||||
|
||||
func (c *Collector) getProcessOwner(logger log.Logger, hProcess windows.Handle) (string, error) {
|
||||
func (c *Collector) getProcessOwner(logger *slog.Logger, hProcess windows.Handle) (string, error) {
|
||||
var tok windows.Token
|
||||
|
||||
if err := windows.OpenProcessToken(hProcess, windows.TOKEN_QUERY, &tok); err != nil {
|
||||
@@ -583,7 +593,9 @@ func (c *Collector) getProcessOwner(logger log.Logger, hProcess windows.Handle)
|
||||
|
||||
defer func(tok windows.Token) {
|
||||
if err := tok.Close(); err != nil {
|
||||
_ = level.Warn(logger).Log("msg", "Token close failed", "err", err)
|
||||
logger.Warn("Token close failed",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
}
|
||||
}(tok)
|
||||
|
||||
@@ -620,7 +632,7 @@ func (c *Collector) openProcess(pid uint32) (windows.Handle, bool, error) {
|
||||
return 0, false, fmt.Errorf("failed to open process: %w", err)
|
||||
}
|
||||
|
||||
if errors.Is(err, syscall.Errno(0x57)) { // invalid parameter, for PIDs that don't exist
|
||||
if errors.Is(err, windows.Errno(0x57)) { // invalid parameter, for PIDs that don't exist
|
||||
return 0, false, errors.New("process not found")
|
||||
}
|
||||
|
||||
|
||||
@@ -4,48 +4,71 @@ package collector
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
// Prometheus implements prometheus.Collector for a set of Windows Collectors.
|
||||
// Interface guard.
|
||||
var _ prometheus.Collector = (*Prometheus)(nil)
|
||||
|
||||
// Prometheus implements prometheus.Collector for a set of Windows MetricCollectors.
|
||||
type Prometheus struct {
|
||||
maxScrapeDuration time.Duration
|
||||
collectors *Collectors
|
||||
logger log.Logger
|
||||
logger *slog.Logger
|
||||
metricCollectors *MetricCollectors
|
||||
|
||||
// Base metrics returned by Prometheus
|
||||
scrapeDurationDesc *prometheus.Desc
|
||||
scrapeSuccessDesc *prometheus.Desc
|
||||
scrapeTimeoutDesc *prometheus.Desc
|
||||
snapshotDuration *prometheus.Desc
|
||||
scrapeDurationDesc *prometheus.Desc
|
||||
collectorScrapeDurationDesc *prometheus.Desc
|
||||
collectorScrapeSuccessDesc *prometheus.Desc
|
||||
collectorScrapeTimeoutDesc *prometheus.Desc
|
||||
snapshotDuration *prometheus.Desc
|
||||
}
|
||||
|
||||
// NewPrometheus returns a new Prometheus where the set of Collectors must
|
||||
type collectorStatus struct {
|
||||
name string
|
||||
statusCode collectorStatusCode
|
||||
}
|
||||
|
||||
type collectorStatusCode int
|
||||
|
||||
const (
|
||||
pending collectorStatusCode = iota
|
||||
success
|
||||
failed
|
||||
)
|
||||
|
||||
// NewPrometheusCollector returns a new Prometheus where the set of MetricCollectors must
|
||||
// return metrics within the given timeout.
|
||||
func NewPrometheus(timeout time.Duration, cs *Collectors, logger log.Logger) *Prometheus {
|
||||
func (c *MetricCollectors) NewPrometheusCollector(timeout time.Duration, logger *slog.Logger) *Prometheus {
|
||||
return &Prometheus{
|
||||
maxScrapeDuration: timeout,
|
||||
collectors: cs,
|
||||
metricCollectors: c,
|
||||
logger: logger,
|
||||
scrapeDurationDesc: prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, "exporter", "scrape_duration_seconds"),
|
||||
"windows_exporter: Total scrape duration.",
|
||||
nil,
|
||||
nil,
|
||||
),
|
||||
collectorScrapeDurationDesc: prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, "exporter", "collector_duration_seconds"),
|
||||
"windows_exporter: Duration of a collection.",
|
||||
[]string{"collector"},
|
||||
nil,
|
||||
),
|
||||
scrapeSuccessDesc: prometheus.NewDesc(
|
||||
collectorScrapeSuccessDesc: prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, "exporter", "collector_success"),
|
||||
"windows_exporter: Whether the collector was successful.",
|
||||
[]string{"collector"},
|
||||
nil,
|
||||
),
|
||||
scrapeTimeoutDesc: prometheus.NewDesc(
|
||||
collectorScrapeTimeoutDesc: prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, "exporter", "collector_timeout"),
|
||||
"windows_exporter: Whether the collector timed out.",
|
||||
[]string{"collector"},
|
||||
@@ -60,133 +83,159 @@ func NewPrometheus(timeout time.Duration, cs *Collectors, logger log.Logger) *Pr
|
||||
}
|
||||
}
|
||||
|
||||
// Describe sends all the descriptors of the Collectors included to
|
||||
// the provided channel.
|
||||
func (coll *Prometheus) Describe(ch chan<- *prometheus.Desc) {
|
||||
ch <- coll.scrapeDurationDesc
|
||||
ch <- coll.scrapeSuccessDesc
|
||||
}
|
||||
func (p *Prometheus) Describe(_ chan<- *prometheus.Desc) {}
|
||||
|
||||
type collectorOutcome int
|
||||
|
||||
const (
|
||||
pending collectorOutcome = iota
|
||||
success
|
||||
failed
|
||||
)
|
||||
|
||||
// Collect sends the collected metrics from each of the Collectors to
|
||||
// Collect sends the collected metrics from each of the MetricCollectors to
|
||||
// prometheus.
|
||||
func (coll *Prometheus) Collect(ch chan<- prometheus.Metric) {
|
||||
func (p *Prometheus) Collect(ch chan<- prometheus.Metric) {
|
||||
t := time.Now()
|
||||
|
||||
scrapeContext, err := coll.collectors.PrepareScrapeContext()
|
||||
// Scrape Performance Counters for all collectors
|
||||
scrapeContext, err := p.metricCollectors.PrepareScrapeContext()
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
coll.snapshotDuration,
|
||||
p.snapshotDuration,
|
||||
prometheus.GaugeValue,
|
||||
time.Since(t).Seconds(),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
ch <- prometheus.NewInvalidMetric(coll.scrapeSuccessDesc, fmt.Errorf("failed to prepare scrape: %w", err))
|
||||
ch <- prometheus.NewInvalidMetric(p.collectorScrapeSuccessDesc, fmt.Errorf("failed to prepare scrape: %w", err))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// WaitGroup to wait for all collectors to finish
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(len(coll.collectors.collectors))
|
||||
collectorOutcomes := make(map[string]collectorOutcome)
|
||||
for name := range coll.collectors.collectors {
|
||||
collectorOutcomes[name] = pending
|
||||
}
|
||||
wg.Add(len(p.metricCollectors.Collectors))
|
||||
|
||||
metricsBuffer := make(chan prometheus.Metric)
|
||||
l := sync.Mutex{}
|
||||
finished := false
|
||||
go func() {
|
||||
for m := range metricsBuffer {
|
||||
l.Lock()
|
||||
if !finished {
|
||||
ch <- m
|
||||
}
|
||||
l.Unlock()
|
||||
}
|
||||
}()
|
||||
// Using a channel to collect the status of each collector
|
||||
// A channel is safe to use concurrently while a map is not
|
||||
collectorStatusCh := make(chan collectorStatus, len(p.metricCollectors.Collectors))
|
||||
|
||||
for name, c := range coll.collectors.collectors {
|
||||
go func(name string, c Collector) {
|
||||
// Execute all collectors concurrently
|
||||
// timeout handling is done in the execute function
|
||||
for name, metricsCollector := range p.metricCollectors.Collectors {
|
||||
go func(name string, metricsCollector Collector) {
|
||||
defer wg.Done()
|
||||
outcome := coll.execute(coll.logger, name, c, scrapeContext, metricsBuffer)
|
||||
l.Lock()
|
||||
if !finished {
|
||||
collectorOutcomes[name] = outcome
|
||||
|
||||
collectorStatusCh <- collectorStatus{
|
||||
name: name,
|
||||
statusCode: p.execute(name, metricsCollector, scrapeContext, ch),
|
||||
}
|
||||
l.Unlock()
|
||||
}(name, c)
|
||||
}(name, metricsCollector)
|
||||
}
|
||||
|
||||
allDone := make(chan struct{})
|
||||
go func() {
|
||||
wg.Wait()
|
||||
close(allDone)
|
||||
close(metricsBuffer)
|
||||
}()
|
||||
// Wait for all collectors to finish
|
||||
wg.Wait()
|
||||
|
||||
// Wait until either all Collectors finish, or timeout expires
|
||||
select {
|
||||
case <-allDone:
|
||||
case <-time.After(coll.maxScrapeDuration):
|
||||
}
|
||||
// Close the channel since we are done writing to it
|
||||
close(collectorStatusCh)
|
||||
|
||||
l.Lock()
|
||||
finished = true
|
||||
|
||||
remainingCollectorNames := make([]string, 0)
|
||||
for name, outcome := range collectorOutcomes {
|
||||
for status := range collectorStatusCh {
|
||||
var successValue, timeoutValue float64
|
||||
if outcome == pending {
|
||||
if status.statusCode == pending {
|
||||
timeoutValue = 1.0
|
||||
remainingCollectorNames = append(remainingCollectorNames, name)
|
||||
}
|
||||
if outcome == success {
|
||||
|
||||
if status.statusCode == success {
|
||||
successValue = 1.0
|
||||
}
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
coll.scrapeSuccessDesc,
|
||||
p.collectorScrapeSuccessDesc,
|
||||
prometheus.GaugeValue,
|
||||
successValue,
|
||||
name,
|
||||
status.name,
|
||||
)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
coll.scrapeTimeoutDesc,
|
||||
p.collectorScrapeTimeoutDesc,
|
||||
prometheus.GaugeValue,
|
||||
timeoutValue,
|
||||
name,
|
||||
status.name,
|
||||
)
|
||||
}
|
||||
|
||||
if len(remainingCollectorNames) > 0 {
|
||||
_ = level.Warn(coll.logger).Log("msg", fmt.Sprintf("Collection timed out, still waiting for %v", remainingCollectorNames))
|
||||
}
|
||||
|
||||
l.Unlock()
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
p.scrapeDurationDesc,
|
||||
prometheus.GaugeValue,
|
||||
time.Since(t).Seconds(),
|
||||
)
|
||||
}
|
||||
|
||||
func (coll *Prometheus) execute(logger log.Logger, name string, c Collector, ctx *types.ScrapeContext, ch chan<- prometheus.Metric) collectorOutcome {
|
||||
t := time.Now()
|
||||
err := c.Collect(ctx, logger, ch)
|
||||
duration := time.Since(t).Seconds()
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
coll.scrapeDurationDesc,
|
||||
prometheus.GaugeValue,
|
||||
duration,
|
||||
name,
|
||||
func (p *Prometheus) execute(name string, c Collector, ctx *types.ScrapeContext, ch chan<- prometheus.Metric) collectorStatusCode {
|
||||
var (
|
||||
err error
|
||||
duration time.Duration
|
||||
timeout atomic.Bool
|
||||
)
|
||||
|
||||
// bufCh is a buffer channel to store the metrics
|
||||
// This is needed because once timeout is reached, the prometheus registry channel is closed.
|
||||
bufCh := make(chan prometheus.Metric, 10)
|
||||
errCh := make(chan error, 1)
|
||||
|
||||
// Execute the collector
|
||||
go func() {
|
||||
errCh <- c.Collect(ctx, p.logger, bufCh)
|
||||
|
||||
close(bufCh)
|
||||
}()
|
||||
|
||||
go func() {
|
||||
defer func() {
|
||||
// This prevents a panic from race-condition when closing the ch channel too early.
|
||||
_ = recover()
|
||||
}()
|
||||
|
||||
// Pass metrics to the prometheus registry
|
||||
// If timeout is reached, the channel is closed.
|
||||
// This will cause a panic if we try to write to it.
|
||||
for m := range bufCh {
|
||||
if !timeout.Load() {
|
||||
ch <- m
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
t := time.Now()
|
||||
|
||||
// Wait for the collector to finish or timeout
|
||||
select {
|
||||
case err = <-errCh:
|
||||
duration = time.Since(t)
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
p.collectorScrapeDurationDesc,
|
||||
prometheus.GaugeValue,
|
||||
duration.Seconds(),
|
||||
name,
|
||||
)
|
||||
case <-time.After(p.maxScrapeDuration):
|
||||
timeout.Store(true)
|
||||
|
||||
duration = time.Since(t)
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
p.collectorScrapeDurationDesc,
|
||||
prometheus.GaugeValue,
|
||||
duration.Seconds(),
|
||||
name,
|
||||
)
|
||||
|
||||
p.logger.Warn(fmt.Sprintf("collector %s timeouted after %s", name, p.maxScrapeDuration))
|
||||
|
||||
return pending
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
_ = level.Error(coll.logger).Log("msg", fmt.Sprintf("collector %s failed after %fs", name, duration), "err", err)
|
||||
p.logger.Error(fmt.Sprintf("collector %s failed after %s", name, p.maxScrapeDuration),
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return failed
|
||||
}
|
||||
|
||||
_ = level.Debug(coll.logger).Log("msg", fmt.Sprintf("collector %s succeeded after %fs.", name, duration))
|
||||
p.logger.Error(fmt.Sprintf("collector %s succeeded after %s", name, p.maxScrapeDuration))
|
||||
|
||||
return success
|
||||
}
|
||||
|
||||
@@ -3,11 +3,10 @@
|
||||
package remote_fx
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/perflib"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/utils"
|
||||
@@ -74,15 +73,15 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{"RemoteFX Network", "RemoteFX Graphics"}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(log.Logger, *wmi.Client) error {
|
||||
func (c *Collector) Build(*slog.Logger, *wmi.Client) error {
|
||||
// net
|
||||
c.baseTCPRTT = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "net_base_tcp_rtt_seconds"),
|
||||
@@ -206,21 +205,30 @@ func (c *Collector) Build(log.Logger, *wmi.Client) error {
|
||||
[]string{"session_name"},
|
||||
nil,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
if err := c.collectRemoteFXNetworkCount(ctx, logger, ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting terminal services session count metrics", "err", err)
|
||||
logger.Error("failed collecting terminal services session count metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.collectRemoteFXGraphicsCounters(ctx, logger, ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting terminal services session count metrics", "err", err)
|
||||
logger.Error("failed collecting terminal services session count metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -241,9 +249,10 @@ type perflibRemoteFxNetwork struct {
|
||||
RetransmissionRate float64 `perflib:"Percentage of packets that have been retransmitted"`
|
||||
}
|
||||
|
||||
func (c *Collector) collectRemoteFXNetworkCount(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) collectRemoteFXNetworkCount(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
dst := make([]perflibRemoteFxNetwork, 0)
|
||||
|
||||
err := perflib.UnmarshalObject(ctx.PerfObjects["RemoteFX Network"], &dst, logger)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -336,6 +345,7 @@ func (c *Collector) collectRemoteFXNetworkCount(ctx *types.ScrapeContext, logger
|
||||
normalizeSessionName(d.Name),
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -352,9 +362,10 @@ type perflibRemoteFxGraphics struct {
|
||||
SourceFramesPerSecond float64 `perflib:"Source Frames/Second"`
|
||||
}
|
||||
|
||||
func (c *Collector) collectRemoteFXGraphicsCounters(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) collectRemoteFXGraphicsCounters(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
dst := make([]perflibRemoteFxGraphics, 0)
|
||||
|
||||
err := perflib.UnmarshalObject(ctx.PerfObjects["RemoteFX Graphics"], &dst, logger)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -5,13 +5,12 @@ package scheduled_task
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/go-ole/go-ole"
|
||||
"github.com/go-ole/go-ole/oleutil"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
@@ -129,15 +128,15 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
|
||||
c.lastResult = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "last_result"),
|
||||
"The result that was returned the last time the registered task was run",
|
||||
@@ -162,10 +161,13 @@ func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
if err := c.collect(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting user metrics", "err", err)
|
||||
logger.Error("failed collecting user metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -264,10 +266,12 @@ func getScheduledTasks() (ScheduledTasks, error) {
|
||||
defer taskSchedulerObj.Release()
|
||||
|
||||
taskServiceObj := taskSchedulerObj.MustQueryInterface(ole.IID_IDispatch)
|
||||
|
||||
_, err = oleutil.CallMethod(taskServiceObj, "Connect")
|
||||
if err != nil {
|
||||
return scheduledTasks, err
|
||||
}
|
||||
|
||||
defer taskServiceObj.Release()
|
||||
|
||||
res, err := oleutil.CallMethod(taskServiceObj, "GetFolder", `\`)
|
||||
@@ -325,6 +329,7 @@ func fetchTasksRecursively(folder *ole.IDispatch, scheduledTasks *ScheduledTasks
|
||||
err = oleutil.ForEach(subFolders, func(v *ole.VARIANT) error {
|
||||
subFolder := v.ToIDispatch()
|
||||
defer subFolder.Release()
|
||||
|
||||
return fetchTasksRecursively(subFolder, scheduledTasks)
|
||||
})
|
||||
|
||||
@@ -338,6 +343,7 @@ func parseTask(task *ole.IDispatch) (ScheduledTask, error) {
|
||||
if err != nil {
|
||||
return scheduledTask, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if tempErr := taskNameVar.Clear(); tempErr != nil {
|
||||
err = tempErr
|
||||
@@ -348,6 +354,7 @@ func parseTask(task *ole.IDispatch) (ScheduledTask, error) {
|
||||
if err != nil {
|
||||
return scheduledTask, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if tempErr := taskPathVar.Clear(); tempErr != nil {
|
||||
err = tempErr
|
||||
@@ -358,6 +365,7 @@ func parseTask(task *ole.IDispatch) (ScheduledTask, error) {
|
||||
if err != nil {
|
||||
return scheduledTask, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if tempErr := taskEnabledVar.Clear(); tempErr != nil {
|
||||
err = tempErr
|
||||
@@ -368,6 +376,7 @@ func parseTask(task *ole.IDispatch) (ScheduledTask, error) {
|
||||
if err != nil {
|
||||
return scheduledTask, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if tempErr := taskStateVar.Clear(); tempErr != nil {
|
||||
err = tempErr
|
||||
@@ -378,6 +387,7 @@ func parseTask(task *ole.IDispatch) (ScheduledTask, error) {
|
||||
if err != nil {
|
||||
return scheduledTask, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if tempErr := taskNumberOfMissedRunsVar.Clear(); tempErr != nil {
|
||||
err = tempErr
|
||||
@@ -388,6 +398,7 @@ func parseTask(task *ole.IDispatch) (ScheduledTask, error) {
|
||||
if err != nil {
|
||||
return scheduledTask, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if tempErr := taskLastTaskResultVar.Clear(); tempErr != nil {
|
||||
err = tempErr
|
||||
@@ -396,9 +407,11 @@ func parseTask(task *ole.IDispatch) (ScheduledTask, error) {
|
||||
|
||||
scheduledTask.Name = taskNameVar.ToString()
|
||||
scheduledTask.Path = strings.ReplaceAll(taskPathVar.ToString(), "\\", "/")
|
||||
|
||||
if val, ok := taskEnabledVar.Value().(bool); ok {
|
||||
scheduledTask.Enabled = val
|
||||
}
|
||||
|
||||
scheduledTask.State = TaskState(taskStateVar.Val)
|
||||
scheduledTask.MissedRunsCount = float64(taskNumberOfMissedRunsVar.Val)
|
||||
scheduledTask.LastTaskResult = TaskResult(taskLastTaskResultVar.Val)
|
||||
|
||||
@@ -5,16 +5,13 @@ package service
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/utils"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/yusufpapurcu/wmi"
|
||||
"golang.org/x/sys/windows"
|
||||
@@ -24,30 +21,25 @@ import (
|
||||
const Name = "service"
|
||||
|
||||
type Config struct {
|
||||
ServiceWhereClause string `yaml:"service_where_clause"`
|
||||
UseAPI bool `yaml:"use_api"`
|
||||
V2 bool `yaml:"v2"`
|
||||
ServiceInclude *regexp.Regexp `yaml:"service_include"`
|
||||
ServiceExclude *regexp.Regexp `yaml:"service_exclude"`
|
||||
}
|
||||
|
||||
var ConfigDefaults = Config{
|
||||
ServiceWhereClause: "",
|
||||
UseAPI: false,
|
||||
V2: false,
|
||||
ServiceInclude: types.RegExpAny,
|
||||
ServiceExclude: types.RegExpEmpty,
|
||||
}
|
||||
|
||||
// A Collector is a Prometheus Collector for WMI Win32_Service metrics.
|
||||
// A Collector is a Prometheus Collector for service metrics.
|
||||
type Collector struct {
|
||||
serviceWhereClause *string
|
||||
useAPI *bool
|
||||
v2 *bool
|
||||
config Config
|
||||
|
||||
wmiClient *wmi.Client
|
||||
state *prometheus.Desc
|
||||
processID *prometheus.Desc
|
||||
info *prometheus.Desc
|
||||
startMode *prometheus.Desc
|
||||
|
||||
Information *prometheus.Desc
|
||||
State *prometheus.Desc
|
||||
StartMode *prometheus.Desc
|
||||
Status *prometheus.Desc
|
||||
StateV2 *prometheus.Desc
|
||||
serviceManagerHandle *mgr.Mgr
|
||||
}
|
||||
|
||||
func New(config *Config) *Collector {
|
||||
@@ -55,372 +47,162 @@ func New(config *Config) *Collector {
|
||||
config = &ConfigDefaults
|
||||
}
|
||||
|
||||
if config.ServiceExclude == nil {
|
||||
config.ServiceExclude = ConfigDefaults.ServiceExclude
|
||||
}
|
||||
|
||||
if config.ServiceInclude == nil {
|
||||
config.ServiceInclude = ConfigDefaults.ServiceInclude
|
||||
}
|
||||
|
||||
c := &Collector{
|
||||
serviceWhereClause: &config.ServiceWhereClause,
|
||||
useAPI: &config.UseAPI,
|
||||
config: *config,
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func NewWithFlags(app *kingpin.Application) *Collector {
|
||||
return &Collector{
|
||||
serviceWhereClause: app.Flag(
|
||||
"collector.service.services-where",
|
||||
"WQL 'where' clause to use in WMI metrics query. Limits the response to the services you specify and reduces the size of the response.",
|
||||
).Default(ConfigDefaults.ServiceWhereClause).String(),
|
||||
useAPI: app.Flag(
|
||||
"collector.service.use-api",
|
||||
"Use API calls to collect service data instead of WMI. Flag 'collector.service.services-where' won't be effective.",
|
||||
).Default(strconv.FormatBool(ConfigDefaults.UseAPI)).Bool(),
|
||||
v2: app.Flag(
|
||||
"collector.service.v2",
|
||||
"Enable V2 service collector. This collector can services state much more efficiently, can't provide general service information.",
|
||||
).Default(strconv.FormatBool(ConfigDefaults.V2)).Bool(),
|
||||
c := &Collector{
|
||||
config: ConfigDefaults,
|
||||
}
|
||||
|
||||
var serviceExclude, serviceInclude string
|
||||
|
||||
app.Flag(
|
||||
"collector.service.exclude",
|
||||
"Regexp of service to exclude. Service name (not the display name!) must both match include and not match exclude to be included.",
|
||||
).Default(c.config.ServiceExclude.String()).StringVar(&serviceExclude)
|
||||
|
||||
app.Flag(
|
||||
"collector.service.include",
|
||||
"Regexp of service to include. Process name (not the display name!) must both match include and not match exclude to be included.",
|
||||
).Default(c.config.ServiceInclude.String()).StringVar(&serviceInclude)
|
||||
|
||||
app.Action(func(*kingpin.ParseContext) error {
|
||||
var err error
|
||||
|
||||
c.config.ServiceExclude, err = regexp.Compile(fmt.Sprintf("^(?:%s)$", serviceExclude))
|
||||
if err != nil {
|
||||
return fmt.Errorf("collector.process.exclude: %w", err)
|
||||
}
|
||||
|
||||
c.config.ServiceInclude, err = regexp.Compile(fmt.Sprintf("^(?:%s)$", serviceInclude))
|
||||
if err != nil {
|
||||
return fmt.Errorf("collector.process.include: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
return nil
|
||||
}
|
||||
func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
func (c *Collector) Build(logger log.Logger, wmiClient *wmi.Client) error {
|
||||
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
|
||||
return errors.New("wmiClient or SWbemServicesClient is nil")
|
||||
if c.config.ServiceInclude.String() == "^(?:.*)$" && c.config.ServiceExclude.String() == "^(?:)$" {
|
||||
logger.Warn("No filters specified for service collector. This will generate a very large number of metrics!")
|
||||
}
|
||||
|
||||
c.wmiClient = wmiClient
|
||||
|
||||
logger = log.With(logger, "collector", Name)
|
||||
|
||||
if utils.IsEmpty(c.serviceWhereClause) {
|
||||
_ = level.Warn(logger).Log("msg", "No where-clause specified for service collector. This will generate a very large number of metrics!")
|
||||
}
|
||||
if *c.useAPI {
|
||||
_ = level.Warn(logger).Log("msg", "API collection is enabled.")
|
||||
}
|
||||
|
||||
c.Information = prometheus.NewDesc(
|
||||
c.info = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "info"),
|
||||
"A metric with a constant '1' value labeled with service information",
|
||||
[]string{"name", "display_name", "process_id", "run_as"},
|
||||
[]string{"name", "display_name", "run_as", "path_name"},
|
||||
nil,
|
||||
)
|
||||
c.State = prometheus.NewDesc(
|
||||
c.state = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "state"),
|
||||
"The state of the service (State)",
|
||||
[]string{"name", "state"},
|
||||
[]string{"name", "status"},
|
||||
nil,
|
||||
)
|
||||
c.StartMode = prometheus.NewDesc(
|
||||
c.startMode = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "start_mode"),
|
||||
"The start mode of the service (StartMode)",
|
||||
[]string{"name", "start_mode"},
|
||||
nil,
|
||||
)
|
||||
c.Status = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "status"),
|
||||
"The status of the service (Status)",
|
||||
[]string{"name", "status"},
|
||||
nil,
|
||||
)
|
||||
c.StateV2 = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "state"),
|
||||
"The state of the service (State)",
|
||||
[]string{"name", "display_name", "status"},
|
||||
c.processID = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "process"),
|
||||
"Process of started service. The value is the creation time of the process as a unix timestamp.",
|
||||
[]string{"name", "process_id"},
|
||||
nil,
|
||||
)
|
||||
|
||||
// EnumServiceStatusEx requires only SC_MANAGER_ENUM_SERVICE.
|
||||
handle, err := windows.OpenSCManager(nil, nil, windows.SC_MANAGER_ENUMERATE_SERVICE)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open scm: %w", err)
|
||||
}
|
||||
|
||||
c.serviceManagerHandle = &mgr.Mgr{Handle: handle}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close(logger *slog.Logger) error {
|
||||
if err := c.serviceManagerHandle.Disconnect(); err != nil {
|
||||
logger.Warn("Failed to disconnect from scm",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
var err error
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
switch {
|
||||
case *c.useAPI:
|
||||
if err = c.collectAPI(logger, ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting API service metrics:", "err", err)
|
||||
}
|
||||
case *c.v2:
|
||||
if err = c.collectAPIV2(logger, ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting API service metrics:", "err", err)
|
||||
}
|
||||
default:
|
||||
if err = c.collectWMI(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting WMI service metrics:", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// Win32_Service docs:
|
||||
// - https://msdn.microsoft.com/en-us/library/aa394418(v=vs.85).aspx
|
||||
type Win32_Service struct {
|
||||
DisplayName string
|
||||
Name string
|
||||
ProcessId uint32
|
||||
State string
|
||||
Status string
|
||||
StartMode string
|
||||
StartName *string
|
||||
}
|
||||
|
||||
var (
|
||||
allStates = []string{
|
||||
"stopped",
|
||||
"start pending",
|
||||
"stop pending",
|
||||
"running",
|
||||
"continue pending",
|
||||
"pause pending",
|
||||
"paused",
|
||||
"unknown",
|
||||
}
|
||||
apiStateValues = map[uint32]string{
|
||||
windows.SERVICE_CONTINUE_PENDING: "continue pending",
|
||||
windows.SERVICE_PAUSE_PENDING: "pause pending",
|
||||
windows.SERVICE_PAUSED: "paused",
|
||||
windows.SERVICE_RUNNING: "running",
|
||||
windows.SERVICE_START_PENDING: "start pending",
|
||||
windows.SERVICE_STOP_PENDING: "stop pending",
|
||||
windows.SERVICE_STOPPED: "stopped",
|
||||
}
|
||||
allStartModes = []string{
|
||||
"boot",
|
||||
"system",
|
||||
"auto",
|
||||
"manual",
|
||||
"disabled",
|
||||
}
|
||||
apiStartModeValues = map[uint32]string{
|
||||
windows.SERVICE_AUTO_START: "auto",
|
||||
windows.SERVICE_BOOT_START: "boot",
|
||||
windows.SERVICE_DEMAND_START: "manual",
|
||||
windows.SERVICE_DISABLED: "disabled",
|
||||
windows.SERVICE_SYSTEM_START: "system",
|
||||
}
|
||||
allStatuses = []string{
|
||||
"ok",
|
||||
"error",
|
||||
"degraded",
|
||||
"unknown",
|
||||
"pred fail",
|
||||
"starting",
|
||||
"stopping",
|
||||
"service",
|
||||
"stressed",
|
||||
"nonrecover",
|
||||
"no contact",
|
||||
"lost comm",
|
||||
}
|
||||
)
|
||||
|
||||
func (c *Collector) collectWMI(ch chan<- prometheus.Metric) error {
|
||||
var dst []Win32_Service
|
||||
query := "SELECT * FROM Win32_Service"
|
||||
|
||||
if *c.serviceWhereClause != "" {
|
||||
query += " WHERE " + *c.serviceWhereClause
|
||||
}
|
||||
|
||||
if err := c.wmiClient.Query(query, &dst); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, service := range dst {
|
||||
pid := strconv.FormatUint(uint64(service.ProcessId), 10)
|
||||
|
||||
runAs := ""
|
||||
if service.StartName != nil {
|
||||
runAs = *service.StartName
|
||||
}
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.Information,
|
||||
prometheus.GaugeValue,
|
||||
1.0,
|
||||
strings.ToLower(service.Name),
|
||||
service.DisplayName,
|
||||
pid,
|
||||
runAs,
|
||||
if err := c.collect(logger, ch); err != nil {
|
||||
logger.Error("failed collecting API service metrics:",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
for _, state := range allStates {
|
||||
isCurrentState := 0.0
|
||||
if state == strings.ToLower(service.State) {
|
||||
isCurrentState = 1.0
|
||||
}
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.State,
|
||||
prometheus.GaugeValue,
|
||||
isCurrentState,
|
||||
strings.ToLower(service.Name),
|
||||
state,
|
||||
)
|
||||
}
|
||||
|
||||
for _, startMode := range allStartModes {
|
||||
isCurrentStartMode := 0.0
|
||||
if startMode == strings.ToLower(service.StartMode) {
|
||||
isCurrentStartMode = 1.0
|
||||
}
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.StartMode,
|
||||
prometheus.GaugeValue,
|
||||
isCurrentStartMode,
|
||||
strings.ToLower(service.Name),
|
||||
startMode,
|
||||
)
|
||||
}
|
||||
|
||||
for _, status := range allStatuses {
|
||||
isCurrentStatus := 0.0
|
||||
if status == strings.ToLower(service.Status) {
|
||||
isCurrentStatus = 1.0
|
||||
}
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.Status,
|
||||
prometheus.GaugeValue,
|
||||
isCurrentStatus,
|
||||
strings.ToLower(service.Name),
|
||||
status,
|
||||
)
|
||||
}
|
||||
return fmt.Errorf("failed collecting API service metrics: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) collectAPI(logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
svcmgrConnection, err := mgr.Connect()
|
||||
func (c *Collector) collect(logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
services, err := c.queryAllServices()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer svcmgrConnection.Disconnect() //nolint:errcheck
|
||||
logger.Warn("Failed to query services",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
// List All Services from the Services Manager.
|
||||
serviceList, err := svcmgrConnection.ListServices()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Iterate through the Services List.
|
||||
for _, service := range serviceList {
|
||||
(func() {
|
||||
// Get UTF16 service name.
|
||||
serviceName, err := syscall.UTF16PtrFromString(service)
|
||||
if err != nil {
|
||||
_ = level.Warn(logger).Log("msg", fmt.Sprintf("Service %s get name error: %#v", service, err))
|
||||
return
|
||||
}
|
||||
|
||||
// Open connection for service handler.
|
||||
serviceHandle, err := windows.OpenService(svcmgrConnection.Handle, serviceName, windows.GENERIC_READ)
|
||||
if err != nil {
|
||||
_ = level.Warn(logger).Log("msg", fmt.Sprintf("Open service %s error: %#v", service, err))
|
||||
return
|
||||
}
|
||||
|
||||
// Create handle for each service.
|
||||
serviceManager := &mgr.Service{Name: service, Handle: serviceHandle}
|
||||
defer serviceManager.Close()
|
||||
|
||||
// Get Service Configuration.
|
||||
serviceConfig, err := serviceManager.Config()
|
||||
if err != nil {
|
||||
_ = level.Warn(logger).Log("msg", fmt.Sprintf("Get service %s config error: %#v", service, err))
|
||||
return
|
||||
}
|
||||
|
||||
// Get Service Current Status.
|
||||
serviceStatus, err := serviceManager.Query()
|
||||
if err != nil {
|
||||
_ = level.Warn(logger).Log("msg", fmt.Sprintf("Get service %s status error: %#v", service, err))
|
||||
return
|
||||
}
|
||||
|
||||
pid := strconv.FormatUint(uint64(serviceStatus.ProcessId), 10)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.Information,
|
||||
prometheus.GaugeValue,
|
||||
1.0,
|
||||
strings.ToLower(service),
|
||||
serviceConfig.DisplayName,
|
||||
pid,
|
||||
serviceConfig.ServiceStartName,
|
||||
)
|
||||
|
||||
for _, state := range apiStateValues {
|
||||
isCurrentState := 0.0
|
||||
if state == apiStateValues[uint32(serviceStatus.State)] {
|
||||
isCurrentState = 1.0
|
||||
}
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.State,
|
||||
prometheus.GaugeValue,
|
||||
isCurrentState,
|
||||
strings.ToLower(service),
|
||||
state,
|
||||
)
|
||||
}
|
||||
|
||||
for _, startMode := range apiStartModeValues {
|
||||
isCurrentStartMode := 0.0
|
||||
if startMode == apiStartModeValues[serviceConfig.StartType] {
|
||||
isCurrentStartMode = 1.0
|
||||
}
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.StartMode,
|
||||
prometheus.GaugeValue,
|
||||
isCurrentStartMode,
|
||||
strings.ToLower(service),
|
||||
startMode,
|
||||
)
|
||||
}
|
||||
})()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) collectAPIV2(logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
services, err := c.queryAllServiceStates(logger)
|
||||
if err != nil {
|
||||
_ = level.Warn(logger).Log("msg", "Failed to query services", "err", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if services == nil {
|
||||
_ = level.Warn(logger).Log("msg", "No services queried")
|
||||
logger.Warn("No services queried")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var isCurrentState float64
|
||||
// Iterate through the Services List.
|
||||
for _, service := range services {
|
||||
serviceName := windows.UTF16PtrToString(service.ServiceName)
|
||||
if c.config.ServiceExclude.MatchString(serviceName) ||
|
||||
!c.config.ServiceInclude.MatchString(serviceName) {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, svc := range services {
|
||||
for state, stateValue := range apiStateValues {
|
||||
isCurrentState = 0.0
|
||||
if state == svc.ServiceStatusProcess.CurrentState {
|
||||
isCurrentState = 1.0
|
||||
}
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.StateV2,
|
||||
prometheus.GaugeValue,
|
||||
isCurrentState,
|
||||
windows.UTF16PtrToString(svc.ServiceName),
|
||||
windows.UTF16PtrToString(svc.DisplayName),
|
||||
stateValue,
|
||||
if err := c.collectService(ch, logger, service); err != nil {
|
||||
logger.Warn("failed collecting service info",
|
||||
slog.Any("err", err),
|
||||
slog.String("service", windows.UTF16PtrToString(service.ServiceName)),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -428,56 +210,207 @@ func (c *Collector) collectAPIV2(logger log.Logger, ch chan<- prometheus.Metric)
|
||||
return nil
|
||||
}
|
||||
|
||||
// queryAllServiceStates returns all service states of the current Windows system
|
||||
// This is realized by ask Service Manager directly.
|
||||
//
|
||||
// Unless explicitly stated otherwise all files in this repository are licensed
|
||||
// under the Apache License Version 2.0.
|
||||
// This product includes software developed at Datadog (https://www.datadoghq.com/).
|
||||
// Copyright 2016-present Datadog, Inc.
|
||||
//
|
||||
// Source: https://github.com/DataDog/datadog-agent/blob/afbd8b6c87939c92610c654cb07fdfd439e4fb27/pkg/util/winutil/scmmonitor.go#L61-L96
|
||||
func (c *Collector) queryAllServiceStates(logger log.Logger) ([]windows.ENUM_SERVICE_STATUS_PROCESS, error) {
|
||||
// EnumServiceStatusEx requires only SC_MANAGER_ENUM_SERVICE.
|
||||
h, err := windows.OpenSCManager(nil, nil, windows.SC_MANAGER_ENUMERATE_SERVICE)
|
||||
var apiStateValues = map[uint32]string{
|
||||
windows.SERVICE_CONTINUE_PENDING: "continue pending",
|
||||
windows.SERVICE_PAUSE_PENDING: "pause pending",
|
||||
windows.SERVICE_PAUSED: "paused",
|
||||
windows.SERVICE_RUNNING: "running",
|
||||
windows.SERVICE_START_PENDING: "start pending",
|
||||
windows.SERVICE_STOP_PENDING: "stop pending",
|
||||
windows.SERVICE_STOPPED: "stopped",
|
||||
}
|
||||
|
||||
var apiStartModeValues = map[uint32]string{
|
||||
windows.SERVICE_AUTO_START: "auto",
|
||||
windows.SERVICE_BOOT_START: "boot",
|
||||
windows.SERVICE_DEMAND_START: "manual",
|
||||
windows.SERVICE_DISABLED: "disabled",
|
||||
windows.SERVICE_SYSTEM_START: "system",
|
||||
}
|
||||
|
||||
func (c *Collector) collectService(ch chan<- prometheus.Metric, logger *slog.Logger, service windows.ENUM_SERVICE_STATUS_PROCESS) error {
|
||||
// Open connection for service handler.
|
||||
serviceHandle, err := windows.OpenService(c.serviceManagerHandle.Handle, service.ServiceName, windows.SERVICE_QUERY_CONFIG)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to open scm: %w", err)
|
||||
return fmt.Errorf("failed to open service: %w", err)
|
||||
}
|
||||
|
||||
m := &mgr.Mgr{Handle: h}
|
||||
defer func() {
|
||||
if err := m.Disconnect(); err != nil {
|
||||
_ = level.Warn(logger).Log("msg", "Failed to disconnect from scm", "err", err)
|
||||
}
|
||||
}()
|
||||
serviceNameString := windows.UTF16PtrToString(service.ServiceName)
|
||||
|
||||
var bytesNeeded, servicesReturned uint32
|
||||
var buf []byte
|
||||
for {
|
||||
var p *byte
|
||||
if len(buf) > 0 {
|
||||
p = &buf[0]
|
||||
// Create handle for each service.
|
||||
serviceManager := &mgr.Service{Name: serviceNameString, Handle: serviceHandle}
|
||||
defer func(serviceManager *mgr.Service) {
|
||||
if err := serviceManager.Close(); err != nil {
|
||||
logger.Warn("failed to close service handle",
|
||||
slog.Any("err", err),
|
||||
slog.String("service", serviceNameString),
|
||||
)
|
||||
}
|
||||
err = windows.EnumServicesStatusEx(m.Handle, windows.SC_ENUM_PROCESS_INFO,
|
||||
windows.SERVICE_WIN32, windows.SERVICE_STATE_ALL,
|
||||
p, uint32(len(buf)), &bytesNeeded, &servicesReturned, nil, nil)
|
||||
if err == nil {
|
||||
break
|
||||
}(serviceManager)
|
||||
|
||||
// Get Service Configuration.
|
||||
serviceConfig, err := serviceManager.Config()
|
||||
if err != nil {
|
||||
if !errors.Is(err, windows.ERROR_FILE_NOT_FOUND) && !errors.Is(err, windows.ERROR_MUI_FILE_NOT_FOUND) {
|
||||
return fmt.Errorf("failed to get service configuration: %w", err)
|
||||
}
|
||||
if !errors.Is(err, windows.ERROR_MORE_DATA) {
|
||||
return nil, fmt.Errorf("failed to enum services %w", err)
|
||||
|
||||
logger.Debug("failed collecting service",
|
||||
slog.Any("err", err),
|
||||
slog.String("service", serviceNameString),
|
||||
)
|
||||
}
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.info,
|
||||
prometheus.GaugeValue,
|
||||
1.0,
|
||||
serviceNameString,
|
||||
serviceConfig.DisplayName,
|
||||
serviceConfig.ServiceStartName,
|
||||
serviceConfig.BinaryPathName,
|
||||
)
|
||||
|
||||
var (
|
||||
isCurrentStartMode float64
|
||||
isCurrentState float64
|
||||
)
|
||||
|
||||
for _, startMode := range apiStartModeValues {
|
||||
isCurrentStartMode = 0.0
|
||||
if startMode == apiStartModeValues[serviceConfig.StartType] {
|
||||
isCurrentStartMode = 1.0
|
||||
}
|
||||
if bytesNeeded <= uint32(len(buf)) {
|
||||
return nil, err
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.startMode,
|
||||
prometheus.GaugeValue,
|
||||
isCurrentStartMode,
|
||||
serviceNameString,
|
||||
startMode,
|
||||
)
|
||||
}
|
||||
|
||||
for state, stateValue := range apiStateValues {
|
||||
isCurrentState = 0.0
|
||||
if state == service.ServiceStatusProcess.CurrentState {
|
||||
isCurrentState = 1.0
|
||||
}
|
||||
buf = make([]byte, bytesNeeded)
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.state,
|
||||
prometheus.GaugeValue,
|
||||
isCurrentState,
|
||||
serviceNameString,
|
||||
stateValue,
|
||||
)
|
||||
}
|
||||
|
||||
processID := strconv.FormatUint(uint64(service.ServiceStatusProcess.ProcessId), 10)
|
||||
|
||||
if processID != "0" { //nolint: nestif
|
||||
processStartTime, err := getProcessStartTime(logger, service.ServiceStatusProcess.ProcessId)
|
||||
if err != nil {
|
||||
if errors.Is(err, windows.ERROR_ACCESS_DENIED) {
|
||||
logger.Debug("failed to get process start time",
|
||||
slog.String("service", serviceNameString),
|
||||
slog.Any("err", err),
|
||||
)
|
||||
} else {
|
||||
logger.Warn("failed to get process start time",
|
||||
slog.String("service", serviceNameString),
|
||||
slog.Any("err", err),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.processID,
|
||||
prometheus.GaugeValue,
|
||||
float64(processStartTime/1_000_000_000),
|
||||
serviceNameString,
|
||||
processID,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// queryAllServices returns all service states of the current Windows system
|
||||
// This is realized by ask Service Manager directly.
|
||||
func (c *Collector) queryAllServices() ([]windows.ENUM_SERVICE_STATUS_PROCESS, error) {
|
||||
var (
|
||||
bytesNeeded uint32
|
||||
servicesReturned uint32
|
||||
resumeHandle uint32
|
||||
)
|
||||
|
||||
if err := windows.EnumServicesStatusEx(
|
||||
c.serviceManagerHandle.Handle,
|
||||
windows.SC_STATUS_PROCESS_INFO,
|
||||
windows.SERVICE_WIN32,
|
||||
windows.SERVICE_STATE_ALL,
|
||||
nil,
|
||||
0,
|
||||
&bytesNeeded,
|
||||
&servicesReturned,
|
||||
&resumeHandle,
|
||||
nil,
|
||||
); !errors.Is(err, windows.ERROR_MORE_DATA) {
|
||||
return nil, fmt.Errorf("could not fetch buffer size for EnumServicesStatusEx: %w", err)
|
||||
}
|
||||
|
||||
buf := make([]byte, bytesNeeded)
|
||||
if err := windows.EnumServicesStatusEx(
|
||||
c.serviceManagerHandle.Handle,
|
||||
windows.SC_STATUS_PROCESS_INFO,
|
||||
windows.SERVICE_WIN32,
|
||||
windows.SERVICE_STATE_ALL,
|
||||
&buf[0],
|
||||
bytesNeeded,
|
||||
&bytesNeeded,
|
||||
&servicesReturned,
|
||||
&resumeHandle,
|
||||
nil,
|
||||
); err != nil {
|
||||
return nil, fmt.Errorf("could not query windows service list: %w", err)
|
||||
}
|
||||
|
||||
if servicesReturned == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
services := unsafe.Slice((*windows.ENUM_SERVICE_STATUS_PROCESS)(unsafe.Pointer(&buf[0])), servicesReturned)
|
||||
services := unsafe.Slice((*windows.ENUM_SERVICE_STATUS_PROCESS)(unsafe.Pointer(&buf[0])), int(servicesReturned))
|
||||
|
||||
return services, nil
|
||||
}
|
||||
|
||||
func getProcessStartTime(logger *slog.Logger, pid uint32) (uint64, error) {
|
||||
handle, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, pid)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to open process %w", err)
|
||||
}
|
||||
|
||||
defer func(handle windows.Handle) {
|
||||
err := windows.CloseHandle(handle)
|
||||
if err != nil {
|
||||
logger.Warn("failed to close process handle",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
}
|
||||
}(handle)
|
||||
|
||||
var creation windows.Filetime
|
||||
|
||||
var exit windows.Filetime
|
||||
|
||||
var krn windows.Filetime
|
||||
|
||||
var user windows.Filetime
|
||||
|
||||
err = windows.GetProcessTimes(handle, &creation, &exit, &krn, &user)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("failed to get process times %w", err)
|
||||
}
|
||||
|
||||
return uint64(creation.Nanoseconds()), nil
|
||||
}
|
||||
|
||||
@@ -3,11 +3,10 @@
|
||||
package smb
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/perflib"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
@@ -47,17 +46,17 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{
|
||||
"SMB Server Shares",
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
|
||||
// desc creates a new prometheus description
|
||||
desc := func(metricName string, description string, labels ...string) *prometheus.Desc {
|
||||
return prometheus.NewDesc(
|
||||
@@ -75,10 +74,12 @@ func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
}
|
||||
|
||||
// Collect collects smb metrics and sends them to prometheus.
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
if err := c.collectServerShares(ctx, logger, ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed to collect server share metrics", "err", err)
|
||||
logger.Error("failed to collect server share metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
@@ -94,12 +95,15 @@ type perflibServerShares struct {
|
||||
TreeConnectCount float64 `perflib:"Tree Connect Count"`
|
||||
}
|
||||
|
||||
func (c *Collector) collectServerShares(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) collectServerShares(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
var data []perflibServerShares
|
||||
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects["SMB Server Shares"], &data, logger); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, instance := range data {
|
||||
labelName := c.toLabelName(instance.Name)
|
||||
if !strings.HasSuffix(labelName, "_total") {
|
||||
@@ -118,6 +122,7 @@ func (c *Collector) collectServerShares(ctx *types.ScrapeContext, logger log.Log
|
||||
instance.TreeConnectCount,
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -125,5 +130,6 @@ func (c *Collector) collectServerShares(ctx *types.ScrapeContext, logger log.Log
|
||||
func (c *Collector) toLabelName(name string) string {
|
||||
s := strings.ReplaceAll(strings.Join(strings.Fields(strings.ToLower(name)), "_"), ".", "_")
|
||||
s = strings.ReplaceAll(s, "__", "_")
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -3,11 +3,10 @@
|
||||
package smbclient
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/perflib"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
@@ -69,17 +68,17 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{
|
||||
"SMB Client Shares",
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
|
||||
// desc creates a new prometheus description
|
||||
desc := func(metricName string, description string, labels []string) *prometheus.Desc {
|
||||
return prometheus.NewDesc(
|
||||
@@ -179,10 +178,13 @@ func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
}
|
||||
|
||||
// Collect collects smb client metrics and sends them to prometheus.
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
if err := c.collectClientShares(ctx, logger, ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "Error in ClientShares", "err", err)
|
||||
logger.Error("Error in ClientShares",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -216,12 +218,15 @@ type perflibClientShares struct {
|
||||
WriteRequestsPerSec float64 `perflib:"Write Requests/sec"`
|
||||
}
|
||||
|
||||
func (c *Collector) collectClientShares(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) collectClientShares(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
var data []perflibClientShares
|
||||
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects["SMB Client Shares"], &data, logger); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, instance := range data {
|
||||
if instance.Name == "_Total" {
|
||||
continue
|
||||
@@ -380,5 +385,6 @@ func (c *Collector) collectClientShares(ctx *types.ScrapeContext, logger log.Log
|
||||
serverValue, shareValue,
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -4,11 +4,10 @@ package smtp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"regexp"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/perflib"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
@@ -134,18 +133,18 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{"SMTP Server"}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(logger log.Logger, _ *wmi.Client) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
_ = level.Info(logger).Log("msg", "smtp collector is in an experimental state! Metrics for this collector have not been tested.")
|
||||
logger.Info("smtp collector is in an experimental state! Metrics for this collector have not been tested.")
|
||||
|
||||
c.badMailedMessagesBadPickupFileTotal = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "badmailed_messages_bad_pickup_file_total"),
|
||||
@@ -405,12 +404,16 @@ func (c *Collector) Build(logger log.Logger, _ *wmi.Client) error {
|
||||
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
if err := c.collect(ctx, logger, ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting smtp metrics", "err", err)
|
||||
logger.Error("failed collecting smtp metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -462,9 +465,11 @@ type PerflibSMTPServer struct {
|
||||
RoutingTableLookupsTotal float64 `perflib:"Routing Table Lookups Total"`
|
||||
}
|
||||
|
||||
func (c *Collector) collect(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
var dst []PerflibSMTPServer
|
||||
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects["SMTP Server"], &dst, logger); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -763,5 +768,6 @@ func (c *Collector) collect(ctx *types.ScrapeContext, logger log.Logger, ch chan
|
||||
server.Name,
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -3,9 +3,10 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log/slog"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/perflib"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
@@ -25,6 +26,8 @@ type Collector struct {
|
||||
contextSwitchesTotal *prometheus.Desc
|
||||
exceptionDispatchesTotal *prometheus.Desc
|
||||
processorQueueLength *prometheus.Desc
|
||||
processes *prometheus.Desc
|
||||
processesLimit *prometheus.Desc
|
||||
systemCallsTotal *prometheus.Desc
|
||||
systemUpTime *prometheus.Desc
|
||||
threads *prometheus.Desc
|
||||
@@ -50,15 +53,15 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{"System"}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
|
||||
c.contextSwitchesTotal = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "context_switches_total"),
|
||||
"Total number of context switches (WMI source: PerfOS_System.ContextSwitchesPersec)",
|
||||
@@ -71,6 +74,19 @@ func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
c.processes = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "processes"),
|
||||
"Current number of processes (WMI source: PerfOS_System.Processes)",
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
c.processesLimit = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "processes_limit"),
|
||||
"Maximum number of processes.",
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
|
||||
c.processorQueueLength = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "processor_queue_length"),
|
||||
"Length of processor queue (WMI source: PerfOS_System.ProcessorQueueLength)",
|
||||
@@ -95,17 +111,22 @@ func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
if err := c.collect(ctx, logger, ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting system metrics", "err", err)
|
||||
logger.Error("failed collecting system metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -117,16 +138,23 @@ type system struct {
|
||||
ProcessorQueueLength float64 `perflib:"Processor Queue Length"`
|
||||
SystemCallsPersec float64 `perflib:"System Calls/sec"`
|
||||
SystemUpTime float64 `perflib:"System Up Time"`
|
||||
Processes float64 `perflib:"Processes"`
|
||||
Threads float64 `perflib:"Threads"`
|
||||
}
|
||||
|
||||
func (c *Collector) collect(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
var dst []system
|
||||
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects["System"], &dst, logger); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(dst) == 0 {
|
||||
return errors.New("no data returned from Performance Counter")
|
||||
}
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.contextSwitchesTotal,
|
||||
prometheus.CounterValue,
|
||||
@@ -142,6 +170,11 @@ func (c *Collector) collect(ctx *types.ScrapeContext, logger log.Logger, ch chan
|
||||
prometheus.GaugeValue,
|
||||
dst[0].ProcessorQueueLength,
|
||||
)
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.processes,
|
||||
prometheus.GaugeValue,
|
||||
dst[0].Processes,
|
||||
)
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.systemCallsTotal,
|
||||
prometheus.CounterValue,
|
||||
@@ -157,5 +190,15 @@ func (c *Collector) collect(ctx *types.ScrapeContext, logger log.Logger, ch chan
|
||||
prometheus.GaugeValue,
|
||||
dst[0].Threads,
|
||||
)
|
||||
|
||||
// Windows has no defined limit, and is based off available resources. This currently isn't calculated by WMI and is set to default value.
|
||||
// https://techcommunity.microsoft.com/t5/windows-blog-archive/pushing-the-limits-of-windows-processes-and-threads/ba-p/723824
|
||||
// https://docs.microsoft.com/en-us/windows/win32/cimwin32prov/win32-operatingsystem
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.processesLimit,
|
||||
prometheus.GaugeValue,
|
||||
float64(4294967295),
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
16
pkg/collector/tcp/const.go
Normal file
16
pkg/collector/tcp/const.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package tcp
|
||||
|
||||
// Win32_PerfRawData_Tcpip_TCPv4 docs
|
||||
// - https://msdn.microsoft.com/en-us/library/aa394341(v=vs.85).aspx
|
||||
// The TCPv6 performance object uses the same fields.
|
||||
const (
|
||||
ConnectionFailures = "Connection Failures"
|
||||
ConnectionsActive = "Connections Active"
|
||||
ConnectionsEstablished = "Connections Established"
|
||||
ConnectionsPassive = "Connections Passive"
|
||||
ConnectionsReset = "Connections Reset"
|
||||
SegmentsPersec = "Segments/sec"
|
||||
SegmentsReceivedPersec = "Segments Received/sec"
|
||||
SegmentsRetransmittedPersec = "Segments Retransmitted/sec"
|
||||
SegmentsSentPersec = "Segments Sent/sec"
|
||||
)
|
||||
@@ -3,10 +3,11 @@
|
||||
package tcp
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/perflib"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/perfdata"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/yusufpapurcu/wmi"
|
||||
@@ -22,6 +23,9 @@ var ConfigDefaults = Config{}
|
||||
type Collector struct {
|
||||
config Config
|
||||
|
||||
perfDataCollector4 *perfdata.Collector
|
||||
perfDataCollector6 *perfdata.Collector
|
||||
|
||||
connectionFailures *prometheus.Desc
|
||||
connectionsActive *prometheus.Desc
|
||||
connectionsEstablished *prometheus.Desc
|
||||
@@ -53,15 +57,39 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
return []string{"TCPv4"}, nil
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
|
||||
counters := []string{
|
||||
ConnectionFailures,
|
||||
ConnectionsActive,
|
||||
ConnectionsEstablished,
|
||||
ConnectionsPassive,
|
||||
ConnectionsReset,
|
||||
SegmentsPersec,
|
||||
SegmentsReceivedPersec,
|
||||
SegmentsRetransmittedPersec,
|
||||
SegmentsSentPersec,
|
||||
}
|
||||
|
||||
var err error
|
||||
|
||||
c.perfDataCollector4, err = perfdata.NewCollector("TCPv4", nil, counters)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create TCPv4 collector: %w", err)
|
||||
}
|
||||
|
||||
c.perfDataCollector6, err = perfdata.NewCollector("TCPv6", nil, counters)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create TCPv6 collector: %w", err)
|
||||
}
|
||||
|
||||
c.connectionFailures = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "connection_failures_total"),
|
||||
"(TCP.ConnectionFailures)",
|
||||
@@ -116,111 +144,96 @@ func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
[]string{"af"},
|
||||
nil,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
if err := c.collect(ctx, logger, ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting tcp metrics", "err", err)
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
if err := c.collect(ch); err != nil {
|
||||
logger.Error("failed collecting tcp metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Win32_PerfRawData_Tcpip_TCPv4 docs
|
||||
// - https://msdn.microsoft.com/en-us/library/aa394341(v=vs.85).aspx
|
||||
// The TCPv6 performance object uses the same fields.
|
||||
type tcp struct {
|
||||
ConnectionFailures float64 `perflib:"Connection Failures"`
|
||||
ConnectionsActive float64 `perflib:"Connections Active"`
|
||||
ConnectionsEstablished float64 `perflib:"Connections Established"`
|
||||
ConnectionsPassive float64 `perflib:"Connections Passive"`
|
||||
ConnectionsReset float64 `perflib:"Connections Reset"`
|
||||
SegmentsPersec float64 `perflib:"Segments/sec"`
|
||||
SegmentsReceivedPersec float64 `perflib:"Segments Received/sec"`
|
||||
SegmentsRetransmittedPersec float64 `perflib:"Segments Retransmitted/sec"`
|
||||
SegmentsSentPersec float64 `perflib:"Segments Sent/sec"`
|
||||
}
|
||||
|
||||
func writeTCPCounters(metrics tcp, labels []string, c *Collector, ch chan<- prometheus.Metric) {
|
||||
func writeTCPCounters(metrics map[string]perfdata.CounterValues, labels []string, c *Collector, ch chan<- prometheus.Metric) {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.connectionFailures,
|
||||
prometheus.CounterValue,
|
||||
metrics.ConnectionFailures,
|
||||
metrics[ConnectionFailures].FirstValue,
|
||||
labels...,
|
||||
)
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.connectionsActive,
|
||||
prometheus.CounterValue,
|
||||
metrics.ConnectionsActive,
|
||||
metrics[ConnectionsActive].FirstValue,
|
||||
labels...,
|
||||
)
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.connectionsEstablished,
|
||||
prometheus.GaugeValue,
|
||||
metrics.ConnectionsEstablished,
|
||||
metrics[ConnectionsEstablished].FirstValue,
|
||||
labels...,
|
||||
)
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.connectionsPassive,
|
||||
prometheus.CounterValue,
|
||||
metrics.ConnectionsPassive,
|
||||
metrics[ConnectionsPassive].FirstValue,
|
||||
labels...,
|
||||
)
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.connectionsReset,
|
||||
prometheus.CounterValue,
|
||||
metrics.ConnectionsReset,
|
||||
metrics[ConnectionsReset].FirstValue,
|
||||
labels...,
|
||||
)
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.segmentsTotal,
|
||||
prometheus.CounterValue,
|
||||
metrics.SegmentsPersec,
|
||||
metrics[SegmentsPersec].FirstValue,
|
||||
labels...,
|
||||
)
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.segmentsReceivedTotal,
|
||||
prometheus.CounterValue,
|
||||
metrics.SegmentsReceivedPersec,
|
||||
metrics[SegmentsReceivedPersec].FirstValue,
|
||||
labels...,
|
||||
)
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.segmentsRetransmittedTotal,
|
||||
prometheus.CounterValue,
|
||||
metrics.SegmentsRetransmittedPersec,
|
||||
metrics[SegmentsRetransmittedPersec].FirstValue,
|
||||
labels...,
|
||||
)
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.segmentsSentTotal,
|
||||
prometheus.CounterValue,
|
||||
metrics.SegmentsSentPersec,
|
||||
metrics[SegmentsSentPersec].FirstValue,
|
||||
labels...,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *Collector) collect(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
var dst []tcp
|
||||
|
||||
// TCPv4 counters
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects["TCPv4"], &dst, logger); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(dst) != 0 {
|
||||
writeTCPCounters(dst[0], []string{"ipv4"}, c, ch)
|
||||
func (c *Collector) collect(ch chan<- prometheus.Metric) error {
|
||||
data, err := c.perfDataCollector4.Collect()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to collect TCPv4 metrics: %w", err)
|
||||
}
|
||||
|
||||
// TCPv6 counters
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects["TCPv6"], &dst, logger); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(dst) != 0 {
|
||||
writeTCPCounters(dst[0], []string{"ipv6"}, c, ch)
|
||||
writeTCPCounters(data[perfdata.EmptyInstance], []string{"ipv4"}, c, ch)
|
||||
|
||||
data, err = c.perfDataCollector6.Collect()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to collect TCPv6 metrics: %w", err)
|
||||
}
|
||||
|
||||
writeTCPCounters(data[perfdata.EmptyInstance], []string{"ipv6"}, c, ch)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -4,10 +4,9 @@ package teradici_pcoip
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log/slog"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/yusufpapurcu/wmi"
|
||||
@@ -92,17 +91,18 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(logger log.Logger, wmiClient *wmi.Client) error {
|
||||
_ = level.Warn(logger).
|
||||
Log("msg", "teradici_pcoip collector is deprecated and will be removed in the future.", "collector", Name)
|
||||
func (c *Collector) Build(logger *slog.Logger, wmiClient *wmi.Client) error {
|
||||
logger.Warn("teradici_pcoip collector is deprecated and will be removed in the future.",
|
||||
slog.String("collector", Name),
|
||||
)
|
||||
|
||||
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
|
||||
return errors.New("wmiClient or SWbemServicesClient is nil")
|
||||
@@ -336,33 +336,54 @@ func (c *Collector) Build(logger log.Logger, wmiClient *wmi.Client) error {
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
if err := c.collectAudio(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting teradici session audio metrics", "err", err)
|
||||
logger.Error("failed collecting teradici session audio metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.collectGeneral(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting teradici session general metrics", "err", err)
|
||||
logger.Error("failed collecting teradici session general metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.collectImaging(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting teradici session imaging metrics", "err", err)
|
||||
logger.Error("failed collecting teradici session imaging metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.collectNetwork(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting teradici session network metrics", "err", err)
|
||||
logger.Error("failed collecting teradici session network metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.collectUsb(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting teradici session USB metrics", "err", err)
|
||||
logger.Error("failed collecting teradici session USB metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -423,6 +444,7 @@ func (c *Collector) collectAudio(ch chan<- prometheus.Metric) error {
|
||||
if err := c.wmiClient.Query("SELECT * FROM win32_PerfRawData_TeradiciPerf_PCoIPSessionAudioStatistics", &dst); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(dst) == 0 {
|
||||
return errors.New("WMI query returned empty result set")
|
||||
}
|
||||
@@ -465,6 +487,7 @@ func (c *Collector) collectGeneral(ch chan<- prometheus.Metric) error {
|
||||
if err := c.wmiClient.Query("SELECT * FROM win32_PerfRawData_TeradiciPerf_PCoIPSessionGeneralStatistics", &dst); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(dst) == 0 {
|
||||
return errors.New("WMI query returned empty result set")
|
||||
}
|
||||
@@ -519,6 +542,7 @@ func (c *Collector) collectImaging(ch chan<- prometheus.Metric) error {
|
||||
if err := c.wmiClient.Query("SELECT * FROM win32_PerfRawData_TeradiciPerf_PCoIPSessionImagingStatistics", &dst); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(dst) == 0 {
|
||||
return errors.New("WMI query returned empty result set")
|
||||
}
|
||||
@@ -597,6 +621,7 @@ func (c *Collector) collectNetwork(ch chan<- prometheus.Metric) error {
|
||||
if err := c.wmiClient.Query("SELECT * FROM win32_PerfRawData_TeradiciPerf_PCoIPSessionNetworkStatistics", &dst); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(dst) == 0 {
|
||||
return errors.New("WMI query returned empty result set")
|
||||
}
|
||||
@@ -669,6 +694,7 @@ func (c *Collector) collectUsb(ch chan<- prometheus.Metric) error {
|
||||
if err := c.wmiClient.Query("SELECT * FROM win32_PerfRawData_TeradiciPerf_PCoIPSessionUsbStatistics", &dst); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(dst) == 0 {
|
||||
return errors.New("WMI query returned empty result set")
|
||||
}
|
||||
|
||||
@@ -5,18 +5,17 @@ package terminal_services
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/headers/wtsapi32"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/perflib"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/yusufpapurcu/wmi"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -32,17 +31,20 @@ type Win32_ServerFeature struct {
|
||||
ID uint32
|
||||
}
|
||||
|
||||
func isConnectionBrokerServer(logger log.Logger, wmiClient *wmi.Client) bool {
|
||||
func isConnectionBrokerServer(logger *slog.Logger, wmiClient *wmi.Client) bool {
|
||||
var dst []Win32_ServerFeature
|
||||
if err := wmiClient.Query("SELECT * FROM Win32_ServerFeature", &dst); err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, d := range dst {
|
||||
if d.ID == ConnectionBrokerFeatureID {
|
||||
return true
|
||||
}
|
||||
}
|
||||
_ = level.Debug(logger).Log("msg", "host is not a connection broker skipping Connection Broker performance metrics.")
|
||||
|
||||
logger.Debug("host is not a connection broker skipping Connection Broker performance metrics.")
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -55,7 +57,7 @@ type Collector struct {
|
||||
|
||||
connectionBrokerEnabled bool
|
||||
|
||||
hServer syscall.Handle
|
||||
hServer windows.Handle
|
||||
|
||||
sessionInfo *prometheus.Desc
|
||||
connectionBrokerPerformance *prometheus.Desc
|
||||
@@ -94,14 +96,14 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{
|
||||
"Terminal Services Session",
|
||||
"Remote Desktop Connection Broker Counterset",
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
err := wtsapi32.WTSCloseServer(c.hServer)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to close WTS server: %w", err)
|
||||
@@ -110,8 +112,8 @@ func (c *Collector) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(logger log.Logger, wmiClient *wmi.Client) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Build(logger *slog.Logger, wmiClient *wmi.Client) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
c.connectionBrokerEnabled = isConnectionBrokerServer(logger, wmiClient)
|
||||
|
||||
@@ -218,24 +220,35 @@ func (c *Collector) Build(logger log.Logger, wmiClient *wmi.Client) error {
|
||||
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
if err := c.collectWTSSessions(logger, ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting terminal services session infos", "err", err)
|
||||
logger.Error("failed collecting terminal services session infos",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.collectTSSessionCounters(ctx, logger, ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting terminal services session count metrics", "err", err)
|
||||
logger.Error("failed collecting terminal services session count metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// only collect CollectionBrokerPerformance if host is a Connection Broker
|
||||
if c.connectionBrokerEnabled {
|
||||
if err := c.collectCollectionBrokerPerformanceCounter(ctx, logger, ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting Connection Broker performance metrics", "err", err)
|
||||
logger.Error("failed collecting Connection Broker performance metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -258,13 +271,15 @@ type perflibTerminalServicesSession struct {
|
||||
WorkingSetPeak float64 `perflib:"Working Set Peak"`
|
||||
}
|
||||
|
||||
func (c *Collector) collectTSSessionCounters(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) collectTSSessionCounters(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
dst := make([]perflibTerminalServicesSession, 0)
|
||||
|
||||
err := perflib.UnmarshalObject(ctx.PerfObjects["Terminal Services Session"], &dst, logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
names := make(map[string]bool)
|
||||
|
||||
for _, d := range dst {
|
||||
@@ -277,6 +292,7 @@ func (c *Collector) collectTSSessionCounters(ctx *types.ScrapeContext, logger lo
|
||||
if _, ok := names[n]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
names[n] = true
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
@@ -373,6 +389,7 @@ func (c *Collector) collectTSSessionCounters(ctx *types.ScrapeContext, logger lo
|
||||
d.Name,
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -382,13 +399,15 @@ type perflibRemoteDesktopConnectionBrokerCounterset struct {
|
||||
FailedConnections float64 `perflib:"Failed Connections"`
|
||||
}
|
||||
|
||||
func (c *Collector) collectCollectionBrokerPerformanceCounter(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) collectCollectionBrokerPerformanceCounter(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
dst := make([]perflibRemoteDesktopConnectionBrokerCounterset, 0)
|
||||
|
||||
err := perflib.UnmarshalObject(ctx.PerfObjects["Remote Desktop Connection Broker Counterset"], &dst, logger)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(dst) == 0 {
|
||||
return errors.New("WMI query returned empty result set")
|
||||
}
|
||||
@@ -417,7 +436,7 @@ func (c *Collector) collectCollectionBrokerPerformanceCounter(ctx *types.ScrapeC
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) collectWTSSessions(logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
func (c *Collector) collectWTSSessions(logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
sessions, err := wtsapi32.WTSEnumerateSessionsEx(c.hServer, logger)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to enumerate WTS sessions: %w", err)
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
@@ -28,8 +29,6 @@ import (
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/dimchansky/utfbom"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
dto "github.com/prometheus/client_model/go"
|
||||
@@ -97,17 +96,18 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(logger log.Logger, _ *wmi.Client) error {
|
||||
_ = level.Info(logger).
|
||||
Log("msg", "textfile Collector directories: "+strings.Join(c.config.TextFileDirectories, ","), "collector", Name)
|
||||
func (c *Collector) Build(logger *slog.Logger, _ *wmi.Client) error {
|
||||
logger.Info("textfile Collector directories: "+strings.Join(c.config.TextFileDirectories, ","),
|
||||
slog.String("collector", Name),
|
||||
)
|
||||
|
||||
c.mTimeDesc = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, "textfile", "mtime_seconds"),
|
||||
@@ -123,11 +123,14 @@ func (c *Collector) Build(logger log.Logger, _ *wmi.Client) error {
|
||||
// Duplicates will be detected where the metric name, labels and label values are identical.
|
||||
func duplicateMetricEntry(metricFamilies []*dto.MetricFamily) bool {
|
||||
uniqueMetrics := make(map[string]map[string]string)
|
||||
|
||||
for _, metricFamily := range metricFamilies {
|
||||
metricName := metricFamily.GetName()
|
||||
|
||||
for _, metric := range metricFamily.GetMetric() {
|
||||
metricLabels := metric.GetLabel()
|
||||
labels := make(map[string]string)
|
||||
|
||||
for _, label := range metricLabels {
|
||||
labels[label.GetName()] = label.GetValue()
|
||||
}
|
||||
@@ -138,17 +141,21 @@ func duplicateMetricEntry(metricFamilies []*dto.MetricFamily) bool {
|
||||
if mapContainsKey && reflect.DeepEqual(uniqueMetrics[metricName], labels) {
|
||||
return true
|
||||
}
|
||||
|
||||
uniqueMetrics[metricName] = labels
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (c *Collector) convertMetricFamily(logger log.Logger, metricFamily *dto.MetricFamily, ch chan<- prometheus.Metric) {
|
||||
func (c *Collector) convertMetricFamily(logger *slog.Logger, metricFamily *dto.MetricFamily, ch chan<- prometheus.Metric) {
|
||||
var valType prometheus.ValueType
|
||||
|
||||
var val float64
|
||||
|
||||
allLabelNames := map[string]struct{}{}
|
||||
|
||||
for _, metric := range metricFamily.GetMetric() {
|
||||
labels := metric.GetLabel()
|
||||
for _, label := range labels {
|
||||
@@ -160,12 +167,15 @@ func (c *Collector) convertMetricFamily(logger log.Logger, metricFamily *dto.Met
|
||||
|
||||
for _, metric := range metricFamily.GetMetric() {
|
||||
if metric.TimestampMs != nil {
|
||||
_ = level.Warn(logger).Log("msg", fmt.Sprintf("Ignoring unsupported custom timestamp on textfile Collector metric %v", metric))
|
||||
logger.Warn(fmt.Sprintf("Ignoring unsupported custom timestamp on textfile Collector metric %v", metric))
|
||||
}
|
||||
|
||||
labels := metric.GetLabel()
|
||||
|
||||
var names []string
|
||||
|
||||
var values []string
|
||||
|
||||
for _, label := range labels {
|
||||
names = append(names, label.GetName())
|
||||
values = append(values, label.GetValue())
|
||||
@@ -173,12 +183,15 @@ func (c *Collector) convertMetricFamily(logger log.Logger, metricFamily *dto.Met
|
||||
|
||||
for k := range allLabelNames {
|
||||
present := false
|
||||
|
||||
for _, name := range names {
|
||||
if k == name {
|
||||
present = true
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !present {
|
||||
names = append(names, k)
|
||||
values = append(values, "")
|
||||
@@ -230,9 +243,11 @@ func (c *Collector) convertMetricFamily(logger log.Logger, metricFamily *dto.Met
|
||||
buckets, values...,
|
||||
)
|
||||
default:
|
||||
_ = level.Error(logger).Log("msg", "unknown metric type for file")
|
||||
logger.Error("unknown metric type for file")
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if metricType == dto.MetricType_GAUGE || metricType == dto.MetricType_COUNTER || metricType == dto.MetricType_UNTYPED {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
prometheus.NewDesc(
|
||||
@@ -254,6 +269,7 @@ func (c *Collector) exportMTimes(mTimes map[string]time.Time, ch chan<- promethe
|
||||
for filename := range mTimes {
|
||||
filenames = append(filenames, filename)
|
||||
}
|
||||
|
||||
sort.Strings(filenames)
|
||||
|
||||
for _, filename := range filenames {
|
||||
@@ -280,6 +296,7 @@ func (cr carriageReturnFilteringReader) Read(p []byte) (int, error) {
|
||||
}
|
||||
|
||||
pi := 0
|
||||
|
||||
for i := range n {
|
||||
if buf[i] != '\r' {
|
||||
p[pi] = buf[i]
|
||||
@@ -291,8 +308,8 @@ func (cr carriageReturnFilteringReader) Read(p []byte) (int, error) {
|
||||
}
|
||||
|
||||
// Collect implements the Collector interface.
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
errorMetric := 0.0
|
||||
mTimes := map[string]time.Time{}
|
||||
|
||||
@@ -305,43 +322,68 @@ func (c *Collector) Collect(_ *types.ScrapeContext, logger log.Logger, ch chan<-
|
||||
for _, directory := range c.config.TextFileDirectories {
|
||||
err := filepath.WalkDir(directory, func(path string, dirEntry os.DirEntry, err error) error {
|
||||
if err != nil {
|
||||
_ = level.Error(logger).Log("msg", "Error reading directory: "+path, "err", err)
|
||||
logger.Error("Error reading directory: "+path,
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
errorMetric = 1.0
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if !dirEntry.IsDir() && strings.HasSuffix(dirEntry.Name(), ".prom") {
|
||||
_ = level.Debug(logger).Log("msg", "Processing file: "+path)
|
||||
logger.Debug("Processing file: " + path)
|
||||
|
||||
families_array, err := scrapeFile(path, logger)
|
||||
if err != nil {
|
||||
_ = level.Error(logger).Log("msg", fmt.Sprintf("Error scraping file: %q. Skip File.", path), "err", err)
|
||||
logger.Error(fmt.Sprintf("Error scraping file: %q. Skip File.", path),
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
errorMetric = 1.0
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
fileInfo, err := os.Stat(path)
|
||||
if err != nil {
|
||||
_ = level.Error(logger).Log("msg", fmt.Sprintf("Error reading file info: %q. Skip File.", path), "err", err)
|
||||
logger.Error(fmt.Sprintf("Error reading file info: %q. Skip File.", path),
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
errorMetric = 1.0
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
if _, hasName := mTimes[fileInfo.Name()]; hasName {
|
||||
_ = level.Error(logger).Log("msg", fmt.Sprintf("Duplicate filename detected: %q. Skip File.", path))
|
||||
logger.Error(fmt.Sprintf("Duplicate filename detected: %q. Skip File.", path))
|
||||
|
||||
errorMetric = 1.0
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
mTimes[fileInfo.Name()] = fileInfo.ModTime()
|
||||
|
||||
metricFamilies = append(metricFamilies, families_array...)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil && directory != "" {
|
||||
_ = level.Error(logger).Log("msg", "Error reading textfile Collector directory: "+directory, "err", err)
|
||||
logger.Error("Error reading textfile Collector directory: "+directory,
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
errorMetric = 1.0
|
||||
}
|
||||
}
|
||||
|
||||
// If duplicates are detected across *multiple* files, return error.
|
||||
if duplicateMetricEntry(metricFamilies) {
|
||||
_ = level.Error(logger).Log("msg", "Duplicate metrics detected across multiple files")
|
||||
logger.Error("Duplicate metrics detected across multiple files")
|
||||
|
||||
errorMetric = 1.0
|
||||
} else {
|
||||
for _, mf := range metricFamilies {
|
||||
@@ -359,24 +401,32 @@ func (c *Collector) Collect(_ *types.ScrapeContext, logger log.Logger, ch chan<-
|
||||
),
|
||||
prometheus.GaugeValue, errorMetric,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func scrapeFile(path string, log log.Logger) ([]*dto.MetricFamily, error) {
|
||||
func scrapeFile(path string, logger *slog.Logger) ([]*dto.MetricFamily, error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var parser expfmt.TextParser
|
||||
|
||||
r, encoding := utfbom.Skip(carriageReturnFilteringReader{r: file})
|
||||
if err = checkBOM(encoding); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
parsedFamilies, err := parser.TextToMetricFamilies(r)
|
||||
|
||||
closeErr := file.Close()
|
||||
if closeErr != nil {
|
||||
_ = level.Warn(log).Log("msg", fmt.Sprintf("Error closing file %q", path), "err", closeErr)
|
||||
logger.Warn("error closing file "+path,
|
||||
slog.Any("err", closeErr),
|
||||
)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -386,11 +436,13 @@ func scrapeFile(path string, log log.Logger) ([]*dto.MetricFamily, error) {
|
||||
|
||||
for _, mf := range parsedFamilies {
|
||||
families_array = append(families_array, mf)
|
||||
|
||||
for _, m := range mf.GetMetric() {
|
||||
if m.TimestampMs != nil {
|
||||
return nil, errors.New("textfile contains unsupported client-side timestamps")
|
||||
}
|
||||
}
|
||||
|
||||
if mf.Help == nil {
|
||||
help := "Metric read from " + path
|
||||
mf.Help = &help
|
||||
@@ -401,6 +453,7 @@ func scrapeFile(path string, log log.Logger) ([]*dto.MetricFamily, error) {
|
||||
if duplicateMetricEntry(families_array) {
|
||||
return nil, errors.New("duplicate metrics detected")
|
||||
}
|
||||
|
||||
return families_array, nil
|
||||
}
|
||||
|
||||
@@ -414,5 +467,6 @@ func checkBOM(encoding utfbom.Encoding) error {
|
||||
|
||||
func getDefaultPath() string {
|
||||
execPath, _ := os.Executable()
|
||||
|
||||
return filepath.Join(filepath.Dir(execPath), "textfile_inputs")
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ func TestCRFilter(t *testing.T) {
|
||||
|
||||
sr := strings.NewReader("line 1\r\nline 2")
|
||||
cr := carriageReturnFilteringReader{r: sr}
|
||||
|
||||
b, err := io.ReadAll(cr)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
@@ -43,9 +44,11 @@ func TestCheckBOM(t *testing.T) {
|
||||
if d.err == "" && err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if d.err != "" && err == nil {
|
||||
t.Errorf("Missing expected error %s", d.err)
|
||||
}
|
||||
|
||||
if err != nil && !strings.Contains(err.Error(), d.err) {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
@@ -2,11 +2,11 @@ package textfile_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"io"
|
||||
"log/slog"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/collector"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/collector/textfile"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
@@ -18,7 +18,7 @@ var baseDir = "../../../tools/textfile-test"
|
||||
|
||||
//nolint:paralleltest
|
||||
func TestMultipleDirectories(t *testing.T) {
|
||||
logger := log.NewLogfmtLogger(os.Stdout)
|
||||
logger := slog.New(slog.NewTextHandler(io.Discard, nil))
|
||||
testDir := baseDir + "/multiple-dirs"
|
||||
testDirs := fmt.Sprintf("%[1]s/dir1,%[1]s/dir2,%[1]s/dir3", testDir)
|
||||
|
||||
@@ -33,16 +33,21 @@ func TestMultipleDirectories(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error %s", err)
|
||||
}
|
||||
|
||||
metrics := make(chan prometheus.Metric)
|
||||
got := ""
|
||||
|
||||
go func() {
|
||||
for {
|
||||
var metric dto.Metric
|
||||
|
||||
val := <-metrics
|
||||
|
||||
err := val.Write(&metric)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error %s", err)
|
||||
}
|
||||
|
||||
got += metric.String()
|
||||
}
|
||||
}()
|
||||
@@ -61,7 +66,7 @@ func TestMultipleDirectories(t *testing.T) {
|
||||
|
||||
//nolint:paralleltest
|
||||
func TestDuplicateFileName(t *testing.T) {
|
||||
logger := log.NewLogfmtLogger(os.Stdout)
|
||||
logger := slog.New(slog.NewTextHandler(io.Discard, nil))
|
||||
testDir := baseDir + "/duplicate-filename"
|
||||
textFileCollector := textfile.New(&textfile.Config{
|
||||
TextFileDirectories: []string{testDir},
|
||||
@@ -74,19 +79,25 @@ func TestDuplicateFileName(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error %s", err)
|
||||
}
|
||||
|
||||
metrics := make(chan prometheus.Metric)
|
||||
got := ""
|
||||
|
||||
go func() {
|
||||
for {
|
||||
var metric dto.Metric
|
||||
|
||||
val := <-metrics
|
||||
|
||||
err := val.Write(&metric)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error %s", err)
|
||||
}
|
||||
|
||||
got += metric.String()
|
||||
}
|
||||
}()
|
||||
|
||||
err = textFileCollector.Collect(scrapeContext, logger, metrics)
|
||||
if err != nil {
|
||||
t.Errorf("Unexpected error %s", err)
|
||||
|
||||
@@ -4,10 +4,9 @@ package thermalzone
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log/slog"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/yusufpapurcu/wmi"
|
||||
@@ -49,15 +48,15 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, wmiClient *wmi.Client) error {
|
||||
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
|
||||
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
|
||||
return errors.New("wmiClient or SWbemServicesClient is nil")
|
||||
}
|
||||
@@ -87,17 +86,22 @@ func (c *Collector) Build(_ log.Logger, wmiClient *wmi.Client) error {
|
||||
},
|
||||
nil,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
if err := c.collect(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting thermalzone metrics", "err", err)
|
||||
logger.Error("failed collecting thermalzone metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -4,15 +4,17 @@ package time
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/headers/kernel32"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/perflib"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/winversion"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/yusufpapurcu/wmi"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
const Name = "time"
|
||||
@@ -25,6 +27,8 @@ var ConfigDefaults = Config{}
|
||||
type Collector struct {
|
||||
config Config
|
||||
|
||||
currentTime *prometheus.Desc
|
||||
timezone *prometheus.Desc
|
||||
clockFrequencyAdjustmentPPBTotal *prometheus.Desc
|
||||
computedTimeOffset *prometheus.Desc
|
||||
ntpClientTimeSourceCount *prometheus.Desc
|
||||
@@ -53,19 +57,31 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{"Windows Time Service"}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
|
||||
if winversion.WindowsVersionFloat() <= 6.1 {
|
||||
return errors.New("windows version older than Server 2016 detected. The time collector will not run and should be disabled via CLI flags or configuration file")
|
||||
}
|
||||
|
||||
c.currentTime = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "current_timestamp_seconds"),
|
||||
"OperatingSystem.LocalDateTime",
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
c.timezone = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "timezone"),
|
||||
"OperatingSystem.LocalDateTime",
|
||||
[]string{"timezone"},
|
||||
nil,
|
||||
)
|
||||
c.clockFrequencyAdjustmentPPBTotal = prometheus.NewDesc(
|
||||
prometheus.BuildFQName(types.Namespace, Name, "clock_frequency_adjustment_ppb_total"),
|
||||
"Total adjustment made to the local system clock frequency by W32Time in Parts Per Billion (PPB) units.",
|
||||
@@ -102,18 +118,34 @@ func (c *Collector) Build(_ log.Logger, _ *wmi.Client) error {
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
if err := c.collect(ctx, logger, ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting time metrics", "err", err)
|
||||
return err
|
||||
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
errs := make([]error, 0, 2)
|
||||
|
||||
if err := c.collectTime(ch); err != nil {
|
||||
logger.Error("failed collecting time metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
errs = append(errs, err)
|
||||
}
|
||||
return nil
|
||||
|
||||
if err := c.collectNTP(ctx, logger, ch); err != nil {
|
||||
logger.Error("failed collecting time ntp metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
||||
return errors.Join(errs...)
|
||||
}
|
||||
|
||||
// Perflib "Windows Time Service".
|
||||
@@ -121,18 +153,49 @@ type windowsTime struct {
|
||||
ClockFrequencyAdjustmentPPBTotal float64 `perflib:"Clock Frequency Adjustment (ppb)"`
|
||||
ComputedTimeOffset float64 `perflib:"Computed Time Offset"`
|
||||
NTPClientTimeSourceCount float64 `perflib:"NTP Client Time Source Count"`
|
||||
NTPRoundtripDelay float64 `perflib:"NTP Roundtrip Delay"`
|
||||
NTPRoundTripDelay float64 `perflib:"NTP Roundtrip Delay"`
|
||||
NTPServerIncomingRequestsTotal float64 `perflib:"NTP Server Incoming Requests"`
|
||||
NTPServerOutgoingResponsesTotal float64 `perflib:"NTP Server Outgoing Responses"`
|
||||
}
|
||||
|
||||
func (c *Collector) collect(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) collectTime(ch chan<- prometheus.Metric) error {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.currentTime,
|
||||
prometheus.GaugeValue,
|
||||
float64(time.Now().Unix()),
|
||||
)
|
||||
|
||||
timeZoneInfo, err := kernel32.GetDynamicTimeZoneInformation()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// timeZoneKeyName contains the english name of the timezone.
|
||||
timezoneName := windows.UTF16ToString(timeZoneInfo.TimeZoneKeyName[:])
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.timezone,
|
||||
prometheus.GaugeValue,
|
||||
1.0,
|
||||
timezoneName,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) collectNTP(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
|
||||
var dst []windowsTime // Single-instance class, array is required but will have single entry.
|
||||
|
||||
if err := perflib.UnmarshalObject(ctx.PerfObjects["Windows Time Service"], &dst, logger); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(dst) == 0 {
|
||||
return errors.New("no data returned for Windows Time Service")
|
||||
}
|
||||
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.clockFrequencyAdjustmentPPBTotal,
|
||||
prometheus.CounterValue,
|
||||
@@ -151,7 +214,7 @@ func (c *Collector) collect(ctx *types.ScrapeContext, logger log.Logger, ch chan
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.ntpRoundTripDelay,
|
||||
prometheus.GaugeValue,
|
||||
dst[0].NTPRoundtripDelay/1000000, // microseconds -> seconds
|
||||
dst[0].NTPRoundTripDelay/1000000, // microseconds -> seconds
|
||||
)
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.ntpServerIncomingRequestsTotal,
|
||||
@@ -163,5 +226,6 @@ func (c *Collector) collect(ctx *types.ScrapeContext, logger log.Logger, ch chan
|
||||
prometheus.CounterValue,
|
||||
dst[0].NTPServerOutgoingResponsesTotal,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
package collector
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/yusufpapurcu/wmi"
|
||||
)
|
||||
|
||||
type Collectors struct {
|
||||
collectors Map
|
||||
wmiClient *wmi.Client
|
||||
perfCounterQuery string
|
||||
type MetricCollectors struct {
|
||||
Collectors Map
|
||||
WMIClient *wmi.Client
|
||||
PerfCounterQuery string
|
||||
}
|
||||
|
||||
type (
|
||||
@@ -21,13 +22,13 @@ type (
|
||||
|
||||
// Collector interface that a collector has to implement.
|
||||
type Collector interface {
|
||||
Build(logger log.Logger, wmiClient *wmi.Client) error
|
||||
Build(logger *slog.Logger, wmiClient *wmi.Client) error
|
||||
// Close closes the collector
|
||||
Close() error
|
||||
Close(logger *slog.Logger) error
|
||||
// GetName get the name of the collector
|
||||
GetName() string
|
||||
// GetPerfCounter returns the perf counter required by the collector
|
||||
GetPerfCounter(logger log.Logger) ([]string, error)
|
||||
GetPerfCounter(logger *slog.Logger) ([]string, error)
|
||||
// Collect Get new metrics and expose them via prometheus registry.
|
||||
Collect(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) (err error)
|
||||
Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) (err error)
|
||||
}
|
||||
|
||||
@@ -4,10 +4,9 @@ package vmware
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log/slog"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/perflib"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
@@ -67,15 +66,15 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(_ log.Logger, wmiClient *wmi.Client) error {
|
||||
func (c *Collector) Build(_ *slog.Logger, wmiClient *wmi.Client) error {
|
||||
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
|
||||
return errors.New("wmiClient or SWbemServicesClient is nil")
|
||||
}
|
||||
@@ -197,21 +196,30 @@ func (c *Collector) Build(_ log.Logger, wmiClient *wmi.Client) error {
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
if err := c.collectMem(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting vmware memory metrics", "err", err)
|
||||
logger.Error("failed collecting vmware memory metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.collectCpu(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting vmware cpu metrics", "err", err)
|
||||
logger.Error("failed collecting vmware cpu metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -245,6 +253,7 @@ func (c *Collector) collectMem(ch chan<- prometheus.Metric) error {
|
||||
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_vmGuestLib_VMem", &dst); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(dst) == 0 {
|
||||
return errors.New("WMI query returned empty result set")
|
||||
}
|
||||
@@ -333,6 +342,7 @@ func (c *Collector) collectCpu(ch chan<- prometheus.Metric) error {
|
||||
if err := c.wmiClient.Query("SELECT * FROM Win32_PerfRawData_vmGuestLib_VCPU", &dst); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(dst) == 0 {
|
||||
return errors.New("WMI query returned empty result set")
|
||||
}
|
||||
|
||||
@@ -4,10 +4,9 @@ package vmware_blast
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log/slog"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/types"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/yusufpapurcu/wmi"
|
||||
@@ -139,17 +138,18 @@ func (c *Collector) GetName() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
func (c *Collector) GetPerfCounter(_ log.Logger) ([]string, error) {
|
||||
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() error {
|
||||
func (c *Collector) Close(_ *slog.Logger) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Collector) Build(logger log.Logger, wmiClient *wmi.Client) error {
|
||||
_ = level.Warn(logger).
|
||||
Log("msg", "vmware_blast collector is deprecated and will be removed in the future.", "collector", Name)
|
||||
func (c *Collector) Build(logger *slog.Logger, wmiClient *wmi.Client) error {
|
||||
logger.Warn("vmware_blast collector is deprecated and will be removed in the future.",
|
||||
slog.String("collector", Name),
|
||||
)
|
||||
|
||||
if wmiClient == nil || wmiClient.SWbemServicesClient == nil {
|
||||
return errors.New("wmiClient or SWbemServicesClient is nil")
|
||||
@@ -582,61 +582,110 @@ func (c *Collector) Build(logger log.Logger, wmiClient *wmi.Client) error {
|
||||
nil,
|
||||
nil,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Collect sends the metric values for each metric
|
||||
// to the provided prometheus Metric channel.
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = log.With(logger, "collector", Name)
|
||||
func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
|
||||
logger = logger.With(slog.String("collector", Name))
|
||||
if err := c.collectAudio(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting vmware blast audio metrics", "err", err)
|
||||
logger.Error("failed collecting vmware blast audio metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.collectCdr(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting vmware blast CDR metrics", "err", err)
|
||||
logger.Error("failed collecting vmware blast CDR metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.collectClipboard(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting vmware blast clipboard metrics", "err", err)
|
||||
logger.Error("failed collecting vmware blast clipboard metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.collectHtml5Mmr(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting vmware blast HTML5 MMR metrics", "err", err)
|
||||
logger.Error("failed collecting vmware blast HTML5 MMR metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.collectImaging(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting vmware blast imaging metrics", "err", err)
|
||||
logger.Error("failed collecting vmware blast imaging metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.collectRtav(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting vmware blast RTAV metrics", "err", err)
|
||||
logger.Error("failed collecting vmware blast RTAV metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.collectSerialPortandScanner(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting vmware blast serial port and scanner metrics", "err", err)
|
||||
logger.Error("failed collecting vmware blast serial port and scanner metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.collectSession(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting vmware blast metrics", "err", err)
|
||||
logger.Error("failed collecting vmware blast metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.collectSkypeforBusinessControl(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting vmware blast skype for business control metrics", "err", err)
|
||||
logger.Error("failed collecting vmware blast skype for business control metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.collectThinPrint(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting vmware blast thin print metrics", "err", err)
|
||||
logger.Error("failed collecting vmware blast thin print metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.collectUsb(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting vmware blast USB metrics", "err", err)
|
||||
logger.Error("failed collecting vmware blast USB metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.collectWindowsMediaMmr(ch); err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed collecting vmware blast windows media MMR metrics", "err", err)
|
||||
logger.Error("failed collecting vmware blast windows media MMR metrics",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -14,16 +14,16 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
@@ -37,9 +37,11 @@ type Resolver struct {
|
||||
}
|
||||
|
||||
// NewResolver returns a Resolver structure.
|
||||
func NewResolver(file string, logger log.Logger, insecureSkipVerify bool) (*Resolver, error) {
|
||||
func NewResolver(file string, logger *slog.Logger, insecureSkipVerify bool) (*Resolver, error) {
|
||||
flags := map[string]string{}
|
||||
|
||||
var fileBytes []byte
|
||||
|
||||
var err error
|
||||
if strings.HasPrefix(file, "http://") || strings.HasPrefix(file, "https://") {
|
||||
fileBytes, err = readFromURL(file, logger, insecureSkipVerify)
|
||||
@@ -57,7 +59,7 @@ func NewResolver(file string, logger log.Logger, insecureSkipVerify bool) (*Reso
|
||||
|
||||
err = yaml.Unmarshal(fileBytes, &rawValues)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("failed to unmarshal configuration file: %w", err)
|
||||
}
|
||||
|
||||
// Flatten nested YAML values
|
||||
@@ -71,35 +73,42 @@ func NewResolver(file string, logger log.Logger, insecureSkipVerify bool) (*Reso
|
||||
return &Resolver{flags: flags}, nil
|
||||
}
|
||||
|
||||
func readFromFile(file string, logger log.Logger) ([]byte, error) {
|
||||
_ = level.Info(logger).Log("msg", fmt.Sprintf("Loading configuration file: %v", file))
|
||||
func readFromFile(file string, logger *slog.Logger) ([]byte, error) {
|
||||
logger.Info("Loading configuration file: " + file)
|
||||
|
||||
if _, err := os.Stat(file); err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("failed to read configuration file: %w", err)
|
||||
}
|
||||
|
||||
fileBytes, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("failed to read configuration file: %w", err)
|
||||
}
|
||||
|
||||
return fileBytes, err
|
||||
return fileBytes, nil
|
||||
}
|
||||
|
||||
func readFromURL(file string, logger log.Logger, insecureSkipVerify bool) ([]byte, error) {
|
||||
_ = level.Info(logger).Log("msg", fmt.Sprintf("Loading configuration file from URL: %v", file))
|
||||
func readFromURL(file string, logger *slog.Logger, insecureSkipVerify bool) ([]byte, error) {
|
||||
logger.Info("Loading configuration file from URL: " + file)
|
||||
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: insecureSkipVerify}, //nolint:gosec
|
||||
}
|
||||
|
||||
if insecureSkipVerify {
|
||||
_ = level.Warn(logger).Log("msg", "Loading configuration file with TLS verification disabled")
|
||||
logger.Warn("Loading configuration file with TLS verification disabled")
|
||||
}
|
||||
|
||||
client := &http.Client{Transport: tr}
|
||||
|
||||
resp, err := client.Get(file)
|
||||
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, file, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, fmt.Errorf("failed to create HTTP request: %w", err)
|
||||
}
|
||||
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read configuration file from URL: %w", err)
|
||||
}
|
||||
|
||||
defer resp.Body.Close()
|
||||
@@ -130,6 +139,7 @@ func (c *Resolver) Bind(app *kingpin.Application, args []string) error {
|
||||
}
|
||||
|
||||
c.setDefault(app)
|
||||
|
||||
if pc.SelectedCommand != nil {
|
||||
c.setDefault(pc.SelectedCommand)
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
// or {"a": {"b":[1,2]}} => {"a.b.0":1, "a.b.1": 2}.
|
||||
func flatten(data map[string]interface{}) map[string]string {
|
||||
ret := make(map[string]string)
|
||||
|
||||
for k, v := range data {
|
||||
switch typed := v.(type) {
|
||||
case map[interface{}]interface{}:
|
||||
@@ -30,11 +31,13 @@ func flatten(data map[string]interface{}) map[string]string {
|
||||
ret[k] = fmt.Sprint(typed)
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func flattenSlice(data []interface{}) map[string]string {
|
||||
ret := make(map[string]string)
|
||||
|
||||
for idx, v := range data {
|
||||
switch typed := v.(type) {
|
||||
case map[interface{}]interface{}:
|
||||
@@ -53,15 +56,18 @@ func flattenSlice(data []interface{}) map[string]string {
|
||||
ret[strconv.Itoa(idx)] = fmt.Sprint(typed)
|
||||
}
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func convertMap(originalMap map[interface{}]interface{}) map[string]interface{} {
|
||||
convertedMap := map[string]interface{}{}
|
||||
|
||||
for key, value := range originalMap {
|
||||
if keyString, ok := key.(string); ok {
|
||||
convertedMap[keyString] = value
|
||||
}
|
||||
}
|
||||
|
||||
return convertedMap
|
||||
}
|
||||
|
||||
@@ -18,7 +18,9 @@ func TestConfigFlattening(t *testing.T) {
|
||||
|
||||
log:
|
||||
level: debug`)
|
||||
|
||||
var data map[string]interface{}
|
||||
|
||||
err := yaml.Unmarshal(goodYamlConfig, &data)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
package kernel32
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
var (
|
||||
kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
kernel32 = windows.NewLazySystemDLL("kernel32.dll")
|
||||
|
||||
procGetDynamicTimeZoneInformationSys = kernel32.NewProc("GetDynamicTimeZoneInformation")
|
||||
kernelLocalFileTimeToFileTime = kernel32.NewProc("LocalFileTimeToFileTime")
|
||||
)
|
||||
|
||||
// SYSTEMTIME contains a date and time.
|
||||
@@ -50,3 +52,11 @@ func GetDynamicTimeZoneInformation() (DynamicTimezoneInformation, error) {
|
||||
|
||||
return tzi, nil
|
||||
}
|
||||
|
||||
func LocalFileTimeToFileTime(localFileTime, utcFileTime *windows.Filetime) uint32 {
|
||||
ret, _, _ := kernelLocalFileTimeToFileTime.Call(
|
||||
uintptr(unsafe.Pointer(localFileTime)),
|
||||
uintptr(unsafe.Pointer(utcFileTime)))
|
||||
|
||||
return uint32(ret)
|
||||
}
|
||||
|
||||
@@ -76,6 +76,7 @@ func netApiBufferFree(buffer *wKSTAInfo102) {
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/lmwksta/nf-lmwksta-netwkstagetinfo
|
||||
func netWkstaGetInfo() (wKSTAInfo102, uint32, error) {
|
||||
var lpwi *wKSTAInfo102
|
||||
|
||||
pLevel := uintptr(102)
|
||||
|
||||
r1, _, _ := procNetWkstaGetInfo.Call(0, pLevel, uintptr(unsafe.Pointer(&lpwi)))
|
||||
@@ -86,6 +87,7 @@ func netWkstaGetInfo() (wKSTAInfo102, uint32, error) {
|
||||
}
|
||||
|
||||
deref := *lpwi
|
||||
|
||||
return deref, 0, nil
|
||||
}
|
||||
|
||||
@@ -95,6 +97,7 @@ func GetWorkstationInfo() (WorkstationInfo, error) {
|
||||
if err != nil {
|
||||
return WorkstationInfo{}, err
|
||||
}
|
||||
|
||||
workstationInfo := WorkstationInfo{
|
||||
PlatformId: info.wki102_platform_id,
|
||||
ComputerName: windows.UTF16PtrToString(info.wki102_computername),
|
||||
@@ -104,5 +107,6 @@ func GetWorkstationInfo() (WorkstationInfo, error) {
|
||||
LanRoot: windows.UTF16PtrToString(info.wki102_lanroot),
|
||||
LoggedOnUsers: info.wki102_logged_on_users,
|
||||
}
|
||||
|
||||
return workstationInfo, nil
|
||||
}
|
||||
|
||||
@@ -132,7 +132,9 @@ func GlobalMemoryStatusEx() (MemoryStatus, error) {
|
||||
// https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsysteminfo
|
||||
func GetSystemInfo() SystemInfo {
|
||||
var info lpSystemInfo
|
||||
|
||||
procGetSystemInfo.Call(uintptr(unsafe.Pointer(&info))) //nolint:errcheck
|
||||
|
||||
return SystemInfo{
|
||||
Arch: ProcessorArchitecture(info.Arch.WProcessorArchitecture),
|
||||
PageSize: info.DwPageSize,
|
||||
@@ -153,12 +155,16 @@ func GetComputerName(f WinComputerNameFormat) (string, error) {
|
||||
// 1kb buffer to accept computer name. This should be more than enough as the maximum size
|
||||
// returned is the max length of a DNS name, which this author believes is 253 characters.
|
||||
size := 1024
|
||||
|
||||
var buffer [1024]uint16
|
||||
|
||||
r1, _, err := procGetComputerNameExW.Call(uintptr(f), uintptr(unsafe.Pointer(&buffer)), uintptr(unsafe.Pointer(&size)))
|
||||
if r1 == 0 {
|
||||
return "", err
|
||||
}
|
||||
|
||||
bytes := buffer[0:size]
|
||||
out := utf16.Decode(bytes)
|
||||
|
||||
return string(out), nil
|
||||
}
|
||||
|
||||
@@ -2,11 +2,9 @@ package wtsapi32
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"log/slog"
|
||||
"unsafe"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
@@ -105,30 +103,30 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
func WTSOpenServer(server string) (syscall.Handle, error) {
|
||||
func WTSOpenServer(server string) (windows.Handle, error) {
|
||||
var (
|
||||
err error
|
||||
serverName *uint16
|
||||
)
|
||||
|
||||
if server != "" {
|
||||
serverName, err = syscall.UTF16PtrFromString(server)
|
||||
serverName, err = windows.UTF16PtrFromString(server)
|
||||
if err != nil {
|
||||
return syscall.InvalidHandle, err
|
||||
return windows.InvalidHandle, err
|
||||
}
|
||||
}
|
||||
|
||||
r1, _, err := procWTSOpenServerEx.Call(uintptr(unsafe.Pointer(serverName)))
|
||||
serverHandle := syscall.Handle(r1)
|
||||
serverHandle := windows.Handle(r1)
|
||||
|
||||
if serverHandle == syscall.InvalidHandle {
|
||||
return syscall.InvalidHandle, err
|
||||
if serverHandle == windows.InvalidHandle {
|
||||
return windows.InvalidHandle, err
|
||||
}
|
||||
|
||||
return serverHandle, nil
|
||||
}
|
||||
|
||||
func WTSCloseServer(server syscall.Handle) error {
|
||||
func WTSCloseServer(server windows.Handle) error {
|
||||
r1, _, err := procWTSCloseServer.Call(uintptr(server))
|
||||
|
||||
if r1 != 1 {
|
||||
@@ -152,8 +150,9 @@ func WTSFreeMemoryEx(class WTSTypeClass, pMemory uintptr, numberOfEntries uint32
|
||||
return nil
|
||||
}
|
||||
|
||||
func WTSEnumerateSessionsEx(server syscall.Handle, logger log.Logger) ([]WTSSession, error) {
|
||||
func WTSEnumerateSessionsEx(server windows.Handle, logger *slog.Logger) ([]WTSSession, error) {
|
||||
var sessionInfoPointer uintptr
|
||||
|
||||
var count uint32
|
||||
|
||||
pLevel := uint32(1)
|
||||
@@ -173,7 +172,7 @@ func WTSEnumerateSessionsEx(server syscall.Handle, logger log.Logger) ([]WTSSess
|
||||
defer func(class WTSTypeClass, pMemory uintptr, NumberOfEntries uint32) {
|
||||
err := WTSFreeMemoryEx(class, pMemory, NumberOfEntries)
|
||||
if err != nil {
|
||||
_ = level.Error(logger).Log("msg", "failed to free memory", "err", fmt.Errorf("WTSEnumerateSessionsEx: %w", err))
|
||||
logger.Warn("failed to free memory", "err", fmt.Errorf("WTSEnumerateSessionsEx: %w", err))
|
||||
}
|
||||
}(WTSTypeSessionInfoLevel1, sessionInfoPointer, count)
|
||||
}
|
||||
@@ -182,6 +181,7 @@ func WTSEnumerateSessionsEx(server syscall.Handle, logger log.Logger) ([]WTSSess
|
||||
sessionSize := unsafe.Sizeof(sizeTest)
|
||||
|
||||
sessions := make([]WTSSession, 0, count)
|
||||
|
||||
for i := range count {
|
||||
curPtr := unsafe.Pointer(sessionInfoPointer + (uintptr(i) * sessionSize))
|
||||
data := (*wtsSessionInfo1)(curPtr)
|
||||
|
||||
19
pkg/httphandler/health.go
Normal file
19
pkg/httphandler/health.go
Normal file
@@ -0,0 +1,19 @@
|
||||
package httphandler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type HealthHandler struct{}
|
||||
|
||||
// Interface guard.
|
||||
var _ http.Handler = (*HealthHandler)(nil)
|
||||
|
||||
func NewHealthHandler() HealthHandler {
|
||||
return HealthHandler{}
|
||||
}
|
||||
|
||||
func (h HealthHandler) ServeHTTP(w http.ResponseWriter, _ *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write([]byte(`{"status":"ok"}`))
|
||||
}
|
||||
192
pkg/httphandler/httphandler.go
Normal file
192
pkg/httphandler/httphandler.go
Normal file
@@ -0,0 +1,192 @@
|
||||
package httphandler
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"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"
|
||||
"github.com/prometheus/client_golang/prometheus/collectors/version"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
)
|
||||
|
||||
// Interface guard.
|
||||
var _ http.Handler = (*MetricsHTTPHandler)(nil)
|
||||
|
||||
const defaultScrapeTimeout = 10.0
|
||||
|
||||
type MetricsHTTPHandler struct {
|
||||
metricCollectors *collector.MetricCollectors
|
||||
// exporterMetricsRegistry is a separate registry for the metrics about
|
||||
// the exporter itself.
|
||||
exporterMetricsRegistry *prometheus.Registry
|
||||
|
||||
logger *slog.Logger
|
||||
options Options
|
||||
concurrencyCh chan struct{}
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
DisableExporterMetrics bool
|
||||
TimeoutMargin float64
|
||||
MaxRequests int
|
||||
}
|
||||
|
||||
func New(logger *slog.Logger, metricCollectors *collector.MetricCollectors, options *Options) *MetricsHTTPHandler {
|
||||
if options == nil {
|
||||
options = &Options{
|
||||
DisableExporterMetrics: false,
|
||||
TimeoutMargin: 0.5,
|
||||
MaxRequests: 5,
|
||||
}
|
||||
}
|
||||
|
||||
handler := &MetricsHTTPHandler{
|
||||
metricCollectors: metricCollectors,
|
||||
logger: logger,
|
||||
options: *options,
|
||||
concurrencyCh: make(chan struct{}, options.MaxRequests),
|
||||
}
|
||||
|
||||
if !options.DisableExporterMetrics {
|
||||
handler.exporterMetricsRegistry = prometheus.NewRegistry()
|
||||
handler.exporterMetricsRegistry.MustRegister(
|
||||
collectors.NewBuildInfoCollector(),
|
||||
collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}),
|
||||
collectors.NewGoCollector(),
|
||||
)
|
||||
}
|
||||
|
||||
return handler
|
||||
}
|
||||
|
||||
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()),
|
||||
)
|
||||
|
||||
scrapeTimeout := c.getScrapeTimeout(logger, r)
|
||||
|
||||
handler, err := c.handlerFactory(logger, scrapeTimeout, r.URL.Query()["collect[]"])
|
||||
if err != nil {
|
||||
logger.Warn("Couldn't create filtered metrics handler",
|
||||
slog.Any("err", err),
|
||||
)
|
||||
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
_, _ = w.Write([]byte(fmt.Sprintf("Couldn't create filtered metrics handler: %s", err)))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
handler.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
func (c *MetricsHTTPHandler) getScrapeTimeout(logger *slog.Logger, r *http.Request) time.Duration {
|
||||
var timeoutSeconds float64
|
||||
|
||||
if v := r.Header.Get("X-Prometheus-Scrape-Timeout-Seconds"); v != "" {
|
||||
var err error
|
||||
|
||||
timeoutSeconds, err = strconv.ParseFloat(v, 64)
|
||||
if err != nil {
|
||||
logger.Warn(fmt.Sprintf("Couldn't parse X-Prometheus-Scrape-Timeout-Seconds: %q. Defaulting timeout to %f", v, defaultScrapeTimeout))
|
||||
}
|
||||
}
|
||||
|
||||
if timeoutSeconds == 0 {
|
||||
timeoutSeconds = defaultScrapeTimeout
|
||||
}
|
||||
|
||||
timeoutSeconds -= c.options.TimeoutMargin
|
||||
|
||||
return time.Duration(timeoutSeconds) * time.Second
|
||||
}
|
||||
|
||||
func (c *MetricsHTTPHandler) handlerFactory(logger *slog.Logger, scrapeTimeout time.Duration, requestedCollectors []string) (http.Handler, error) {
|
||||
reg := prometheus.NewRegistry()
|
||||
|
||||
var metricCollectors *collector.MetricCollectors
|
||||
if len(requestedCollectors) == 0 {
|
||||
metricCollectors = c.metricCollectors
|
||||
} else {
|
||||
filteredCollectors := make(collector.Map)
|
||||
|
||||
for _, name := range requestedCollectors {
|
||||
metricCollector, ok := c.metricCollectors.Collectors[name]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("couldn't find collector %s", name)
|
||||
}
|
||||
|
||||
filteredCollectors[name] = metricCollector
|
||||
}
|
||||
|
||||
metricCollectors = &collector.MetricCollectors{
|
||||
Collectors: filteredCollectors,
|
||||
WMIClient: c.metricCollectors.WMIClient,
|
||||
PerfCounterQuery: c.metricCollectors.PerfCounterQuery,
|
||||
}
|
||||
}
|
||||
|
||||
reg.MustRegister(version.NewCollector("windows_exporter"))
|
||||
|
||||
if err := reg.Register(metricCollectors.NewPrometheusCollector(scrapeTimeout, c.logger)); err != nil {
|
||||
return nil, fmt.Errorf("couldn't register Prometheus collector: %w", err)
|
||||
}
|
||||
|
||||
var handler http.Handler
|
||||
if c.exporterMetricsRegistry != nil {
|
||||
handler = promhttp.HandlerFor(
|
||||
prometheus.Gatherers{c.exporterMetricsRegistry, reg},
|
||||
promhttp.HandlerOpts{
|
||||
ErrorLog: slog.NewLogLogger(logger.Handler(), slog.LevelError),
|
||||
ErrorHandling: promhttp.ContinueOnError,
|
||||
MaxRequestsInFlight: c.options.MaxRequests,
|
||||
Registry: c.exporterMetricsRegistry,
|
||||
},
|
||||
)
|
||||
|
||||
// Note that we have to use h.exporterMetricsRegistry here to
|
||||
// use the same promhttp metrics for all expositions.
|
||||
handler = promhttp.InstrumentMetricHandler(
|
||||
c.exporterMetricsRegistry, handler,
|
||||
)
|
||||
} else {
|
||||
handler = promhttp.HandlerFor(
|
||||
reg,
|
||||
promhttp.HandlerOpts{
|
||||
ErrorLog: slog.NewLogLogger(logger.Handler(), slog.LevelError),
|
||||
ErrorHandling: promhttp.ContinueOnError,
|
||||
MaxRequestsInFlight: c.options.MaxRequests,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
return c.withConcurrencyLimit(handler.ServeHTTP), nil
|
||||
}
|
||||
|
||||
func (c *MetricsHTTPHandler) withConcurrencyLimit(next http.HandlerFunc) http.HandlerFunc {
|
||||
if c.options.MaxRequests <= 0 {
|
||||
return next
|
||||
}
|
||||
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
select {
|
||||
case c.concurrencyCh <- struct{}{}:
|
||||
defer func() { <-c.concurrencyCh }()
|
||||
default:
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
_, _ = w.Write([]byte("Too many concurrent requests"))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
next(w, r)
|
||||
}
|
||||
}
|
||||
45
pkg/httphandler/version.go
Normal file
45
pkg/httphandler/version.go
Normal file
@@ -0,0 +1,45 @@
|
||||
package httphandler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/prometheus/common/version"
|
||||
)
|
||||
|
||||
type VersionHandler struct{}
|
||||
|
||||
// Same struct prometheus uses for their /version endpoint.
|
||||
// Separate copy to avoid pulling all of prometheus as a dependency.
|
||||
type prometheusVersion struct {
|
||||
Version string `json:"version"`
|
||||
Revision string `json:"revision"`
|
||||
Branch string `json:"branch"`
|
||||
BuildUser string `json:"buildUser"`
|
||||
BuildDate string `json:"buildDate"`
|
||||
GoVersion string `json:"goVersion"`
|
||||
}
|
||||
|
||||
// Interface guard.
|
||||
var _ http.Handler = (*VersionHandler)(nil)
|
||||
|
||||
func NewVersionHandler() VersionHandler {
|
||||
return VersionHandler{}
|
||||
}
|
||||
|
||||
func (h VersionHandler) ServeHTTP(w http.ResponseWriter, _ *http.Request) {
|
||||
// we can't use "version" directly as it is a package, and not an object that
|
||||
// can be serialized.
|
||||
err := json.NewEncoder(w).Encode(prometheusVersion{
|
||||
Version: version.Version,
|
||||
Revision: version.Revision,
|
||||
Branch: version.Branch,
|
||||
BuildUser: version.BuildUser,
|
||||
BuildDate: version.BuildDate,
|
||||
GoVersion: version.GoVersion,
|
||||
})
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("error encoding JSON: %s", err), http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,9 @@ func init() {
|
||||
if err != nil {
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
_ = logger.Error(102, fmt.Sprintf("Failed to detect service: %v", err))
|
||||
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@@ -59,7 +61,9 @@ func init() {
|
||||
if err != nil {
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
_ = logger.Info(100, "Attempting to start exporter service")
|
||||
|
||||
go func() {
|
||||
err = svc.Run(serviceName, &windowsExporterService{})
|
||||
if err != nil {
|
||||
|
||||
@@ -1,21 +1,14 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
// Package eventlog provides a Logger that writes to Windows Event Log.
|
||||
package eventlog
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
"golang.org/x/sys/windows"
|
||||
goeventlog "golang.org/x/sys/windows/svc/eventlog"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -25,109 +18,36 @@ const (
|
||||
neLogOemCode = uint32(3299)
|
||||
)
|
||||
|
||||
type Priority struct {
|
||||
etype int
|
||||
// Interface guard.
|
||||
var _ io.Writer = (*Writer)(nil)
|
||||
|
||||
type Writer struct {
|
||||
handle windows.Handle
|
||||
}
|
||||
|
||||
// NewEventLogLogger returns a new Logger which writes to Windows EventLog in event log format.
|
||||
// The body of the log message is the formatted output from the Logger returned
|
||||
// by newLogger.
|
||||
func NewEventLogLogger(w *goeventlog.Log, newLogger func(io.Writer) log.Logger) log.Logger {
|
||||
l := &eventlogLogger{
|
||||
w: w,
|
||||
newLogger: newLogger,
|
||||
prioritySelector: defaultPrioritySelector,
|
||||
bufPool: sync.Pool{New: func() interface{} {
|
||||
return &loggerBuf{}
|
||||
}},
|
||||
// NewEventLogWriter returns a new Writer which writes to Windows EventLog.
|
||||
func NewEventLogWriter(handle windows.Handle) *Writer {
|
||||
return &Writer{handle: handle}
|
||||
}
|
||||
|
||||
func (w *Writer) Write(p []byte) (int, error) {
|
||||
var eType uint16
|
||||
|
||||
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
|
||||
default:
|
||||
eType = windows.EVENTLOG_INFORMATION_TYPE
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
type eventlogLogger struct {
|
||||
w *goeventlog.Log
|
||||
newLogger func(io.Writer) log.Logger
|
||||
prioritySelector PrioritySelector
|
||||
bufPool sync.Pool
|
||||
}
|
||||
|
||||
func (l *eventlogLogger) Log(keyvals ...interface{}) error {
|
||||
priority := l.prioritySelector(keyvals...)
|
||||
|
||||
lb, err := l.getLoggerBuf()
|
||||
msg, err := windows.UTF16PtrFromString(string(p))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defer l.putLoggerBuf(lb)
|
||||
if err := lb.logger.Log(keyvals...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// golang.org/x/sys/windows/svc/eventlog does not provide func which allows to send more than one string.
|
||||
// See: https://github.com/golang/go/issues/59780
|
||||
|
||||
msg, err := syscall.UTF16PtrFromString(lb.buf.String())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error convert string to UTF-16: %w", err)
|
||||
return 0, fmt.Errorf("error convert string to UTF-16: %w", err)
|
||||
}
|
||||
|
||||
ss := []*uint16{msg, nil, nil, nil, nil, nil, nil, nil, nil}
|
||||
return windows.ReportEvent(l.w.Handle, uint16(priority.etype), 0, neLogOemCode, 0, 9, 0, &ss[0], nil)
|
||||
}
|
||||
|
||||
type loggerBuf struct {
|
||||
buf *bytes.Buffer
|
||||
logger log.Logger
|
||||
}
|
||||
|
||||
func (l *eventlogLogger) getLoggerBuf() (*loggerBuf, error) {
|
||||
lb, ok := l.bufPool.Get().(*loggerBuf)
|
||||
if !ok {
|
||||
return nil, errors.New("failed to get loggerBuf from pool")
|
||||
}
|
||||
|
||||
if lb.buf == nil {
|
||||
lb.buf = &bytes.Buffer{}
|
||||
lb.logger = l.newLogger(lb.buf)
|
||||
} else {
|
||||
lb.buf.Reset()
|
||||
}
|
||||
return lb, nil
|
||||
}
|
||||
|
||||
func (l *eventlogLogger) putLoggerBuf(lb *loggerBuf) {
|
||||
l.bufPool.Put(lb)
|
||||
}
|
||||
|
||||
// PrioritySelector inspects the list of keyvals and selects an eventlog priority.
|
||||
type PrioritySelector func(keyvals ...interface{}) Priority
|
||||
|
||||
// defaultPrioritySelector convert a kit/log level into a Windows Eventlog level.
|
||||
func defaultPrioritySelector(keyvals ...interface{}) Priority {
|
||||
l := len(keyvals)
|
||||
|
||||
eType := windows.EVENTLOG_SUCCESS
|
||||
|
||||
for i := 0; i < l; i += 2 {
|
||||
if keyvals[i] == level.Key() {
|
||||
var val interface{}
|
||||
if i+1 < l {
|
||||
val = keyvals[i+1]
|
||||
}
|
||||
if v, ok := val.(level.Value); ok {
|
||||
switch v {
|
||||
case level.ErrorValue():
|
||||
eType = windows.EVENTLOG_ERROR_TYPE
|
||||
case level.WarnValue():
|
||||
eType = windows.EVENTLOG_WARNING_TYPE
|
||||
case level.InfoValue():
|
||||
eType = windows.EVENTLOG_INFORMATION_TYPE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Priority{etype: eType}
|
||||
|
||||
return len(p), windows.ReportEvent(w.handle, eType, 0, neLogOemCode, 0, 9, 0, &ss[0], nil)
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@ package flag
|
||||
import (
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/log"
|
||||
"github.com/prometheus/common/promlog"
|
||||
promlogflag "github.com/prometheus/common/promlog/flag"
|
||||
"github.com/prometheus/common/promslog"
|
||||
"github.com/prometheus/common/promslog/flag"
|
||||
)
|
||||
|
||||
// FileFlagName is the canonical flag name to configure the log file.
|
||||
@@ -29,15 +29,9 @@ const FileFlagHelp = "Output file of log messages. One of [stdout, stderr, event
|
||||
// AddFlags adds the flags used by this package to the Kingpin application.
|
||||
// To use the default Kingpin application, call AddFlags(kingpin.CommandLine).
|
||||
func AddFlags(a *kingpin.Application, config *log.Config) {
|
||||
config.Level = &promlog.AllowedLevel{}
|
||||
a.Flag(promlogflag.LevelFlagName, promlogflag.LevelFlagHelp).
|
||||
Default("info").SetValue(config.Level)
|
||||
config.Config = new(promslog.Config)
|
||||
flag.AddFlags(a, config.Config)
|
||||
|
||||
config.File = &log.AllowedFile{}
|
||||
a.Flag(FileFlagName, FileFlagHelp).
|
||||
Default("stderr").SetValue(config.File)
|
||||
|
||||
config.Format = &promlog.AllowedFormat{}
|
||||
a.Flag(promlogflag.FormatFlagName, promlogflag.FormatFlagHelp).
|
||||
Default("logfmt").SetValue(config.Format)
|
||||
a.Flag(FileFlagName, FileFlagHelp).Default("stderr").SetValue(config.File)
|
||||
}
|
||||
|
||||
@@ -4,12 +4,12 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"os"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/log/eventlog"
|
||||
"github.com/prometheus/common/promlog"
|
||||
goeventlog "golang.org/x/sys/windows/svc/eventlog"
|
||||
"github.com/prometheus/common/promslog"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// AllowedFile is a settable identifier for the output file that the logger can have.
|
||||
@@ -25,71 +25,45 @@ func (f *AllowedFile) String() string {
|
||||
// Set updates the value of the allowed format.
|
||||
func (f *AllowedFile) Set(s string) error {
|
||||
f.s = s
|
||||
|
||||
switch s {
|
||||
case "stdout":
|
||||
f.w = os.Stdout
|
||||
case "stderr":
|
||||
f.w = os.Stderr
|
||||
case "eventlog":
|
||||
f.w = nil
|
||||
handle, err := windows.RegisterEventSource(nil, windows.StringToUTF16Ptr("windows_exporter"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open event log: %w", err)
|
||||
}
|
||||
|
||||
f.w = eventlog.NewEventLogWriter(handle)
|
||||
default:
|
||||
file, err := os.OpenFile(s, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0o200)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("failed to open log file: %w", err)
|
||||
}
|
||||
|
||||
f.w = file
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Config is a struct containing configurable settings for the logger.
|
||||
type Config struct {
|
||||
promlog.Config
|
||||
*promslog.Config
|
||||
|
||||
File *AllowedFile
|
||||
}
|
||||
|
||||
func New(config *Config) (log.Logger, error) {
|
||||
func New(config *Config) (*slog.Logger, error) {
|
||||
if config.File == nil {
|
||||
return nil, errors.New("log file undefined")
|
||||
}
|
||||
|
||||
if config.Format == nil {
|
||||
return nil, errors.New("log format undefined")
|
||||
}
|
||||
config.Config.Writer = config.File.w
|
||||
config.Config.Style = promslog.GoKitStyle
|
||||
|
||||
var (
|
||||
l log.Logger
|
||||
loggerFunc func(io.Writer) log.Logger
|
||||
)
|
||||
|
||||
switch config.Format.String() {
|
||||
case "json":
|
||||
loggerFunc = log.NewJSONLogger
|
||||
case "logfmt":
|
||||
loggerFunc = log.NewLogfmtLogger
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported log.format %q", config.Format.String())
|
||||
}
|
||||
|
||||
switch {
|
||||
case config.File.s == "eventlog":
|
||||
|
||||
w, err := goeventlog.Open("windows_exporter")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
l = eventlog.NewEventLogLogger(w, loggerFunc)
|
||||
case config.File.w == nil:
|
||||
panic("logger: file writer is nil")
|
||||
default:
|
||||
l = loggerFunc(log.NewSyncWriter(config.File.w))
|
||||
}
|
||||
|
||||
promlogConfig := promlog.Config{
|
||||
Format: config.Format,
|
||||
Level: config.Level,
|
||||
}
|
||||
|
||||
return promlog.NewWithLogger(l, &promlogConfig), nil
|
||||
return promslog.New(config.Config), nil
|
||||
}
|
||||
|
||||
236
pkg/perfdata/collector.go
Normal file
236
pkg/perfdata/collector.go
Normal file
@@ -0,0 +1,236 @@
|
||||
//go:build windows
|
||||
|
||||
package perfdata
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
const EmptyInstance = "------"
|
||||
|
||||
type Collector struct {
|
||||
time time.Time
|
||||
object string
|
||||
counters []Counter
|
||||
handle pdhQueryHandle
|
||||
}
|
||||
|
||||
type Counter struct {
|
||||
Name string
|
||||
Instances map[string]pdhCounterHandle
|
||||
Type uint32
|
||||
Frequency float64
|
||||
}
|
||||
|
||||
type CounterValues struct {
|
||||
Type prometheus.ValueType
|
||||
FirstValue float64
|
||||
SecondValue float64
|
||||
}
|
||||
|
||||
func NewCollector(object string, instances []string, counters []string) (*Collector, error) {
|
||||
var handle pdhQueryHandle
|
||||
|
||||
if ret := PdhOpenQuery(0, 0, &handle); ret != ErrorSuccess {
|
||||
return nil, NewPdhError(ret)
|
||||
}
|
||||
|
||||
if len(instances) == 0 {
|
||||
instances = []string{EmptyInstance}
|
||||
}
|
||||
|
||||
collector := &Collector{
|
||||
object: object,
|
||||
counters: make([]Counter, 0, len(counters)),
|
||||
handle: handle,
|
||||
}
|
||||
|
||||
for _, counterName := range counters {
|
||||
if counterName == "*" {
|
||||
return nil, errors.New("wildcard counters are not supported")
|
||||
}
|
||||
|
||||
counter := Counter{
|
||||
Name: counterName,
|
||||
Instances: make(map[string]pdhCounterHandle, len(instances)),
|
||||
}
|
||||
|
||||
var counterPath string
|
||||
|
||||
for _, instance := range instances {
|
||||
counterPath = formatCounterPath(object, instance, counterName)
|
||||
|
||||
var counterHandle pdhCounterHandle
|
||||
|
||||
if ret := PdhAddEnglishCounter(handle, counterPath, 0, &counterHandle); ret != ErrorSuccess {
|
||||
return nil, fmt.Errorf("failed to add counter %s: %w", counterPath, NewPdhError(ret))
|
||||
}
|
||||
|
||||
counter.Instances[instance] = counterHandle
|
||||
|
||||
if counter.Type == 0 {
|
||||
// Get the info with the current buffer size
|
||||
bufLen := uint32(0)
|
||||
|
||||
if ret := PdhGetCounterInfo(counterHandle, 0, &bufLen, nil); ret != PdhMoreData {
|
||||
return nil, fmt.Errorf("PdhGetCounterInfo: %w", NewPdhError(ret))
|
||||
}
|
||||
|
||||
buf := make([]byte, bufLen)
|
||||
if ret := PdhGetCounterInfo(counterHandle, 0, &bufLen, &buf[0]); ret != ErrorSuccess {
|
||||
return nil, fmt.Errorf("PdhGetCounterInfo: %w", NewPdhError(ret))
|
||||
}
|
||||
|
||||
ci := (*PdhCounterInfo)(unsafe.Pointer(&buf[0]))
|
||||
counter.Type = ci.DwType
|
||||
|
||||
frequency := float64(0)
|
||||
|
||||
if ret := PdhGetCounterTimeBase(counterHandle, &frequency); ret != ErrorSuccess {
|
||||
return nil, fmt.Errorf("PdhGetCounterTimeBase: %w", NewPdhError(ret))
|
||||
}
|
||||
|
||||
counter.Frequency = frequency
|
||||
}
|
||||
}
|
||||
|
||||
collector.counters = append(collector.counters, counter)
|
||||
}
|
||||
|
||||
if len(collector.counters) == 0 {
|
||||
return nil, errors.New("no counters configured")
|
||||
}
|
||||
|
||||
if _, err := collector.Collect(); err != nil {
|
||||
return nil, fmt.Errorf("failed to collect initial data: %w", err)
|
||||
}
|
||||
|
||||
return collector, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Collect() (map[string]map[string]CounterValues, error) {
|
||||
if len(c.counters) == 0 {
|
||||
return map[string]map[string]CounterValues{}, nil
|
||||
}
|
||||
|
||||
if ret := PdhCollectQueryData(c.handle); ret != ErrorSuccess {
|
||||
return nil, fmt.Errorf("failed to collect query data: %w", NewPdhError(ret))
|
||||
}
|
||||
|
||||
c.time = time.Now()
|
||||
|
||||
var data map[string]map[string]CounterValues
|
||||
|
||||
for _, counter := range c.counters {
|
||||
for _, instance := range counter.Instances {
|
||||
// Get the info with the current buffer size
|
||||
var itemCount uint32
|
||||
|
||||
// Get the info with the current buffer size
|
||||
bufLen := uint32(0)
|
||||
|
||||
ret := PdhGetRawCounterArray(instance, &bufLen, &itemCount, nil)
|
||||
if ret != PdhMoreData {
|
||||
return nil, fmt.Errorf("PdhGetRawCounterArray: %w", NewPdhError(ret))
|
||||
}
|
||||
|
||||
buf := make([]byte, bufLen)
|
||||
|
||||
ret = PdhGetRawCounterArray(instance, &bufLen, &itemCount, &buf[0])
|
||||
if ret != ErrorSuccess {
|
||||
if err := NewPdhError(ret); !isKnownCounterDataError(err) {
|
||||
return nil, fmt.Errorf("PdhGetRawCounterArray: %w", err)
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
items := (*[1 << 20]PdhRawCounterItem)(unsafe.Pointer(&buf[0]))[:itemCount]
|
||||
|
||||
if data == nil {
|
||||
data = make(map[string]map[string]CounterValues, itemCount)
|
||||
}
|
||||
|
||||
var metricType prometheus.ValueType
|
||||
if val, ok := supportedCounterTypes[counter.Type]; ok {
|
||||
metricType = val
|
||||
} else {
|
||||
metricType = prometheus.GaugeValue
|
||||
}
|
||||
|
||||
for _, item := range items {
|
||||
if item.RawValue.CStatus == PdhCstatusValidData || item.RawValue.CStatus == PdhCstatusNewData {
|
||||
instanceName := windows.UTF16PtrToString(item.SzName)
|
||||
if strings.HasSuffix(instanceName, "_Total") {
|
||||
continue
|
||||
}
|
||||
|
||||
if instanceName == "" {
|
||||
instanceName = EmptyInstance
|
||||
}
|
||||
|
||||
if _, ok := data[instanceName]; !ok {
|
||||
data[instanceName] = make(map[string]CounterValues, len(c.counters))
|
||||
}
|
||||
|
||||
values := CounterValues{
|
||||
Type: metricType,
|
||||
}
|
||||
|
||||
// This is a workaround for the issue with the elapsed time counter type.
|
||||
// Source: https://github.com/prometheus-community/windows_exporter/pull/335/files#diff-d5d2528f559ba2648c2866aec34b1eaa5c094dedb52bd0ff22aa5eb83226bd8dR76-R83
|
||||
|
||||
switch counter.Type {
|
||||
case PERF_ELAPSED_TIME:
|
||||
values.FirstValue = float64(item.RawValue.FirstValue-WindowsEpoch) / counter.Frequency
|
||||
values.SecondValue = float64(item.RawValue.SecondValue-WindowsEpoch) / counter.Frequency
|
||||
case PERF_100NSEC_TIMER, PERF_PRECISION_100NS_TIMER:
|
||||
values.FirstValue = float64(item.RawValue.FirstValue) * TicksToSecondScaleFactor
|
||||
values.SecondValue = float64(item.RawValue.SecondValue) * TicksToSecondScaleFactor
|
||||
default:
|
||||
values.FirstValue = float64(item.RawValue.FirstValue)
|
||||
values.SecondValue = float64(item.RawValue.SecondValue)
|
||||
}
|
||||
|
||||
data[instanceName][counter.Name] = values
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (c *Collector) Close() {
|
||||
PdhCloseQuery(c.handle)
|
||||
}
|
||||
|
||||
func formatCounterPath(object, instance, counterName string) string {
|
||||
var counterPath string
|
||||
|
||||
if instance == EmptyInstance {
|
||||
counterPath = fmt.Sprintf(`\%s\%s`, object, counterName)
|
||||
} else {
|
||||
counterPath = fmt.Sprintf(`\%s(%s)\%s`, object, instance, counterName)
|
||||
}
|
||||
|
||||
return counterPath
|
||||
}
|
||||
|
||||
func isKnownCounterDataError(err error) bool {
|
||||
var pdhErr *Error
|
||||
|
||||
return errors.As(err, &pdhErr) && (pdhErr.ErrorCode == PdhInvalidData ||
|
||||
pdhErr.ErrorCode == PdhCalcNegativeDenominator ||
|
||||
pdhErr.ErrorCode == PdhCalcNegativeValue ||
|
||||
pdhErr.ErrorCode == PdhCstatusInvalidData ||
|
||||
pdhErr.ErrorCode == PdhCstatusNoInstance ||
|
||||
pdhErr.ErrorCode == PdhNoData)
|
||||
}
|
||||
49
pkg/perfdata/collector_bench_test.go
Normal file
49
pkg/perfdata/collector_bench_test.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package perfdata_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/prometheus-community/windows_exporter/pkg/perfdata"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func BenchmarkTestCollector(b *testing.B) {
|
||||
counters := []string{
|
||||
"% Processor Time",
|
||||
"% Privileged Time",
|
||||
"% User Time",
|
||||
"Creating Process ID",
|
||||
"Elapsed Time",
|
||||
"Handle Count",
|
||||
"ID Process",
|
||||
"IO Data Bytes/sec",
|
||||
"IO Data Operations/sec",
|
||||
"IO Other Bytes/sec",
|
||||
"IO Other Operations/sec",
|
||||
"IO Read Bytes/sec",
|
||||
"IO Read Operations/sec",
|
||||
"IO Write Bytes/sec",
|
||||
"IO Write Operations/sec",
|
||||
"Page Faults/sec",
|
||||
"Page File Bytes Peak",
|
||||
"Page File Bytes",
|
||||
"Pool Nonpaged Bytes",
|
||||
"Pool Paged Bytes",
|
||||
"Priority Base",
|
||||
"Private Bytes",
|
||||
"Thread Count",
|
||||
"Virtual Bytes Peak",
|
||||
"Virtual Bytes",
|
||||
"Working Set - Private",
|
||||
"Working Set Peak",
|
||||
"Working Set",
|
||||
}
|
||||
performanceData, err := perfdata.NewCollector("Process", []string{"*"}, counters)
|
||||
require.NoError(b, err)
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = performanceData.Collect()
|
||||
}
|
||||
|
||||
performanceData.Close()
|
||||
}
|
||||
86
pkg/perfdata/collector_test.go
Normal file
86
pkg/perfdata/collector_test.go
Normal file
@@ -0,0 +1,86 @@
|
||||
//go:build windows
|
||||
|
||||
package perfdata_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/prometheus-community/windows_exporter/pkg/perfdata"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCollector(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
for _, tc := range []struct {
|
||||
object string
|
||||
instances []string
|
||||
counters []string
|
||||
}{
|
||||
{
|
||||
object: "Memory",
|
||||
counters: []string{
|
||||
"Available Bytes",
|
||||
"Available KBytes",
|
||||
"Available MBytes",
|
||||
"Cache Bytes",
|
||||
"Cache Bytes Peak",
|
||||
"Cache Faults/sec",
|
||||
"Commit Limit",
|
||||
"Committed Bytes",
|
||||
"Demand Zero Faults/sec",
|
||||
"Free & Zero Page List Bytes",
|
||||
"Free System Page Table Entries",
|
||||
"Modified Page List Bytes",
|
||||
"Page Reads/sec",
|
||||
},
|
||||
}, {
|
||||
object: "TCPv4",
|
||||
counters: []string{
|
||||
"Connection Failures",
|
||||
"Connections Active",
|
||||
"Connections Established",
|
||||
"Connections Passive",
|
||||
"Connections Reset",
|
||||
"Segments/sec",
|
||||
"Segments Received/sec",
|
||||
"Segments Retransmitted/sec",
|
||||
"Segments Sent/sec",
|
||||
},
|
||||
}, {
|
||||
object: "Process",
|
||||
instances: []string{"*"},
|
||||
counters: []string{
|
||||
"Thread Count",
|
||||
"ID Process",
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(tc.object, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
performanceData, err := perfdata.NewCollector(tc.object, tc.instances, tc.counters)
|
||||
require.NoError(t, err)
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
|
||||
data, err := performanceData.Collect()
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, data)
|
||||
|
||||
for instance, d := range data {
|
||||
require.NotEmpty(t, d)
|
||||
|
||||
if instance == "Idle" || instance == "Secure System" {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, c := range tc.counters {
|
||||
assert.NotZerof(t, d[c].FirstValue, "object: %s, instance: %s, counter: %s", tc.object, instance, c)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
78
pkg/perfdata/const.go
Normal file
78
pkg/perfdata/const.go
Normal file
@@ -0,0 +1,78 @@
|
||||
//go:build windows
|
||||
|
||||
package perfdata
|
||||
|
||||
import "github.com/prometheus/client_golang/prometheus"
|
||||
|
||||
// Conversion factors.
|
||||
const (
|
||||
TicksToSecondScaleFactor = 1 / 1e7
|
||||
WindowsEpoch = 116444736000000000
|
||||
)
|
||||
|
||||
// Based on https://github.com/leoluk/perflib_exporter/blob/master/collector/mapper.go
|
||||
//
|
||||
//goland:noinspection GoUnusedConst
|
||||
const (
|
||||
PERF_COUNTER_RAWCOUNT_HEX = 0x00000000
|
||||
PERF_COUNTER_LARGE_RAWCOUNT_HEX = 0x00000100
|
||||
PERF_COUNTER_TEXT = 0x00000b00
|
||||
PERF_COUNTER_RAWCOUNT = 0x00010000
|
||||
PERF_COUNTER_LARGE_RAWCOUNT = 0x00010100
|
||||
PERF_DOUBLE_RAW = 0x00012000
|
||||
PERF_COUNTER_DELTA = 0x00400400
|
||||
PERF_COUNTER_LARGE_DELTA = 0x00400500
|
||||
PERF_SAMPLE_COUNTER = 0x00410400
|
||||
PERF_COUNTER_QUEUELEN_TYPE = 0x00450400
|
||||
PERF_COUNTER_LARGE_QUEUELEN_TYPE = 0x00450500
|
||||
PERF_COUNTER_100NS_QUEUELEN_TYPE = 0x00550500
|
||||
PERF_COUNTER_OBJ_TIME_QUEUELEN_TYPE = 0x00650500
|
||||
PERF_COUNTER_COUNTER = 0x10410400
|
||||
PERF_COUNTER_BULK_COUNT = 0x10410500
|
||||
PERF_RAW_FRACTION = 0x20020400
|
||||
PERF_LARGE_RAW_FRACTION = 0x20020500
|
||||
PERF_COUNTER_TIMER = 0x20410500
|
||||
PERF_PRECISION_SYSTEM_TIMER = 0x20470500
|
||||
PERF_100NSEC_TIMER = 0x20510500
|
||||
PERF_PRECISION_100NS_TIMER = 0x20570500
|
||||
PERF_OBJ_TIME_TIMER = 0x20610500
|
||||
PERF_PRECISION_OBJECT_TIMER = 0x20670500
|
||||
PERF_SAMPLE_FRACTION = 0x20c20400
|
||||
PERF_COUNTER_TIMER_INV = 0x21410500
|
||||
PERF_100NSEC_TIMER_INV = 0x21510500
|
||||
PERF_COUNTER_MULTI_TIMER = 0x22410500
|
||||
PERF_100NSEC_MULTI_TIMER = 0x22510500
|
||||
PERF_COUNTER_MULTI_TIMER_INV = 0x23410500
|
||||
PERF_100NSEC_MULTI_TIMER_INV = 0x23510500
|
||||
PERF_AVERAGE_TIMER = 0x30020400
|
||||
PERF_ELAPSED_TIME = 0x30240500
|
||||
PERF_COUNTER_NODATA = 0x40000200
|
||||
PERF_AVERAGE_BULK = 0x40020500
|
||||
PERF_SAMPLE_BASE = 0x40030401
|
||||
PERF_AVERAGE_BASE = 0x40030402
|
||||
PERF_RAW_BASE = 0x40030403
|
||||
PERF_PRECISION_TIMESTAMP = 0x40030500
|
||||
PERF_LARGE_RAW_BASE = 0x40030503
|
||||
PERF_COUNTER_MULTI_BASE = 0x42030500
|
||||
PERF_COUNTER_HISTOGRAM_TYPE = 0x80000000
|
||||
)
|
||||
|
||||
var supportedCounterTypes = map[uint32]prometheus.ValueType{
|
||||
PERF_COUNTER_RAWCOUNT_HEX: prometheus.GaugeValue,
|
||||
PERF_COUNTER_LARGE_RAWCOUNT_HEX: prometheus.GaugeValue,
|
||||
PERF_COUNTER_RAWCOUNT: prometheus.GaugeValue,
|
||||
PERF_COUNTER_LARGE_RAWCOUNT: prometheus.GaugeValue,
|
||||
PERF_COUNTER_DELTA: prometheus.CounterValue,
|
||||
PERF_COUNTER_COUNTER: prometheus.CounterValue,
|
||||
PERF_COUNTER_BULK_COUNT: prometheus.CounterValue,
|
||||
PERF_RAW_FRACTION: prometheus.GaugeValue,
|
||||
PERF_LARGE_RAW_FRACTION: prometheus.GaugeValue,
|
||||
PERF_100NSEC_TIMER: prometheus.CounterValue,
|
||||
PERF_PRECISION_100NS_TIMER: prometheus.CounterValue,
|
||||
PERF_SAMPLE_FRACTION: prometheus.GaugeValue,
|
||||
PERF_100NSEC_TIMER_INV: prometheus.CounterValue,
|
||||
PERF_ELAPSED_TIME: prometheus.GaugeValue,
|
||||
PERF_SAMPLE_BASE: prometheus.GaugeValue,
|
||||
PERF_RAW_BASE: prometheus.GaugeValue,
|
||||
PERF_LARGE_RAW_BASE: prometheus.GaugeValue,
|
||||
}
|
||||
18
pkg/perfdata/error.go
Normal file
18
pkg/perfdata/error.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package perfdata
|
||||
|
||||
// Error represents error returned from Performance Counters API.
|
||||
type Error struct {
|
||||
ErrorCode uint32
|
||||
errorText string
|
||||
}
|
||||
|
||||
func (m *Error) Error() string {
|
||||
return m.errorText
|
||||
}
|
||||
|
||||
func NewPdhError(code uint32) error {
|
||||
return &Error{
|
||||
ErrorCode: code,
|
||||
errorText: PdhFormatError(code),
|
||||
}
|
||||
}
|
||||
638
pkg/perfdata/pdh.go
Normal file
638
pkg/perfdata/pdh.go
Normal file
@@ -0,0 +1,638 @@
|
||||
// Copyright (c) 2010-2024 The win Authors. All rights reserved.
|
||||
// Copyright (c) 2024 The prometheus-community Authors. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// 3. The names of the authors may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
|
||||
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// This is the official list of 'win' authors for copyright purposes.
|
||||
//
|
||||
// Alexander Neumann <an2048@googlemail.com>
|
||||
// Joseph Watson <jtwatson@linux-consulting.us>
|
||||
// Kevin Pors <krpors@gmail.com>
|
||||
|
||||
//go:build windows
|
||||
|
||||
package perfdata
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/prometheus-community/windows_exporter/pkg/headers/kernel32"
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// Error codes.
|
||||
const (
|
||||
ErrorSuccess = 0
|
||||
ErrorFailure = 1
|
||||
ErrorInvalidFunction = 1
|
||||
EpochDifferenceMicros int64 = 11644473600000000
|
||||
)
|
||||
|
||||
type (
|
||||
HANDLE uintptr
|
||||
)
|
||||
|
||||
// PDH error codes, which can be returned by all Pdh* functions. Taken from mingw-w64 pdhmsg.h
|
||||
|
||||
const (
|
||||
PdhCstatusValidData = 0x00000000 // The returned data is valid.
|
||||
PdhCstatusNewData = 0x00000001 // The return data value is valid and different from the last sample.
|
||||
PdhCstatusNoMachine = 0x800007D0 // Unable to connect to the specified computer, or the computer is offline.
|
||||
PdhCstatusNoInstance = 0x800007D1
|
||||
PdhMoreData = 0x800007D2 // The PdhGetFormattedCounterArray* function can return this if there's 'more data to be displayed'.
|
||||
PdhCstatusItemNotValidated = 0x800007D3
|
||||
PdhRetry = 0x800007D4
|
||||
PdhNoData = 0x800007D5 // The query does not currently contain any counters (for example, limited access)
|
||||
PdhCalcNegativeDenominator = 0x800007D6
|
||||
PdhCalcNegativeTimebase = 0x800007D7
|
||||
PdhCalcNegativeValue = 0x800007D8
|
||||
PdhDialogCancelled = 0x800007D9
|
||||
PdhEndOfLogFile = 0x800007DA
|
||||
PdhAsyncQueryTimeout = 0x800007DB
|
||||
PdhCannotSetDefaultRealtimeDatasource = 0x800007DC
|
||||
PdhCstatusNoObject = 0xC0000BB8
|
||||
PdhCstatusNoCounter = 0xC0000BB9 // The specified counter could not be found.
|
||||
PdhCstatusInvalidData = 0xC0000BBA // The counter was successfully found, but the data returned is not valid.
|
||||
PdhMemoryAllocationFailure = 0xC0000BBB
|
||||
PdhInvalidHandle = 0xC0000BBC
|
||||
PdhInvalidArgument = 0xC0000BBD // Required argument is missing or incorrect.
|
||||
PdhFunctionNotFound = 0xC0000BBE
|
||||
PdhCstatusNoCountername = 0xC0000BBF
|
||||
PdhCstatusBadCountername = 0xC0000BC0 // Unable to parse the counter path. Check the format and syntax of the specified path.
|
||||
PdhInvalidBuffer = 0xC0000BC1
|
||||
PdhInsufficientBuffer = 0xC0000BC2
|
||||
PdhCannotConnectMachine = 0xC0000BC3
|
||||
PdhInvalidPath = 0xC0000BC4
|
||||
PdhInvalidInstance = 0xC0000BC5
|
||||
PdhInvalidData = 0xC0000BC6 // specified counter does not contain valid data or a successful status code.
|
||||
PdhNoDialogData = 0xC0000BC7
|
||||
PdhCannotReadNameStrings = 0xC0000BC8
|
||||
PdhLogFileCreateError = 0xC0000BC9
|
||||
PdhLogFileOpenError = 0xC0000BCA
|
||||
PdhLogTypeNotFound = 0xC0000BCB
|
||||
PdhNoMoreData = 0xC0000BCC
|
||||
PdhEntryNotInLogFile = 0xC0000BCD
|
||||
PdhDataSourceIsLogFile = 0xC0000BCE
|
||||
PdhDataSourceIsRealTime = 0xC0000BCF
|
||||
PdhUnableReadLogHeader = 0xC0000BD0
|
||||
PdhFileNotFound = 0xC0000BD1
|
||||
PdhFileAlreadyExists = 0xC0000BD2
|
||||
PdhNotImplemented = 0xC0000BD3
|
||||
PdhStringNotFound = 0xC0000BD4
|
||||
PdhUnableMapNameFiles = 0x80000BD5
|
||||
PdhUnknownLogFormat = 0xC0000BD6
|
||||
PdhUnknownLogsvcCommand = 0xC0000BD7
|
||||
PdhLogsvcQueryNotFound = 0xC0000BD8
|
||||
PdhLogsvcNotOpened = 0xC0000BD9
|
||||
PdhWbemError = 0xC0000BDA
|
||||
PdhAccessDenied = 0xC0000BDB
|
||||
PdhLogFileTooSmall = 0xC0000BDC
|
||||
PdhInvalidDatasource = 0xC0000BDD
|
||||
PdhInvalidSqldb = 0xC0000BDE
|
||||
PdhNoCounters = 0xC0000BDF
|
||||
PdhSQLAllocFailed = 0xC0000BE0
|
||||
PdhSQLAllocconFailed = 0xC0000BE1
|
||||
PdhSQLExecDirectFailed = 0xC0000BE2
|
||||
PdhSQLFetchFailed = 0xC0000BE3
|
||||
PdhSQLRowcountFailed = 0xC0000BE4
|
||||
PdhSQLMoreResultsFailed = 0xC0000BE5
|
||||
PdhSQLConnectFailed = 0xC0000BE6
|
||||
PdhSQLBindFailed = 0xC0000BE7
|
||||
PdhCannotConnectWmiServer = 0xC0000BE8
|
||||
PdhPlaCollectionAlreadyRunning = 0xC0000BE9
|
||||
PdhPlaErrorScheduleOverlap = 0xC0000BEA
|
||||
PdhPlaCollectionNotFound = 0xC0000BEB
|
||||
PdhPlaErrorScheduleElapsed = 0xC0000BEC
|
||||
PdhPlaErrorNostart = 0xC0000BED
|
||||
PdhPlaErrorAlreadyExists = 0xC0000BEE
|
||||
PdhPlaErrorTypeMismatch = 0xC0000BEF
|
||||
PdhPlaErrorFilepath = 0xC0000BF0
|
||||
PdhPlaServiceError = 0xC0000BF1
|
||||
PdhPlaValidationError = 0xC0000BF2
|
||||
PdhPlaValidationWarning = 0x80000BF3
|
||||
PdhPlaErrorNameTooLong = 0xC0000BF4
|
||||
PdhInvalidSQLLogFormat = 0xC0000BF5
|
||||
PdhCounterAlreadyInQuery = 0xC0000BF6
|
||||
PdhBinaryLogCorrupt = 0xC0000BF7
|
||||
PdhLogSampleTooSmall = 0xC0000BF8
|
||||
PdhOsLaterVersion = 0xC0000BF9
|
||||
PdhOsEarlierVersion = 0xC0000BFA
|
||||
PdhIncorrectAppendTime = 0xC0000BFB
|
||||
PdhUnmatchedAppendCounter = 0xC0000BFC
|
||||
PdhSQLAlterDetailFailed = 0xC0000BFD
|
||||
PdhQueryPerfDataTimeout = 0xC0000BFE
|
||||
)
|
||||
|
||||
var PDHErrors = map[uint32]string{
|
||||
PdhCstatusValidData: "PDH_CSTATUS_VALID_DATA",
|
||||
PdhCstatusNewData: "PDH_CSTATUS_NEW_DATA",
|
||||
PdhCstatusNoMachine: "PDH_CSTATUS_NO_MACHINE",
|
||||
PdhCstatusNoInstance: "PDH_CSTATUS_NO_INSTANCE",
|
||||
PdhMoreData: "PDH_MORE_DATA",
|
||||
PdhCstatusItemNotValidated: "PDH_CSTATUS_ITEM_NOT_VALIDATED",
|
||||
PdhRetry: "PDH_RETRY",
|
||||
PdhNoData: "PDH_NO_DATA",
|
||||
PdhCalcNegativeDenominator: "PDH_CALC_NEGATIVE_DENOMINATOR",
|
||||
PdhCalcNegativeTimebase: "PDH_CALC_NEGATIVE_TIMEBASE",
|
||||
PdhCalcNegativeValue: "PDH_CALC_NEGATIVE_VALUE",
|
||||
PdhDialogCancelled: "PDH_DIALOG_CANCELLED",
|
||||
PdhEndOfLogFile: "PDH_END_OF_LOG_FILE",
|
||||
PdhAsyncQueryTimeout: "PDH_ASYNC_QUERY_TIMEOUT",
|
||||
PdhCannotSetDefaultRealtimeDatasource: "PDH_CANNOT_SET_DEFAULT_REALTIME_DATASOURCE",
|
||||
PdhCstatusNoObject: "PDH_CSTATUS_NO_OBJECT",
|
||||
PdhCstatusNoCounter: "PDH_CSTATUS_NO_COUNTER",
|
||||
PdhCstatusInvalidData: "PDH_CSTATUS_INVALID_DATA",
|
||||
PdhMemoryAllocationFailure: "PDH_MEMORY_ALLOCATION_FAILURE",
|
||||
PdhInvalidHandle: "PDH_INVALID_HANDLE",
|
||||
PdhInvalidArgument: "PDH_INVALID_ARGUMENT",
|
||||
PdhFunctionNotFound: "PDH_FUNCTION_NOT_FOUND",
|
||||
PdhCstatusNoCountername: "PDH_CSTATUS_NO_COUNTERNAME",
|
||||
PdhCstatusBadCountername: "PDH_CSTATUS_BAD_COUNTERNAME",
|
||||
PdhInvalidBuffer: "PDH_INVALID_BUFFER",
|
||||
PdhInsufficientBuffer: "PDH_INSUFFICIENT_BUFFER",
|
||||
PdhCannotConnectMachine: "PDH_CANNOT_CONNECT_MACHINE",
|
||||
PdhInvalidPath: "PDH_INVALID_PATH",
|
||||
PdhInvalidInstance: "PDH_INVALID_INSTANCE",
|
||||
PdhInvalidData: "PDH_INVALID_DATA",
|
||||
PdhNoDialogData: "PDH_NO_DIALOG_DATA",
|
||||
PdhCannotReadNameStrings: "PDH_CANNOT_READ_NAME_STRINGS",
|
||||
PdhLogFileCreateError: "PDH_LOG_FILE_CREATE_ERROR",
|
||||
PdhLogFileOpenError: "PDH_LOG_FILE_OPEN_ERROR",
|
||||
PdhLogTypeNotFound: "PDH_LOG_TYPE_NOT_FOUND",
|
||||
PdhNoMoreData: "PDH_NO_MORE_DATA",
|
||||
PdhEntryNotInLogFile: "PDH_ENTRY_NOT_IN_LOG_FILE",
|
||||
PdhDataSourceIsLogFile: "PDH_DATA_SOURCE_IS_LOG_FILE",
|
||||
PdhDataSourceIsRealTime: "PDH_DATA_SOURCE_IS_REAL_TIME",
|
||||
PdhUnableReadLogHeader: "PDH_UNABLE_READ_LOG_HEADER",
|
||||
PdhFileNotFound: "PDH_FILE_NOT_FOUND",
|
||||
PdhFileAlreadyExists: "PDH_FILE_ALREADY_EXISTS",
|
||||
PdhNotImplemented: "PDH_NOT_IMPLEMENTED",
|
||||
PdhStringNotFound: "PDH_STRING_NOT_FOUND",
|
||||
PdhUnableMapNameFiles: "PDH_UNABLE_MAP_NAME_FILES",
|
||||
PdhUnknownLogFormat: "PDH_UNKNOWN_LOG_FORMAT",
|
||||
PdhUnknownLogsvcCommand: "PDH_UNKNOWN_LOGSVC_COMMAND",
|
||||
PdhLogsvcQueryNotFound: "PDH_LOGSVC_QUERY_NOT_FOUND",
|
||||
PdhLogsvcNotOpened: "PDH_LOGSVC_NOT_OPENED",
|
||||
PdhWbemError: "PDH_WBEM_ERROR",
|
||||
PdhAccessDenied: "PDH_ACCESS_DENIED",
|
||||
PdhLogFileTooSmall: "PDH_LOG_FILE_TOO_SMALL",
|
||||
PdhInvalidDatasource: "PDH_INVALID_DATASOURCE",
|
||||
PdhInvalidSqldb: "PDH_INVALID_SQLDB",
|
||||
PdhNoCounters: "PDH_NO_COUNTERS",
|
||||
PdhSQLAllocFailed: "PDH_SQL_ALLOC_FAILED",
|
||||
PdhSQLAllocconFailed: "PDH_SQL_ALLOCCON_FAILED",
|
||||
PdhSQLExecDirectFailed: "PDH_SQL_EXEC_DIRECT_FAILED",
|
||||
PdhSQLFetchFailed: "PDH_SQL_FETCH_FAILED",
|
||||
PdhSQLRowcountFailed: "PDH_SQL_ROWCOUNT_FAILED",
|
||||
PdhSQLMoreResultsFailed: "PDH_SQL_MORE_RESULTS_FAILED",
|
||||
PdhSQLConnectFailed: "PDH_SQL_CONNECT_FAILED",
|
||||
PdhSQLBindFailed: "PDH_SQL_BIND_FAILED",
|
||||
PdhCannotConnectWmiServer: "PDH_CANNOT_CONNECT_WMI_SERVER",
|
||||
PdhPlaCollectionAlreadyRunning: "PDH_PLA_COLLECTION_ALREADY_RUNNING",
|
||||
PdhPlaErrorScheduleOverlap: "PDH_PLA_ERROR_SCHEDULE_OVERLAP",
|
||||
PdhPlaCollectionNotFound: "PDH_PLA_COLLECTION_NOT_FOUND",
|
||||
PdhPlaErrorScheduleElapsed: "PDH_PLA_ERROR_SCHEDULE_ELAPSED",
|
||||
PdhPlaErrorNostart: "PDH_PLA_ERROR_NOSTART",
|
||||
PdhPlaErrorAlreadyExists: "PDH_PLA_ERROR_ALREADY_EXISTS",
|
||||
PdhPlaErrorTypeMismatch: "PDH_PLA_ERROR_TYPE_MISMATCH",
|
||||
PdhPlaErrorFilepath: "PDH_PLA_ERROR_FILEPATH",
|
||||
PdhPlaServiceError: "PDH_PLA_SERVICE_ERROR",
|
||||
PdhPlaValidationError: "PDH_PLA_VALIDATION_ERROR",
|
||||
PdhPlaValidationWarning: "PDH_PLA_VALIDATION_WARNING",
|
||||
PdhPlaErrorNameTooLong: "PDH_PLA_ERROR_NAME_TOO_LONG",
|
||||
PdhInvalidSQLLogFormat: "PDH_INVALID_SQL_LOG_FORMAT",
|
||||
PdhCounterAlreadyInQuery: "PDH_COUNTER_ALREADY_IN_QUERY",
|
||||
PdhBinaryLogCorrupt: "PDH_BINARY_LOG_CORRUPT",
|
||||
PdhLogSampleTooSmall: "PDH_LOG_SAMPLE_TOO_SMALL",
|
||||
PdhOsLaterVersion: "PDH_OS_LATER_VERSION",
|
||||
PdhOsEarlierVersion: "PDH_OS_EARLIER_VERSION",
|
||||
PdhIncorrectAppendTime: "PDH_INCORRECT_APPEND_TIME",
|
||||
PdhUnmatchedAppendCounter: "PDH_UNMATCHED_APPEND_COUNTER",
|
||||
PdhSQLAlterDetailFailed: "PDH_SQL_ALTER_DETAIL_FAILED",
|
||||
PdhQueryPerfDataTimeout: "PDH_QUERY_PERF_DATA_TIMEOUT",
|
||||
}
|
||||
|
||||
// Formatting options for GetFormattedCounterValue().
|
||||
//
|
||||
//goland:noinspection GoUnusedConst
|
||||
const (
|
||||
PdhFmtRaw = 0x00000010
|
||||
PdhFmtAnsi = 0x00000020
|
||||
PdhFmtUnicode = 0x00000040
|
||||
PdhFmtLong = 0x00000100 // Return data as a long int.
|
||||
PdhFmtDouble = 0x00000200 // Return data as a double precision floating point real.
|
||||
PdhFmtLarge = 0x00000400 // Return data as a 64 bit integer.
|
||||
PdhFmtNoscale = 0x00001000 // can be OR-ed: Do not apply the counter's default scaling factor.
|
||||
PdhFmt1000 = 0x00002000 // can be OR-ed: multiply the actual value by 1,000.
|
||||
PdhFmtNodata = 0x00004000 // can be OR-ed: unknown what this is for, MSDN says nothing.
|
||||
PdhFmtNocap100 = 0x00008000 // can be OR-ed: do not cap values > 100.
|
||||
PerfDetailCostly = 0x00010000
|
||||
PerfDetailStandard = 0x0000FFFF
|
||||
)
|
||||
|
||||
type (
|
||||
pdhQueryHandle HANDLE // query handle
|
||||
pdhCounterHandle HANDLE // counter handle
|
||||
)
|
||||
|
||||
var (
|
||||
libPdhDll = windows.NewLazySystemDLL("pdh.dll")
|
||||
|
||||
pdhAddCounterW = libPdhDll.NewProc("PdhAddCounterW")
|
||||
pdhAddEnglishCounterW = libPdhDll.NewProc("PdhAddEnglishCounterW")
|
||||
pdhCloseQuery = libPdhDll.NewProc("PdhCloseQuery")
|
||||
pdhCollectQueryData = libPdhDll.NewProc("PdhCollectQueryData")
|
||||
pdhCollectQueryDataWithTime = libPdhDll.NewProc("PdhCollectQueryDataWithTime")
|
||||
pdhGetFormattedCounterValue = libPdhDll.NewProc("PdhGetFormattedCounterValue")
|
||||
pdhGetFormattedCounterArrayW = libPdhDll.NewProc("PdhGetFormattedCounterArrayW")
|
||||
pdhOpenQuery = libPdhDll.NewProc("PdhOpenQuery")
|
||||
pdhValidatePathW = libPdhDll.NewProc("PdhValidatePathW")
|
||||
pdhExpandWildCardPathW = libPdhDll.NewProc("PdhExpandWildCardPathW")
|
||||
pdhGetCounterInfoW = libPdhDll.NewProc("PdhGetCounterInfoW")
|
||||
pdhGetRawCounterValue = libPdhDll.NewProc("PdhGetRawCounterValue")
|
||||
pdhGetRawCounterArrayW = libPdhDll.NewProc("PdhGetRawCounterArrayW")
|
||||
pdhPdhGetCounterTimeBase = libPdhDll.NewProc("PdhGetCounterTimeBase")
|
||||
)
|
||||
|
||||
// PdhAddCounter adds the specified counter to the query. This is the internationalized version. Preferably, use the
|
||||
// function PdhAddEnglishCounter instead. hQuery is the query handle, which has been fetched by PdhOpenQuery.
|
||||
// szFullCounterPath is a full, internationalized counter path (this will differ per Windows language version).
|
||||
// dwUserData is a 'user-defined value', which becomes part of the counter information. To retrieve this value
|
||||
// later, call PdhGetCounterInfo() and access dwQueryUserData of the PdhCounterInfo structure.
|
||||
//
|
||||
// Examples of szFullCounterPath (in an English version of Windows):
|
||||
//
|
||||
// \\Processor(_Total)\\% Idle Time
|
||||
// \\Processor(_Total)\\% Processor Time
|
||||
// \\LogicalDisk(C:)\% Free Space
|
||||
//
|
||||
// To view all (internationalized...) counters on a system, there are three non-programmatic ways: perfmon utility,
|
||||
// the typeperf command, and the registry editor. perfmon.exe is perhaps the easiest way, because it's basically a
|
||||
// full implementation of the pdh.dll API, except with a GUI and all that. The registry setting also provides an
|
||||
// interface to the available counters, and can be found at the following key:
|
||||
//
|
||||
// HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Perflib\CurrentLanguage
|
||||
//
|
||||
// This registry key contains several values as follows:
|
||||
//
|
||||
// 1
|
||||
// 1847
|
||||
// 2
|
||||
// System
|
||||
// 4
|
||||
// Memory
|
||||
// 6
|
||||
// % Processor Time
|
||||
// ... many, many more
|
||||
//
|
||||
// Somehow, these numeric values can be used as szFullCounterPath too:
|
||||
//
|
||||
// \2\6 will correspond to \\System\% Processor Time
|
||||
//
|
||||
// The typeperf command may also be pretty easy. To find all performance counters, simply execute:
|
||||
//
|
||||
// typeperf -qx
|
||||
func PdhAddCounter(hQuery pdhQueryHandle, szFullCounterPath string, dwUserData uintptr, phCounter *pdhCounterHandle) uint32 {
|
||||
ptxt, _ := windows.UTF16PtrFromString(szFullCounterPath)
|
||||
ret, _, _ := pdhAddCounterW.Call(
|
||||
uintptr(hQuery),
|
||||
uintptr(unsafe.Pointer(ptxt)),
|
||||
dwUserData,
|
||||
uintptr(unsafe.Pointer(phCounter)))
|
||||
|
||||
return uint32(ret)
|
||||
}
|
||||
|
||||
// PdhAddEnglishCounterSupported returns true if PdhAddEnglishCounterW Win API function was found in pdh.dll.
|
||||
// PdhAddEnglishCounterW function is not supported on pre-Windows Vista systems.
|
||||
func PdhAddEnglishCounterSupported() bool {
|
||||
return pdhAddEnglishCounterW != nil
|
||||
}
|
||||
|
||||
// PdhAddEnglishCounter adds the specified language-neutral counter to the query. See the PdhAddCounter function. This function only exists on
|
||||
// Windows versions higher than Vista.
|
||||
func PdhAddEnglishCounter(hQuery pdhQueryHandle, szFullCounterPath string, dwUserData uintptr, phCounter *pdhCounterHandle) uint32 {
|
||||
if pdhAddEnglishCounterW == nil {
|
||||
return ErrorInvalidFunction
|
||||
}
|
||||
|
||||
ptxt, _ := windows.UTF16PtrFromString(szFullCounterPath)
|
||||
ret, _, _ := pdhAddEnglishCounterW.Call(
|
||||
uintptr(hQuery),
|
||||
uintptr(unsafe.Pointer(ptxt)),
|
||||
dwUserData,
|
||||
uintptr(unsafe.Pointer(phCounter)))
|
||||
|
||||
return uint32(ret)
|
||||
}
|
||||
|
||||
// PdhCloseQuery closes all counters contained in the specified query, closes all handles related to the query,
|
||||
// and frees all memory associated with the query.
|
||||
func PdhCloseQuery(hQuery pdhQueryHandle) uint32 {
|
||||
ret, _, _ := pdhCloseQuery.Call(uintptr(hQuery))
|
||||
|
||||
return uint32(ret)
|
||||
}
|
||||
|
||||
// PdhCollectQueryData collects the current raw data value for all counters in the specified query and updates the status
|
||||
// code of each counter. With some counters, this function needs to be repeatedly called before the value
|
||||
// of the counter can be extracted with PdhGetFormattedCounterValue(). For example, the following code
|
||||
// requires at least two calls:
|
||||
//
|
||||
// var handle win.PDH_HQUERY
|
||||
// var counterHandle win.PDH_HCOUNTER
|
||||
// ret := win.PdhOpenQuery(0, 0, &handle)
|
||||
// ret = win.PdhAddEnglishCounter(handle, "\\Processor(_Total)\\% Idle Time", 0, &counterHandle)
|
||||
// var derp win.PDH_FMT_COUNTERVALUE_DOUBLE
|
||||
//
|
||||
// ret = win.PdhCollectQueryData(handle)
|
||||
// fmt.Printf("Collect return code is %x\n", ret) // return code will be PDH_CSTATUS_INVALID_DATA
|
||||
// ret = win.PdhGetFormattedCounterValueDouble(counterHandle, 0, &derp)
|
||||
//
|
||||
// ret = win.PdhCollectQueryData(handle)
|
||||
// fmt.Printf("Collect return code is %x\n", ret) // return code will be ERROR_SUCCESS
|
||||
// ret = win.PdhGetFormattedCounterValueDouble(counterHandle, 0, &derp)
|
||||
//
|
||||
// The PdhCollectQueryData will return an error in the first call because it needs two values for
|
||||
// displaying the correct data for the processor idle time. The second call will have a 0 return code.
|
||||
func PdhCollectQueryData(hQuery pdhQueryHandle) uint32 {
|
||||
ret, _, _ := pdhCollectQueryData.Call(uintptr(hQuery))
|
||||
|
||||
return uint32(ret)
|
||||
}
|
||||
|
||||
// PdhCollectQueryDataWithTime queries data from perfmon, retrieving the device/windows timestamp from the node it was collected on.
|
||||
// Converts the filetime structure to a GO time class and returns the native time.
|
||||
func PdhCollectQueryDataWithTime(hQuery pdhQueryHandle) (uint32, time.Time) {
|
||||
var localFileTime windows.Filetime
|
||||
|
||||
ret, _, _ := pdhCollectQueryDataWithTime.Call(uintptr(hQuery), uintptr(unsafe.Pointer(&localFileTime)))
|
||||
|
||||
if ret == ErrorSuccess {
|
||||
var utcFileTime windows.Filetime
|
||||
|
||||
if ret := kernel32.LocalFileTimeToFileTime(&localFileTime, &utcFileTime); ret == 0 {
|
||||
return uint32(ErrorFailure), time.Now()
|
||||
}
|
||||
|
||||
retTime := time.Unix(0, utcFileTime.Nanoseconds())
|
||||
|
||||
return uint32(ErrorSuccess), retTime
|
||||
}
|
||||
|
||||
return uint32(ret), time.Now()
|
||||
}
|
||||
|
||||
// PdhGetFormattedCounterValueDouble formats the given hCounter using a 'double'. The result is set into the specialized union struct pValue.
|
||||
// This function does not directly translate to a Windows counterpart due to union specialization tricks.
|
||||
func PdhGetFormattedCounterValueDouble(hCounter pdhCounterHandle, lpdwType *uint32, pValue *PdhFmtCountervalueDouble) uint32 {
|
||||
ret, _, _ := pdhGetFormattedCounterValue.Call(
|
||||
uintptr(hCounter),
|
||||
uintptr(PdhFmtDouble|PdhFmtNocap100),
|
||||
uintptr(unsafe.Pointer(lpdwType)),
|
||||
uintptr(unsafe.Pointer(pValue)))
|
||||
|
||||
return uint32(ret)
|
||||
}
|
||||
|
||||
// PdhGetFormattedCounterArrayDouble returns an array of formatted counter values. Use this function when you want to format the counter values of a
|
||||
// counter that contains a wildcard character for the instance name. The itemBuffer must a slice of type PdhFmtCountervalueItemDouble.
|
||||
// An example of how this function can be used:
|
||||
//
|
||||
// okPath := "\\Process(*)\\% Processor Time" // notice the wildcard * character
|
||||
//
|
||||
// // omitted all necessary stuff ...
|
||||
//
|
||||
// var bufSize uint32
|
||||
// var bufCount uint32
|
||||
// var size uint32 = uint32(unsafe.Sizeof(win.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE{}))
|
||||
// var emptyBuf [1]win.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE // need at least 1 addressable null ptr.
|
||||
//
|
||||
// for {
|
||||
// // collect
|
||||
// ret := win.PdhCollectQueryData(queryHandle)
|
||||
// if ret == win.ERROR_SUCCESS {
|
||||
// ret = win.PdhGetFormattedCounterArrayDouble(counterHandle, &bufSize, &bufCount, &emptyBuf[0]) // uses null ptr here according to MSDN.
|
||||
// if ret == win.PDH_MORE_DATA {
|
||||
// filledBuf := make([]win.PDH_FMT_COUNTERVALUE_ITEM_DOUBLE, bufCount*size)
|
||||
// ret = win.PdhGetFormattedCounterArrayDouble(counterHandle, &bufSize, &bufCount, &filledBuf[0])
|
||||
// for i := 0; i < int(bufCount); i++ {
|
||||
// c := filledBuf[i]
|
||||
// var s string = win.UTF16PtrToString(c.SzName)
|
||||
// fmt.Printf("Index %d -> %s, value %v\n", i, s, c.FmtValue.DoubleValue)
|
||||
// }
|
||||
//
|
||||
// filledBuf = nil
|
||||
// // Need to at least set bufSize to zero, because if not, the function will not
|
||||
// // return PDH_MORE_DATA and will not set the bufSize.
|
||||
// bufCount = 0
|
||||
// bufSize = 0
|
||||
// }
|
||||
//
|
||||
// time.Sleep(2000 * time.Millisecond)
|
||||
// }
|
||||
// }
|
||||
func PdhGetFormattedCounterArrayDouble(hCounter pdhCounterHandle, lpdwBufferSize *uint32, lpdwBufferCount *uint32, itemBuffer *byte) uint32 {
|
||||
ret, _, _ := pdhGetFormattedCounterArrayW.Call(
|
||||
uintptr(hCounter),
|
||||
uintptr(PdhFmtDouble|PdhFmtNocap100),
|
||||
uintptr(unsafe.Pointer(lpdwBufferSize)),
|
||||
uintptr(unsafe.Pointer(lpdwBufferCount)),
|
||||
uintptr(unsafe.Pointer(itemBuffer)))
|
||||
|
||||
return uint32(ret)
|
||||
}
|
||||
|
||||
// PdhOpenQuery creates a new query that is used to manage the collection of performance data.
|
||||
// szDataSource is a null terminated string that specifies the name of the log file from which to
|
||||
// retrieve the performance data. If 0, performance data is collected from a real-time data source.
|
||||
// dwUserData is a user-defined value to associate with this query. To retrieve the user data later,
|
||||
// call PdhGetCounterInfo and access dwQueryUserData of the PdhCounterInfo structure. phQuery is
|
||||
// the handle to the query, and must be used in subsequent calls. This function returns a PDH_
|
||||
// constant error code, or ErrorSuccess if the call succeeded.
|
||||
func PdhOpenQuery(szDataSource uintptr, dwUserData uintptr, phQuery *pdhQueryHandle) uint32 {
|
||||
ret, _, _ := pdhOpenQuery.Call(
|
||||
szDataSource,
|
||||
dwUserData,
|
||||
uintptr(unsafe.Pointer(phQuery)))
|
||||
|
||||
return uint32(ret)
|
||||
}
|
||||
|
||||
// PdhExpandWildCardPath examines the specified computer or log file and returns those counter paths that match the given counter path
|
||||
// which contains wildcard characters. The general counter path format is as follows:
|
||||
//
|
||||
// \\computer\object(parent/instance#index)\counter
|
||||
//
|
||||
// The parent, instance, index, and counter components of the counter path may contain either a valid name or a wildcard character.
|
||||
// The computer, parent, instance, and index components are not necessary for all counters.
|
||||
//
|
||||
// The following is a list of the possible formats:
|
||||
//
|
||||
// \\computer\object(parent/instance#index)\counter
|
||||
// \\computer\object(parent/instance)\counter
|
||||
// \\computer\object(instance#index)\counter
|
||||
// \\computer\object(instance)\counter
|
||||
// \\computer\object\counter
|
||||
// \object(parent/instance#index)\counter
|
||||
// \object(parent/instance)\counter
|
||||
// \object(instance#index)\counter
|
||||
// \object(instance)\counter
|
||||
// \object\counter
|
||||
// Use an asterisk (*) as the wildcard character, for example, \object(*)\counter.
|
||||
//
|
||||
// If a wildcard character is specified in the parent name, all instances of the specified object
|
||||
// that match the specified instance and counter fields will be returned.
|
||||
// For example, \object(*/instance)\counter.
|
||||
//
|
||||
// If a wildcard character is specified in the instance name, all instances of the specified object and parent object will be returned if all instance names
|
||||
// corresponding to the specified index match the wildcard character. For example, \object(parent/*)\counter.
|
||||
// If the object does not contain an instance, an error occurs.
|
||||
//
|
||||
// If a wildcard character is specified in the counter name, all counters of the specified object are returned.
|
||||
//
|
||||
// Partial counter path string matches (for example, "pro*") are supported.
|
||||
func PdhExpandWildCardPath(szWildCardPath string, mszExpandedPathList *uint16, pcchPathListLength *uint32) uint32 {
|
||||
ptxt, _ := windows.UTF16PtrFromString(szWildCardPath)
|
||||
flags := uint32(0) // expand instances and counters
|
||||
ret, _, _ := pdhExpandWildCardPathW.Call(
|
||||
0, // search counters on local computer
|
||||
uintptr(unsafe.Pointer(ptxt)),
|
||||
uintptr(unsafe.Pointer(mszExpandedPathList)),
|
||||
uintptr(unsafe.Pointer(pcchPathListLength)),
|
||||
uintptr(unsafe.Pointer(&flags)))
|
||||
|
||||
return uint32(ret)
|
||||
}
|
||||
|
||||
// PdhValidatePath validates a path. Will return ErrorSuccess when ok, or PdhCstatusBadCountername when the path is erroneous.
|
||||
func PdhValidatePath(path string) uint32 {
|
||||
ptxt, _ := windows.UTF16PtrFromString(path)
|
||||
ret, _, _ := pdhValidatePathW.Call(uintptr(unsafe.Pointer(ptxt)))
|
||||
|
||||
return uint32(ret)
|
||||
}
|
||||
|
||||
func PdhFormatError(msgID uint32) string {
|
||||
var flags uint32 = windows.FORMAT_MESSAGE_FROM_HMODULE | windows.FORMAT_MESSAGE_ARGUMENT_ARRAY | windows.FORMAT_MESSAGE_IGNORE_INSERTS
|
||||
|
||||
buf := make([]uint16, 300)
|
||||
_, err := windows.FormatMessage(flags, libPdhDll.Handle(), msgID, 0, buf, nil)
|
||||
|
||||
if err == nil {
|
||||
return windows.UTF16PtrToString(&buf[0])
|
||||
}
|
||||
|
||||
return fmt.Sprintf("(pdhErr=%d) %s", msgID, err.Error())
|
||||
}
|
||||
|
||||
// PdhGetCounterInfo retrieves information about a counter, such as data size, counter type, path, and user-supplied data values
|
||||
// hCounter [in]
|
||||
// Handle of the counter from which you want to retrieve information. The PdhAddCounter function returns this handle.
|
||||
//
|
||||
// bRetrieveExplainText [in]
|
||||
// Determines whether explain text is retrieved. If you set this parameter to TRUE, the explain text for the counter is retrieved.
|
||||
// If you set this parameter to FALSE, the field in the returned buffer is NULL.
|
||||
//
|
||||
// pdwBufferSize [in, out]
|
||||
// Size of the lpBuffer buffer, in bytes. If zero on input, the function returns PdhMoreData and sets this parameter to the required buffer size.
|
||||
// If the buffer is larger than the required size, the function sets this parameter to the actual size of the buffer that was used.
|
||||
// If the specified size on input is greater than zero but less than the required size, you should not rely on the returned size to reallocate the buffer.
|
||||
//
|
||||
// lpBuffer [out]
|
||||
// Caller-allocated buffer that receives a PdhCounterInfo structure.
|
||||
// The structure is variable-length, because the string data is appended to the end of the fixed-format portion of the structure.
|
||||
// This is done so that all data is returned in a single buffer allocated by the caller. Set to NULL if pdwBufferSize is zero.
|
||||
func PdhGetCounterInfo(hCounter pdhCounterHandle, bRetrieveExplainText int, pdwBufferSize *uint32, lpBuffer *byte) uint32 {
|
||||
ret, _, _ := pdhGetCounterInfoW.Call(
|
||||
uintptr(hCounter),
|
||||
uintptr(bRetrieveExplainText),
|
||||
uintptr(unsafe.Pointer(pdwBufferSize)),
|
||||
uintptr(unsafe.Pointer(lpBuffer)))
|
||||
|
||||
return uint32(ret)
|
||||
}
|
||||
|
||||
// PdhGetRawCounterValue returns the current raw value of the counter.
|
||||
// If the specified counter instance does not exist, this function will return ErrorSuccess
|
||||
// and the CStatus member of the PdhRawCounter structure will contain PdhCstatusNoInstance.
|
||||
//
|
||||
// hCounter [in]
|
||||
// Handle of the counter from which to retrieve the current raw value. The PdhAddCounter function returns this handle.
|
||||
//
|
||||
// lpdwType [out]
|
||||
// Receives the counter type. For a list of counter types, see the Counter Types section of the Windows Server 2003 Deployment Kit.
|
||||
// This parameter is optional.
|
||||
//
|
||||
// pValue [out]
|
||||
// A PdhRawCounter structure that receives the counter value.
|
||||
func PdhGetRawCounterValue(hCounter pdhCounterHandle, lpdwType *uint32, pValue *PdhRawCounter) uint32 {
|
||||
ret, _, _ := pdhGetRawCounterValue.Call(
|
||||
uintptr(hCounter),
|
||||
uintptr(unsafe.Pointer(lpdwType)),
|
||||
uintptr(unsafe.Pointer(pValue)))
|
||||
|
||||
return uint32(ret)
|
||||
}
|
||||
|
||||
// PdhGetRawCounterArray returns an array of raw values from the specified counter. Use this function when you want to retrieve the raw counter values
|
||||
// of a counter that contains a wildcard character for the instance name.
|
||||
// hCounter
|
||||
// Handle of the counter for whose current raw instance values you want to retrieve. The PdhAddCounter function returns this handle.
|
||||
//
|
||||
// lpdwBufferSize
|
||||
// Size of the ItemBuffer buffer, in bytes. If zero on input, the function returns PdhMoreData and sets this parameter to the required buffer size.
|
||||
// If the buffer is larger than the required size, the function sets this parameter to the actual size of the buffer that was used.
|
||||
// If the specified size on input is greater than zero but less than the required size, you should not rely on the returned size to reallocate the buffer.
|
||||
//
|
||||
// lpdwItemCount
|
||||
// Number of raw counter values in the ItemBuffer buffer.
|
||||
//
|
||||
// ItemBuffer
|
||||
// Caller-allocated buffer that receives the array of PdhRawCounterItem structures; the structures contain the raw instance counter values.
|
||||
// Set to NULL if lpdwBufferSize is zero.
|
||||
func PdhGetRawCounterArray(hCounter pdhCounterHandle, lpdwBufferSize *uint32, lpdwBufferCount *uint32, itemBuffer *byte) uint32 {
|
||||
ret, _, _ := pdhGetRawCounterArrayW.Call(
|
||||
uintptr(hCounter),
|
||||
uintptr(unsafe.Pointer(lpdwBufferSize)),
|
||||
uintptr(unsafe.Pointer(lpdwBufferCount)),
|
||||
uintptr(unsafe.Pointer(itemBuffer)))
|
||||
|
||||
return uint32(ret)
|
||||
}
|
||||
|
||||
// PdhGetCounterTimeBase returns the time base of the specified counter.
|
||||
// hCounter
|
||||
// Handle of the counter for whose current raw instance values you want to retrieve. The PdhAddCounter function returns this handle.
|
||||
//
|
||||
// lpdwItemCount
|
||||
// Time base that specifies the number of performance values a counter samples per second.
|
||||
func PdhGetCounterTimeBase(hCounter pdhCounterHandle, pTimeBase *float64) uint32 {
|
||||
ret, _, _ := pdhPdhGetCounterTimeBase.Call(
|
||||
uintptr(hCounter),
|
||||
uintptr(unsafe.Pointer(pTimeBase)))
|
||||
|
||||
return uint32(ret)
|
||||
}
|
||||
143
pkg/perfdata/pdh_amd64.go
Normal file
143
pkg/perfdata/pdh_amd64.go
Normal file
@@ -0,0 +1,143 @@
|
||||
// Copyright (c) 2010-2024 The win Authors. All rights reserved.
|
||||
// Copyright (c) 2024 The prometheus-community Authors. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// 3. The names of the authors may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
|
||||
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// This is the official list of 'win' authors for copyright purposes.
|
||||
//
|
||||
// Alexander Neumann <an2048@googlemail.com>
|
||||
// Joseph Watson <jtwatson@linux-consulting.us>
|
||||
// Kevin Pors <krpors@gmail.com>
|
||||
|
||||
//go:build windows
|
||||
|
||||
package perfdata
|
||||
|
||||
import "golang.org/x/sys/windows"
|
||||
|
||||
// PdhFmtCountervalueDouble is a union specialization for double values.
|
||||
type PdhFmtCountervalueDouble struct {
|
||||
CStatus uint32
|
||||
DoubleValue float64
|
||||
}
|
||||
|
||||
// PdhFmtCounterValueLarge is a union specialization for 64-bit integer values.
|
||||
type PdhFmtCounterValueLarge struct {
|
||||
CStatus uint32
|
||||
LargeValue int64
|
||||
}
|
||||
|
||||
// PdhFmtCounterValueLong is a union specialization for long values.
|
||||
type PdhFmtCounterValueLong struct {
|
||||
CStatus uint32
|
||||
LongValue int32
|
||||
padding [4]byte //nolint:unused // Memory reservation
|
||||
}
|
||||
|
||||
// PdhFmtCountervalueItemDouble is a union specialization for double values, used by PdhGetFormattedCounterArrayDouble.
|
||||
type PdhFmtCountervalueItemDouble struct {
|
||||
SzName *uint16
|
||||
FmtValue PdhFmtCountervalueDouble
|
||||
}
|
||||
|
||||
// PdhFmtCounterValueItemLarge is a union specialization for 'large' values, used by PdhGetFormattedCounterArrayLarge().
|
||||
type PdhFmtCounterValueItemLarge struct {
|
||||
SzName *uint16 // pointer to a string
|
||||
FmtValue PdhFmtCounterValueLarge
|
||||
}
|
||||
|
||||
// PdhFmtCounterValueItemLong is a union specialization for long values, used by PdhGetFormattedCounterArrayLong().
|
||||
type PdhFmtCounterValueItemLong struct {
|
||||
SzName *uint16 // pointer to a string
|
||||
FmtValue PdhFmtCounterValueLong
|
||||
}
|
||||
|
||||
// PdhCounterInfo structure contains information describing the properties of a counter. This information also includes the counter path.
|
||||
type PdhCounterInfo struct {
|
||||
// Size of the structure, including the appended strings, in bytes.
|
||||
DwLength uint32
|
||||
// Counter type. For a list of counter types,
|
||||
// see the Counter Types section of the <a "href=http://go.microsoft.com/fwlink/p/?linkid=84422">Windows Server 2003 Deployment Kit</a>.
|
||||
// The counter type constants are defined in Winperf.h.
|
||||
DwType uint32
|
||||
// Counter version information. Not used.
|
||||
CVersion uint32
|
||||
// Counter status that indicates if the counter value is valid. For a list of possible values,
|
||||
// see <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa371894(v=vs.85).aspx">Checking PDH Interface Return Values</a>.
|
||||
CStatus uint32
|
||||
// Scale factor to use when computing the displayable value of the counter. The scale factor is a power of ten.
|
||||
// The valid range of this parameter is PDH_MIN_SCALE (–7) (the returned value is the actual value times 10–⁷) to
|
||||
// Pdh_MAX_SCALE (+7) (the returned value is the actual value times 10⁺⁷). A value of zero will set the scale to one, so that the actual value is returned.
|
||||
LScale int32
|
||||
// Default scale factor as suggested by the counter's provider.
|
||||
LDefaultScale int32
|
||||
// The value passed in the dwUserData parameter when calling PdhAddCounter.
|
||||
DwUserData *uint32
|
||||
// The value passed in the dwUserData parameter when calling PdhOpenQuery.
|
||||
DwQueryUserData *uint32
|
||||
// Null-terminated string that specifies the full counter path. The string follows this structure in memory.
|
||||
SzFullPath *uint16 // pointer to a string
|
||||
// Null-terminated string that contains the name of the computer specified in the counter path. Is NULL, if the path does not specify a computer.
|
||||
// The string follows this structure in memory.
|
||||
SzMachineName *uint16 // pointer to a string
|
||||
// Null-terminated string that contains the name of the performance object specified in the counter path. The string follows this structure in memory.
|
||||
SzObjectName *uint16 // pointer to a string
|
||||
// Null-terminated string that contains the name of the object instance specified in the counter path. Is NULL, if the path does not specify an instance.
|
||||
// The string follows this structure in memory.
|
||||
SzInstanceName *uint16 // pointer to a string
|
||||
// Null-terminated string that contains the name of the parent instance specified in the counter path.
|
||||
// Is NULL, if the path does not specify a parent instance. The string follows this structure in memory.
|
||||
SzParentInstance *uint16 // pointer to a string
|
||||
// Instance index specified in the counter path. Is 0, if the path does not specify an instance index.
|
||||
DwInstanceIndex uint32 // pointer to a string
|
||||
// Null-terminated string that contains the counter name. The string follows this structure in memory.
|
||||
SzCounterName *uint16 // pointer to a string
|
||||
// Help text that describes the counter. Is NULL if the source is a log file.
|
||||
SzExplainText *uint16 // pointer to a string
|
||||
// Start of the string data that is appended to the structure.
|
||||
DataBuffer [1]uint32 // pointer to an extra space
|
||||
}
|
||||
|
||||
// The PdhRawCounter structure returns the data as it was collected from the counter provider.
|
||||
// No translation, formatting, or other interpretation is performed on the data.
|
||||
type PdhRawCounter struct {
|
||||
// Counter status that indicates if the counter value is valid. Check this member before using the data in a calculation or displaying its value.
|
||||
// For a list of possible values, see https://docs.microsoft.com/windows/desktop/PerfCtrs/checking-pdh-interface-return-values
|
||||
CStatus uint32
|
||||
// Local time for when the data was collected
|
||||
TimeStamp windows.Filetime
|
||||
// First raw counter value.
|
||||
FirstValue int64
|
||||
// Second raw counter value. Rate counters require two values in order to compute a displayable value.
|
||||
SecondValue int64
|
||||
// If the counter type contains the PERF_MULTI_COUNTER flag, this member contains the additional counter data used in the calculation.
|
||||
// For example, the PERF_100NSEC_MULTI_TIMER counter type contains the PERF_MULTI_COUNTER flag.
|
||||
MultiCount uint32
|
||||
}
|
||||
|
||||
type PdhRawCounterItem struct {
|
||||
// Pointer to a null-terminated string that specifies the instance name of the counter. The string is appended to the end of this structure.
|
||||
SzName *uint16
|
||||
// A PdhRawCounter structure that contains the raw counter value of the instance
|
||||
RawValue PdhRawCounter
|
||||
}
|
||||
143
pkg/perfdata/pdh_arm64.go
Normal file
143
pkg/perfdata/pdh_arm64.go
Normal file
@@ -0,0 +1,143 @@
|
||||
// Copyright (c) 2010-2024 The win Authors. All rights reserved.
|
||||
// Copyright (c) 2024 The prometheus-community Authors. All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// 3. The names of the authors may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
|
||||
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
// This is the official list of 'win' authors for copyright purposes.
|
||||
//
|
||||
// Alexander Neumann <an2048@googlemail.com>
|
||||
// Joseph Watson <jtwatson@linux-consulting.us>
|
||||
// Kevin Pors <krpors@gmail.com>
|
||||
|
||||
//go:build windows
|
||||
|
||||
package perfdata
|
||||
|
||||
import "golang.org/x/sys/windows"
|
||||
|
||||
// PdhFmtCountervalueDouble is a union specialization for double values.
|
||||
type PdhFmtCountervalueDouble struct {
|
||||
CStatus uint32
|
||||
DoubleValue float64
|
||||
}
|
||||
|
||||
// PdhFmtCounterValueLarge is a union specialization for 64-bit integer values.
|
||||
type PdhFmtCountervalueLarge struct {
|
||||
CStatus uint32
|
||||
LargeValue int64
|
||||
}
|
||||
|
||||
// PdhFmtCounterValueLong is a union specialization for long values.
|
||||
type PdhFmtCountervalueLong struct {
|
||||
CStatus uint32
|
||||
LongValue int32
|
||||
padding [4]byte //nolint:unused // Memory reservation
|
||||
}
|
||||
|
||||
type PdhFmtCountervalueItemDouble struct {
|
||||
SzName *uint16
|
||||
FmtValue PdhFmtCountervalueDouble
|
||||
}
|
||||
|
||||
// PdhFmtCounterValueItemLarge is a union specialization for 'large' values, used by PdhGetFormattedCounterArrayLarge().
|
||||
type PdhFmtCountervalueItemLarge struct {
|
||||
SzName *uint16 // pointer to a string
|
||||
FmtValue PdhFmtCountervalueLarge
|
||||
}
|
||||
|
||||
// PdhFmtCounterValueItemLong is a union specialization for long values, used by PdhGetFormattedCounterArrayLong().
|
||||
type PdhFmtCountervalueItemLong struct {
|
||||
SzName *uint16 // pointer to a string
|
||||
FmtValue PdhFmtCountervalueLong
|
||||
}
|
||||
|
||||
// PdhCounterInfo structure contains information describing the properties of a counter. This information also includes the counter path.
|
||||
type PdhCounterInfo struct {
|
||||
// Size of the structure, including the appended strings, in bytes.
|
||||
DwLength uint32
|
||||
// Counter type. For a list of counter types, see the Counter Types section
|
||||
// of the Windows Server 2003 Deployment Kit (http://go.microsoft.com/fwlink/p/?linkid=84422).
|
||||
// The counter type constants are defined in Winperf.h.
|
||||
DwType uint32
|
||||
// Counter version information. Not used.
|
||||
CVersion uint32
|
||||
// Counter status that indicates if the counter value is valid. For a list of possible values,
|
||||
// see <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa371894(v=vs.85).aspx">Checking PDH Interface Return Values</a>.
|
||||
CStatus uint32
|
||||
// Scale factor to use when computing the displayable value of the counter. The scale factor is a power of ten.
|
||||
// The valid range of this parameter is PDH_MIN_SCALE (–7) (the returned value is the actual value times 10–⁷) to
|
||||
// Pdh_MAX_SCALE (+7) (the returned value is the actual value times 10⁺⁷). A value of zero will set the scale to one, so that the actual value is returned.
|
||||
LScale int32
|
||||
// Default scale factor as suggested by the counter's provider.
|
||||
LDefaultScale int32
|
||||
// The value passed in the dwUserData parameter when calling PdhAddCounter.
|
||||
DwUserData *uint32
|
||||
// The value passed in the dwUserData parameter when calling PdhOpenQuery.
|
||||
DwQueryUserData *uint32
|
||||
// Null-terminated string that specifies the full counter path. The string follows this structure in memory.
|
||||
SzFullPath *uint16 // pointer to a string
|
||||
// Null-terminated string that contains the name of the computer specified in the counter path. Is NULL, if the path does not specify a computer.
|
||||
// The string follows this structure in memory.
|
||||
SzMachineName *uint16 // pointer to a string
|
||||
// Null-terminated string that contains the name of the performance object specified in the counter path. The string follows this structure in memory.
|
||||
SzObjectName *uint16 // pointer to a string
|
||||
// Null-terminated string that contains the name of the object instance specified in the counter path. Is NULL, if the path does not specify an instance.
|
||||
// The string follows this structure in memory.
|
||||
SzInstanceName *uint16 // pointer to a string
|
||||
// Null-terminated string that contains the name of the parent instance specified in the counter path.
|
||||
// Is NULL, if the path does not specify a parent instance.
|
||||
// The string follows this structure in memory.
|
||||
SzParentInstance *uint16 // pointer to a string
|
||||
// Instance index specified in the counter path. Is 0, if the path does not specify an instance index.
|
||||
DwInstanceIndex uint32 // pointer to a string
|
||||
// Null-terminated string that contains the counter name. The string follows this structure in memory.
|
||||
SzCounterName *uint16 // pointer to a string
|
||||
// Help text that describes the counter. Is NULL if the source is a log file.
|
||||
SzExplainText *uint16 // pointer to a string
|
||||
// Start of the string data that is appended to the structure.
|
||||
DataBuffer [1]uint32 // pointer to an extra space
|
||||
}
|
||||
|
||||
// The PdhRawCounter structure returns the data as it was collected from the counter provider.
|
||||
// No translation, formatting, or other interpretation is performed on the data.
|
||||
type PdhRawCounter struct {
|
||||
// Counter status that indicates if the counter value is valid. Check this member before using the data in a calculation or displaying its value.
|
||||
// For a list of possible values, see https://docs.microsoft.com/windows/desktop/PerfCtrs/checking-pdh-interface-return-values
|
||||
CStatus uint32
|
||||
// Local time for when the data was collected
|
||||
TimeStamp windows.Filetime
|
||||
// First raw counter value.
|
||||
FirstValue int64
|
||||
// Second raw counter value. Rate counters require two values in order to compute a displayable value.
|
||||
SecondValue int64
|
||||
// If the counter type contains the PERF_MULTI_COUNTER flag, this member contains the additional counter data used in the calculation.
|
||||
// For example, the PERF_100NSEC_MULTI_TIMER counter type contains the PERF_MULTI_COUNTER flag.
|
||||
MultiCount uint32
|
||||
}
|
||||
|
||||
type PdhRawCounterItem struct {
|
||||
// Pointer to a null-terminated string that specifies the instance name of the counter. The string is appended to the end of this structure.
|
||||
SzName *uint16
|
||||
// A PdhRawCounter structure that contains the raw counter value of the instance
|
||||
RawValue PdhRawCounter
|
||||
}
|
||||
@@ -31,11 +31,13 @@ type NameTable struct {
|
||||
|
||||
func (t *NameTable) LookupString(index uint32) string {
|
||||
t.initialize()
|
||||
|
||||
return t.table.index[index]
|
||||
}
|
||||
|
||||
func (t *NameTable) LookupIndex(str string) uint32 {
|
||||
t.initialize()
|
||||
|
||||
return t.table.string[str]
|
||||
}
|
||||
|
||||
@@ -56,7 +58,9 @@ func (t *NameTable) initialize() {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
r := bytes.NewReader(buffer)
|
||||
|
||||
for {
|
||||
index, err := readUTF16String(r)
|
||||
if err != nil {
|
||||
|
||||
@@ -119,6 +119,8 @@ import (
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// TODO: There's a LittleEndian field in the PERF header - we ought to check it.
|
||||
@@ -204,7 +206,7 @@ func queryRawData(query string) ([]byte, error) {
|
||||
|
||||
buffer = make([]byte, bufLen)
|
||||
|
||||
name, err := syscall.UTF16PtrFromString(query)
|
||||
name, err := windows.UTF16PtrFromString(query)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to encode query string: %w", err)
|
||||
}
|
||||
@@ -212,21 +214,23 @@ func queryRawData(query string) ([]byte, error) {
|
||||
for {
|
||||
bufLen := uint32(len(buffer))
|
||||
|
||||
//nolint:forbidigo // Legacy Code
|
||||
err := syscall.RegQueryValueEx(
|
||||
syscall.HKEY_PERFORMANCE_DATA,
|
||||
windows.HKEY_PERFORMANCE_DATA,
|
||||
name,
|
||||
nil,
|
||||
&valType,
|
||||
(*byte)(unsafe.Pointer(&buffer[0])),
|
||||
&bufLen)
|
||||
|
||||
if errors.Is(err, error(syscall.ERROR_MORE_DATA)) {
|
||||
if errors.Is(err, error(syscall.ERROR_MORE_DATA)) { //nolint:forbidigo // Legacy Code
|
||||
newBuffer := make([]byte, len(buffer)+16384)
|
||||
copy(newBuffer, buffer)
|
||||
buffer = newBuffer
|
||||
|
||||
continue
|
||||
} else if err != nil {
|
||||
var errNo syscall.Errno
|
||||
var errNo syscall.Errno //nolint:forbidigo // Legacy Code
|
||||
if errors.As(err, &errNo) {
|
||||
return nil, fmt.Errorf("ReqQueryValueEx failed: %w errno %d", err, uint(errNo))
|
||||
}
|
||||
@@ -276,6 +280,7 @@ func QueryPerformanceData(query string) ([]*PerfObject, error) {
|
||||
// Read global header
|
||||
|
||||
header := new(perfDataBlock)
|
||||
|
||||
err = header.BinaryReadFrom(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read performance data block for %q with: %w", query, err)
|
||||
@@ -300,6 +305,7 @@ func QueryPerformanceData(query string) ([]*PerfObject, error) {
|
||||
}
|
||||
|
||||
obj := new(perfObjectType)
|
||||
|
||||
err = obj.BinaryReadFrom(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -329,6 +335,7 @@ func QueryPerformanceData(query string) ([]*PerfObject, error) {
|
||||
|
||||
for i := range numCounterDefs {
|
||||
def := new(perfCounterDefinition)
|
||||
|
||||
err := def.BinaryReadFrom(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -410,7 +417,9 @@ func parseCounterBlock(b []byte, r io.ReadSeeker, pos int64, defs []*PerfCounter
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
block := new(perfCounterBlock)
|
||||
|
||||
err = block.BinaryReadFrom(r)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
@@ -453,7 +462,6 @@ func convertCounterValue(counterDef *perfCounterDefinition, buffer []byte, value
|
||||
272696576 64bit rate
|
||||
|
||||
*/
|
||||
|
||||
switch counterDef.CounterSize {
|
||||
case 4:
|
||||
value = int64(bo.Uint32(buffer[valueOffset:(valueOffset + 4)]))
|
||||
|
||||
@@ -3,7 +3,8 @@ package perflib
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
/*
|
||||
@@ -36,7 +37,7 @@ type perfDataBlock struct {
|
||||
HeaderLength uint32
|
||||
NumObjectTypes uint32
|
||||
DefaultObject int32
|
||||
SystemTime syscall.Systemtime
|
||||
SystemTime windows.Systemtime
|
||||
_ uint32 // TODO
|
||||
PerfTime int64
|
||||
PerfFreq int64
|
||||
|
||||
@@ -3,11 +3,9 @@ package perflib
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
"github.com/go-kit/log/level"
|
||||
)
|
||||
|
||||
// Conversion factors.
|
||||
@@ -16,14 +14,16 @@ const (
|
||||
WindowsEpoch = 116444736000000000
|
||||
)
|
||||
|
||||
func UnmarshalObject(obj *PerfObject, vs interface{}, logger log.Logger) error {
|
||||
func UnmarshalObject(obj *PerfObject, vs interface{}, logger *slog.Logger) error {
|
||||
if obj == nil {
|
||||
return errors.New("counter not found")
|
||||
}
|
||||
|
||||
rv := reflect.ValueOf(vs)
|
||||
if rv.Kind() != reflect.Ptr || rv.IsNil() {
|
||||
return fmt.Errorf("%v is nil or not a pointer to slice", reflect.TypeOf(vs))
|
||||
}
|
||||
|
||||
ev := rv.Elem()
|
||||
if ev.Kind() != reflect.Slice {
|
||||
return fmt.Errorf("%v is not slice", reflect.TypeOf(vs))
|
||||
@@ -40,6 +40,7 @@ func UnmarshalObject(obj *PerfObject, vs interface{}, logger log.Logger) error {
|
||||
rt := target.Type()
|
||||
|
||||
counters := make(map[string]*PerfCounter, len(instance.Counters))
|
||||
|
||||
for _, ctr := range instance.Counters {
|
||||
if ctr.Def.IsBaseValue && !ctr.Def.IsNanosecondCounter {
|
||||
counters[ctr.Def.Name+"_Base"] = ctr
|
||||
@@ -50,10 +51,12 @@ func UnmarshalObject(obj *PerfObject, vs interface{}, logger log.Logger) error {
|
||||
|
||||
for i := range target.NumField() {
|
||||
f := rt.Field(i)
|
||||
|
||||
tag := f.Tag.Get("perflib")
|
||||
if tag == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
secondValue := false
|
||||
|
||||
st := strings.Split(tag, ",")
|
||||
@@ -67,12 +70,15 @@ func UnmarshalObject(obj *PerfObject, vs interface{}, logger log.Logger) error {
|
||||
|
||||
ctr, found := counters[tag]
|
||||
if !found {
|
||||
_ = level.Debug(logger).Log("msg", fmt.Sprintf("missing counter %q, have %v", tag, counterMapKeys(counters)))
|
||||
logger.Debug(fmt.Sprintf("missing counter %q, have %v", tag, counterMapKeys(counters)))
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if !target.Field(i).CanSet() {
|
||||
return fmt.Errorf("tagged field %v cannot be written to", f.Name)
|
||||
}
|
||||
|
||||
if fieldType := target.Field(i).Type(); fieldType != reflect.TypeOf((*float64)(nil)).Elem() {
|
||||
return fmt.Errorf("tagged field %v has wrong type %v, must be float64", f.Name, fieldType)
|
||||
}
|
||||
@@ -81,7 +87,9 @@ func UnmarshalObject(obj *PerfObject, vs interface{}, logger log.Logger) error {
|
||||
if !ctr.Def.HasSecondValue {
|
||||
return fmt.Errorf("tagged field %v expected a SecondValue, which was not present", f.Name)
|
||||
}
|
||||
|
||||
target.Field(i).SetFloat(float64(ctr.SecondValue))
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -108,5 +116,6 @@ func counterMapKeys(m map[string]*PerfCounter) []string {
|
||||
for k := range m {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
|
||||
return keys
|
||||
}
|
||||
|
||||
@@ -3,12 +3,14 @@ package perflib
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"syscall"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// readUTF16StringAtPos Read an unterminated UTF16 string at a given position, specifying its length.
|
||||
func readUTF16StringAtPos(r io.ReadSeeker, absPos int64, length uint32) (string, error) {
|
||||
value := make([]uint16, length/2)
|
||||
|
||||
_, err := r.Seek(absPos, io.SeekStart)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -19,7 +21,7 @@ func readUTF16StringAtPos(r io.ReadSeeker, absPos int64, length uint32) (string,
|
||||
return "", err
|
||||
}
|
||||
|
||||
return syscall.UTF16ToString(value), nil
|
||||
return windows.UTF16ToString(value), nil
|
||||
}
|
||||
|
||||
// readUTF16String Reads a null-terminated UTF16 string at the current offset.
|
||||
@@ -43,5 +45,5 @@ func readUTF16String(r io.Reader) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return syscall.UTF16ToString(out), nil
|
||||
return windows.UTF16ToString(out), nil
|
||||
}
|
||||
|
||||
@@ -18,5 +18,6 @@ func GetPerflibSnapshot(objNames string) (map[string]*PerfObject, error) {
|
||||
for _, obj := range objects {
|
||||
indexed[obj.Name] = obj
|
||||
}
|
||||
|
||||
return indexed, nil
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package perflib
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log/slog"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/go-kit/log"
|
||||
)
|
||||
|
||||
type simple struct {
|
||||
@@ -114,11 +114,14 @@ func TestUnmarshalPerflib(t *testing.T) {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
logger := slog.New(slog.NewTextHandler(io.Discard, nil))
|
||||
output := make([]simple, 0)
|
||||
err := UnmarshalObject(c.obj, &output, log.NewNopLogger())
|
||||
|
||||
err := UnmarshalObject(c.obj, &output, logger)
|
||||
if err != nil && !c.expectError {
|
||||
t.Errorf("Did not expect error, got %q", err)
|
||||
}
|
||||
|
||||
if err == nil && c.expectError {
|
||||
t.Errorf("Expected an error, but got ok")
|
||||
}
|
||||
|
||||
@@ -3,10 +3,11 @@
|
||||
package testutils
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log/slog"
|
||||
"testing"
|
||||
|
||||
"github.com/alecthomas/kingpin/v2"
|
||||
"github.com/go-kit/log"
|
||||
"github.com/prometheus-community/windows_exporter/pkg/collector"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -15,7 +16,7 @@ import (
|
||||
func FuncBenchmarkCollector[C collector.Collector](b *testing.B, name string, collectFunc collector.BuilderWithFlags[C]) {
|
||||
b.Helper()
|
||||
|
||||
logger := log.NewNopLogger()
|
||||
logger := slog.New(slog.NewTextHandler(io.Discard, nil))
|
||||
|
||||
c := collectFunc(kingpin.CommandLine)
|
||||
collectors := collector.New(map[string]collector.Collector{name: c})
|
||||
@@ -28,6 +29,7 @@ func FuncBenchmarkCollector[C collector.Collector](b *testing.B, name string, co
|
||||
require.NoError(b, err)
|
||||
|
||||
metrics := make(chan prometheus.Metric)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
<-metrics
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
package types
|
||||
|
||||
const (
|
||||
DefaultCollectors = "cpu,cs,logical_disk,physical_disk,net,os,service,system"
|
||||
DefaultCollectors = "cpu,cs,memory,logical_disk,physical_disk,net,os,service,system"
|
||||
DefaultCollectorsPlaceholder = "[defaults]"
|
||||
Namespace = "windows"
|
||||
)
|
||||
|
||||
@@ -12,14 +12,17 @@ func ExpandEnabledCollectors(enabled string) []string {
|
||||
expanded := strings.ReplaceAll(enabled, types.DefaultCollectorsPlaceholder, types.DefaultCollectors)
|
||||
separated := strings.Split(expanded, ",")
|
||||
unique := map[string]bool{}
|
||||
|
||||
for _, s := range separated {
|
||||
if s != "" {
|
||||
unique[s] = true
|
||||
}
|
||||
}
|
||||
|
||||
result := make([]string, 0, len(unique))
|
||||
for s := range unique {
|
||||
result = append(result, s)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -38,13 +38,16 @@ func TestExpandEnabled(t *testing.T) {
|
||||
success = false
|
||||
} else {
|
||||
sort.Strings(testCase.expectedOutput)
|
||||
|
||||
for idx := range output {
|
||||
if output[idx] != testCase.expectedOutput[idx] {
|
||||
success = false
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !success {
|
||||
t.Error("For", testCase.input, "expected", testCase.expectedOutput, "got", output)
|
||||
}
|
||||
|
||||
@@ -10,11 +10,8 @@ func BoolToFloat(b bool) float64 {
|
||||
if b {
|
||||
return 1.0
|
||||
}
|
||||
return 0.0
|
||||
}
|
||||
|
||||
func IsEmpty(v *string) bool {
|
||||
return v == nil || *v == ""
|
||||
return 0.0
|
||||
}
|
||||
|
||||
func ToPTR[t any](v t) *t {
|
||||
|
||||
Reference in New Issue
Block a user