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
2 changed files with 67 additions and 49 deletions

View File

@@ -4,6 +4,10 @@
package main package main
import ( 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" "encoding/json"
"fmt" "fmt"
"net/http" "net/http"
@@ -16,12 +20,10 @@ import (
"sync" "sync"
"time" "time"
"golang.org/x/sys/windows/svc"
"github.com/StackExchange/wmi" "github.com/StackExchange/wmi"
"github.com/prometheus-community/windows_exporter/collector" "github.com/prometheus-community/windows_exporter/collector"
"github.com/prometheus-community/windows_exporter/config" "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"
"github.com/prometheus/client_golang/prometheus/collectors" "github.com/prometheus/client_golang/prometheus/collectors"
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
@@ -50,7 +52,6 @@ type prometheusVersion struct {
const ( const (
defaultCollectors = "cpu,cs,logical_disk,net,os,service,system,textfile" defaultCollectors = "cpu,cs,logical_disk,net,os,service,system,textfile"
defaultCollectorsPlaceholder = "[defaults]" defaultCollectorsPlaceholder = "[defaults]"
serviceName = "windows_exporter"
) )
var ( 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.", "Seconds to subtract from the timeout allowed by the client. Tune to allow for overhead or high loads.",
).Default("0.5").Float64() ).Default("0.5").Float64()
) )
log.AddFlags(kingpin.CommandLine) log.AddFlags(kingpin.CommandLine)
kingpin.Version(version.Print("windows_exporter")) kingpin.Version(version.Print("windows_exporter"))
kingpin.HelpFlag.Short('h') kingpin.HelpFlag.Short('h')
@@ -297,7 +297,7 @@ func main() {
// Load values from configuration file(s). Executable flags must first be parsed, in order // Load values from configuration file(s). Executable flags must first be parsed, in order
// to load the specified file(s). // to load the specified file(s).
kingpin.Parse() kingpin.Parse()
log.Debug("Logging has Started")
if *configFile != "" { if *configFile != "" {
resolver, err := config.NewResolver(*configFile) resolver, err := config.NewResolver(*configFile)
if err != nil { if err != nil {
@@ -327,21 +327,6 @@ func main() {
initWbem() 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) collectors, err := loadCollectors(*enabledCollectors)
if err != nil { if err != nil {
log.Fatalf("Couldn't load collectors: %s", err) log.Fatalf("Couldn't load collectors: %s", err)
@@ -421,7 +406,7 @@ func main() {
}() }()
for { for {
if <-stopCh { if <-initiate.StopCh {
log.Info("Shutting down windows_exporter") log.Info("Shutting down windows_exporter")
break 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 { type metricsHandler struct {
timeoutMargin float64 timeoutMargin float64
collectorFactory func(timeout time.Duration, requestedCollectors []string) (error, *windowsCollector) 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)
}
}()
}
}