2019-04-05 13:59:40 +00:00
|
|
|
package collector
|
|
|
|
|
|
|
|
import (
|
2020-05-24 15:23:13 +00:00
|
|
|
"fmt"
|
2020-12-03 10:20:28 +00:00
|
|
|
"sort"
|
2019-08-03 11:15:45 +00:00
|
|
|
"strconv"
|
2020-02-09 20:09:26 +00:00
|
|
|
"strings"
|
2019-08-03 11:15:45 +00:00
|
|
|
|
2019-04-05 13:59:40 +00:00
|
|
|
"github.com/leoluk/perflib_exporter/perflib"
|
2021-01-30 10:16:53 +00:00
|
|
|
"github.com/prometheus-community/windows_exporter/log"
|
2019-04-05 13:59:40 +00:00
|
|
|
"github.com/prometheus/client_golang/prometheus"
|
2019-08-03 11:15:45 +00:00
|
|
|
"golang.org/x/sys/windows/registry"
|
2019-04-05 13:59:40 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// ...
|
|
|
|
const (
|
|
|
|
// TODO: Make package-local
|
2020-05-24 18:36:25 +00:00
|
|
|
Namespace = "windows"
|
2019-04-05 13:59:40 +00:00
|
|
|
|
|
|
|
// Conversion factors
|
|
|
|
ticksToSecondsScaleFactor = 1 / 1e7
|
|
|
|
windowsEpoch = 116444736000000000
|
|
|
|
)
|
|
|
|
|
2019-08-03 11:15:45 +00:00
|
|
|
// getWindowsVersion reads the version number of the OS from the Registry
|
|
|
|
// See https://docs.microsoft.com/en-us/windows/desktop/sysinfo/operating-system-version
|
|
|
|
func getWindowsVersion() float64 {
|
|
|
|
k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE)
|
|
|
|
if err != nil {
|
|
|
|
log.Warn("Couldn't open registry", err)
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
defer func() {
|
|
|
|
err = k.Close()
|
|
|
|
if err != nil {
|
|
|
|
log.Warnf("Failed to close registry key: %v", err)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
currentv, _, err := k.GetStringValue("CurrentVersion")
|
|
|
|
if err != nil {
|
|
|
|
log.Warn("Couldn't open registry to determine current Windows version:", err)
|
|
|
|
return 0
|
|
|
|
}
|
|
|
|
|
|
|
|
currentv_flt, err := strconv.ParseFloat(currentv, 64)
|
|
|
|
|
|
|
|
log.Debugf("Detected Windows version %f\n", currentv_flt)
|
|
|
|
|
|
|
|
return currentv_flt
|
|
|
|
}
|
|
|
|
|
2020-02-09 20:09:26 +00:00
|
|
|
type collectorBuilder func() (Collector, error)
|
|
|
|
|
|
|
|
var (
|
|
|
|
builders = make(map[string]collectorBuilder)
|
|
|
|
perfCounterDependencies = make(map[string]string)
|
|
|
|
)
|
|
|
|
|
|
|
|
func registerCollector(name string, builder collectorBuilder, perfCounterNames ...string) {
|
|
|
|
builders[name] = builder
|
2020-09-01 04:45:46 +00:00
|
|
|
addPerfCounterDependencies(name, perfCounterNames)
|
|
|
|
}
|
|
|
|
|
|
|
|
func addPerfCounterDependencies(name string, perfCounterNames []string) {
|
2020-02-09 20:09:26 +00:00
|
|
|
perfIndicies := make([]string, 0, len(perfCounterNames))
|
|
|
|
for _, cn := range perfCounterNames {
|
|
|
|
perfIndicies = append(perfIndicies, MapCounterToIndex(cn))
|
|
|
|
}
|
|
|
|
perfCounterDependencies[name] = strings.Join(perfIndicies, " ")
|
|
|
|
}
|
|
|
|
|
|
|
|
func Available() []string {
|
|
|
|
cs := make([]string, 0, len(builders))
|
|
|
|
for c := range builders {
|
|
|
|
cs = append(cs, c)
|
|
|
|
}
|
|
|
|
return cs
|
|
|
|
}
|
|
|
|
func Build(collector string) (Collector, error) {
|
2020-05-24 15:23:13 +00:00
|
|
|
builder, exists := builders[collector]
|
|
|
|
if !exists {
|
|
|
|
return nil, fmt.Errorf("Unknown collector %q", collector)
|
|
|
|
}
|
|
|
|
return builder()
|
2020-02-09 20:09:26 +00:00
|
|
|
}
|
|
|
|
func getPerfQuery(collectors []string) string {
|
|
|
|
parts := make([]string, 0, len(collectors))
|
|
|
|
for _, c := range collectors {
|
|
|
|
if p := perfCounterDependencies[c]; p != "" {
|
|
|
|
parts = append(parts, p)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return strings.Join(parts, " ")
|
|
|
|
}
|
2019-04-05 13:59:40 +00:00
|
|
|
|
|
|
|
// Collector is the interface a collector has to implement.
|
|
|
|
type Collector interface {
|
|
|
|
// Get new metrics and expose them via prometheus registry.
|
|
|
|
Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) (err error)
|
|
|
|
}
|
|
|
|
|
|
|
|
type ScrapeContext struct {
|
|
|
|
perfObjects map[string]*perflib.PerfObject
|
|
|
|
}
|
|
|
|
|
|
|
|
// PrepareScrapeContext creates a ScrapeContext to be used during a single scrape
|
2020-02-09 20:09:26 +00:00
|
|
|
func PrepareScrapeContext(collectors []string) (*ScrapeContext, error) {
|
|
|
|
q := getPerfQuery(collectors) // TODO: Memoize
|
|
|
|
objs, err := getPerflibSnapshot(q)
|
2019-04-05 13:59:40 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &ScrapeContext{objs}, nil
|
|
|
|
}
|
2020-06-07 15:40:55 +00:00
|
|
|
func boolToFloat(b bool) float64 {
|
|
|
|
if b {
|
|
|
|
return 1.0
|
|
|
|
}
|
|
|
|
return 0.0
|
|
|
|
}
|
2020-11-01 17:26:14 +00:00
|
|
|
|
|
|
|
func find(slice []string, val string) bool {
|
|
|
|
for _, item := range slice {
|
|
|
|
if item == val {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
2020-12-03 10:20:28 +00:00
|
|
|
|
|
|
|
// Used by more complex collectors where user input specifies enabled child collectors.
|
|
|
|
// Splits provided child collectors and deduplicate.
|
|
|
|
func expandEnabledChildCollectors(enabled string) []string {
|
|
|
|
separated := strings.Split(enabled, ",")
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
// Ensure result is ordered, to prevent test failure
|
|
|
|
sort.Strings(result)
|
|
|
|
return result
|
|
|
|
}
|