Skip to content

Commit

Permalink
Merge pull request prometheus-community#1047 from jammiemil/master
Browse files Browse the repository at this point in the history
fix Windows Service timeout during high CPU (eg. post Windows Update)
  • Loading branch information
breed808 authored Aug 24, 2022
2 parents 23c379e + 135b746 commit b5f9ee0
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 49 deletions.
56 changes: 7 additions & 49 deletions exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"
Expand Down Expand Up @@ -50,7 +52,6 @@ type prometheusVersion struct {
const (
defaultCollectors = "cpu,cs,logical_disk,net,os,service,system,textfile"
defaultCollectorsPlaceholder = "[defaults]"
serviceName = "windows_exporter"
)

var (
Expand Down Expand Up @@ -289,15 +290,14 @@ 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')

// 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 {
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -421,7 +406,7 @@ func main() {
}()

for {
if <-stopCh {
if <-initiate.StopCh {
log.Info("Shutting down windows_exporter")
break
}
Expand Down Expand Up @@ -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)
Expand Down
60 changes: 60 additions & 0 deletions initiate/initiate.go
Original file line number Diff line number Diff line change
@@ -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)
}
}()
}
}

0 comments on commit b5f9ee0

Please sign in to comment.