Merge pull request #1047 from jammiemil/master

fix Windows Service timeout during high CPU (eg. post Windows Update)
This commit is contained in:
Ben Reedy 2022-08-24 21:00:22 +10:00 committed by GitHub
commit 4b226cde40
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 67 additions and 49 deletions

View File

@ -4,6 +4,10 @@
package main
import (
//Its important that we do these first so that we can register with the windows service control ASAP to avoid timeouts
"github.com/prometheus-community/windows_exporter/initiate"
"github.com/prometheus-community/windows_exporter/log"
"encoding/json"
"fmt"
"net/http"
@ -16,12 +20,10 @@ import (
"sync"
"time"
"golang.org/x/sys/windows/svc"
"github.com/StackExchange/wmi"
"github.com/prometheus-community/windows_exporter/collector"
"github.com/prometheus-community/windows_exporter/config"
"github.com/prometheus-community/windows_exporter/log"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/collectors"
"github.com/prometheus/client_golang/prometheus/promhttp"
@ -50,7 +52,6 @@ type prometheusVersion struct {
const (
defaultCollectors = "cpu,cs,logical_disk,net,os,service,system,textfile"
defaultCollectorsPlaceholder = "[defaults]"
serviceName = "windows_exporter"
)
var (
@ -289,7 +290,6 @@ func main() {
"Seconds to subtract from the timeout allowed by the client. Tune to allow for overhead or high loads.",
).Default("0.5").Float64()
)
log.AddFlags(kingpin.CommandLine)
kingpin.Version(version.Print("windows_exporter"))
kingpin.HelpFlag.Short('h')
@ -297,7 +297,7 @@ func main() {
// Load values from configuration file(s). Executable flags must first be parsed, in order
// to load the specified file(s).
kingpin.Parse()
log.Debug("Logging has Started")
if *configFile != "" {
resolver, err := config.NewResolver(*configFile)
if err != nil {
@ -327,21 +327,6 @@ func main() {
initWbem()
isService, err := svc.IsWindowsService()
if err != nil {
log.Fatal(err)
}
stopCh := make(chan bool)
if isService {
go func() {
err = svc.Run(serviceName, &windowsExporterService{stopCh: stopCh})
if err != nil {
log.Errorf("Failed to start service: %v", err)
}
}()
}
collectors, err := loadCollectors(*enabledCollectors)
if err != nil {
log.Fatalf("Couldn't load collectors: %s", err)
@ -421,7 +406,7 @@ func main() {
}()
for {
if <-stopCh {
if <-initiate.StopCh {
log.Info("Shutting down windows_exporter")
break
}
@ -463,33 +448,6 @@ func withConcurrencyLimit(n int, next http.HandlerFunc) http.HandlerFunc {
}
}
type windowsExporterService struct {
stopCh chan<- bool
}
func (s *windowsExporterService) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (ssec bool, errno uint32) {
const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown
changes <- svc.Status{State: svc.StartPending}
changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
loop:
for {
select {
case c := <-r:
switch c.Cmd {
case svc.Interrogate:
changes <- c.CurrentStatus
case svc.Stop, svc.Shutdown:
s.stopCh <- true
break loop
default:
log.Error(fmt.Sprintf("unexpected control request #%d", c))
}
}
}
changes <- svc.Status{State: svc.StopPending}
return
}
type metricsHandler struct {
timeoutMargin float64
collectorFactory func(timeout time.Duration, requestedCollectors []string) (error, *windowsCollector)

60
initiate/initiate.go Normal file
View File

@ -0,0 +1,60 @@
//This package allows us to initiate Time Sensitive components (Like registering the windows service) as early as possible in the startup process
package initiate
import (
"fmt"
"github.com/prometheus-community/windows_exporter/log"
"golang.org/x/sys/windows/svc"
)
const (
serviceName = "windows_exporter"
)
type windowsExporterService struct {
stopCh chan<- bool
}
func (s *windowsExporterService) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (ssec bool, errno uint32) {
const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown
changes <- svc.Status{State: svc.StartPending}
changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
loop:
for {
select {
case c := <-r:
switch c.Cmd {
case svc.Interrogate:
changes <- c.CurrentStatus
case svc.Stop, svc.Shutdown:
log.Debug("Service Stop Received")
s.stopCh <- true
break loop
default:
log.Error(fmt.Sprintf("unexpected control request #%d", c))
}
}
}
changes <- svc.Status{State: svc.StopPending}
return
}
var StopCh = make(chan bool)
func init() {
log.Debug("Checking if We are a service")
isService, err := svc.IsWindowsService()
if err != nil {
log.Fatal(err)
}
log.Debug("Attempting to start exporter service")
if isService {
go func() {
err = svc.Run(serviceName, &windowsExporterService{stopCh: StopCh})
if err != nil {
log.Errorf("Failed to start service: %v", err)
}
}()
}
}