diff --git a/.editorconfig b/.editorconfig index dd4715ef9..688f63734 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,6 +1,16 @@ root = true -[*.wxs] +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +tab_width = 4 +[*.{json,wxs,xml}] indent_style = space -indent_size = 4 \ No newline at end of file +indent_size = 4 + +[*.{yml,yaml}] +indent_style = space +indent_size = 2 \ No newline at end of file diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 67ed1bc80..d0d752a76 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -105,4 +105,4 @@ jobs: uses: golangci/golangci-lint-action@v6 with: version: v1.60 - args: "--timeout=5m --max-same-issues=0" \ No newline at end of file + args: "--max-same-issues=0" \ No newline at end of file diff --git a/.golangci.yaml b/.golangci.yaml index f8c93f2da..65a996417 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -1,13 +1,11 @@ linters: enable-all: true disable: - - containedctx - - contextcheck - cyclop - depguard - - dogsled - dupl - err113 + - execinquery - exhaustive - exhaustruct - exportloopref @@ -17,21 +15,16 @@ linters: - gocognit - goconst - gocyclo - - godox - - inamedparam - - ireturn + - gomnd - lll + - maintidx - mnd - - nlreturn - - noctx - testpackage - varnamelen - wrapcheck - - wsl - - execinquery - - gomnd - - stylecheck - - maintidx + +run: + timeout: 5m linters-settings: gosec: @@ -52,6 +45,9 @@ linters-settings: # Support string case: `camel`, `pascal`, `kebab`, `snake`, `upperSnake`, `goCamel`, `goPascal`, `goKebab`, `goSnake`, `upper`, `lower`, `header` json: camel yaml: snake + gomoddirectives: + replace-allow-list: + - github.com/prometheus/common # https://github.com/prometheus/common/pull/694 forbidigo: forbid: - "^(fmt\\.Print(|f|ln)|print|println)$" @@ -59,6 +55,23 @@ linters-settings: msg: use golang.org/x/sys/windows instead of syscall - p: "^windows\\.NewLazyDLL$" msg: use NewLazySystemDLL instead NewLazyDLL + sloglint: + no-mixed-args: true + kv-only: false + attr-only: true + no-global: "all" + context: "scope" + static-msg: false + no-raw-keys: false + key-naming-case: snake + forbidden-keys: + - time + - level + - msg + - source + args-on-sep-lines: true + stylecheck: + checks: ["all", "-ST1003"] issues: exclude: - don't use underscores in Go names @@ -72,3 +85,7 @@ issues: - text: "don't use ALL_CAPS in Go names; use CamelCase" linters: - revive + - path: pkg/perflib/ + linters: + - godox + - stylecheck diff --git a/.promu.yml b/.promu.yml index 67a904239..80678c915 100644 --- a/.promu.yml +++ b/.promu.yml @@ -1,19 +1,24 @@ go: - version: 1.20 + # Whenever the Go version is updated here, + # .github/workflows should also be updated. + version: 1.23 repository: - path: github.com/prometheus-community/windows_exporter + path: github.com/prometheus-community/windows_exporter build: - binaries: - - name: windows_exporter - ldflags: | - -X github.com/prometheus/common/version.Version={{.Version}} - -X github.com/prometheus/common/version.Revision={{.Revision}} - -X github.com/prometheus/common/version.Branch={{.Branch}} - -X github.com/prometheus/common/version.BuildUser={{user}}@{{host}} - -X github.com/prometheus/common/version.BuildDate={{date "20060102-15:04:05"}} + binaries: + - name: windows_exporter + tags: + all: + - trimpath + ldflags: | + -X github.com/prometheus/common/version.Version={{.Version}} + -X github.com/prometheus/common/version.Revision={{.Revision}} + -X github.com/prometheus/common/version.Branch={{.Branch}} + -X github.com/prometheus/common/version.BuildUser={{user}}@{{host}} + -X github.com/prometheus/common/version.BuildDate={{date "20060102-15:04:05"}} tarball: - files: - - LICENSE + files: + - LICENSE crossbuild: - platforms: - - windows + platforms: + - windows diff --git a/exporter.go b/exporter.go index 4bd28de06..7d0c393e5 100644 --- a/exporter.go +++ b/exporter.go @@ -11,9 +11,9 @@ import ( "github.com/prometheus-community/windows_exporter/pkg/initiate" "context" - "encoding/json" "errors" "fmt" + "log/slog" "net/http" "net/http/pprof" "os" @@ -25,7 +25,6 @@ import ( "time" "github.com/alecthomas/kingpin/v2" - "github.com/go-kit/log/level" "github.com/prometheus-community/windows_exporter/pkg/collector" "github.com/prometheus-community/windows_exporter/pkg/config" "github.com/prometheus-community/windows_exporter/pkg/httphandler" @@ -39,17 +38,6 @@ import ( "golang.org/x/sys/windows" ) -// 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"` -} - // Mapping of priority names to uin32 values required by windows.SetPriorityClass. var priorityStringToInt = map[string]uint32{ "realtime": windows.REALTIME_PRIORITY_CLASS, @@ -60,29 +48,13 @@ var priorityStringToInt = map[string]uint32{ "low": windows.IDLE_PRIORITY_CLASS, } -func setPriorityWindows(pid int, priority uint32) error { - // 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, - false, uint32(pid), - ) - if err != nil { - return err - } - - if err = windows.SetPriorityClass(handle, priority); err != nil { - return err - } - - if err = windows.CloseHandle(handle); err != nil { - return fmt.Errorf("failed to close handle: %w", err) - } - - return nil +func main() { + os.Exit(run()) } -func main() { +func run() int { app := kingpin.New("windows_exporter", "A metrics collector for Windows.") + var ( configFile = app.Flag( "config.file", @@ -138,25 +110,41 @@ func main() { // Load values from configuration file(s). Executable flags must first be parsed, in order // to load the specified file(s). - kingpin.MustParse(app.Parse(os.Args[1:])) + if _, err := app.Parse(os.Args[1:]); err != nil { + //nolint:sloglint // we do not have an logger yet + slog.Error("Failed to parse CLI args", + slog.Any("err", err), + ) + + return 1 + } + logger, err := winlog.New(winlogConfig) if err != nil { - _ = level.Error(logger).Log("err", err) - os.Exit(1) - } + //nolint:sloglint // we do not have an logger yet + slog.Error("failed to create logger", + slog.Any("err", err), + ) - _ = level.Debug(logger).Log("msg", "Logging has Started") + return 1 + } if *configFile != "" { resolver, err := config.NewResolver(*configFile, logger, *insecureSkipVerify) if err != nil { - _ = level.Error(logger).Log("msg", "could not load config file", "err", err) - os.Exit(1) + logger.Error("could not load config file", + slog.Any("err", err), + ) + + return 1 } - err = resolver.Bind(app, os.Args[1:]) - if err != nil { - _ = level.Error(logger).Log("err", err) - os.Exit(1) + + if err = resolver.Bind(app, os.Args[1:]); err != nil { + logger.Error("Failed to bind configuration", + slog.Any("err", err), + ) + + return 1 } // NOTE: This is temporary fix for issue #1092, calling kingpin.Parse @@ -165,34 +153,43 @@ func main() { *webConfig.WebListenAddresses = (*webConfig.WebListenAddresses)[1:] // Parse flags once more to include those discovered in configuration file(s). - kingpin.MustParse(app.Parse(os.Args[1:])) + if _, err = app.Parse(os.Args[1:]); err != nil { + logger.Error("Failed to parse CLI args from YAML file", + slog.Any("err", err), + ) + + return 1 + } logger, err = winlog.New(winlogConfig) if err != nil { - _ = level.Error(logger).Log("err", err) - os.Exit(1) + //nolint:sloglint // we do not have an logger yet + slog.Error("failed to create logger", + slog.Any("err", err), + ) + + return 1 } } - if *printCollectors { - collectorNames := collector.Available() - sort.Strings(collectorNames) + logger.Debug("Logging has Started") - fmt.Printf("Available collectors:\n") //nolint:forbidigo - for _, n := range collectorNames { - fmt.Printf(" - %s\n", n) //nolint:forbidigo - } + if *printCollectors { + printCollectorsToStdout() - return + return 0 } // Only set process priority if a non-default and valid value has been set - if *processPriority != "normal" && priorityStringToInt[*processPriority] != 0 { - _ = level.Debug(logger).Log("msg", "setting process priority to "+*processPriority) - err = setPriorityWindows(os.Getpid(), priorityStringToInt[*processPriority]) - if err != nil { - _ = level.Error(logger).Log("msg", "failed to set process priority", "err", err) - os.Exit(1) + if priority, ok := priorityStringToInt[*processPriority]; ok && priority != windows.NORMAL_PRIORITY_CLASS { + logger.Debug("setting process priority to " + *processPriority) + + if err = setPriorityWindows(os.Getpid(), priority); err != nil { + logger.Error("failed to set process priority", + slog.Any("err", err), + ) + + return 1 } } @@ -200,60 +197,35 @@ func main() { collectors.Enable(enabledCollectorList) // Initialize collectors before loading - err = collectors.Build(logger) - if err != nil { - _ = level.Error(logger).Log("msg", "Couldn't load collectors", "err", err) - os.Exit(1) - } - err = collectors.SetPerfCounterQuery(logger) - if err != nil { - _ = level.Error(logger).Log("msg", "Couldn't set performance counter query", "err", err) - os.Exit(1) + if err = collectors.Build(logger); err != nil { + logger.Error("Couldn't load collectors", + slog.Any("err", err), + ) + + return 1 } - if u, err := user.Current(); err != nil { - _ = level.Warn(logger).Log("msg", "Unable to determine which user is running this exporter. More info: https://github.com/golang/go/issues/37348") - } else { - _ = level.Info(logger).Log("msg", fmt.Sprintf("Running as %v", u.Username)) + if err = collectors.SetPerfCounterQuery(logger); err != nil { + logger.Error("Couldn't set performance counter query", + slog.Any("err", err), + ) - if strings.Contains(u.Username, "ContainerAdministrator") || strings.Contains(u.Username, "ContainerUser") { - _ = level.Warn(logger).Log("msg", "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.") - } + return 1 } - _ = level.Info(logger).Log("msg", fmt.Sprintf("Enabled collectors: %v", strings.Join(enabledCollectorList, ", "))) + logCurrentUser(logger) + + logger.Info("Enabled collectors: " + strings.Join(enabledCollectorList, ", ")) mux := http.NewServeMux() + mux.Handle("GET /health", httphandler.NewHealthHandler()) + mux.Handle("GET /version", httphandler.NewVersionHandler()) mux.Handle("GET "+*metricsPath, httphandler.New(logger, collectors, &httphandler.Options{ DisableExporterMetrics: *disableExporterMetrics, TimeoutMargin: *timeoutMargin, MaxRequests: *maxRequests, })) - mux.HandleFunc("GET /health", func(w http.ResponseWriter, _ *http.Request) { - w.Header().Set("Content-Type", "application/json") - _, err := fmt.Fprintln(w, `{"status":"ok"}`) - if err != nil { - _ = level.Debug(logger).Log("msg", "Failed to write to stream", "err", err) - } - }) - - mux.HandleFunc("GET /version", func(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) - } - }) - if *debugEnabled { mux.HandleFunc("GET /debug/pprof/", pprof.Index) mux.HandleFunc("GET /debug/pprof/cmdline", pprof.Cmdline) @@ -262,9 +234,14 @@ func main() { mux.HandleFunc("GET /debug/pprof/trace", pprof.Trace) } - _ = level.Info(logger).Log("msg", "Starting windows_exporter", "version", version.Info()) - _ = level.Info(logger).Log("msg", "Build context", "build_context", version.BuildContext()) - _ = level.Debug(logger).Log("msg", "Go MAXPROCS", "procs", runtime.GOMAXPROCS(0)) + logger.Info("Starting windows_exporter", + slog.String("version", version.Version), + slog.String("branch", version.Branch), + slog.String("revision", version.GetRevision()), + slog.String("goversion", version.GoVersion), + slog.String("builddate", version.BuildDate), + slog.Int("maxprocs", runtime.GOMAXPROCS(0)), + ) server := &http.Server{ ReadHeaderTimeout: 5 * time.Second, @@ -274,11 +251,14 @@ func main() { Handler: mux, } + errCh := make(chan error, 1) + go func() { if err := web.ListenAndServe(server, webConfig, logger); err != nil && !errors.Is(err, http.ErrServerClosed) { - _ = level.Error(logger).Log("msg", "cannot start windows_exporter", "err", err) - os.Exit(1) + errCh <- err } + + errCh <- nil }() ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, os.Kill) @@ -286,9 +266,17 @@ func main() { select { case <-ctx.Done(): - _ = level.Info(logger).Log("msg", "Shutting down windows_exporter via kill signal") + logger.Info("Shutting down windows_exporter via kill signal") case <-initiate.StopCh: - _ = level.Info(logger).Log("msg", "Shutting down windows_exporter via service control") + logger.Info("Shutting down windows_exporter via service control") + case err := <-errCh: + if err != nil { + logger.Error("Failed to start windows_exporter", + slog.Any("err", err), + ) + + return 1 + } } ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) @@ -296,5 +284,53 @@ func main() { _ = server.Shutdown(ctx) - _ = level.Info(logger).Log("msg", "windows_exporter has shut down") + logger.Info("windows_exporter has shut down") + + return 0 +} + +func printCollectorsToStdout() { + collectorNames := collector.Available() + sort.Strings(collectorNames) + + fmt.Println("Available collectors:") //nolint:forbidigo + + for _, n := range collectorNames { + fmt.Printf(" - %s\n", n) //nolint:forbidigo + } +} + +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.") + } + + return + } + + logger.Warn("Unable to determine which user is running this exporter. More info: https://github.com/golang/go/issues/37348") +} + +func setPriorityWindows(pid int, priority uint32) error { + // 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, + false, uint32(pid), + ) + if err != nil { + return fmt.Errorf("failed to open own process: %w", err) + } + + if err = windows.SetPriorityClass(handle, priority); err != nil { + return fmt.Errorf("failed to set priority class: %w", err) + } + + if err = windows.CloseHandle(handle); err != nil { + return fmt.Errorf("failed to close handle: %w", err) + } + + return nil } diff --git a/go.mod b/go.mod index 21202ca8f..b00027939 100644 --- a/go.mod +++ b/go.mod @@ -6,17 +6,15 @@ require ( github.com/Microsoft/hcsshim v0.12.6 github.com/alecthomas/kingpin/v2 v2.4.0 github.com/dimchansky/utfbom v1.1.1 - github.com/go-kit/log v0.2.1 github.com/go-ole/go-ole v1.3.0 github.com/google/uuid v1.6.0 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.20.3 github.com/prometheus/client_model v0.6.1 github.com/prometheus/common v0.59.1 - github.com/prometheus/exporter-toolkit v0.12.0 + github.com/prometheus/exporter-toolkit v0.13.0 github.com/stretchr/testify v1.9.0 github.com/yusufpapurcu/wmi v1.2.4 - golang.org/x/exp v0.0.0-20231006140011-7918f672742d golang.org/x/sys v0.25.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -30,7 +28,6 @@ require ( github.com/containerd/errdefs v0.1.0 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/jpillora/backoff v1.0.0 // indirect github.com/klauspost/compress v1.17.9 // indirect @@ -53,3 +50,6 @@ require ( google.golang.org/protobuf v1.34.2 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) + +// https://github.com/prometheus/common/pull/694 +replace github.com/prometheus/common v0.59.1 => github.com/jkroepke/prometheus-common v0.0.0-20240907211841-5f9af24b97ad diff --git a/go.sum b/go.sum index 8de4ef7de..7b77742b7 100644 --- a/go.sum +++ b/go.sum @@ -30,10 +30,6 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= -github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= -github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= @@ -63,6 +59,8 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/jkroepke/prometheus-common v0.0.0-20240907211841-5f9af24b97ad h1:sFDfDs4nDXjES8PdrFPiXeYt8dtaxn10M/Ebxe4IuiI= +github.com/jkroepke/prometheus-common v0.0.0-20240907211841-5f9af24b97ad/go.mod h1:GpWM7dewqmVYcd7SmRaiWVe9SSqjf0UrwnYnpEZNuT0= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= @@ -90,10 +88,8 @@ github.com/prometheus/client_golang v1.20.3/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/j github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/common v0.59.1 h1:LXb1quJHWm1P6wq/U824uxYi4Sg0oGvNeUm1z5dJoX0= -github.com/prometheus/common v0.59.1/go.mod h1:GpWM7dewqmVYcd7SmRaiWVe9SSqjf0UrwnYnpEZNuT0= -github.com/prometheus/exporter-toolkit v0.12.0 h1:DkE5RcEZR3lQA2QD5JLVQIf41dFKNsVMXFhgqcif7fo= -github.com/prometheus/exporter-toolkit v0.12.0/go.mod h1:fQH0KtTn0yrrS0S82kqppRjDDiwMfIQUwT+RBRRhwUc= +github.com/prometheus/exporter-toolkit v0.13.0 h1:lmA0Q+8IaXgmFRKw09RldZmZdnvu9wwcDLIXGmTPw1c= +github.com/prometheus/exporter-toolkit v0.13.0/go.mod h1:2uop99EZl80KdXhv/MxVI2181fMcwlsumFOqBecGkG0= github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= @@ -122,8 +118,6 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= diff --git a/pkg/collector/ad/ad.go b/pkg/collector/ad/ad.go index 9a360fa98..4d3dfd5a4 100644 --- a/pkg/collector/ad/ad.go +++ b/pkg/collector/ad/ad.go @@ -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(_ log.Logger) 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") } diff --git a/pkg/collector/adcs/adcs.go b/pkg/collector/adcs/adcs.go index d425f07f2..d89d0611b 100644 --- a/pkg/collector/adcs/adcs.go +++ b/pkg/collector/adcs/adcs.go @@ -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(_ log.Logger) 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") } diff --git a/pkg/collector/adfs/adfs.go b/pkg/collector/adfs/adfs.go index 2b30d7f54..722e8d536 100644 --- a/pkg/collector/adfs/adfs.go +++ b/pkg/collector/adfs/adfs.go @@ -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(_ log.Logger) 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 } diff --git a/pkg/collector/cache/cache.go b/pkg/collector/cache/cache.go index 1632fa523..6e33351cb 100644 --- a/pkg/collector/cache/cache.go +++ b/pkg/collector/cache/cache.go @@ -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(_ log.Logger) 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 } diff --git a/pkg/collector/collector.go b/pkg/collector/collector.go index 4d2f5acd2..88cb73560 100644 --- a/pkg/collector/collector.go +++ b/pkg/collector/collector.go @@ -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" @@ -151,7 +151,7 @@ func New(collectors Map) *MetricCollectors { } } -func (c *MetricCollectors) SetPerfCounterQuery(logger log.Logger) error { +func (c *MetricCollectors) SetPerfCounterQuery(logger *slog.Logger) error { var ( err error @@ -190,7 +190,7 @@ func (c *MetricCollectors) Enable(enabledCollectors []string) { } // Build To be called by the exporter for collector initialization. -func (c *MetricCollectors) Build(logger log.Logger) error { +func (c *MetricCollectors) Build(logger *slog.Logger) error { var err error c.WMIClient.SWbemServicesClient, err = wmi.InitializeSWbemServices(c.WMIClient) @@ -222,11 +222,11 @@ func (c *MetricCollectors) PrepareScrapeContext() (*types.ScrapeContext, error) } // Close To be called by the exporter for collector cleanup. -func (c *MetricCollectors) Close() error { +func (c *MetricCollectors) Close(logger *slog.Logger) error { errs := make([]error, 0, len(c.Collectors)) for _, collector := range c.Collectors { - if err := collector.Close(nil); err != nil { + if err := collector.Close(logger); err != nil { errs = append(errs, err) } } diff --git a/pkg/collector/container/container.go b/pkg/collector/container/container.go index 29d032309..023e52015 100644 --- a/pkg/collector/container/container.go +++ b/pkg/collector/container/container.go @@ -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(_ log.Logger) 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 } diff --git a/pkg/collector/cpu/cpu.go b/pkg/collector/cpu/cpu.go index 2c52fa6c4..f88d507c5 100644 --- a/pkg/collector/cpu/cpu.go +++ b/pkg/collector/cpu/cpu.go @@ -3,10 +3,10 @@ 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/client_golang/prometheus" @@ -58,15 +58,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{"Processor Information"}, nil } -func (c *Collector) Close(_ log.Logger) 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", @@ -181,8 +181,8 @@ 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)) return c.CollectFull(ctx, logger, ch) } @@ -216,9 +216,10 @@ 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 @@ -230,6 +231,7 @@ func (c *Collector) CollectFull(ctx *types.ScrapeContext, logger log.Logger, ch if strings.Contains(strings.ToLower(cpu.Name), "_total") { continue } + core := cpu.Name coreCount++ diff --git a/pkg/collector/cpu_info/cpu_info.go b/pkg/collector/cpu_info/cpu_info.go index 65ed45ca7..3d123c9d8 100644 --- a/pkg/collector/cpu_info/cpu_info.go +++ b/pkg/collector/cpu_info/cpu_info.go @@ -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(_ log.Logger) 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") } diff --git a/pkg/collector/cs/cs.go b/pkg/collector/cs/cs.go index e45d8262a..21d6fe550 100644 --- a/pkg/collector/cs/cs.go +++ b/pkg/collector/cs/cs.go @@ -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" @@ -53,20 +53,19 @@ 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(_ log.Logger) error { +func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(logger log.Logger, _ *wmi.Client) error { - _ = level.Warn(logger). - Log("msg", "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.") +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"), @@ -90,17 +89,22 @@ func (c *Collector) Build(logger 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 } @@ -130,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 diff --git a/pkg/collector/dfsr/dfsr.go b/pkg/collector/dfsr/dfsr.go index ab6a04654..9e2e09bd0 100644 --- a/pkg/collector/dfsr/dfsr.go +++ b/pkg/collector/dfsr/dfsr.go @@ -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(_ log.Logger) 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 } diff --git a/pkg/collector/dhcp/dhcp.go b/pkg/collector/dhcp/dhcp.go index e5c2dd2ca..a715f54b5 100644 --- a/pkg/collector/dhcp/dhcp.go +++ b/pkg/collector/dhcp/dhcp.go @@ -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(_ log.Logger) 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 } diff --git a/pkg/collector/diskdrive/diskdrive.go b/pkg/collector/diskdrive/diskdrive.go index d4dccaf0d..276072c77 100644 --- a/pkg/collector/diskdrive/diskdrive.go +++ b/pkg/collector/diskdrive/diskdrive.go @@ -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(_ log.Logger) 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") } diff --git a/pkg/collector/dns/dns.go b/pkg/collector/dns/dns.go index 742877b58..d6e65cf63 100644 --- a/pkg/collector/dns/dns.go +++ b/pkg/collector/dns/dns.go @@ -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(_ log.Logger) 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") } diff --git a/pkg/collector/exchange/exchange.go b/pkg/collector/exchange/exchange.go index a1d06f21e..263be7343 100644 --- a/pkg/collector/exchange/exchange.go +++ b/pkg/collector/exchange/exchange.go @@ -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(_ log.Logger) 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 } diff --git a/pkg/collector/fsrmquota/fsrmquota.go b/pkg/collector/fsrmquota/fsrmquota.go index 889b5e388..10f15f8d9 100644 --- a/pkg/collector/fsrmquota/fsrmquota.go +++ b/pkg/collector/fsrmquota/fsrmquota.go @@ -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(_ log.Logger) 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 } diff --git a/pkg/collector/hyperv/hyperv.go b/pkg/collector/hyperv/hyperv.go index dcc533b8c..1a474e68d 100644 --- a/pkg/collector/hyperv/hyperv.go +++ b/pkg/collector/hyperv/hyperv.go @@ -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(_ log.Logger) 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 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 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 :Hv VP 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, ":Hv VP ")) + logger.Warn(fmt.Sprintf("Unexpected format of Name in collectVmCpuUsage: %q, expected %q. Skipping.", obj.Name, ":Hv VP ")) + 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 ")) + logger.Warn(fmt.Sprintf("Unexpected format of core identifier in collectVmCpuUsage: %q, expected %q. Skipping.", parts[1], "Hv VP ")) + continue } + vmName := parts[0] coreId := coreParts[2] diff --git a/pkg/collector/iis/iis.go b/pkg/collector/iis/iis.go index be3dbe5a3..378ec6e80 100644 --- a/pkg/collector/iis/iis.go +++ b/pkg/collector/iis/iis.go @@ -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(_ log.Logger) 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) + logger.Warn("Couldn't open registry to determine IIS version", + slog.Any("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)) + 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 := 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 := 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 } diff --git a/pkg/collector/license/license.go b/pkg/collector/license/license.go index b4b089e55..2d87c2882 100644 --- a/pkg/collector/license/license.go +++ b/pkg/collector/license/license.go @@ -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(_ log.Logger) 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 } diff --git a/pkg/collector/logical_disk/logical_disk.go b/pkg/collector/logical_disk/logical_disk.go index 68a66d067..eb2cd8bb6 100644 --- a/pkg/collector/logical_disk/logical_disk.go +++ b/pkg/collector/logical_disk/logical_disk.go @@ -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(_ log.Logger) 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) diff --git a/pkg/collector/logon/logon.go b/pkg/collector/logon/logon.go index c2d66e34b..beebdd0cc 100644 --- a/pkg/collector/logon/logon.go +++ b/pkg/collector/logon/logon.go @@ -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(_ log.Logger) 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 } diff --git a/pkg/collector/memory/memory.go b/pkg/collector/memory/memory.go index 3f38f4c87..b563f53fa 100644 --- a/pkg/collector/memory/memory.go +++ b/pkg/collector/memory/memory.go @@ -8,10 +8,9 @@ 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" @@ -89,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(_ log.Logger) 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"+ @@ -326,18 +325,24 @@ 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)) errs := make([]error, 0, 2) if err := c.collectPerformanceData(ctx, logger, ch); err != nil { - _ = level.Error(logger).Log("msg", "failed collecting memory metrics", "err", err) + logger.Error("failed collecting memory metrics", + slog.Any("err", err), + ) + errs = append(errs, err) } if err := c.collectGlobalMemoryStatus(ch); err != nil { - _ = level.Error(logger).Log("msg", "failed collecting memory metrics", "err", err) + logger.Error("failed collecting memory metrics", + slog.Any("err", err), + ) + errs = append(errs, err) } @@ -408,9 +413,11 @@ type memory struct { WriteCopiesPersec float64 `perflib:"Write Copies/sec"` } -func (c *Collector) collectPerformanceData(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 } diff --git a/pkg/collector/mscluster/mscluster.go b/pkg/collector/mscluster/mscluster.go index f86d66158..69eedab76 100644 --- a/pkg/collector/mscluster/mscluster.go +++ b/pkg/collector/mscluster/mscluster.go @@ -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(_ log.Logger) 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 } diff --git a/pkg/collector/msmq/msmq.go b/pkg/collector/msmq/msmq.go index ceab9d712..91f1a1a3c 100644 --- a/pkg/collector/msmq/msmq.go +++ b/pkg/collector/msmq/msmq.go @@ -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(_ log.Logger) 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 } diff --git a/pkg/collector/mssql/mssql.go b/pkg/collector/mssql/mssql.go index 60d68214f..a36331437 100644 --- a/pkg/collector/mssql/mssql.go +++ b/pkg/collector/mssql/mssql.go @@ -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(_ log.Logger) 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 diff --git a/pkg/collector/net/net.go b/pkg/collector/net/net.go index b93c509d5..4bf449336 100644 --- a/pkg/collector/net/net.go +++ b/pkg/collector/net/net.go @@ -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(_ log.Logger) 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 { diff --git a/pkg/collector/netframework_clrexceptions/netframework_clrexceptions.go b/pkg/collector/netframework_clrexceptions/netframework_clrexceptions.go index 7c9f3d94a..51575320e 100644 --- a/pkg/collector/netframework_clrexceptions/netframework_clrexceptions.go +++ b/pkg/collector/netframework_clrexceptions/netframework_clrexceptions.go @@ -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(_ log.Logger) 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 } diff --git a/pkg/collector/netframework_clrinterop/netframework_clrinterop.go b/pkg/collector/netframework_clrinterop/netframework_clrinterop.go index 2611a0665..0e049f24b 100644 --- a/pkg/collector/netframework_clrinterop/netframework_clrinterop.go +++ b/pkg/collector/netframework_clrinterop/netframework_clrinterop.go @@ -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(_ log.Logger) 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 } diff --git a/pkg/collector/netframework_clrjit/netframework_clrjit.go b/pkg/collector/netframework_clrjit/netframework_clrjit.go index e60a9ccb7..6d92670e4 100644 --- a/pkg/collector/netframework_clrjit/netframework_clrjit.go +++ b/pkg/collector/netframework_clrjit/netframework_clrjit.go @@ -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(_ log.Logger) 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 } diff --git a/pkg/collector/netframework_clrloading/netframework_clrloading.go b/pkg/collector/netframework_clrloading/netframework_clrloading.go index b0f3c5e53..2ee2a9b61 100644 --- a/pkg/collector/netframework_clrloading/netframework_clrloading.go +++ b/pkg/collector/netframework_clrloading/netframework_clrloading.go @@ -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(_ log.Logger) 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 } diff --git a/pkg/collector/netframework_clrlocksandthreads/netframework_clrlocksandthreads.go b/pkg/collector/netframework_clrlocksandthreads/netframework_clrlocksandthreads.go index 66e8408b4..c12ed09f3 100644 --- a/pkg/collector/netframework_clrlocksandthreads/netframework_clrlocksandthreads.go +++ b/pkg/collector/netframework_clrlocksandthreads/netframework_clrlocksandthreads.go @@ -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(_ log.Logger) 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 } diff --git a/pkg/collector/netframework_clrmemory/netframework_clrmemory.go b/pkg/collector/netframework_clrmemory/netframework_clrmemory.go index 28babd3eb..d5ddf8c17 100644 --- a/pkg/collector/netframework_clrmemory/netframework_clrmemory.go +++ b/pkg/collector/netframework_clrmemory/netframework_clrmemory.go @@ -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(_ log.Logger) 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 } diff --git a/pkg/collector/netframework_clrremoting/netframework_clrremoting.go b/pkg/collector/netframework_clrremoting/netframework_clrremoting.go index 8e4da93d3..13eb00894 100644 --- a/pkg/collector/netframework_clrremoting/netframework_clrremoting.go +++ b/pkg/collector/netframework_clrremoting/netframework_clrremoting.go @@ -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(_ log.Logger) 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 } diff --git a/pkg/collector/netframework_clrsecurity/netframework_clrsecurity.go b/pkg/collector/netframework_clrsecurity/netframework_clrsecurity.go index 95f330915..24f1ab23e 100644 --- a/pkg/collector/netframework_clrsecurity/netframework_clrsecurity.go +++ b/pkg/collector/netframework_clrsecurity/netframework_clrsecurity.go @@ -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(_ log.Logger) 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 } diff --git a/pkg/collector/nps/nps.go b/pkg/collector/nps/nps.go index 8f631ca76..607455c76 100644 --- a/pkg/collector/nps/nps.go +++ b/pkg/collector/nps/nps.go @@ -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(_ log.Logger) 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 } diff --git a/pkg/collector/os/os.go b/pkg/collector/os/os.go index 76067c8c8..feb483021 100644 --- a/pkg/collector/os/os.go +++ b/pkg/collector/os/os.go @@ -5,14 +5,13 @@ package os import ( "errors" "fmt" + "log/slog" "os" "strconv" "strings" "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" @@ -102,18 +101,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{"Paging File"}, nil } -func (c *Collector) Close(_ log.Logger) error { +func (c *Collector) Close(_ *slog.Logger) error { return nil } -func (c *Collector) Build(logger log.Logger, _ *wmi.Client) error { - _ = level.Warn(logger). - Log("msg", "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.") +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 { @@ -221,40 +219,56 @@ func (c *Collector) Build(logger 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)) errs := make([]error, 0, 5) c.collect(ch) if err := c.collectHostname(ch); err != nil { - _ = level.Error(logger).Log("msg", "failed collecting os metrics", "err", err) + logger.Error("failed collecting os metrics", + slog.Any("err", err), + ) + errs = append(errs, err) } if err := c.collectLoggedInUserCount(ch); err != nil { - _ = level.Error(logger).Log("msg", "failed collecting os user count metrics", "err", err) + logger.Error("failed collecting os user count metrics", + slog.Any("err", err), + ) + errs = append(errs, err) } if err := c.collectMemory(ch); err != nil { - _ = level.Error(logger).Log("msg", "failed collecting os memory metrics", "err", err) + logger.Error("failed collecting os memory metrics", + slog.Any("err", err), + ) + errs = append(errs, err) } if err := c.collectTime(ch); err != nil { - _ = level.Error(logger).Log("msg", "failed collecting os time metrics", "err", err) + logger.Error("failed collecting os time metrics", + slog.Any("err", err), + ) + errs = append(errs, err) } if err := c.collectPaging(ctx, logger, ch); err != nil { - _ = level.Error(logger).Log("msg", "failed collecting os paging metrics", "err", err) + logger.Error("failed collecting os paging metrics", + slog.Any("err", err), + ) + errs = append(errs, err) } @@ -281,10 +295,12 @@ func (c *Collector) collectHostname(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 @@ -366,7 +382,7 @@ func (c *Collector) collectMemory(ch chan<- prometheus.Metric) error { return nil } -func (c *Collector) collectPaging(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error { +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 { @@ -378,12 +394,13 @@ func (c *Collector) collectPaging(ctx *types.ScrapeContext, logger log.Logger, c 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()) } @@ -401,10 +418,12 @@ func (c *Collector) collectPaging(ctx *types.ScrapeContext, logger log.Logger, c // Get current page file usage. var pfbRaw float64 + for _, pageFile := range pfc { if strings.Contains(strings.ToLower(pageFile.Name), "_total") { continue } + pfbRaw += pageFile.Usage } @@ -424,7 +443,7 @@ func (c *Collector) collectPaging(ctx *types.ScrapeContext, logger log.Logger, c 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( diff --git a/pkg/collector/perfdata/perfdata.go b/pkg/collector/perfdata/perfdata.go index 6efcbb243..f37e9c057 100644 --- a/pkg/collector/perfdata/perfdata.go +++ b/pkg/collector/perfdata/perfdata.go @@ -5,13 +5,12 @@ package perfdata import ( "encoding/json" "fmt" + "log/slog" "maps" "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/perfdata" "github.com/prometheus-community/windows_exporter/pkg/types" "github.com/prometheus/client_golang/prometheus" @@ -82,11 +81,11 @@ 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(_ log.Logger) error { +func (c *Collector) Close(_ *slog.Logger) error { for _, object := range c.config.Objects { object.collector.Close() } @@ -94,8 +93,8 @@ func (c *Collector) Close(_ log.Logger) error { return nil } -func (c *Collector) Build(logger log.Logger, _ *wmi.Client) error { - _ = level.Warn(logger).Log("msg", "The perfdata collector is in an experimental state! The configuration may change in future. Please report any issues.") +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))) @@ -115,11 +114,15 @@ 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(_ *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error { +func (c *Collector) Collect(_ *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error { if err := c.collect(ch); err != nil { - _ = level.Error(logger).Log("msg", "failed collecting performance data metrics", "err", err) + logger.Error("failed collecting performance data metrics", + slog.Any("err", err), + ) + return err } + return nil } @@ -138,6 +141,7 @@ func (c *Collector) collect(ch chan<- prometheus.Metric) error { } metricType := value.Type + if val, ok := object.Counters[counter]; ok { switch val.Type { case "counter": diff --git a/pkg/collector/perfdata/perfdata_collector_test.go b/pkg/collector/perfdata/perfdata_collector_test.go index 973518ddf..eddbd2b10 100644 --- a/pkg/collector/perfdata/perfdata_collector_test.go +++ b/pkg/collector/perfdata/perfdata_collector_test.go @@ -4,13 +4,13 @@ package perfdata_test import ( "fmt" + "io" + "log/slog" "net/http" "net/http/httptest" - "os" "regexp" "testing" - "github.com/go-kit/log" "github.com/prometheus-community/windows_exporter/pkg/collector/perfdata" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" @@ -27,7 +27,9 @@ func (a collectorAdapter) Describe(_ chan<- *prometheus.Desc) {} // Collect implements the prometheus.Collector interface. func (a collectorAdapter) Collect(ch chan<- prometheus.Metric) { - if err := a.Collector.Collect(nil, log.NewLogfmtLogger(os.Stdout), ch); err != nil { + 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)) } } @@ -67,7 +69,7 @@ func TestCollector(t *testing.T) { }, }) - logger := log.NewLogfmtLogger(os.Stdout) + logger := slog.New(slog.NewTextHandler(io.Discard, nil)) err := perfDataCollector.Build(logger, nil) require.NoError(t, err) diff --git a/pkg/collector/physical_disk/physical_disk.go b/pkg/collector/physical_disk/physical_disk.go index 729d28ee2..2a5816bda 100644 --- a/pkg/collector/physical_disk/physical_disk.go +++ b/pkg/collector/physical_disk/physical_disk.go @@ -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(_ log.Logger) 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 } diff --git a/pkg/collector/printer/printer.go b/pkg/collector/printer/printer.go index 518127c4a..a22dd2bac 100644 --- a/pkg/collector/printer/printer.go +++ b/pkg/collector/printer/printer.go @@ -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(_ log.Logger) 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 } diff --git a/pkg/collector/process/process.go b/pkg/collector/process/process.go index 8b098fed3..7e422b793 100644 --- a/pkg/collector/process/process.go +++ b/pkg/collector/process/process.go @@ -5,14 +5,13 @@ package process import ( "errors" "fmt" + "log/slog" "regexp" "strconv" "strings" "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" @@ -123,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(_ log.Logger) 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") @@ -141,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( @@ -284,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 @@ -295,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), + ) } } @@ -315,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 } } @@ -322,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( @@ -484,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 } @@ -500,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) @@ -527,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)), @@ -544,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)), @@ -569,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 { @@ -582,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) diff --git a/pkg/collector/prometheus.go b/pkg/collector/prometheus.go index 0e07f35d8..9c6ef06e9 100644 --- a/pkg/collector/prometheus.go +++ b/pkg/collector/prometheus.go @@ -4,12 +4,11 @@ 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" ) @@ -20,7 +19,7 @@ var _ prometheus.Collector = (*Prometheus)(nil) // Prometheus implements prometheus.Collector for a set of Windows MetricCollectors. type Prometheus struct { maxScrapeDuration time.Duration - logger log.Logger + logger *slog.Logger metricCollectors *MetricCollectors // Base metrics returned by Prometheus @@ -46,7 +45,7 @@ const ( // NewPrometheusCollector returns a new Prometheus where the set of MetricCollectors must // return metrics within the given timeout. -func (c *MetricCollectors) NewPrometheusCollector(timeout time.Duration, logger log.Logger) *Prometheus { +func (c *MetricCollectors) NewPrometheusCollector(timeout time.Duration, logger *slog.Logger) *Prometheus { return &Prometheus{ maxScrapeDuration: timeout, metricCollectors: c, @@ -180,6 +179,7 @@ func (p *Prometheus) execute(name string, c Collector, ctx *types.ScrapeContext, // Execute the collector go func() { errCh <- c.Collect(ctx, p.logger, bufCh) + close(bufCh) }() @@ -222,18 +222,20 @@ func (p *Prometheus) execute(name string, c Collector, ctx *types.ScrapeContext, name, ) - _ = level.Warn(p.logger).Log("msg", fmt.Sprintf("collector %s timeouted after %s", name, p.maxScrapeDuration)) + p.logger.Warn(fmt.Sprintf("collector %s timeouted after %s", name, p.maxScrapeDuration)) return pending } if err != nil { - _ = level.Error(p.logger).Log("msg", fmt.Sprintf("collector %s failed after %s", 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(p.logger).Log("msg", fmt.Sprintf("collector %s succeeded after %s.", name, duration)) + p.logger.Error(fmt.Sprintf("collector %s succeeded after %s", name, p.maxScrapeDuration)) return success } diff --git a/pkg/collector/remote_fx/remote_fx.go b/pkg/collector/remote_fx/remote_fx.go index 02e63c1d0..c6ad73129 100644 --- a/pkg/collector/remote_fx/remote_fx.go +++ b/pkg/collector/remote_fx/remote_fx.go @@ -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(_ log.Logger) 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 diff --git a/pkg/collector/scheduled_task/scheduled_task.go b/pkg/collector/scheduled_task/scheduled_task.go index 353565deb..861bafe45 100644 --- a/pkg/collector/scheduled_task/scheduled_task.go +++ b/pkg/collector/scheduled_task/scheduled_task.go @@ -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(_ log.Logger) 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) diff --git a/pkg/collector/service/service.go b/pkg/collector/service/service.go index a3234d3ce..9b1f8237a 100644 --- a/pkg/collector/service/service.go +++ b/pkg/collector/service/service.go @@ -5,13 +5,12 @@ package service import ( "errors" "fmt" + "log/slog" "regexp" "strconv" "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/client_golang/prometheus" "github.com/yusufpapurcu/wmi" @@ -103,15 +102,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) 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)) if c.config.ServiceInclude.String() == "^(?:.*)$" && c.config.ServiceExclude.String() == "^(?:)$" { - _ = level.Warn(logger).Log("msg", "No filters specified for service collector. This will generate a very large number of metrics!") + logger.Warn("No filters specified for service collector. This will generate a very large number of metrics!") } c.info = prometheus.NewDesc( @@ -150,9 +149,11 @@ func (c *Collector) Build(logger log.Logger, _ *wmi.Client) error { return nil } -func (c *Collector) Close(logger log.Logger) error { +func (c *Collector) Close(logger *slog.Logger) error { if err := c.serviceManagerHandle.Disconnect(); err != nil { - _ = level.Warn(logger).Log("msg", "Failed to disconnect from scm", "err", err) + logger.Warn("Failed to disconnect from scm", + slog.Any("err", err), + ) } return nil @@ -160,11 +161,13 @@ func (c *Collector) Close(logger log.Logger) 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 API service metrics:", "err", err) + logger.Error("failed collecting API service metrics:", + slog.Any("err", err), + ) return fmt.Errorf("failed collecting API service metrics: %w", err) } @@ -172,15 +175,19 @@ func (c *Collector) Collect(_ *types.ScrapeContext, logger log.Logger, ch chan<- 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 { services, err := c.queryAllServices() if err != nil { - _ = level.Warn(logger).Log("msg", "Failed to query services", "err", err) + logger.Warn("Failed to query services", + slog.Any("err", err), + ) + return err } if services == nil { - _ = level.Warn(logger).Log("msg", "No services queried") + logger.Warn("No services queried") + return nil } @@ -193,7 +200,10 @@ func (c *Collector) collect(logger log.Logger, ch chan<- prometheus.Metric) erro } if err := c.collectService(ch, logger, service); err != nil { - _ = level.Warn(logger).Log("msg", "failed collecting service info", "err", err, "service", windows.UTF16PtrToString(service.ServiceName)) + logger.Warn("failed collecting service info", + slog.Any("err", err), + slog.String("service", windows.UTF16PtrToString(service.ServiceName)), + ) } } @@ -218,7 +228,7 @@ var apiStartModeValues = map[uint32]string{ windows.SERVICE_SYSTEM_START: "system", } -func (c *Collector) collectService(ch chan<- prometheus.Metric, logger log.Logger, service windows.ENUM_SERVICE_STATUS_PROCESS) error { +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 { @@ -231,7 +241,10 @@ func (c *Collector) collectService(ch chan<- prometheus.Metric, logger log.Logge serviceManager := &mgr.Service{Name: serviceNameString, Handle: serviceHandle} defer func(serviceManager *mgr.Service) { if err := serviceManager.Close(); err != nil { - _ = level.Warn(logger).Log("msg", "failed to close service handle", "err", err, "service", serviceNameString) + logger.Warn("failed to close service handle", + slog.Any("err", err), + slog.String("service", serviceNameString), + ) } }(serviceManager) @@ -242,7 +255,10 @@ func (c *Collector) collectService(ch chan<- prometheus.Metric, logger log.Logge return fmt.Errorf("failed to get service configuration: %w", err) } - _ = level.Debug(logger).Log("msg", "failed collecting service", "err", err, "service", serviceNameString) + logger.Debug("failed collecting service", + slog.Any("err", err), + slog.String("service", serviceNameString), + ) } ch <- prometheus.MustNewConstMetric( @@ -295,9 +311,15 @@ func (c *Collector) collectService(ch chan<- prometheus.Metric, logger log.Logge processStartTime, err := getProcessStartTime(logger, service.ServiceStatusProcess.ProcessId) if err != nil { if errors.Is(err, windows.ERROR_ACCESS_DENIED) { - _ = level.Debug(logger).Log("msg", "failed to get process start time", "err", err, "service", serviceNameString) + logger.Debug("failed to get process start time", + slog.String("service", serviceNameString), + slog.Any("err", err), + ) } else { - _ = level.Warn(logger).Log("msg", "failed to get process start time", "err", err, "service", serviceNameString) + logger.Warn("failed to get process start time", + slog.String("service", serviceNameString), + slog.Any("err", err), + ) } } else { ch <- prometheus.MustNewConstMetric( @@ -362,7 +384,7 @@ func (c *Collector) queryAllServices() ([]windows.ENUM_SERVICE_STATUS_PROCESS, e return services, nil } -func getProcessStartTime(logger log.Logger, pid uint32) (uint64, error) { +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) @@ -371,13 +393,18 @@ func getProcessStartTime(logger log.Logger, pid uint32) (uint64, error) { defer func(handle windows.Handle) { err := windows.CloseHandle(handle) if err != nil { - _ = level.Warn(logger).Log("msg", "failed to close process handle", "err", err) + 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) diff --git a/pkg/collector/smb/smb.go b/pkg/collector/smb/smb.go index 3f1b13c93..8c6d9f37e 100644 --- a/pkg/collector/smb/smb.go +++ b/pkg/collector/smb/smb.go @@ -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(_ log.Logger) 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 } diff --git a/pkg/collector/smbclient/smbclient.go b/pkg/collector/smbclient/smbclient.go index bee9992cc..498f31754 100644 --- a/pkg/collector/smbclient/smbclient.go +++ b/pkg/collector/smbclient/smbclient.go @@ -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(_ log.Logger) 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 } diff --git a/pkg/collector/smtp/smtp.go b/pkg/collector/smtp/smtp.go index 5b348dc86..aba30dee8 100644 --- a/pkg/collector/smtp/smtp.go +++ b/pkg/collector/smtp/smtp.go @@ -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(_ log.Logger) 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 } diff --git a/pkg/collector/system/system.go b/pkg/collector/system/system.go index 8a7e1b591..fc02e0c51 100644 --- a/pkg/collector/system/system.go +++ b/pkg/collector/system/system.go @@ -4,10 +4,9 @@ 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" @@ -54,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(_ log.Logger) 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)", @@ -112,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 } @@ -138,9 +142,11 @@ type system struct { 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 } diff --git a/pkg/collector/tcp/tcp.go b/pkg/collector/tcp/tcp.go index dd1e2c0fe..08fc765aa 100644 --- a/pkg/collector/tcp/tcp.go +++ b/pkg/collector/tcp/tcp.go @@ -4,10 +4,9 @@ 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/perfdata" "github.com/prometheus-community/windows_exporter/pkg/types" "github.com/prometheus/client_golang/prometheus" @@ -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(_ log.Logger) 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, @@ -145,17 +144,22 @@ 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(_ *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 tcp metrics", "err", err) + logger.Error("failed collecting tcp metrics", + slog.Any("err", err), + ) + return err } + return nil } diff --git a/pkg/collector/teradici_pcoip/teradici_pcoip.go b/pkg/collector/teradici_pcoip/teradici_pcoip.go index 66df68eb4..6484a9f93 100644 --- a/pkg/collector/teradici_pcoip/teradici_pcoip.go +++ b/pkg/collector/teradici_pcoip/teradici_pcoip.go @@ -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(_ log.Logger) 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") } diff --git a/pkg/collector/terminal_services/terminal_services.go b/pkg/collector/terminal_services/terminal_services.go index ebdedc09b..ac243621f 100644 --- a/pkg/collector/terminal_services/terminal_services.go +++ b/pkg/collector/terminal_services/terminal_services.go @@ -5,12 +5,11 @@ package terminal_services import ( "errors" "fmt" + "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/headers/wtsapi32" "github.com/prometheus-community/windows_exporter/pkg/perflib" "github.com/prometheus-community/windows_exporter/pkg/types" @@ -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 } @@ -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(_ log.Logger) 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(_ log.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)) 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) diff --git a/pkg/collector/textfile/textfile.go b/pkg/collector/textfile/textfile.go index dc4527470..498ea16ca 100644 --- a/pkg/collector/textfile/textfile.go +++ b/pkg/collector/textfile/textfile.go @@ -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(_ log.Logger) 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") } diff --git a/pkg/collector/textfile/textfile_test.go b/pkg/collector/textfile/textfile_test.go index 5ff3007dd..44b20d393 100644 --- a/pkg/collector/textfile/textfile_test.go +++ b/pkg/collector/textfile/textfile_test.go @@ -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) } diff --git a/pkg/collector/textfile/textfile_test_test.go b/pkg/collector/textfile/textfile_test_test.go index b5de5c3a2..08b061166 100644 --- a/pkg/collector/textfile/textfile_test_test.go +++ b/pkg/collector/textfile/textfile_test_test.go @@ -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) diff --git a/pkg/collector/thermalzone/thermalzone.go b/pkg/collector/thermalzone/thermalzone.go index 07209304a..ca5929523 100644 --- a/pkg/collector/thermalzone/thermalzone.go +++ b/pkg/collector/thermalzone/thermalzone.go @@ -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(_ log.Logger) 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 } diff --git a/pkg/collector/time/time.go b/pkg/collector/time/time.go index 058a81a7d..0d08c0d06 100644 --- a/pkg/collector/time/time.go +++ b/pkg/collector/time/time.go @@ -4,11 +4,10 @@ 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" @@ -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{"Windows Time Service"}, nil } -func (c *Collector) Close(_ log.Logger) 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") } @@ -119,23 +118,30 @@ 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)) errs := make([]error, 0, 2) if err := c.collectTime(ch); err != nil { - _ = level.Error(logger).Log("msg", "failed collecting time metrics", "err", err) + logger.Error("failed collecting time metrics", + slog.Any("err", err), + ) + errs = append(errs, err) } if err := c.collectNTP(ctx, logger, ch); err != nil { - _ = level.Error(logger).Log("msg", "failed collecting time ntp metrics", "err", err) + logger.Error("failed collecting time ntp metrics", + slog.Any("err", err), + ) + errs = append(errs, err) } @@ -177,9 +183,11 @@ func (c *Collector) collectTime(ch chan<- prometheus.Metric) error { return nil } -func (c *Collector) collectNTP(ctx *types.ScrapeContext, logger log.Logger, ch chan<- prometheus.Metric) error { - logger = log.With(logger, "collector", Name) +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 } diff --git a/pkg/collector/types.go b/pkg/collector/types.go index c40bb2414..9fd59230e 100644 --- a/pkg/collector/types.go +++ b/pkg/collector/types.go @@ -1,8 +1,9 @@ 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" @@ -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(logger log.Logger) 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) } diff --git a/pkg/collector/vmware/vmware.go b/pkg/collector/vmware/vmware.go index 07707350b..e1676a26b 100644 --- a/pkg/collector/vmware/vmware.go +++ b/pkg/collector/vmware/vmware.go @@ -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(_ log.Logger) 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") } diff --git a/pkg/collector/vmware_blast/vmware_blast.go b/pkg/collector/vmware_blast/vmware_blast.go index d2fe8b384..f4d1c821b 100644 --- a/pkg/collector/vmware_blast/vmware_blast.go +++ b/pkg/collector/vmware_blast/vmware_blast.go @@ -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(_ log.Logger) 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 } diff --git a/pkg/config/config.go b/pkg/config/config.go index d620a3bb4..64284ea3a 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -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) } diff --git a/pkg/config/flatten.go b/pkg/config/flatten.go index da09f6d7d..bc89d0c5b 100644 --- a/pkg/config/flatten.go +++ b/pkg/config/flatten.go @@ -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 } diff --git a/pkg/config/flatten_test.go b/pkg/config/flatten_test.go index f1bbb33d2..69d5c9dad 100644 --- a/pkg/config/flatten_test.go +++ b/pkg/config/flatten_test.go @@ -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) diff --git a/pkg/headers/netapi32/netapi32.go b/pkg/headers/netapi32/netapi32.go index b471ead02..e2eba152a 100644 --- a/pkg/headers/netapi32/netapi32.go +++ b/pkg/headers/netapi32/netapi32.go @@ -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 } diff --git a/pkg/headers/sysinfoapi/sysinfoapi.go b/pkg/headers/sysinfoapi/sysinfoapi.go index 21fa4acf0..2761f7b2b 100644 --- a/pkg/headers/sysinfoapi/sysinfoapi.go +++ b/pkg/headers/sysinfoapi/sysinfoapi.go @@ -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 } diff --git a/pkg/headers/wtsapi32/wtsapi32.go b/pkg/headers/wtsapi32/wtsapi32.go index 437391d1d..5e0ca8f4f 100644 --- a/pkg/headers/wtsapi32/wtsapi32.go +++ b/pkg/headers/wtsapi32/wtsapi32.go @@ -2,10 +2,9 @@ package wtsapi32 import ( "fmt" + "log/slog" "unsafe" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "golang.org/x/sys/windows" ) @@ -151,8 +150,9 @@ func WTSFreeMemoryEx(class WTSTypeClass, pMemory uintptr, numberOfEntries uint32 return nil } -func WTSEnumerateSessionsEx(server windows.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) @@ -172,7 +172,7 @@ func WTSEnumerateSessionsEx(server windows.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) } @@ -181,6 +181,7 @@ func WTSEnumerateSessionsEx(server windows.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) diff --git a/pkg/httphandler/health.go b/pkg/httphandler/health.go new file mode 100644 index 000000000..56dcec3b4 --- /dev/null +++ b/pkg/httphandler/health.go @@ -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"}`)) +} diff --git a/pkg/httphandler/httphandler.go b/pkg/httphandler/httphandler.go index 4e1c490ca..8efa33203 100644 --- a/pkg/httphandler/httphandler.go +++ b/pkg/httphandler/httphandler.go @@ -2,13 +2,11 @@ package httphandler import ( "fmt" - stdlog "log" + "log/slog" "net/http" "strconv" "time" - "github.com/go-kit/log" - "github.com/go-kit/log/level" "github.com/google/uuid" "github.com/prometheus-community/windows_exporter/pkg/collector" "github.com/prometheus/client_golang/prometheus" @@ -28,7 +26,7 @@ type MetricsHTTPHandler struct { // the exporter itself. exporterMetricsRegistry *prometheus.Registry - logger log.Logger + logger *slog.Logger options Options concurrencyCh chan struct{} } @@ -39,7 +37,7 @@ type Options struct { MaxRequests int } -func New(logger log.Logger, metricCollectors *collector.MetricCollectors, options *Options) *MetricsHTTPHandler { +func New(logger *slog.Logger, metricCollectors *collector.MetricCollectors, options *Options) *MetricsHTTPHandler { if options == nil { options = &Options{ DisableExporterMetrics: false, @@ -68,28 +66,37 @@ func New(logger log.Logger, metricCollectors *collector.MetricCollectors, option } func (c *MetricsHTTPHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { - logger := log.With(c.logger, "remote", r.RemoteAddr, "correlation_id", uuid.New().String()) + 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(scrapeTimeout, logger, r.URL.Query()["collect[]"]) + + handler, err := c.handlerFactory(logger, scrapeTimeout, r.URL.Query()["collect[]"]) if err != nil { - _ = level.Warn(logger).Log("msg", "Couldn't create filtered metrics handler", "err", err) + 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 log.Logger, r *http.Request) time.Duration { +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 { - _ = level.Warn(logger).Log("msg", fmt.Sprintf("Couldn't parse X-Prometheus-Scrape-Timeout-Seconds: %q. Defaulting timeout to %f", v, defaultScrapeTimeout)) + logger.Warn(fmt.Sprintf("Couldn't parse X-Prometheus-Scrape-Timeout-Seconds: %q. Defaulting timeout to %f", v, defaultScrapeTimeout)) } } @@ -102,7 +109,7 @@ func (c *MetricsHTTPHandler) getScrapeTimeout(logger log.Logger, r *http.Request return time.Duration(timeoutSeconds) * time.Second } -func (c *MetricsHTTPHandler) handlerFactory(scrapeTimeout time.Duration, logger log.Logger, requestedCollectors []string) (http.Handler, error) { +func (c *MetricsHTTPHandler) handlerFactory(logger *slog.Logger, scrapeTimeout time.Duration, requestedCollectors []string) (http.Handler, error) { reg := prometheus.NewRegistry() var metricCollectors *collector.MetricCollectors @@ -110,6 +117,7 @@ func (c *MetricsHTTPHandler) handlerFactory(scrapeTimeout time.Duration, logger metricCollectors = c.metricCollectors } else { filteredCollectors := make(collector.Map) + for _, name := range requestedCollectors { metricCollector, ok := c.metricCollectors.Collectors[name] if !ok { @@ -127,6 +135,7 @@ func (c *MetricsHTTPHandler) handlerFactory(scrapeTimeout time.Duration, logger } 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) } @@ -136,7 +145,7 @@ func (c *MetricsHTTPHandler) handlerFactory(scrapeTimeout time.Duration, logger handler = promhttp.HandlerFor( prometheus.Gatherers{c.exporterMetricsRegistry, reg}, promhttp.HandlerOpts{ - ErrorLog: stdlog.New(log.NewStdlibAdapter(level.Error(logger)), "", stdlog.Lshortfile), + ErrorLog: slog.NewLogLogger(logger.Handler(), slog.LevelError), ErrorHandling: promhttp.ContinueOnError, MaxRequestsInFlight: c.options.MaxRequests, Registry: c.exporterMetricsRegistry, @@ -152,7 +161,7 @@ func (c *MetricsHTTPHandler) handlerFactory(scrapeTimeout time.Duration, logger handler = promhttp.HandlerFor( reg, promhttp.HandlerOpts{ - ErrorLog: stdlog.New(log.NewStdlibAdapter(level.Error(logger)), "", 0), + ErrorLog: slog.NewLogLogger(logger.Handler(), slog.LevelError), ErrorHandling: promhttp.ContinueOnError, MaxRequestsInFlight: c.options.MaxRequests, }, @@ -174,6 +183,7 @@ func (c *MetricsHTTPHandler) withConcurrencyLimit(next http.HandlerFunc) http.Ha default: w.WriteHeader(http.StatusServiceUnavailable) _, _ = w.Write([]byte("Too many concurrent requests")) + return } diff --git a/pkg/httphandler/version.go b/pkg/httphandler/version.go new file mode 100644 index 000000000..fd80ca287 --- /dev/null +++ b/pkg/httphandler/version.go @@ -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) + } +} diff --git a/pkg/initiate/initiate.go b/pkg/initiate/initiate.go index 6efc1eea6..ced9332e6 100644 --- a/pkg/initiate/initiate.go +++ b/pkg/initiate/initiate.go @@ -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 { diff --git a/pkg/log/eventlog/eventlog.go b/pkg/log/eventlog/eventlog.go index 8b7fd2138..63b4876de 100644 --- a/pkg/log/eventlog/eventlog.go +++ b/pkg/log/eventlog/eventlog.go @@ -1,20 +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" - "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 ( @@ -24,109 +18,36 @@ const ( neLogOemCode = uint32(3299) ) -type Priority struct { - etype int -} - -// 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{} - }}, - } +// Interface guard. +var _ io.Writer = (*Writer)(nil) - return l +type Writer struct { + handle windows.Handle } -type eventlogLogger struct { - w *goeventlog.Log - newLogger func(io.Writer) log.Logger - prioritySelector PrioritySelector - bufPool sync.Pool +// NewEventLogWriter returns a new Writer which writes to Windows EventLog. +func NewEventLogWriter(handle windows.Handle) *Writer { + return &Writer{handle: handle} } -func (l *eventlogLogger) Log(keyvals ...interface{}) error { - priority := l.prioritySelector(keyvals...) - - lb, err := l.getLoggerBuf() - if err != nil { - return err - } +func (w *Writer) Write(p []byte) (int, error) { + var eType uint16 - defer l.putLoggerBuf(lb) - if err := lb.logger.Log(keyvals...); err != nil { - return err + 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 } - // 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 := windows.UTF16PtrFromString(lb.buf.String()) + msg, err := windows.UTF16PtrFromString(string(p)) 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) } diff --git a/pkg/log/flag/flag.go b/pkg/log/flag/flag.go index d16ffa784..d03df52a3 100644 --- a/pkg/log/flag/flag.go +++ b/pkg/log/flag/flag.go @@ -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) } diff --git a/pkg/log/logger.go b/pkg/log/logger.go index 4bf89015b..ea18c675d 100644 --- a/pkg/log/logger.go +++ b/pkg/log/logger.go @@ -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") - } - - 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, - } + config.Config.Writer = config.File.w + config.Config.Style = promslog.GoKitStyle - return promlog.NewWithLogger(l, &promlogConfig), nil + return promslog.New(config.Config), nil } diff --git a/pkg/perfdata/collector.go b/pkg/perfdata/collector.go index efd3e5aa3..acd602646 100644 --- a/pkg/perfdata/collector.go +++ b/pkg/perfdata/collector.go @@ -96,6 +96,7 @@ func NewCollector(object string, instances []string, counters []string) (*Collec if ret := PdhGetCounterTimeBase(counterHandle, &frequency); ret != ErrorSuccess { return nil, fmt.Errorf("PdhGetCounterTimeBase: %w", NewPdhError(ret)) } + counter.Frequency = frequency } } @@ -134,6 +135,7 @@ func (c *Collector) Collect() (map[string]map[string]CounterValues, error) { // 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)) diff --git a/pkg/perfdata/pdh.go b/pkg/perfdata/pdh.go index c00d94ff3..21e4a232b 100644 --- a/pkg/perfdata/pdh.go +++ b/pkg/perfdata/pdh.go @@ -537,8 +537,10 @@ func PdhValidatePath(path string) uint32 { 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]) } @@ -617,6 +619,7 @@ func PdhGetRawCounterArray(hCounter pdhCounterHandle, lpdwBufferSize *uint32, lp uintptr(unsafe.Pointer(lpdwBufferSize)), uintptr(unsafe.Pointer(lpdwBufferCount)), uintptr(unsafe.Pointer(itemBuffer))) + return uint32(ret) } @@ -630,5 +633,6 @@ func PdhGetCounterTimeBase(hCounter pdhCounterHandle, pTimeBase *float64) uint32 ret, _, _ := pdhPdhGetCounterTimeBase.Call( uintptr(hCounter), uintptr(unsafe.Pointer(pTimeBase))) + return uint32(ret) } diff --git a/pkg/perflib/nametable.go b/pkg/perflib/nametable.go index b10a76103..75065f7d4 100644 --- a/pkg/perflib/nametable.go +++ b/pkg/perflib/nametable.go @@ -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 { diff --git a/pkg/perflib/perflib.go b/pkg/perflib/perflib.go index 227905174..58b650b87 100644 --- a/pkg/perflib/perflib.go +++ b/pkg/perflib/perflib.go @@ -227,6 +227,7 @@ func queryRawData(query string) ([]byte, error) { newBuffer := make([]byte, len(buffer)+16384) copy(newBuffer, buffer) buffer = newBuffer + continue } else if err != nil { var errNo syscall.Errno //nolint:forbidigo // Legacy Code @@ -279,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) @@ -303,6 +305,7 @@ func QueryPerformanceData(query string) ([]*PerfObject, error) { } obj := new(perfObjectType) + err = obj.BinaryReadFrom(r) if err != nil { return nil, err @@ -332,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 @@ -413,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 @@ -456,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)])) diff --git a/pkg/perflib/unmarshal.go b/pkg/perflib/unmarshal.go index 0219609bc..f4e0b31a7 100644 --- a/pkg/perflib/unmarshal.go +++ b/pkg/perflib/unmarshal.go @@ -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 } diff --git a/pkg/perflib/utf16.go b/pkg/perflib/utf16.go index e107a3402..81e2c324f 100644 --- a/pkg/perflib/utf16.go +++ b/pkg/perflib/utf16.go @@ -10,6 +10,7 @@ import ( // 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 diff --git a/pkg/perflib/utils.go b/pkg/perflib/utils.go index bca5011f7..ab101a9c7 100644 --- a/pkg/perflib/utils.go +++ b/pkg/perflib/utils.go @@ -18,5 +18,6 @@ func GetPerflibSnapshot(objNames string) (map[string]*PerfObject, error) { for _, obj := range objects { indexed[obj.Name] = obj } + return indexed, nil } diff --git a/pkg/perflib/utils_test.go b/pkg/perflib/utils_test.go index a60c9d8f1..828438341 100644 --- a/pkg/perflib/utils_test.go +++ b/pkg/perflib/utils_test.go @@ -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") } diff --git a/pkg/testutils/testutils.go b/pkg/testutils/testutils.go index 5120b02a6..d4a1fa5c6 100644 --- a/pkg/testutils/testutils.go +++ b/pkg/testutils/testutils.go @@ -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 diff --git a/pkg/utils/collector.go b/pkg/utils/collector.go index 766712930..d62ebec63 100644 --- a/pkg/utils/collector.go +++ b/pkg/utils/collector.go @@ -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 } diff --git a/pkg/utils/collector_test.go b/pkg/utils/collector_test.go index 9865d7b62..98ba0d8fe 100644 --- a/pkg/utils/collector_test.go +++ b/pkg/utils/collector_test.go @@ -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) } diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 0bc79e7a0..1823ad535 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -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 {