diff --git a/exporter.go b/exporter.go index 95d468e7..09906ffb 100644 --- a/exporter.go +++ b/exporter.go @@ -38,16 +38,6 @@ import ( "golang.org/x/sys/windows" ) -// Mapping of priority names to uin32 values required by windows.SetPriorityClass. -var priorityStringToInt = map[string]uint32{ - "realtime": windows.REALTIME_PRIORITY_CLASS, - "high": windows.HIGH_PRIORITY_CLASS, - "abovenormal": windows.ABOVE_NORMAL_PRIORITY_CLASS, - "normal": windows.NORMAL_PRIORITY_CLASS, - "belownormal": windows.BELOW_NORMAL_PRIORITY_CLASS, - "low": windows.IDLE_PRIORITY_CLASS, -} - func main() { os.Exit(run()) } @@ -180,17 +170,12 @@ func run() int { return 0 } - // Only set process priority if a non-default and valid value has been set - if priority, ok := priorityStringToInt[*processPriority]; ok && priority != windows.NORMAL_PRIORITY_CLASS { - logger.Debug("setting process priority to " + *processPriority) + if err = setPriorityWindows(logger, os.Getpid(), *processPriority); err != nil { + logger.Error("failed to set process priority", + slog.Any("err", err), + ) - if err = setPriorityWindows(os.Getpid(), priority); err != nil { - logger.Error("failed to set process priority", - slog.Any("err", err), - ) - - return 1 - } + return 1 } enabledCollectorList := utils.ExpandEnabledCollectors(*enabledCollectors) @@ -305,20 +290,43 @@ func printCollectorsToStdout() { } func logCurrentUser(logger *slog.Logger) { - if u, err := user.Current(); err == nil { - logger.Info("Running as " + u.Username) - - if strings.Contains(u.Username, "ContainerAdministrator") || strings.Contains(u.Username, "ContainerUser") { - logger.Warn("Running as a preconfigured Windows Container user. This may mean you do not have Windows HostProcess containers configured correctly and some functionality will not work as expected.") - } + u, err := user.Current() + if err != nil { + logger.Warn("Unable to determine which user is running this exporter. More info: https://github.com/golang/go/issues/37348", + slog.Any("err", err), + ) return } - logger.Warn("Unable to determine which user is running this exporter. More info: https://github.com/golang/go/issues/37348") + logger.Info("Running as " + u.Username) + + if strings.Contains(u.Username, "ContainerAdministrator") || strings.Contains(u.Username, "ContainerUser") { + logger.Warn("Running as a preconfigured Windows Container user. This may mean you do not have Windows HostProcess containers configured correctly and some functionality will not work as expected.") + } } -func setPriorityWindows(pid int, priority uint32) error { +// setPriorityWindows sets the priority of the current process to the specified value. +func setPriorityWindows(logger *slog.Logger, pid int, priority string) error { + // Mapping of priority names to uin32 values required by windows.SetPriorityClass. + priorityStringToInt := map[string]uint32{ + "realtime": windows.REALTIME_PRIORITY_CLASS, + "high": windows.HIGH_PRIORITY_CLASS, + "abovenormal": windows.ABOVE_NORMAL_PRIORITY_CLASS, + "normal": windows.NORMAL_PRIORITY_CLASS, + "belownormal": windows.BELOW_NORMAL_PRIORITY_CLASS, + "low": windows.IDLE_PRIORITY_CLASS, + } + + winPriority, ok := priorityStringToInt[priority] + + // Only set process priority if a non-default and valid value has been set + if !ok || winPriority != windows.NORMAL_PRIORITY_CLASS { + return nil + } + + logger.Debug("setting process priority to " + priority) + // https://learn.microsoft.com/en-us/windows/win32/procthread/process-security-and-access-rights handle, err := windows.OpenProcess( windows.STANDARD_RIGHTS_REQUIRED|windows.SYNCHRONIZE|windows.SPECIFIC_RIGHTS_ALL, @@ -328,7 +336,7 @@ func setPriorityWindows(pid int, priority uint32) error { return fmt.Errorf("failed to open own process: %w", err) } - if err = windows.SetPriorityClass(handle, priority); err != nil { + if err = windows.SetPriorityClass(handle, winPriority); err != nil { return fmt.Errorf("failed to set priority class: %w", err) } diff --git a/pkg/collector/adcs/adcs.go b/pkg/collector/adcs/adcs.go index d89d0611..4c6fb50b 100644 --- a/pkg/collector/adcs/adcs.go +++ b/pkg/collector/adcs/adcs.go @@ -4,10 +4,11 @@ package adcs import ( "errors" + "fmt" "log/slog" - "strings" "github.com/alecthomas/kingpin/v2" + "github.com/prometheus-community/windows_exporter/pkg/perfdata" "github.com/prometheus-community/windows_exporter/pkg/perflib" "github.com/prometheus-community/windows_exporter/pkg/types" "github.com/prometheus-community/windows_exporter/pkg/utils" @@ -24,6 +25,8 @@ var ConfigDefaults = Config{} type Collector struct { config Config + perfDataCollector *perfdata.Collector + challengeResponseProcessingTime *prometheus.Desc challengeResponsesPerSecond *prometheus.Desc failedRequestsPerSecond *prometheus.Desc @@ -60,6 +63,10 @@ func (c *Collector) GetName() string { } func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) { + if utils.PDHEnabled() { + return []string{}, nil + } + return []string{"Certification Authority"}, nil } @@ -68,6 +75,31 @@ func (c *Collector) Close(_ *slog.Logger) error { } func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error { + if utils.PDHEnabled() { + counters := []string{ + RequestsPerSecond, + RequestProcessingTime, + RetrievalsPerSecond, + RetrievalProcessingTime, + FailedRequestsPerSecond, + IssuedRequestsPerSecond, + PendingRequestsPerSecond, + RequestCryptographicSigningTime, + RequestPolicyModuleProcessingTime, + ChallengeResponsesPerSecond, + ChallengeResponseProcessingTime, + SignedCertificateTimestampListsPerSecond, + SignedCertificateTimestampListProcessingTime, + } + + var err error + + c.perfDataCollector, err = perfdata.NewCollector("Processor Information", []string{"*"}, counters) + if err != nil { + return fmt.Errorf("failed to create Processor Information collector: %w", err) + } + } + c.requestsPerSecond = prometheus.NewDesc( prometheus.BuildFQName(types.Namespace, Name, "requests_total"), "Total certificate requests processed", @@ -151,6 +183,10 @@ func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error { } func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error { + if utils.PDHEnabled() { + return c.collectPDH(ch) + } + logger = logger.With(slog.String("collector", Name)) if err := c.collectADCSCounters(ctx, logger, ch); err != nil { logger.Error("failed collecting ADCS metrics", @@ -163,23 +199,6 @@ func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch ch return nil } -type perflibADCS struct { - Name string - RequestsPerSecond float64 `perflib:"Requests/sec"` - RequestProcessingTime float64 `perflib:"Request processing time (ms)"` - RetrievalsPerSecond float64 `perflib:"Retrievals/sec"` - RetrievalProcessingTime float64 `perflib:"Retrieval processing time (ms)"` - FailedRequestsPerSecond float64 `perflib:"Failed Requests/sec"` - IssuedRequestsPerSecond float64 `perflib:"Issued Requests/sec"` - PendingRequestsPerSecond float64 `perflib:"Pending Requests/sec"` - RequestCryptographicSigningTime float64 `perflib:"Request cryptographic signing time (ms)"` - RequestPolicyModuleProcessingTime float64 `perflib:"Request policy module processing time (ms)"` - ChallengeResponsesPerSecond float64 `perflib:"Challenge Responses/sec"` - ChallengeResponseProcessingTime float64 `perflib:"Challenge Response processing time (ms)"` - SignedCertificateTimestampListsPerSecond float64 `perflib:"Signed Certificate Timestamp Lists/sec"` - SignedCertificateTimestampListProcessingTime float64 `perflib:"Signed Certificate Timestamp List processing time (ms)"` -} - func (c *Collector) collectADCSCounters(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error { dst := make([]perflibADCS, 0) @@ -197,10 +216,10 @@ func (c *Collector) collectADCSCounters(ctx *types.ScrapeContext, logger *slog.L } for _, d := range dst { - n := strings.ToLower(d.Name) - if n == "" { + if d.Name == "" { continue } + ch <- prometheus.MustNewConstMetric( c.requestsPerSecond, prometheus.CounterValue, @@ -283,3 +302,97 @@ func (c *Collector) collectADCSCounters(ctx *types.ScrapeContext, logger *slog.L return nil } + +func (c *Collector) collectPDH(ch chan<- prometheus.Metric) error { + data, err := c.perfDataCollector.Collect() + if err != nil { + return fmt.Errorf("failed to collect Certification Authority (ADCS) metrics: %w", err) + } + + if len(data) == 0 { + return errors.New("perflib query for Certification Authority (ADCS) returned empty result set") + } + + for name, adcsData := range data { + ch <- prometheus.MustNewConstMetric( + c.requestsPerSecond, + prometheus.CounterValue, + adcsData[RequestsPerSecond].FirstValue, + name, + ) + ch <- prometheus.MustNewConstMetric( + c.requestProcessingTime, + prometheus.GaugeValue, + utils.MilliSecToSec(adcsData[RequestProcessingTime].FirstValue), + name, + ) + ch <- prometheus.MustNewConstMetric( + c.retrievalsPerSecond, + prometheus.CounterValue, + adcsData[RetrievalsPerSecond].FirstValue, + name, + ) + ch <- prometheus.MustNewConstMetric( + c.retrievalProcessingTime, + prometheus.GaugeValue, + utils.MilliSecToSec(adcsData[RetrievalProcessingTime].FirstValue), + name, + ) + ch <- prometheus.MustNewConstMetric( + c.failedRequestsPerSecond, + prometheus.CounterValue, + adcsData[FailedRequestsPerSecond].FirstValue, + name, + ) + ch <- prometheus.MustNewConstMetric( + c.issuedRequestsPerSecond, + prometheus.CounterValue, + adcsData[IssuedRequestsPerSecond].FirstValue, + name, + ) + ch <- prometheus.MustNewConstMetric( + c.pendingRequestsPerSecond, + prometheus.CounterValue, + adcsData[PendingRequestsPerSecond].FirstValue, + name, + ) + ch <- prometheus.MustNewConstMetric( + c.requestCryptographicSigningTime, + prometheus.GaugeValue, + utils.MilliSecToSec(adcsData[RequestCryptographicSigningTime].FirstValue), + name, + ) + ch <- prometheus.MustNewConstMetric( + c.requestPolicyModuleProcessingTime, + prometheus.GaugeValue, + utils.MilliSecToSec(adcsData[RequestPolicyModuleProcessingTime].FirstValue), + name, + ) + ch <- prometheus.MustNewConstMetric( + c.challengeResponsesPerSecond, + prometheus.CounterValue, + adcsData[ChallengeResponsesPerSecond].FirstValue, + name, + ) + ch <- prometheus.MustNewConstMetric( + c.challengeResponseProcessingTime, + prometheus.GaugeValue, + utils.MilliSecToSec(adcsData[ChallengeResponseProcessingTime].FirstValue), + name, + ) + ch <- prometheus.MustNewConstMetric( + c.signedCertificateTimestampListsPerSecond, + prometheus.CounterValue, + adcsData[SignedCertificateTimestampListsPerSecond].FirstValue, + name, + ) + ch <- prometheus.MustNewConstMetric( + c.signedCertificateTimestampListProcessingTime, + prometheus.GaugeValue, + utils.MilliSecToSec(adcsData[SignedCertificateTimestampListProcessingTime].FirstValue), + name, + ) + } + + return nil +} diff --git a/pkg/collector/adcs/const.go b/pkg/collector/adcs/const.go new file mode 100644 index 00000000..7e3405db --- /dev/null +++ b/pkg/collector/adcs/const.go @@ -0,0 +1,34 @@ +package adcs + +const ( + RequestsPerSecond = "Requests/sec" + RequestProcessingTime = "Request processing time (ms)" + RetrievalsPerSecond = "Retrievals/sec" + RetrievalProcessingTime = "Retrieval processing time (ms)" + FailedRequestsPerSecond = "Failed Requests/sec" + IssuedRequestsPerSecond = "Issued Requests/sec" + PendingRequestsPerSecond = "Pending Requests/sec" + RequestCryptographicSigningTime = "Request cryptographic signing time (ms)" + RequestPolicyModuleProcessingTime = "Request policy module processing time (ms)" + ChallengeResponsesPerSecond = "Challenge Responses/sec" + ChallengeResponseProcessingTime = "Challenge Response processing time (ms)" + SignedCertificateTimestampListsPerSecond = "Signed Certificate Timestamp Lists/sec" + SignedCertificateTimestampListProcessingTime = "Signed Certificate Timestamp List processing time (ms)" +) + +type perflibADCS struct { + Name string + RequestsPerSecond float64 `perflib:"Requests/sec"` + RequestProcessingTime float64 `perflib:"Request processing time (ms)"` + RetrievalsPerSecond float64 `perflib:"Retrievals/sec"` + RetrievalProcessingTime float64 `perflib:"Retrieval processing time (ms)"` + FailedRequestsPerSecond float64 `perflib:"Failed Requests/sec"` + IssuedRequestsPerSecond float64 `perflib:"Issued Requests/sec"` + PendingRequestsPerSecond float64 `perflib:"Pending Requests/sec"` + RequestCryptographicSigningTime float64 `perflib:"Request cryptographic signing time (ms)"` + RequestPolicyModuleProcessingTime float64 `perflib:"Request policy module processing time (ms)"` + ChallengeResponsesPerSecond float64 `perflib:"Challenge Responses/sec"` + ChallengeResponseProcessingTime float64 `perflib:"Challenge Response processing time (ms)"` + SignedCertificateTimestampListsPerSecond float64 `perflib:"Signed Certificate Timestamp Lists/sec"` + SignedCertificateTimestampListProcessingTime float64 `perflib:"Signed Certificate Timestamp List processing time (ms)"` +} diff --git a/pkg/collector/cpu/const.go b/pkg/collector/cpu/const.go index 76916364..7bc6e697 100644 --- a/pkg/collector/cpu/const.go +++ b/pkg/collector/cpu/const.go @@ -26,3 +26,32 @@ const ( ProcessorUtilityRate = "% Processor Utility" UserTimeSeconds = "% User Time" ) + +type perflibProcessorInformation struct { + Name string + C1TimeSeconds float64 `perflib:"% C1 Time"` + C2TimeSeconds float64 `perflib:"% C2 Time"` + C3TimeSeconds float64 `perflib:"% C3 Time"` + C1TransitionsTotal float64 `perflib:"C1 Transitions/sec"` + C2TransitionsTotal float64 `perflib:"C2 Transitions/sec"` + C3TransitionsTotal float64 `perflib:"C3 Transitions/sec"` + ClockInterruptsTotal float64 `perflib:"Clock Interrupts/sec"` + DPCsQueuedTotal float64 `perflib:"DPCs Queued/sec"` + DPCTimeSeconds float64 `perflib:"% DPC Time"` + IdleBreakEventsTotal float64 `perflib:"Idle Break Events/sec"` + IdleTimeSeconds float64 `perflib:"% Idle Time"` + InterruptsTotal float64 `perflib:"Interrupts/sec"` + InterruptTimeSeconds float64 `perflib:"% Interrupt Time"` + ParkingStatus float64 `perflib:"Parking Status"` + PerformanceLimitPercent float64 `perflib:"% Performance Limit"` + PriorityTimeSeconds float64 `perflib:"% Priority Time"` + PrivilegedTimeSeconds float64 `perflib:"% Privileged Time"` + PrivilegedUtilitySeconds float64 `perflib:"% Privileged Utility"` + ProcessorFrequencyMHz float64 `perflib:"Processor Frequency"` + ProcessorPerformance float64 `perflib:"% Processor Performance"` + ProcessorMPerf float64 `perflib:"% Processor Performance,secondvalue"` + ProcessorTimeSeconds float64 `perflib:"% Processor Time"` + ProcessorUtilityRate float64 `perflib:"% Processor Utility"` + ProcessorRTC float64 `perflib:"% Processor Utility,secondvalue"` + UserTimeSeconds float64 `perflib:"% User Time"` +} diff --git a/pkg/collector/cpu/cpu.go b/pkg/collector/cpu/cpu.go index cacd6593..571c796a 100644 --- a/pkg/collector/cpu/cpu.go +++ b/pkg/collector/cpu/cpu.go @@ -142,7 +142,6 @@ func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error { []string{"core"}, nil, ) - c.cStateSecondsTotal = prometheus.NewDesc( prometheus.BuildFQName(types.Namespace, Name, "cstate_seconds_total"), "Time spent in low-power idle state", @@ -226,42 +225,13 @@ func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error { } func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error { - logger = logger.With(slog.String("collector", Name)) - if utils.PDHEnabled() { return c.collectPDH(ch) } - return c.collectFull(ctx, logger, ch) -} + logger = logger.With(slog.String("collector", Name)) -type perflibProcessorInformation struct { - Name string - C1TimeSeconds float64 `perflib:"% C1 Time"` - C2TimeSeconds float64 `perflib:"% C2 Time"` - C3TimeSeconds float64 `perflib:"% C3 Time"` - C1TransitionsTotal float64 `perflib:"C1 Transitions/sec"` - C2TransitionsTotal float64 `perflib:"C2 Transitions/sec"` - C3TransitionsTotal float64 `perflib:"C3 Transitions/sec"` - ClockInterruptsTotal float64 `perflib:"Clock Interrupts/sec"` - DPCsQueuedTotal float64 `perflib:"DPCs Queued/sec"` - DPCTimeSeconds float64 `perflib:"% DPC Time"` - IdleBreakEventsTotal float64 `perflib:"Idle Break Events/sec"` - IdleTimeSeconds float64 `perflib:"% Idle Time"` - InterruptsTotal float64 `perflib:"Interrupts/sec"` - InterruptTimeSeconds float64 `perflib:"% Interrupt Time"` - ParkingStatus float64 `perflib:"Parking Status"` - PerformanceLimitPercent float64 `perflib:"% Performance Limit"` - PriorityTimeSeconds float64 `perflib:"% Priority Time"` - PrivilegedTimeSeconds float64 `perflib:"% Privileged Time"` - PrivilegedUtilitySeconds float64 `perflib:"% Privileged Utility"` - ProcessorFrequencyMHz float64 `perflib:"Processor Frequency"` - ProcessorPerformance float64 `perflib:"% Processor Performance"` - ProcessorMPerf float64 `perflib:"% Processor Performance,secondvalue"` - ProcessorTimeSeconds float64 `perflib:"% Processor Time"` - ProcessorUtilityRate float64 `perflib:"% Processor Utility"` - ProcessorRTC float64 `perflib:"% Processor Utility,secondvalue"` - UserTimeSeconds float64 `perflib:"% User Time"` + return c.collectFull(ctx, logger, ch) } func (c *Collector) collectFull(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {