mirror of
https://github.com/prometheus-community/windows_exporter.git
synced 2026-02-12 07:56:38 +00:00
Compare commits
54 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d01c66986c | ||
|
|
823ffb7597 | ||
|
|
a90f9cda0f | ||
|
|
31d4c28124 | ||
|
|
e880889f07 | ||
|
|
a283608812 | ||
|
|
8251ddd176 | ||
|
|
4f0a3a89ab | ||
|
|
27cc1072fe | ||
|
|
eb9cf56dee | ||
|
|
3c20887433 | ||
|
|
37d1c4e958 | ||
|
|
33b6e17b2d | ||
|
|
1a9d4afdd6 | ||
|
|
9e198c55a4 | ||
|
|
b309a05bde | ||
|
|
123a055242 | ||
|
|
9308108284 | ||
|
|
0ecf3cd792 | ||
|
|
801444b35b | ||
|
|
f4ab322e5b | ||
|
|
72de199528 | ||
|
|
304972580d | ||
|
|
6322bb124f | ||
|
|
cb6a91b705 | ||
|
|
4d9fb1be72 | ||
|
|
27e26037e3 | ||
|
|
e09497116f | ||
|
|
3099e10555 | ||
|
|
3900504504 | ||
|
|
2c5e30d920 | ||
|
|
b348c245e8 | ||
|
|
578bcc4959 | ||
|
|
31a30474f1 | ||
|
|
ce1005add8 | ||
|
|
6107a59306 | ||
|
|
47656b16bd | ||
|
|
8fc47669be | ||
|
|
1a67ca54b6 | ||
|
|
c73f52338d | ||
|
|
c5f23b4e64 | ||
|
|
411954cf9d | ||
|
|
56be7c63d5 | ||
|
|
6ffe504f7e | ||
|
|
daa6f3d111 | ||
|
|
85fdfb44b8 | ||
|
|
33879449a2 | ||
|
|
462a136673 | ||
|
|
d5e39892cf | ||
|
|
ec0d863c29 | ||
|
|
afc3655a41 | ||
|
|
e25e96a62e | ||
|
|
23d92cfcae | ||
|
|
1258703f23 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -3,3 +3,5 @@ VERSION
|
|||||||
*.swp
|
*.swp
|
||||||
*.un~
|
*.un~
|
||||||
output/
|
output/
|
||||||
|
.vscode
|
||||||
|
.idea
|
||||||
@@ -12,6 +12,7 @@ Name | Description | Enabled by default
|
|||||||
[ad](docs/collector.ad.md) | Active Directory Domain Services |
|
[ad](docs/collector.ad.md) | Active Directory Domain Services |
|
||||||
[cpu](docs/collector.cpu.md) | CPU usage | ✓
|
[cpu](docs/collector.cpu.md) | CPU usage | ✓
|
||||||
[cs](docs/collector.cs.md) | "Computer System" metrics (system properties, num cpus/total memory) | ✓
|
[cs](docs/collector.cs.md) | "Computer System" metrics (system properties, num cpus/total memory) | ✓
|
||||||
|
[container](docs/collector.container.md) | Container metrics |
|
||||||
[dns](docs/collector.dns.md) | DNS Server |
|
[dns](docs/collector.dns.md) | DNS Server |
|
||||||
[hyperv](docs/collector.hyperv.md) | Hyper-V hosts |
|
[hyperv](docs/collector.hyperv.md) | Hyper-V hosts |
|
||||||
[iis](docs/collector.iis.md) | IIS sites and applications |
|
[iis](docs/collector.iis.md) | IIS sites and applications |
|
||||||
@@ -33,6 +34,7 @@ Name | Description | Enabled by default
|
|||||||
[service](docs/collector.service.md) | Service state metrics | ✓
|
[service](docs/collector.service.md) | Service state metrics | ✓
|
||||||
[system](docs/collector.system.md) | System calls | ✓
|
[system](docs/collector.system.md) | System calls | ✓
|
||||||
[tcp](docs/collector.tcp.md) | TCP connections |
|
[tcp](docs/collector.tcp.md) | TCP connections |
|
||||||
|
[thermalzone](docs/collector.thermalzone.md) | Thermal information
|
||||||
[textfile](docs/collector.textfile.md) | Read prometheus metrics from a text file | ✓
|
[textfile](docs/collector.textfile.md) | Read prometheus metrics from a text file | ✓
|
||||||
[vmware](docs/collector.vmware.md) | Performance counters installed by the Vmware Guest agent |
|
[vmware](docs/collector.vmware.md) | Performance counters installed by the Vmware Guest agent |
|
||||||
|
|
||||||
|
|||||||
@@ -40,12 +40,13 @@ after_build:
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
$ErrorActionPreference = "Stop"
|
$ErrorActionPreference = "Stop"
|
||||||
|
$BuildVersion = Get-Content VERSION
|
||||||
# The MSI version is not semver compliant, so just take the numerical parts
|
# The MSI version is not semver compliant, so just take the numerical parts
|
||||||
$Version = $env:APPVEYOR_REPO_TAG_NAME -replace '^v?([0-9\.]+).*$','$1'
|
$MSIVersion = $env:APPVEYOR_REPO_TAG_NAME -replace '^v?([0-9\.]+).*$','$1'
|
||||||
foreach($Arch in "amd64","386") {
|
foreach($Arch in "amd64","386") {
|
||||||
Write-Verbose "Building wmi_exporter $Version msi for $Arch"
|
Write-Verbose "Building wmi_exporter $MSIVersion msi for $Arch"
|
||||||
.\installer\build.ps1 -PathToExecutable .\output\$Arch\wmi_exporter-$Version-$Arch.exe -Version $Version -Arch "$Arch"
|
.\installer\build.ps1 -PathToExecutable .\output\$Arch\wmi_exporter-$BuildVersion-$Arch.exe -Version $MSIVersion -Arch "$Arch"
|
||||||
Move-Item installer\Output\wmi_exporter-$Version-$Arch.msi output\$Arch\
|
Move-Item installer\Output\wmi_exporter-$MSIVersion-$Arch.msi output\$Arch\
|
||||||
}
|
}
|
||||||
- promu checksum output\
|
- promu checksum output\
|
||||||
|
|
||||||
|
|||||||
@@ -454,7 +454,7 @@ func NewADCollector() (Collector, error) {
|
|||||||
|
|
||||||
// Collect sends the metric values for each metric
|
// Collect sends the metric values for each metric
|
||||||
// to the provided prometheus Metric channel.
|
// to the provided prometheus Metric channel.
|
||||||
func (c *ADCollector) Collect(ch chan<- prometheus.Metric) error {
|
func (c *ADCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error {
|
||||||
if desc, err := c.collect(ch); err != nil {
|
if desc, err := c.collect(ch); err != nil {
|
||||||
log.Error("failed collecting ad metrics:", desc, err)
|
log.Error("failed collecting ad metrics:", desc, err)
|
||||||
return err
|
return err
|
||||||
|
|||||||
71
collector/collector.go
Normal file
71
collector/collector.go
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
package collector
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/leoluk/perflib_exporter/perflib"
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
"github.com/prometheus/common/log"
|
||||||
|
"golang.org/x/sys/windows/registry"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ...
|
||||||
|
const (
|
||||||
|
// TODO: Make package-local
|
||||||
|
Namespace = "wmi"
|
||||||
|
|
||||||
|
// Conversion factors
|
||||||
|
ticksToSecondsScaleFactor = 1 / 1e7
|
||||||
|
windowsEpoch = 116444736000000000
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// Factories ...
|
||||||
|
var Factories = make(map[string]func() (Collector, error))
|
||||||
|
|
||||||
|
// 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
|
||||||
|
func PrepareScrapeContext() (*ScrapeContext, error) {
|
||||||
|
objs, err := getPerflibSnapshot()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ScrapeContext{objs}, nil
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
// +build windows
|
// +build windows,cgo
|
||||||
|
|
||||||
package collector
|
package collector
|
||||||
|
|
||||||
@@ -131,7 +131,7 @@ func NewContainerMetricsCollector() (Collector, error) {
|
|||||||
|
|
||||||
// Collect sends the metric values for each metric
|
// Collect sends the metric values for each metric
|
||||||
// to the provided prometheus Metric channel.
|
// to the provided prometheus Metric channel.
|
||||||
func (c *ContainerMetricsCollector) Collect(ch chan<- prometheus.Metric) error {
|
func (c *ContainerMetricsCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error {
|
||||||
if desc, err := c.collect(ch); err != nil {
|
if desc, err := c.collect(ch); err != nil {
|
||||||
log.Error("failed collecting ContainerMetricsCollector metrics:", desc, err)
|
log.Error("failed collecting ContainerMetricsCollector metrics:", desc, err)
|
||||||
return err
|
return err
|
||||||
|
|||||||
382
collector/cpu.go
382
collector/cpu.go
@@ -5,27 +5,73 @@ package collector
|
|||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/StackExchange/wmi"
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/common/log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
Factories["cpu"] = NewCPUCollector
|
Factories["cpu"] = newCPUCollector
|
||||||
}
|
}
|
||||||
|
|
||||||
// A CPUCollector is a Prometheus collector for WMI Win32_PerfRawData_PerfOS_Processor metrics
|
type cpuCollectorBasic struct {
|
||||||
type CPUCollector struct {
|
|
||||||
CStateSecondsTotal *prometheus.Desc
|
CStateSecondsTotal *prometheus.Desc
|
||||||
TimeTotal *prometheus.Desc
|
TimeTotal *prometheus.Desc
|
||||||
InterruptsTotal *prometheus.Desc
|
InterruptsTotal *prometheus.Desc
|
||||||
DPCsTotal *prometheus.Desc
|
DPCsTotal *prometheus.Desc
|
||||||
}
|
}
|
||||||
|
type cpuCollectorFull struct {
|
||||||
|
CStateSecondsTotal *prometheus.Desc
|
||||||
|
TimeTotal *prometheus.Desc
|
||||||
|
InterruptsTotal *prometheus.Desc
|
||||||
|
DPCsTotal *prometheus.Desc
|
||||||
|
ClockInterruptsTotal *prometheus.Desc
|
||||||
|
IdleBreakEventsTotal *prometheus.Desc
|
||||||
|
ParkingStatus *prometheus.Desc
|
||||||
|
ProcessorFrequencyMHz *prometheus.Desc
|
||||||
|
ProcessorMaxFrequencyMHz *prometheus.Desc
|
||||||
|
ProcessorPerformance *prometheus.Desc
|
||||||
|
}
|
||||||
|
|
||||||
// NewCPUCollector constructs a new CPUCollector
|
// newCPUCollector constructs a new cpuCollector, appropriate for the running OS
|
||||||
func NewCPUCollector() (Collector, error) {
|
func newCPUCollector() (Collector, error) {
|
||||||
const subsystem = "cpu"
|
const subsystem = "cpu"
|
||||||
return &CPUCollector{
|
|
||||||
|
version := getWindowsVersion()
|
||||||
|
// For Windows 2008 (version 6.0) or earlier we only have the "Processor"
|
||||||
|
// class. As of Windows 2008 R2 (version 6.1) the more detailed
|
||||||
|
// "ProcessorInformation" set is available (although some of the counters
|
||||||
|
// are added in later versions, so we aren't guaranteed to get all of
|
||||||
|
// them).
|
||||||
|
// Value 6.05 was selected to split between Windows versions.
|
||||||
|
if version < 6.05 {
|
||||||
|
return &cpuCollectorBasic{
|
||||||
|
CStateSecondsTotal: prometheus.NewDesc(
|
||||||
|
prometheus.BuildFQName(Namespace, subsystem, "cstate_seconds_total"),
|
||||||
|
"Time spent in low-power idle state",
|
||||||
|
[]string{"core", "state"},
|
||||||
|
nil,
|
||||||
|
),
|
||||||
|
TimeTotal: prometheus.NewDesc(
|
||||||
|
prometheus.BuildFQName(Namespace, subsystem, "time_total"),
|
||||||
|
"Time that processor spent in different modes (idle, user, system, ...)",
|
||||||
|
[]string{"core", "mode"},
|
||||||
|
nil,
|
||||||
|
),
|
||||||
|
InterruptsTotal: prometheus.NewDesc(
|
||||||
|
prometheus.BuildFQName(Namespace, subsystem, "interrupts_total"),
|
||||||
|
"Total number of received and serviced hardware interrupts",
|
||||||
|
[]string{"core"},
|
||||||
|
nil,
|
||||||
|
),
|
||||||
|
DPCsTotal: prometheus.NewDesc(
|
||||||
|
prometheus.BuildFQName(Namespace, subsystem, "dpcs_total"),
|
||||||
|
"Total number of received and serviced deferred procedure calls (DPCs)",
|
||||||
|
[]string{"core"},
|
||||||
|
nil,
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &cpuCollectorFull{
|
||||||
CStateSecondsTotal: prometheus.NewDesc(
|
CStateSecondsTotal: prometheus.NewDesc(
|
||||||
prometheus.BuildFQName(Namespace, subsystem, "cstate_seconds_total"),
|
prometheus.BuildFQName(Namespace, subsystem, "cstate_seconds_total"),
|
||||||
"Time spent in low-power idle state",
|
"Time spent in low-power idle state",
|
||||||
@@ -38,7 +84,6 @@ func NewCPUCollector() (Collector, error) {
|
|||||||
[]string{"core", "mode"},
|
[]string{"core", "mode"},
|
||||||
nil,
|
nil,
|
||||||
),
|
),
|
||||||
|
|
||||||
InterruptsTotal: prometheus.NewDesc(
|
InterruptsTotal: prometheus.NewDesc(
|
||||||
prometheus.BuildFQName(Namespace, subsystem, "interrupts_total"),
|
prometheus.BuildFQName(Namespace, subsystem, "interrupts_total"),
|
||||||
"Total number of received and serviced hardware interrupts",
|
"Total number of received and serviced hardware interrupts",
|
||||||
@@ -51,164 +96,273 @@ func NewCPUCollector() (Collector, error) {
|
|||||||
[]string{"core"},
|
[]string{"core"},
|
||||||
nil,
|
nil,
|
||||||
),
|
),
|
||||||
|
ClockInterruptsTotal: prometheus.NewDesc(
|
||||||
|
prometheus.BuildFQName(Namespace, subsystem, "clock_interrupts_total"),
|
||||||
|
"Total number of received and serviced clock tick interrupts",
|
||||||
|
[]string{"core"},
|
||||||
|
nil,
|
||||||
|
),
|
||||||
|
IdleBreakEventsTotal: prometheus.NewDesc(
|
||||||
|
prometheus.BuildFQName(Namespace, subsystem, "idle_break_events_total"),
|
||||||
|
"Total number of time processor was woken from idle",
|
||||||
|
[]string{"core"},
|
||||||
|
nil,
|
||||||
|
),
|
||||||
|
ParkingStatus: prometheus.NewDesc(
|
||||||
|
prometheus.BuildFQName(Namespace, subsystem, "parking_status"),
|
||||||
|
"Parking Status represents whether a processor is parked or not",
|
||||||
|
[]string{"core"},
|
||||||
|
nil,
|
||||||
|
),
|
||||||
|
ProcessorFrequencyMHz: prometheus.NewDesc(
|
||||||
|
prometheus.BuildFQName(Namespace, subsystem, "core_frequency_mhz"),
|
||||||
|
"Core frequency in megahertz",
|
||||||
|
[]string{"core"},
|
||||||
|
nil,
|
||||||
|
),
|
||||||
|
ProcessorPerformance: prometheus.NewDesc(
|
||||||
|
prometheus.BuildFQName(Namespace, subsystem, "processor_performance"),
|
||||||
|
"Processor Performance is the average performance of the processor while it is executing instructions, as a percentage of the nominal performance of the processor. On some processors, Processor Performance may exceed 100%",
|
||||||
|
[]string{"core"},
|
||||||
|
nil,
|
||||||
|
),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect sends the metric values for each metric
|
type perflibProcessor struct {
|
||||||
// to the provided prometheus Metric channel.
|
Name string
|
||||||
func (c *CPUCollector) Collect(ch chan<- prometheus.Metric) error {
|
C1Transitions float64 `perflib:"C1 Transitions/sec"`
|
||||||
if desc, err := c.collect(ch); err != nil {
|
C2Transitions float64 `perflib:"C2 Transitions/sec"`
|
||||||
log.Error("failed collecting cpu metrics:", desc, err)
|
C3Transitions float64 `perflib:"C3 Transitions/sec"`
|
||||||
|
DPCRate float64 `perflib:"DPC Rate"`
|
||||||
|
DPCsQueued float64 `perflib:"DPCs Queued/sec"`
|
||||||
|
Interrupts float64 `perflib:"Interrupts/sec"`
|
||||||
|
PercentC2Time float64 `perflib:"% C1 Time"`
|
||||||
|
PercentC3Time float64 `perflib:"% C2 Time"`
|
||||||
|
PercentC1Time float64 `perflib:"% C3 Time"`
|
||||||
|
PercentDPCTime float64 `perflib:"% DPC Time"`
|
||||||
|
PercentIdleTime float64 `perflib:"% Idle Time"`
|
||||||
|
PercentInterruptTime float64 `perflib:"% Interrupt Time"`
|
||||||
|
PercentPrivilegedTime float64 `perflib:"% Privileged Time"`
|
||||||
|
PercentProcessorTime float64 `perflib:"% Processor Time"`
|
||||||
|
PercentUserTime float64 `perflib:"% User Time"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cpuCollectorBasic) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error {
|
||||||
|
data := make([]perflibProcessor, 0)
|
||||||
|
err := unmarshalObject(ctx.perfObjects["Processor"], &data)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Win32_PerfRawData_PerfOS_Processor docs:
|
for _, cpu := range data {
|
||||||
// - https://msdn.microsoft.com/en-us/library/aa394317(v=vs.90).aspx
|
if strings.Contains(strings.ToLower(cpu.Name), "_total") {
|
||||||
type Win32_PerfRawData_PerfOS_Processor struct {
|
|
||||||
Name string
|
|
||||||
C1TransitionsPersec uint64
|
|
||||||
C2TransitionsPersec uint64
|
|
||||||
C3TransitionsPersec uint64
|
|
||||||
DPCRate uint32
|
|
||||||
DPCsQueuedPersec uint32
|
|
||||||
InterruptsPersec uint32
|
|
||||||
PercentC1Time uint64
|
|
||||||
PercentC2Time uint64
|
|
||||||
PercentC3Time uint64
|
|
||||||
PercentDPCTime uint64
|
|
||||||
PercentIdleTime uint64
|
|
||||||
PercentInterruptTime uint64
|
|
||||||
PercentPrivilegedTime uint64
|
|
||||||
PercentProcessorTime uint64
|
|
||||||
PercentUserTime uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
/* NOTE: This is an alternative class, but it is not as widely available. Decide which to use
|
|
||||||
type Win32_PerfRawData_Counters_ProcessorInformation struct {
|
|
||||||
Name string
|
|
||||||
AverageIdleTime uint64
|
|
||||||
C1TransitionsPersec uint64
|
|
||||||
C2TransitionsPersec uint64
|
|
||||||
C3TransitionsPersec uint64
|
|
||||||
ClockInterruptsPersec uint64
|
|
||||||
DPCRate uint64
|
|
||||||
DPCsQueuedPersec uint64
|
|
||||||
IdleBreakEventsPersec uint64
|
|
||||||
InterruptsPersec uint64
|
|
||||||
ParkingStatus uint64
|
|
||||||
PercentC1Time uint64
|
|
||||||
PercentC2Time uint64
|
|
||||||
PercentC3Time uint64
|
|
||||||
PercentDPCTime uint64
|
|
||||||
PercentIdleTime uint64
|
|
||||||
PercentInterruptTime uint64
|
|
||||||
PercentofMaximumFrequency uint64
|
|
||||||
PercentPerformanceLimit uint64
|
|
||||||
PercentPriorityTime uint64
|
|
||||||
PercentPrivilegedTime uint64
|
|
||||||
PercentPrivilegedUtility uint64
|
|
||||||
PercentProcessorPerformance uint64
|
|
||||||
PercentProcessorTime uint64
|
|
||||||
PercentProcessorUtility uint64
|
|
||||||
PercentUserTime uint64
|
|
||||||
PerformanceLimitFlags uint64
|
|
||||||
ProcessorFrequency uint64
|
|
||||||
ProcessorStateFlags uint64
|
|
||||||
}*/
|
|
||||||
|
|
||||||
func (c *CPUCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, error) {
|
|
||||||
var dst []Win32_PerfRawData_PerfOS_Processor
|
|
||||||
q := queryAll(&dst)
|
|
||||||
if err := wmi.Query(q, &dst); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, data := range dst {
|
|
||||||
if strings.Contains(data.Name, "_Total") {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
core := cpu.Name
|
||||||
core := data.Name
|
|
||||||
|
|
||||||
// These are only available from Win32_PerfRawData_Counters_ProcessorInformation, which is only available from Win2008R2+
|
|
||||||
/*ch <- prometheus.MustNewConstMetric(
|
|
||||||
c.ProcessorFrequency,
|
|
||||||
prometheus.GaugeValue,
|
|
||||||
float64(data.ProcessorFrequency),
|
|
||||||
socket, core,
|
|
||||||
)
|
|
||||||
ch <- prometheus.MustNewConstMetric(
|
|
||||||
c.MaximumFrequency,
|
|
||||||
prometheus.GaugeValue,
|
|
||||||
float64(data.PercentofMaximumFrequency)/100*float64(data.ProcessorFrequency),
|
|
||||||
socket, core,
|
|
||||||
)*/
|
|
||||||
|
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.CStateSecondsTotal,
|
c.CStateSecondsTotal,
|
||||||
prometheus.GaugeValue,
|
prometheus.CounterValue,
|
||||||
float64(data.PercentC1Time)*ticksToSecondsScaleFactor,
|
cpu.PercentC1Time,
|
||||||
core, "c1",
|
core, "c1",
|
||||||
)
|
)
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.CStateSecondsTotal,
|
c.CStateSecondsTotal,
|
||||||
prometheus.GaugeValue,
|
prometheus.CounterValue,
|
||||||
float64(data.PercentC2Time)*ticksToSecondsScaleFactor,
|
cpu.PercentC2Time,
|
||||||
core, "c2",
|
core, "c2",
|
||||||
)
|
)
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.CStateSecondsTotal,
|
c.CStateSecondsTotal,
|
||||||
prometheus.GaugeValue,
|
prometheus.CounterValue,
|
||||||
float64(data.PercentC3Time)*ticksToSecondsScaleFactor,
|
cpu.PercentC3Time,
|
||||||
core, "c3",
|
core, "c3",
|
||||||
)
|
)
|
||||||
|
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.TimeTotal,
|
c.TimeTotal,
|
||||||
prometheus.GaugeValue,
|
prometheus.CounterValue,
|
||||||
float64(data.PercentIdleTime)*ticksToSecondsScaleFactor,
|
cpu.PercentIdleTime,
|
||||||
core, "idle",
|
core, "idle",
|
||||||
)
|
)
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.TimeTotal,
|
c.TimeTotal,
|
||||||
prometheus.GaugeValue,
|
prometheus.CounterValue,
|
||||||
float64(data.PercentInterruptTime)*ticksToSecondsScaleFactor,
|
cpu.PercentInterruptTime,
|
||||||
core, "interrupt",
|
core, "interrupt",
|
||||||
)
|
)
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.TimeTotal,
|
c.TimeTotal,
|
||||||
prometheus.GaugeValue,
|
prometheus.CounterValue,
|
||||||
float64(data.PercentDPCTime)*ticksToSecondsScaleFactor,
|
cpu.PercentDPCTime,
|
||||||
core, "dpc",
|
core, "dpc",
|
||||||
)
|
)
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.TimeTotal,
|
c.TimeTotal,
|
||||||
prometheus.GaugeValue,
|
prometheus.CounterValue,
|
||||||
float64(data.PercentPrivilegedTime)*ticksToSecondsScaleFactor,
|
cpu.PercentPrivilegedTime,
|
||||||
core, "privileged",
|
core, "privileged",
|
||||||
)
|
)
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.TimeTotal,
|
c.TimeTotal,
|
||||||
prometheus.GaugeValue,
|
prometheus.CounterValue,
|
||||||
float64(data.PercentUserTime)*ticksToSecondsScaleFactor,
|
cpu.PercentUserTime,
|
||||||
core, "user",
|
core, "user",
|
||||||
)
|
)
|
||||||
|
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.InterruptsTotal,
|
c.InterruptsTotal,
|
||||||
prometheus.CounterValue,
|
prometheus.CounterValue,
|
||||||
float64(data.InterruptsPersec),
|
cpu.Interrupts,
|
||||||
core,
|
core,
|
||||||
)
|
)
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.DPCsTotal,
|
c.DPCsTotal,
|
||||||
prometheus.CounterValue,
|
prometheus.CounterValue,
|
||||||
float64(data.DPCsQueuedPersec),
|
cpu.DPCsQueued,
|
||||||
core,
|
core,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, nil
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type perflibProcessorInformation struct {
|
||||||
|
Name string
|
||||||
|
C1TimeSeconds float64 `perflib:"% C1 Time"`
|
||||||
|
C2TimeSeconds float64 `perflib:"% C2 Time"`
|
||||||
|
C3TimeSeconds float64 `perflib:"% C3 Time"`
|
||||||
|
C1TransitionsTotal float64 `perflib:"C1 Transitions/sec"`
|
||||||
|
C2TransitionsTotal float64 `perflib:"C2 Transitions/sec"`
|
||||||
|
C3TransitionsTotal float64 `perflib:"C3 Transitions/sec"`
|
||||||
|
ClockInterruptsTotal float64 `perflib:"Clock Interrupts/sec"`
|
||||||
|
DPCsQueuedTotal float64 `perflib:"DPCs Queued/sec"`
|
||||||
|
DPCTimeSeconds float64 `perflib:"% DPC Time"`
|
||||||
|
IdleBreakEventsTotal float64 `perflib:"Idle Break Events/sec"`
|
||||||
|
IdleTimeSeconds float64 `perflib:"% Idle Time"`
|
||||||
|
InterruptsTotal float64 `perflib:"Interrupts/sec"`
|
||||||
|
InterruptTimeSeconds float64 `perflib:"% Interrupt Time"`
|
||||||
|
ParkingStatus float64 `perflib:"Parking Status"`
|
||||||
|
PerformanceLimitPercent float64 `perflib:"% Performance Limit"`
|
||||||
|
PriorityTimeSeconds float64 `perflib:"% Priority Time"`
|
||||||
|
PrivilegedTimeSeconds float64 `perflib:"% Privileged Time"`
|
||||||
|
PrivilegedUtilitySeconds float64 `perflib:"% Privileged Utility"`
|
||||||
|
ProcessorFrequencyMHz float64 `perflib:"Processor Frequency"`
|
||||||
|
ProcessorPerformance float64 `perflib:"% Processor Performance"`
|
||||||
|
ProcessorTimeSeconds float64 `perflib:"% Processor Time"`
|
||||||
|
ProcessorUtilityRate float64 `perflib:"% Processor Utility"`
|
||||||
|
UserTimeSeconds float64 `perflib:"% User Time"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cpuCollectorFull) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error {
|
||||||
|
data := make([]perflibProcessorInformation, 0)
|
||||||
|
err := unmarshalObject(ctx.perfObjects["Processor Information"], &data)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, cpu := range data {
|
||||||
|
if strings.Contains(strings.ToLower(cpu.Name), "_total") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
core := cpu.Name
|
||||||
|
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
c.CStateSecondsTotal,
|
||||||
|
prometheus.CounterValue,
|
||||||
|
cpu.C1TimeSeconds,
|
||||||
|
core, "c1",
|
||||||
|
)
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
c.CStateSecondsTotal,
|
||||||
|
prometheus.CounterValue,
|
||||||
|
cpu.C2TimeSeconds,
|
||||||
|
core, "c2",
|
||||||
|
)
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
c.CStateSecondsTotal,
|
||||||
|
prometheus.CounterValue,
|
||||||
|
cpu.C3TimeSeconds,
|
||||||
|
core, "c3",
|
||||||
|
)
|
||||||
|
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
c.TimeTotal,
|
||||||
|
prometheus.CounterValue,
|
||||||
|
cpu.IdleTimeSeconds,
|
||||||
|
core, "idle",
|
||||||
|
)
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
c.TimeTotal,
|
||||||
|
prometheus.CounterValue,
|
||||||
|
cpu.InterruptTimeSeconds,
|
||||||
|
core, "interrupt",
|
||||||
|
)
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
c.TimeTotal,
|
||||||
|
prometheus.CounterValue,
|
||||||
|
cpu.DPCTimeSeconds,
|
||||||
|
core, "dpc",
|
||||||
|
)
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
c.TimeTotal,
|
||||||
|
prometheus.CounterValue,
|
||||||
|
cpu.PrivilegedTimeSeconds,
|
||||||
|
core, "privileged",
|
||||||
|
)
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
c.TimeTotal,
|
||||||
|
prometheus.CounterValue,
|
||||||
|
cpu.UserTimeSeconds,
|
||||||
|
core, "user",
|
||||||
|
)
|
||||||
|
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
c.InterruptsTotal,
|
||||||
|
prometheus.CounterValue,
|
||||||
|
cpu.InterruptsTotal,
|
||||||
|
core,
|
||||||
|
)
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
c.DPCsTotal,
|
||||||
|
prometheus.CounterValue,
|
||||||
|
cpu.DPCsQueuedTotal,
|
||||||
|
core,
|
||||||
|
)
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
c.ClockInterruptsTotal,
|
||||||
|
prometheus.CounterValue,
|
||||||
|
cpu.ClockInterruptsTotal,
|
||||||
|
core,
|
||||||
|
)
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
c.IdleBreakEventsTotal,
|
||||||
|
prometheus.CounterValue,
|
||||||
|
cpu.IdleBreakEventsTotal,
|
||||||
|
core,
|
||||||
|
)
|
||||||
|
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
c.ParkingStatus,
|
||||||
|
prometheus.GaugeValue,
|
||||||
|
cpu.ParkingStatus,
|
||||||
|
core,
|
||||||
|
)
|
||||||
|
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
c.ProcessorFrequencyMHz,
|
||||||
|
prometheus.GaugeValue,
|
||||||
|
cpu.ProcessorFrequencyMHz,
|
||||||
|
core,
|
||||||
|
)
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
c.ProcessorPerformance,
|
||||||
|
prometheus.GaugeValue,
|
||||||
|
cpu.ProcessorPerformance,
|
||||||
|
core,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ func NewCSCollector() (Collector, error) {
|
|||||||
|
|
||||||
// Collect sends the metric values for each metric
|
// Collect sends the metric values for each metric
|
||||||
// to the provided prometheus Metric channel.
|
// to the provided prometheus Metric channel.
|
||||||
func (c *CSCollector) Collect(ch chan<- prometheus.Metric) error {
|
func (c *CSCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error {
|
||||||
if desc, err := c.collect(ch); err != nil {
|
if desc, err := c.collect(ch); err != nil {
|
||||||
log.Error("failed collecting cs metrics:", desc, err)
|
log.Error("failed collecting cs metrics:", desc, err)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -181,7 +181,7 @@ func NewDNSCollector() (Collector, error) {
|
|||||||
|
|
||||||
// Collect sends the metric values for each metric
|
// Collect sends the metric values for each metric
|
||||||
// to the provided prometheus Metric channel.
|
// to the provided prometheus Metric channel.
|
||||||
func (c *DNSCollector) Collect(ch chan<- prometheus.Metric) error {
|
func (c *DNSCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error {
|
||||||
if desc, err := c.collect(ch); err != nil {
|
if desc, err := c.collect(ch); err != nil {
|
||||||
log.Error("failed collecting dns metrics:", desc, err)
|
log.Error("failed collecting dns metrics:", desc, err)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -597,7 +597,7 @@ func NewHyperVCollector() (Collector, error) {
|
|||||||
|
|
||||||
// Collect sends the metric values for each metric
|
// Collect sends the metric values for each metric
|
||||||
// to the provided prometheus Metric channel.
|
// to the provided prometheus Metric channel.
|
||||||
func (c *HyperVCollector) Collect(ch chan<- prometheus.Metric) error {
|
func (c *HyperVCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error {
|
||||||
if desc, err := c.collectVmHealth(ch); err != nil {
|
if desc, err := c.collectVmHealth(ch); err != nil {
|
||||||
log.Error("failed collecting hyperV health status metrics:", desc, err)
|
log.Error("failed collecting hyperV health status metrics:", desc, err)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -818,7 +818,7 @@ func NewIISCollector() (Collector, error) {
|
|||||||
|
|
||||||
// Collect sends the metric values for each metric
|
// Collect sends the metric values for each metric
|
||||||
// to the provided prometheus Metric channel.
|
// to the provided prometheus Metric channel.
|
||||||
func (c *IISCollector) Collect(ch chan<- prometheus.Metric) error {
|
func (c *IISCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error {
|
||||||
if desc, err := c.collect(ch); err != nil {
|
if desc, err := c.collect(ch); err != nil {
|
||||||
log.Error("failed collecting iis metrics:", desc, err)
|
log.Error("failed collecting iis metrics:", desc, err)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ func NewLogicalDiskCollector() (Collector, error) {
|
|||||||
|
|
||||||
// Collect sends the metric values for each metric
|
// Collect sends the metric values for each metric
|
||||||
// to the provided prometheus Metric channel.
|
// to the provided prometheus Metric channel.
|
||||||
func (c *LogicalDiskCollector) Collect(ch chan<- prometheus.Metric) error {
|
func (c *LogicalDiskCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error {
|
||||||
if desc, err := c.collect(ch); err != nil {
|
if desc, err := c.collect(ch); err != nil {
|
||||||
log.Error("failed collecting logical_disk metrics:", desc, err)
|
log.Error("failed collecting logical_disk metrics:", desc, err)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -256,7 +256,7 @@ func NewMemoryCollector() (Collector, error) {
|
|||||||
|
|
||||||
// Collect sends the metric values for each metric
|
// Collect sends the metric values for each metric
|
||||||
// to the provided prometheus Metric channel.
|
// to the provided prometheus Metric channel.
|
||||||
func (c *MemoryCollector) Collect(ch chan<- prometheus.Metric) error {
|
func (c *MemoryCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error {
|
||||||
if desc, err := c.collect(ch); err != nil {
|
if desc, err := c.collect(ch); err != nil {
|
||||||
log.Error("failed collecting memory metrics:", desc, err)
|
log.Error("failed collecting memory metrics:", desc, err)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ type Win32_PerfRawData_MSMQ_MSMQQueueCollector struct {
|
|||||||
func NewMSMQCollector() (Collector, error) {
|
func NewMSMQCollector() (Collector, error) {
|
||||||
const subsystem = "msmq"
|
const subsystem = "msmq"
|
||||||
|
|
||||||
if *msmqWhereClause != "" {
|
if *msmqWhereClause == "" {
|
||||||
log.Warn("No where-clause specified for msmq collector. This will generate a very large number of metrics!")
|
log.Warn("No where-clause specified for msmq collector. This will generate a very large number of metrics!")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +68,7 @@ func NewMSMQCollector() (Collector, error) {
|
|||||||
|
|
||||||
// Collect sends the metric values for each metric
|
// Collect sends the metric values for each metric
|
||||||
// to the provided prometheus Metric channel.
|
// to the provided prometheus Metric channel.
|
||||||
func (c *Win32_PerfRawData_MSMQ_MSMQQueueCollector) Collect(ch chan<- prometheus.Metric) error {
|
func (c *Win32_PerfRawData_MSMQ_MSMQQueueCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error {
|
||||||
if desc, err := c.collect(ch); err != nil {
|
if desc, err := c.collect(ch); err != nil {
|
||||||
log.Error("failed collecting msmq metrics:", desc, err)
|
log.Error("failed collecting msmq metrics:", desc, err)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ func mssqlBuildWMIInstanceClass(suffix string, instance string) string {
|
|||||||
type mssqlCollectorsMap map[string]mssqlCollectorFunc
|
type mssqlCollectorsMap map[string]mssqlCollectorFunc
|
||||||
|
|
||||||
func mssqlAvailableClassCollectors() string {
|
func mssqlAvailableClassCollectors() string {
|
||||||
return "accessmethods,availreplica,bufman,databases,dbreplica,genstats,locks,memmgr,sqlstats"
|
return "accessmethods,availreplica,bufman,databases,dbreplica,genstats,locks,memmgr,sqlstats,sqlerrors,transactions"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *MSSQLCollector) getMSSQLCollectors() mssqlCollectorsMap {
|
func (c *MSSQLCollector) getMSSQLCollectors() mssqlCollectorsMap {
|
||||||
@@ -105,6 +105,8 @@ func (c *MSSQLCollector) getMSSQLCollectors() mssqlCollectorsMap {
|
|||||||
mssqlCollectors["locks"] = c.collectLocks
|
mssqlCollectors["locks"] = c.collectLocks
|
||||||
mssqlCollectors["memmgr"] = c.collectMemoryManager
|
mssqlCollectors["memmgr"] = c.collectMemoryManager
|
||||||
mssqlCollectors["sqlstats"] = c.collectSQLStats
|
mssqlCollectors["sqlstats"] = c.collectSQLStats
|
||||||
|
mssqlCollectors["sqlerrors"] = c.collectSQLErrors
|
||||||
|
mssqlCollectors["transactions"] = c.collectTransactions
|
||||||
|
|
||||||
return mssqlCollectors
|
return mssqlCollectors
|
||||||
}
|
}
|
||||||
@@ -358,6 +360,24 @@ type MSSQLCollector struct {
|
|||||||
SQLStatsSQLReCompilations *prometheus.Desc
|
SQLStatsSQLReCompilations *prometheus.Desc
|
||||||
SQLStatsUnsafeAutoParams *prometheus.Desc
|
SQLStatsUnsafeAutoParams *prometheus.Desc
|
||||||
|
|
||||||
|
// Win32_PerfRawData_{instance}_SQLServerSQLErrors
|
||||||
|
SQLErrorsTotal *prometheus.Desc
|
||||||
|
|
||||||
|
// Win32_PerfRawData_{instance}_SQLServerTransactions
|
||||||
|
TransactionsTempDbFreeSpaceBytes *prometheus.Desc
|
||||||
|
TransactionsLongestTransactionRunningSeconds *prometheus.Desc
|
||||||
|
TransactionsNonSnapshotVersionActiveTotal *prometheus.Desc
|
||||||
|
TransactionsSnapshotActiveTotal *prometheus.Desc
|
||||||
|
TransactionsActiveTotal *prometheus.Desc
|
||||||
|
TransactionsUpdateConflictsTotal *prometheus.Desc
|
||||||
|
TransactionsUpdateSnapshotActiveTotal *prometheus.Desc
|
||||||
|
TransactionsVersionCleanupRateBytes *prometheus.Desc
|
||||||
|
TransactionsVersionGenerationRateBytes *prometheus.Desc
|
||||||
|
TransactionsVersionStoreSizeBytes *prometheus.Desc
|
||||||
|
TransactionsVersionStoreUnits *prometheus.Desc
|
||||||
|
TransactionsVersionStoreCreationUnits *prometheus.Desc
|
||||||
|
TransactionsVersionStoreTruncationUnits *prometheus.Desc
|
||||||
|
|
||||||
mssqlInstances mssqlInstancesType
|
mssqlInstances mssqlInstancesType
|
||||||
mssqlCollectors mssqlCollectorsMap
|
mssqlCollectors mssqlCollectorsMap
|
||||||
mssqlChildCollectorFailure int
|
mssqlChildCollectorFailure int
|
||||||
@@ -1637,6 +1657,94 @@ func NewMSSQLCollector() (Collector, error) {
|
|||||||
nil,
|
nil,
|
||||||
),
|
),
|
||||||
|
|
||||||
|
// Win32_PerfRawData_{instance}_SQLServerSQLErrors
|
||||||
|
SQLErrorsTotal: prometheus.NewDesc(
|
||||||
|
prometheus.BuildFQName(Namespace, subsystem, "sql_errors_total"),
|
||||||
|
"(SQLErrors.Total)",
|
||||||
|
[]string{"instance", "resource"},
|
||||||
|
nil,
|
||||||
|
),
|
||||||
|
|
||||||
|
// Win32_PerfRawData_{instance}_SQLServerTransactions
|
||||||
|
TransactionsTempDbFreeSpaceBytes: prometheus.NewDesc(
|
||||||
|
prometheus.BuildFQName(Namespace, subsystem, "transactions_tempdb_free_space_bytes"),
|
||||||
|
"(Transactions.FreeSpaceInTempDbKB)",
|
||||||
|
[]string{"instance"},
|
||||||
|
nil,
|
||||||
|
),
|
||||||
|
TransactionsLongestTransactionRunningSeconds: prometheus.NewDesc(
|
||||||
|
prometheus.BuildFQName(Namespace, subsystem, "transactions_longest_transaction_running_seconds"),
|
||||||
|
"(Transactions.LongestTransactionRunningTime)",
|
||||||
|
[]string{"instance"},
|
||||||
|
nil,
|
||||||
|
),
|
||||||
|
TransactionsNonSnapshotVersionActiveTotal: prometheus.NewDesc(
|
||||||
|
prometheus.BuildFQName(Namespace, subsystem, "transactions_nonsnapshot_version_active_total"),
|
||||||
|
"(Transactions.NonSnapshotVersionTransactions)",
|
||||||
|
[]string{"instance"},
|
||||||
|
nil,
|
||||||
|
),
|
||||||
|
TransactionsSnapshotActiveTotal: prometheus.NewDesc(
|
||||||
|
prometheus.BuildFQName(Namespace, subsystem, "transactions_snapshot_active_total"),
|
||||||
|
"(Transactions.SnapshotTransactions)",
|
||||||
|
[]string{"instance"},
|
||||||
|
nil,
|
||||||
|
),
|
||||||
|
TransactionsActiveTotal: prometheus.NewDesc(
|
||||||
|
prometheus.BuildFQName(Namespace, subsystem, "transactions_active_total"),
|
||||||
|
"(Transactions.Transactions)",
|
||||||
|
[]string{"instance"},
|
||||||
|
nil,
|
||||||
|
),
|
||||||
|
TransactionsUpdateConflictsTotal: prometheus.NewDesc(
|
||||||
|
prometheus.BuildFQName(Namespace, subsystem, "transactions_update_conflicts_total"),
|
||||||
|
"(Transactions.UpdateConflictRatio)",
|
||||||
|
[]string{"instance"},
|
||||||
|
nil,
|
||||||
|
),
|
||||||
|
TransactionsUpdateSnapshotActiveTotal: prometheus.NewDesc(
|
||||||
|
prometheus.BuildFQName(Namespace, subsystem, "transactions_update_snapshot_active_total"),
|
||||||
|
"(Transactions.UpdateSnapshotTransactions)",
|
||||||
|
[]string{"instance"},
|
||||||
|
nil,
|
||||||
|
),
|
||||||
|
TransactionsVersionCleanupRateBytes: prometheus.NewDesc(
|
||||||
|
prometheus.BuildFQName(Namespace, subsystem, "transactions_version_cleanup_rate_bytes"),
|
||||||
|
"(Transactions.VersionCleanupRateKBs)",
|
||||||
|
[]string{"instance"},
|
||||||
|
nil,
|
||||||
|
),
|
||||||
|
TransactionsVersionGenerationRateBytes: prometheus.NewDesc(
|
||||||
|
prometheus.BuildFQName(Namespace, subsystem, "transactions_version_generation_rate_bytes"),
|
||||||
|
"(Transactions.VersionGenerationRateKBs)",
|
||||||
|
[]string{"instance"},
|
||||||
|
nil,
|
||||||
|
),
|
||||||
|
TransactionsVersionStoreSizeBytes: prometheus.NewDesc(
|
||||||
|
prometheus.BuildFQName(Namespace, subsystem, "transactions_version_store_size_bytes"),
|
||||||
|
"(Transactions.VersionStoreSizeKB)",
|
||||||
|
[]string{"instance"},
|
||||||
|
nil,
|
||||||
|
),
|
||||||
|
TransactionsVersionStoreUnits: prometheus.NewDesc(
|
||||||
|
prometheus.BuildFQName(Namespace, subsystem, "transactions_version_store_units"),
|
||||||
|
"(Transactions.VersionStoreUnitCount)",
|
||||||
|
[]string{"instance"},
|
||||||
|
nil,
|
||||||
|
),
|
||||||
|
TransactionsVersionStoreCreationUnits: prometheus.NewDesc(
|
||||||
|
prometheus.BuildFQName(Namespace, subsystem, "transactions_version_store_creation_units"),
|
||||||
|
"(Transactions.VersionStoreUnitCreation)",
|
||||||
|
[]string{"instance"},
|
||||||
|
nil,
|
||||||
|
),
|
||||||
|
TransactionsVersionStoreTruncationUnits: prometheus.NewDesc(
|
||||||
|
prometheus.BuildFQName(Namespace, subsystem, "transactions_version_store_truncation_units"),
|
||||||
|
"(Transactions.VersionStoreUnitTruncation)",
|
||||||
|
[]string{"instance"},
|
||||||
|
nil,
|
||||||
|
),
|
||||||
|
|
||||||
mssqlInstances: getMSSQLInstances(),
|
mssqlInstances: getMSSQLInstances(),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1687,7 +1795,7 @@ func (c *MSSQLCollector) execute(name string, fn mssqlCollectorFunc, ch chan<- p
|
|||||||
|
|
||||||
// Collect sends the metric values for each metric
|
// Collect sends the metric values for each metric
|
||||||
// to the provided prometheus Metric channel.
|
// to the provided prometheus Metric channel.
|
||||||
func (c *MSSQLCollector) Collect(ch chan<- prometheus.Metric) error {
|
func (c *MSSQLCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error {
|
||||||
wg := sync.WaitGroup{}
|
wg := sync.WaitGroup{}
|
||||||
|
|
||||||
enabled := mssqlExpandEnabledCollectors(*mssqlEnabledCollectors)
|
enabled := mssqlExpandEnabledCollectors(*mssqlEnabledCollectors)
|
||||||
@@ -2575,7 +2683,7 @@ func (c *MSSQLCollector) collectDatabaseReplica(ch chan<- prometheus.Metric, sql
|
|||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
c.DBReplicaTransactionDelay,
|
c.DBReplicaTransactionDelay,
|
||||||
prometheus.GaugeValue,
|
prometheus.GaugeValue,
|
||||||
float64(v.TransactionDelay)*1000.0,
|
float64(v.TransactionDelay)/1000.0,
|
||||||
sqlInstance, replicaName,
|
sqlInstance, replicaName,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -3558,3 +3666,162 @@ func (c *MSSQLCollector) collectSQLStats(ch chan<- prometheus.Metric, sqlInstanc
|
|||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type win32PerfRawDataSQLServerSQLErrors struct {
|
||||||
|
Name string
|
||||||
|
ErrorsPersec uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Win32_PerfRawData_MSSQLSERVER_SQLServerErrors docs:
|
||||||
|
// - https://docs.microsoft.com/en-us/sql/relational-databases/performance-monitor/sql-server-sql-errors-object
|
||||||
|
func (c *MSSQLCollector) collectSQLErrors(ch chan<- prometheus.Metric, sqlInstance string) (*prometheus.Desc, error) {
|
||||||
|
var dst []win32PerfRawDataSQLServerSQLErrors
|
||||||
|
log.Debugf("mssql_sqlerrors collector iterating sql instance %s.", sqlInstance)
|
||||||
|
|
||||||
|
class := mssqlBuildWMIInstanceClass("SQLErrors", sqlInstance)
|
||||||
|
q := queryAllForClassWhere(&dst, class, `Name <> '_Total'`)
|
||||||
|
if err := wmi.Query(q, &dst); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range dst {
|
||||||
|
resource := v.Name
|
||||||
|
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
c.SQLErrorsTotal,
|
||||||
|
prometheus.CounterValue,
|
||||||
|
float64(v.ErrorsPersec),
|
||||||
|
sqlInstance, resource,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type win32PerfRawDataSqlServerTransactions struct {
|
||||||
|
FreeSpaceintempdbKB uint64
|
||||||
|
LongestTransactionRunningTime uint64
|
||||||
|
NonSnapshotVersionTransactions uint64
|
||||||
|
SnapshotTransactions uint64
|
||||||
|
Transactions uint64
|
||||||
|
Updateconflictratio uint64
|
||||||
|
UpdateSnapshotTransactions uint64
|
||||||
|
VersionCleanuprateKBPers uint64
|
||||||
|
VersionGenerationrateKBPers uint64
|
||||||
|
VersionStoreSizeKB uint64
|
||||||
|
VersionStoreunitcount uint64
|
||||||
|
VersionStoreunitcreation uint64
|
||||||
|
VersionStoreunittruncation uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Win32_PerfRawData_MSSQLSERVER_Transactions docs:
|
||||||
|
// - https://docs.microsoft.com/en-us/sql/relational-databases/performance-monitor/sql-server-transactions-object
|
||||||
|
func (c *MSSQLCollector) collectTransactions(ch chan<- prometheus.Metric, sqlInstance string) (*prometheus.Desc, error) {
|
||||||
|
var dst []win32PerfRawDataSqlServerTransactions
|
||||||
|
log.Debugf("mssql_transactions collector iterating sql instance %s.", sqlInstance)
|
||||||
|
|
||||||
|
class := mssqlBuildWMIInstanceClass("Transactions", sqlInstance)
|
||||||
|
q := queryAllForClass(&dst, class)
|
||||||
|
if err := wmi.Query(q, &dst); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(dst) == 0 {
|
||||||
|
return nil, errors.New("WMI query returned empty result set")
|
||||||
|
}
|
||||||
|
|
||||||
|
v := dst[0]
|
||||||
|
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
c.TransactionsTempDbFreeSpaceBytes,
|
||||||
|
prometheus.GaugeValue,
|
||||||
|
float64(v.FreeSpaceintempdbKB*1024),
|
||||||
|
sqlInstance,
|
||||||
|
)
|
||||||
|
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
c.TransactionsLongestTransactionRunningSeconds,
|
||||||
|
prometheus.GaugeValue,
|
||||||
|
float64(v.LongestTransactionRunningTime),
|
||||||
|
sqlInstance,
|
||||||
|
)
|
||||||
|
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
c.TransactionsNonSnapshotVersionActiveTotal,
|
||||||
|
prometheus.CounterValue,
|
||||||
|
float64(v.NonSnapshotVersionTransactions),
|
||||||
|
sqlInstance,
|
||||||
|
)
|
||||||
|
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
c.TransactionsSnapshotActiveTotal,
|
||||||
|
prometheus.CounterValue,
|
||||||
|
float64(v.SnapshotTransactions),
|
||||||
|
sqlInstance,
|
||||||
|
)
|
||||||
|
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
c.TransactionsActiveTotal,
|
||||||
|
prometheus.CounterValue,
|
||||||
|
float64(v.Transactions),
|
||||||
|
sqlInstance,
|
||||||
|
)
|
||||||
|
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
c.TransactionsUpdateConflictsTotal,
|
||||||
|
prometheus.CounterValue,
|
||||||
|
float64(v.Updateconflictratio),
|
||||||
|
sqlInstance,
|
||||||
|
)
|
||||||
|
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
c.TransactionsUpdateSnapshotActiveTotal,
|
||||||
|
prometheus.CounterValue,
|
||||||
|
float64(v.UpdateSnapshotTransactions),
|
||||||
|
sqlInstance,
|
||||||
|
)
|
||||||
|
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
c.TransactionsVersionCleanupRateBytes,
|
||||||
|
prometheus.GaugeValue,
|
||||||
|
float64(v.VersionCleanuprateKBPers*1024),
|
||||||
|
sqlInstance,
|
||||||
|
)
|
||||||
|
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
c.TransactionsVersionGenerationRateBytes,
|
||||||
|
prometheus.GaugeValue,
|
||||||
|
float64(v.VersionGenerationrateKBPers*1024),
|
||||||
|
sqlInstance,
|
||||||
|
)
|
||||||
|
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
c.TransactionsVersionStoreSizeBytes,
|
||||||
|
prometheus.GaugeValue,
|
||||||
|
float64(v.VersionStoreSizeKB*1024),
|
||||||
|
sqlInstance,
|
||||||
|
)
|
||||||
|
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
c.TransactionsVersionStoreUnits,
|
||||||
|
prometheus.CounterValue,
|
||||||
|
float64(v.VersionStoreunitcount),
|
||||||
|
sqlInstance,
|
||||||
|
)
|
||||||
|
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
c.TransactionsVersionStoreCreationUnits,
|
||||||
|
prometheus.CounterValue,
|
||||||
|
float64(v.VersionStoreunitcreation),
|
||||||
|
sqlInstance,
|
||||||
|
)
|
||||||
|
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
c.TransactionsVersionStoreTruncationUnits,
|
||||||
|
prometheus.CounterValue,
|
||||||
|
float64(v.VersionStoreunittruncation),
|
||||||
|
sqlInstance,
|
||||||
|
)
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ func NewNetworkCollector() (Collector, error) {
|
|||||||
|
|
||||||
// Collect sends the metric values for each metric
|
// Collect sends the metric values for each metric
|
||||||
// to the provided prometheus Metric channel.
|
// to the provided prometheus Metric channel.
|
||||||
func (c *NetworkCollector) Collect(ch chan<- prometheus.Metric) error {
|
func (c *NetworkCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error {
|
||||||
if desc, err := c.collect(ch); err != nil {
|
if desc, err := c.collect(ch); err != nil {
|
||||||
log.Error("failed collecting net metrics:", desc, err)
|
log.Error("failed collecting net metrics:", desc, err)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ func NewNETFramework_NETCLRExceptionsCollector() (Collector, error) {
|
|||||||
|
|
||||||
// Collect sends the metric values for each metric
|
// Collect sends the metric values for each metric
|
||||||
// to the provided prometheus Metric channel.
|
// to the provided prometheus Metric channel.
|
||||||
func (c *NETFramework_NETCLRExceptionsCollector) Collect(ch chan<- prometheus.Metric) error {
|
func (c *NETFramework_NETCLRExceptionsCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error {
|
||||||
if desc, err := c.collect(ch); err != nil {
|
if desc, err := c.collect(ch); err != nil {
|
||||||
log.Error("failed collecting win32_perfrawdata_netframework_netclrexceptions metrics:", desc, err)
|
log.Error("failed collecting win32_perfrawdata_netframework_netclrexceptions metrics:", desc, err)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ func NewNETFramework_NETCLRInteropCollector() (Collector, error) {
|
|||||||
|
|
||||||
// Collect sends the metric values for each metric
|
// Collect sends the metric values for each metric
|
||||||
// to the provided prometheus Metric channel.
|
// to the provided prometheus Metric channel.
|
||||||
func (c *NETFramework_NETCLRInteropCollector) Collect(ch chan<- prometheus.Metric) error {
|
func (c *NETFramework_NETCLRInteropCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error {
|
||||||
if desc, err := c.collect(ch); err != nil {
|
if desc, err := c.collect(ch); err != nil {
|
||||||
log.Error("failed collecting win32_perfrawdata_netframework_netclrinterop metrics:", desc, err)
|
log.Error("failed collecting win32_perfrawdata_netframework_netclrinterop metrics:", desc, err)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ func NewNETFramework_NETCLRJitCollector() (Collector, error) {
|
|||||||
|
|
||||||
// Collect sends the metric values for each metric
|
// Collect sends the metric values for each metric
|
||||||
// to the provided prometheus Metric channel.
|
// to the provided prometheus Metric channel.
|
||||||
func (c *NETFramework_NETCLRJitCollector) Collect(ch chan<- prometheus.Metric) error {
|
func (c *NETFramework_NETCLRJitCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error {
|
||||||
if desc, err := c.collect(ch); err != nil {
|
if desc, err := c.collect(ch); err != nil {
|
||||||
log.Error("failed collecting win32_perfrawdata_netframework_netclrjit metrics:", desc, err)
|
log.Error("failed collecting win32_perfrawdata_netframework_netclrjit metrics:", desc, err)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ func NewNETFramework_NETCLRLoadingCollector() (Collector, error) {
|
|||||||
|
|
||||||
// Collect sends the metric values for each metric
|
// Collect sends the metric values for each metric
|
||||||
// to the provided prometheus Metric channel.
|
// to the provided prometheus Metric channel.
|
||||||
func (c *NETFramework_NETCLRLoadingCollector) Collect(ch chan<- prometheus.Metric) error {
|
func (c *NETFramework_NETCLRLoadingCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error {
|
||||||
if desc, err := c.collect(ch); err != nil {
|
if desc, err := c.collect(ch); err != nil {
|
||||||
log.Error("failed collecting win32_perfrawdata_netframework_netclrloading metrics:", desc, err)
|
log.Error("failed collecting win32_perfrawdata_netframework_netclrloading metrics:", desc, err)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ func NewNETFramework_NETCLRLocksAndThreadsCollector() (Collector, error) {
|
|||||||
|
|
||||||
// Collect sends the metric values for each metric
|
// Collect sends the metric values for each metric
|
||||||
// to the provided prometheus Metric channel.
|
// to the provided prometheus Metric channel.
|
||||||
func (c *NETFramework_NETCLRLocksAndThreadsCollector) Collect(ch chan<- prometheus.Metric) error {
|
func (c *NETFramework_NETCLRLocksAndThreadsCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error {
|
||||||
if desc, err := c.collect(ch); err != nil {
|
if desc, err := c.collect(ch); err != nil {
|
||||||
log.Error("failed collecting win32_perfrawdata_netframework_netclrlocksandthreads metrics:", desc, err)
|
log.Error("failed collecting win32_perfrawdata_netframework_netclrlocksandthreads metrics:", desc, err)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ func NewNETFramework_NETCLRMemoryCollector() (Collector, error) {
|
|||||||
|
|
||||||
// Collect sends the metric values for each metric
|
// Collect sends the metric values for each metric
|
||||||
// to the provided prometheus Metric channel.
|
// to the provided prometheus Metric channel.
|
||||||
func (c *NETFramework_NETCLRMemoryCollector) Collect(ch chan<- prometheus.Metric) error {
|
func (c *NETFramework_NETCLRMemoryCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error {
|
||||||
if desc, err := c.collect(ch); err != nil {
|
if desc, err := c.collect(ch); err != nil {
|
||||||
log.Error("failed collecting win32_perfrawdata_netframework_netclrmemory metrics:", desc, err)
|
log.Error("failed collecting win32_perfrawdata_netframework_netclrmemory metrics:", desc, err)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ func NewNETFramework_NETCLRRemotingCollector() (Collector, error) {
|
|||||||
|
|
||||||
// Collect sends the metric values for each metric
|
// Collect sends the metric values for each metric
|
||||||
// to the provided prometheus Metric channel.
|
// to the provided prometheus Metric channel.
|
||||||
func (c *NETFramework_NETCLRRemotingCollector) Collect(ch chan<- prometheus.Metric) error {
|
func (c *NETFramework_NETCLRRemotingCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error {
|
||||||
if desc, err := c.collect(ch); err != nil {
|
if desc, err := c.collect(ch); err != nil {
|
||||||
log.Error("failed collecting win32_perfrawdata_netframework_netclrremoting metrics:", desc, err)
|
log.Error("failed collecting win32_perfrawdata_netframework_netclrremoting metrics:", desc, err)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ func NewNETFramework_NETCLRSecurityCollector() (Collector, error) {
|
|||||||
|
|
||||||
// Collect sends the metric values for each metric
|
// Collect sends the metric values for each metric
|
||||||
// to the provided prometheus Metric channel.
|
// to the provided prometheus Metric channel.
|
||||||
func (c *NETFramework_NETCLRSecurityCollector) Collect(ch chan<- prometheus.Metric) error {
|
func (c *NETFramework_NETCLRSecurityCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error {
|
||||||
if desc, err := c.collect(ch); err != nil {
|
if desc, err := c.collect(ch); err != nil {
|
||||||
log.Error("failed collecting win32_perfrawdata_netframework_netclrsecurity metrics:", desc, err)
|
log.Error("failed collecting win32_perfrawdata_netframework_netclrsecurity metrics:", desc, err)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ func NewOSCollector() (Collector, error) {
|
|||||||
|
|
||||||
// Collect sends the metric values for each metric
|
// Collect sends the metric values for each metric
|
||||||
// to the provided prometheus Metric channel.
|
// to the provided prometheus Metric channel.
|
||||||
func (c *OSCollector) Collect(ch chan<- prometheus.Metric) error {
|
func (c *OSCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error {
|
||||||
if desc, err := c.collect(ch); err != nil {
|
if desc, err := c.collect(ch); err != nil {
|
||||||
log.Error("failed collecting os metrics:", desc, err)
|
log.Error("failed collecting os metrics:", desc, err)
|
||||||
return err
|
return err
|
||||||
|
|||||||
100
collector/perflib.go
Normal file
100
collector/perflib.go
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
package collector
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
perflibCollector "github.com/leoluk/perflib_exporter/collector"
|
||||||
|
"github.com/leoluk/perflib_exporter/perflib"
|
||||||
|
"github.com/prometheus/common/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getPerflibSnapshot() (map[string]*perflib.PerfObject, error) {
|
||||||
|
objects, err := perflib.QueryPerformanceData("Global")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
indexed := make(map[string]*perflib.PerfObject)
|
||||||
|
for _, obj := range objects {
|
||||||
|
indexed[obj.Name] = obj
|
||||||
|
}
|
||||||
|
return indexed, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalObject(obj *perflib.PerfObject, vs interface{}) error {
|
||||||
|
if obj == nil {
|
||||||
|
return fmt.Errorf("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))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure sufficient length
|
||||||
|
if ev.Cap() < len(obj.Instances) {
|
||||||
|
nvs := reflect.MakeSlice(ev.Type(), len(obj.Instances), len(obj.Instances))
|
||||||
|
ev.Set(nvs)
|
||||||
|
}
|
||||||
|
|
||||||
|
for idx, instance := range obj.Instances {
|
||||||
|
target := ev.Index(idx)
|
||||||
|
rt := target.Type()
|
||||||
|
|
||||||
|
counters := make(map[string]*perflib.PerfCounter, len(instance.Counters))
|
||||||
|
for _, ctr := range instance.Counters {
|
||||||
|
if ctr.Def.IsBaseValue && !ctr.Def.IsNanosecondCounter {
|
||||||
|
counters[ctr.Def.Name+"_Base"] = ctr
|
||||||
|
} else {
|
||||||
|
counters[ctr.Def.Name] = ctr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < target.NumField(); i++ {
|
||||||
|
f := rt.Field(i)
|
||||||
|
tag := f.Tag.Get("perflib")
|
||||||
|
if tag == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ctr, found := counters[tag]
|
||||||
|
if !found {
|
||||||
|
log.Debugf("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)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ctr.Def.CounterType {
|
||||||
|
case perflibCollector.PERF_ELAPSED_TIME:
|
||||||
|
target.Field(i).SetFloat(float64(ctr.Value-windowsEpoch) / float64(obj.Frequency))
|
||||||
|
case perflibCollector.PERF_100NSEC_TIMER, perflibCollector.PERF_PRECISION_100NS_TIMER:
|
||||||
|
target.Field(i).SetFloat(float64(ctr.Value) * ticksToSecondsScaleFactor)
|
||||||
|
default:
|
||||||
|
target.Field(i).SetFloat(float64(ctr.Value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if instance.Name != "" && target.FieldByName("Name").CanSet() {
|
||||||
|
target.FieldByName("Name").SetString(instance.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func counterMapKeys(m map[string]*perflib.PerfCounter) []string {
|
||||||
|
keys := make([]string, 0, len(m))
|
||||||
|
for k := range m {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
return keys
|
||||||
|
}
|
||||||
125
collector/perflib_test.go
Normal file
125
collector/perflib_test.go
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
package collector
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
perflibCollector "github.com/leoluk/perflib_exporter/collector"
|
||||||
|
"github.com/leoluk/perflib_exporter/perflib"
|
||||||
|
)
|
||||||
|
|
||||||
|
type simple struct {
|
||||||
|
ValA float64 `perflib:"Something"`
|
||||||
|
ValB float64 `perflib:"Something Else"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalPerflib(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
obj *perflib.PerfObject
|
||||||
|
|
||||||
|
expectedOutput []simple
|
||||||
|
expectError bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "nil check",
|
||||||
|
obj: nil,
|
||||||
|
expectedOutput: []simple{},
|
||||||
|
expectError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Simple",
|
||||||
|
obj: &perflib.PerfObject{
|
||||||
|
Instances: []*perflib.PerfInstance{
|
||||||
|
{
|
||||||
|
Counters: []*perflib.PerfCounter{
|
||||||
|
{
|
||||||
|
Def: &perflib.PerfCounterDef{
|
||||||
|
Name: "Something",
|
||||||
|
CounterType: perflibCollector.PERF_COUNTER_COUNTER,
|
||||||
|
},
|
||||||
|
Value: 123,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedOutput: []simple{{ValA: 123}},
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Multiple properties",
|
||||||
|
obj: &perflib.PerfObject{
|
||||||
|
Instances: []*perflib.PerfInstance{
|
||||||
|
{
|
||||||
|
Counters: []*perflib.PerfCounter{
|
||||||
|
{
|
||||||
|
Def: &perflib.PerfCounterDef{
|
||||||
|
Name: "Something",
|
||||||
|
CounterType: perflibCollector.PERF_COUNTER_COUNTER,
|
||||||
|
},
|
||||||
|
Value: 123,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Def: &perflib.PerfCounterDef{
|
||||||
|
Name: "Something Else",
|
||||||
|
CounterType: perflibCollector.PERF_COUNTER_COUNTER,
|
||||||
|
},
|
||||||
|
Value: 256,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedOutput: []simple{{ValA: 123, ValB: 256}},
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Multiple instances",
|
||||||
|
obj: &perflib.PerfObject{
|
||||||
|
Instances: []*perflib.PerfInstance{
|
||||||
|
{
|
||||||
|
Counters: []*perflib.PerfCounter{
|
||||||
|
{
|
||||||
|
Def: &perflib.PerfCounterDef{
|
||||||
|
Name: "Something",
|
||||||
|
CounterType: perflibCollector.PERF_COUNTER_COUNTER,
|
||||||
|
},
|
||||||
|
Value: 321,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Counters: []*perflib.PerfCounter{
|
||||||
|
{
|
||||||
|
Def: &perflib.PerfCounterDef{
|
||||||
|
Name: "Something",
|
||||||
|
CounterType: perflibCollector.PERF_COUNTER_COUNTER,
|
||||||
|
},
|
||||||
|
Value: 231,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expectedOutput: []simple{{ValA: 321}, {ValA: 231}},
|
||||||
|
expectError: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
output := make([]simple, 0)
|
||||||
|
err := unmarshalObject(c.obj, &output)
|
||||||
|
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")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err == nil && !reflect.DeepEqual(output, c.expectedOutput) {
|
||||||
|
t.Errorf("Output mismatch, expected %+v, got %+v", c.expectedOutput, output)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -135,7 +135,7 @@ func NewProcessCollector() (Collector, error) {
|
|||||||
|
|
||||||
// Collect sends the metric values for each metric
|
// Collect sends the metric values for each metric
|
||||||
// to the provided prometheus Metric channel.
|
// to the provided prometheus Metric channel.
|
||||||
func (c *ProcessCollector) Collect(ch chan<- prometheus.Metric) error {
|
func (c *ProcessCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error {
|
||||||
if desc, err := c.collect(ch); err != nil {
|
if desc, err := c.collect(ch); err != nil {
|
||||||
log.Error("failed collecting process metrics:", desc, err)
|
log.Error("failed collecting process metrics:", desc, err)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ func NewserviceCollector() (Collector, error) {
|
|||||||
|
|
||||||
// Collect sends the metric values for each metric
|
// Collect sends the metric values for each metric
|
||||||
// to the provided prometheus Metric channel.
|
// to the provided prometheus Metric channel.
|
||||||
func (c *serviceCollector) Collect(ch chan<- prometheus.Metric) error {
|
func (c *serviceCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error {
|
||||||
if desc, err := c.collect(ch); err != nil {
|
if desc, err := c.collect(ch); err != nil {
|
||||||
log.Error("failed collecting service metrics:", desc, err)
|
log.Error("failed collecting service metrics:", desc, err)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ func NewSystemCollector() (Collector, error) {
|
|||||||
|
|
||||||
// Collect sends the metric values for each metric
|
// Collect sends the metric values for each metric
|
||||||
// to the provided prometheus Metric channel.
|
// to the provided prometheus Metric channel.
|
||||||
func (c *SystemCollector) Collect(ch chan<- prometheus.Metric) error {
|
func (c *SystemCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error {
|
||||||
if desc, err := c.collect(ch); err != nil {
|
if desc, err := c.collect(ch); err != nil {
|
||||||
log.Error("failed collecting system metrics:", desc, err)
|
log.Error("failed collecting system metrics:", desc, err)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ func NewTCPCollector() (Collector, error) {
|
|||||||
|
|
||||||
// Collect sends the metric values for each metric
|
// Collect sends the metric values for each metric
|
||||||
// to the provided prometheus Metric channel.
|
// to the provided prometheus Metric channel.
|
||||||
func (c *TCPCollector) Collect(ch chan<- prometheus.Metric) error {
|
func (c *TCPCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error {
|
||||||
if desc, err := c.collect(ch); err != nil {
|
if desc, err := c.collect(ch); err != nil {
|
||||||
log.Error("failed collecting tcp metrics:", desc, err)
|
log.Error("failed collecting tcp metrics:", desc, err)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -212,7 +212,7 @@ func (cr carriageReturnFilteringReader) Read(p []byte) (int, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update implements the Collector interface.
|
// Update implements the Collector interface.
|
||||||
func (c *textFileCollector) Collect(ch chan<- prometheus.Metric) error {
|
func (c *textFileCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error {
|
||||||
error := 0.0
|
error := 0.0
|
||||||
mtimes := map[string]time.Time{}
|
mtimes := map[string]time.Time{}
|
||||||
|
|
||||||
|
|||||||
103
collector/thermalzone.go
Normal file
103
collector/thermalzone.go
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
package collector
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/StackExchange/wmi"
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
"github.com/prometheus/common/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
Factories["thermalzone"] = NewThermalZoneCollector
|
||||||
|
}
|
||||||
|
|
||||||
|
// A thermalZoneCollector is a Prometheus collector for WMI Win32_PerfRawData_Counters_ThermalZoneInformation metrics
|
||||||
|
type thermalZoneCollector struct {
|
||||||
|
PercentPassiveLimit *prometheus.Desc
|
||||||
|
Temperature *prometheus.Desc
|
||||||
|
ThrottleReasons *prometheus.Desc
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewThermalZoneCollector ...
|
||||||
|
func NewThermalZoneCollector() (Collector, error) {
|
||||||
|
const subsystem = "thermalzone"
|
||||||
|
return &thermalZoneCollector{
|
||||||
|
Temperature: prometheus.NewDesc(
|
||||||
|
prometheus.BuildFQName(Namespace, subsystem, "temperature_celsius"),
|
||||||
|
"(Temperature)",
|
||||||
|
[]string{
|
||||||
|
"name",
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
),
|
||||||
|
PercentPassiveLimit: prometheus.NewDesc(
|
||||||
|
prometheus.BuildFQName(Namespace, subsystem, "percent_passive_limit"),
|
||||||
|
"(PercentPassiveLimit)",
|
||||||
|
[]string{
|
||||||
|
"name",
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
),
|
||||||
|
ThrottleReasons: prometheus.NewDesc(
|
||||||
|
prometheus.BuildFQName(Namespace, subsystem, "throttle_reasons"),
|
||||||
|
"(ThrottleReasons)",
|
||||||
|
[]string{
|
||||||
|
"name",
|
||||||
|
},
|
||||||
|
nil,
|
||||||
|
),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect sends the metric values for each metric
|
||||||
|
// to the provided prometheus Metric channel.
|
||||||
|
func (c *thermalZoneCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error {
|
||||||
|
if desc, err := c.collect(ch); err != nil {
|
||||||
|
log.Error("failed collecting thermalzone metrics:", desc, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Win32_PerfRawData_Counters_ThermalZoneInformation docs:
|
||||||
|
// https://wutils.com/wmi/root/cimv2/win32_perfrawdata_counters_thermalzoneinformation/
|
||||||
|
type Win32_PerfRawData_Counters_ThermalZoneInformation struct {
|
||||||
|
Name string
|
||||||
|
|
||||||
|
HighPrecisionTemperature uint32
|
||||||
|
PercentPassiveLimit uint32
|
||||||
|
ThrottleReasons uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *thermalZoneCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, error) {
|
||||||
|
var dst []Win32_PerfRawData_Counters_ThermalZoneInformation
|
||||||
|
q := queryAll(&dst)
|
||||||
|
if err := wmi.Query(q, &dst); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, info := range dst {
|
||||||
|
//Divide by 10 and subtract 273.15 to convert decikelvin to celsius
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
c.Temperature,
|
||||||
|
prometheus.GaugeValue,
|
||||||
|
(float64(info.HighPrecisionTemperature)/10.0)-273.15,
|
||||||
|
info.Name,
|
||||||
|
)
|
||||||
|
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
c.PercentPassiveLimit,
|
||||||
|
prometheus.GaugeValue,
|
||||||
|
float64(info.PercentPassiveLimit),
|
||||||
|
info.Name,
|
||||||
|
)
|
||||||
|
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
c.ThrottleReasons,
|
||||||
|
prometheus.GaugeValue,
|
||||||
|
float64(info.ThrottleReasons),
|
||||||
|
info.Name,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
@@ -162,7 +162,7 @@ func NewVmwareCollector() (Collector, error) {
|
|||||||
|
|
||||||
// Collect sends the metric values for each metric
|
// Collect sends the metric values for each metric
|
||||||
// to the provided prometheus Metric channel.
|
// to the provided prometheus Metric channel.
|
||||||
func (c *VmwareCollector) Collect(ch chan<- prometheus.Metric) error {
|
func (c *VmwareCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error {
|
||||||
if desc, err := c.collectMem(ch); err != nil {
|
if desc, err := c.collectMem(ch); err != nil {
|
||||||
log.Error("failed collecting vmware memory metrics:", desc, err)
|
log.Error("failed collecting vmware memory metrics:", desc, err)
|
||||||
return err
|
return err
|
||||||
|
|||||||
@@ -4,27 +4,9 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
|
||||||
"github.com/prometheus/common/log"
|
"github.com/prometheus/common/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ...
|
|
||||||
const (
|
|
||||||
Namespace = "wmi"
|
|
||||||
|
|
||||||
// Conversion factors
|
|
||||||
ticksToSecondsScaleFactor = 1 / 1e7
|
|
||||||
)
|
|
||||||
|
|
||||||
// Factories ...
|
|
||||||
var Factories = make(map[string]func() (Collector, error))
|
|
||||||
|
|
||||||
// Collector is the interface a collector has to implement.
|
|
||||||
type Collector interface {
|
|
||||||
// Get new metrics and expose them via prometheus registry.
|
|
||||||
Collect(ch chan<- prometheus.Metric) (err error)
|
|
||||||
}
|
|
||||||
|
|
||||||
func className(src interface{}) string {
|
func className(src interface{}) string {
|
||||||
s := reflect.Indirect(reflect.ValueOf(src))
|
s := reflect.Indirect(reflect.ValueOf(src))
|
||||||
t := s.Type()
|
t := s.Type()
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
The container collector exposes metrics about containers running on system
|
The container collector exposes metrics about containers running on system
|
||||||
|
|
||||||
|
|||
|
||||||
|
-|-
|
||||||
Metric name prefix | `container`
|
Metric name prefix | `container`
|
||||||
Enabled by default? | No
|
Enabled by default? | No
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ The cpu collector exposes metrics about CPU usage
|
|||||||
|||
|
|||
|
||||||
-|-
|
-|-
|
||||||
Metric name prefix | `cpu`
|
Metric name prefix | `cpu`
|
||||||
Classes | [`Win32_PerfRawData_PerfOS_Processor`](https://msdn.microsoft.com/en-us/library/aa394317(v=vs.90).aspx)
|
Data source | Perflib
|
||||||
|
Counters | `ProcessorInformation` (Windows Server 2008R2 and later) `Processor` (older versions)
|
||||||
Enabled by default? | Yes
|
Enabled by default? | Yes
|
||||||
|
|
||||||
## Flags
|
## Flags
|
||||||
@@ -13,6 +14,7 @@ Enabled by default? | Yes
|
|||||||
None
|
None
|
||||||
|
|
||||||
## Metrics
|
## Metrics
|
||||||
|
These metrics are available on all versions of Windows:
|
||||||
|
|
||||||
Name | Description | Type | Labels
|
Name | Description | Type | Labels
|
||||||
-----|-------------|------|-------
|
-----|-------------|------|-------
|
||||||
@@ -21,6 +23,16 @@ Name | Description | Type | Labels
|
|||||||
`wmi_cpu_interrupts_total` | Total number of received and serviced hardware interrupts | counter | `core`
|
`wmi_cpu_interrupts_total` | Total number of received and serviced hardware interrupts | counter | `core`
|
||||||
`wmi_cpu_dpcs_total` | Total number of received and serviced deferred procedure calls (DPCs) | counter | `core`
|
`wmi_cpu_dpcs_total` | Total number of received and serviced deferred procedure calls (DPCs) | counter | `core`
|
||||||
|
|
||||||
|
These metrics are only exposed on Windows Server 2008R2 and later:
|
||||||
|
|
||||||
|
Name | Description | Type | Labels
|
||||||
|
-----|-------------|------|-------
|
||||||
|
`wmi_cpu_clock_interrupts_total` | Total number of received and serviced clock tick interrupts | `core`
|
||||||
|
`wmi_cpu_idle_break_events_total` | Total number of time processor was woken from idle | `core`
|
||||||
|
`wmi_cpu_parking_status` | Parking Status represents whether a processor is parked or not | `gauge`
|
||||||
|
`wmi_cpu_core_frequency_mhz` | Core frequency in megahertz | `gauge`
|
||||||
|
`wmi_cpu_processor_performance` | Processor Performance is the average performance of the processor while it is executing instructions, as a percentage of the nominal performance of the processor. On some processors, Processor Performance may exceed 100% | `gauge`
|
||||||
|
|
||||||
### Example metric
|
### Example metric
|
||||||
_This collector does not yet have explained examples, we would appreciate your help adding them!_
|
_This collector does not yet have explained examples, we would appreciate your help adding them!_
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
The memory collector exposes metrics about system memory usage
|
The memory collector exposes metrics about system memory usage
|
||||||
|
|
||||||
|
|||
|
||||||
|
-|-
|
||||||
Metric name prefix | `memory`
|
Metric name prefix | `memory`
|
||||||
Classes | `Win32_PerfRawData_PerfOS_Memory`
|
Classes | `Win32_PerfRawData_PerfOS_Memory`
|
||||||
Enabled by default? | Yes
|
Enabled by default? | Yes
|
||||||
|
|||||||
@@ -5,14 +5,14 @@ The mssql collector exposes metrics about the MSSQL server
|
|||||||
|||
|
|||
|
||||||
-|-
|
-|-
|
||||||
Metric name prefix | `mssql`
|
Metric name prefix | `mssql`
|
||||||
Classes | [`Win32_PerfRawData_MSSQLSERVER_SQLServerAccessMethods`](https://docs.microsoft.com/en-us/sql/relational-databases/performance-monitor/sql-server-access-methods-object)<br/>[`Win32_PerfRawData_MSSQLSERVER_SQLServerAvailabilityReplica`](https://docs.microsoft.com/en-us/sql/relational-databases/performance-monitor/sql-server-availability-replica)<br/>[`Win32_PerfRawData_MSSQLSERVER_SQLServerBufferManager`](https://docs.microsoft.com/en-us/sql/relational-databases/performance-monitor/sql-server-buffer-manager-object)<br/>[`Win32_PerfRawData_MSSQLSERVER_SQLServerDatabaseReplica`](https://docs.microsoft.com/en-us/sql/relational-databases/performance-monitor/sql-server-database-replica)<br/>[`Win32_PerfRawData_MSSQLSERVER_SQLServerDatabases`](https://docs.microsoft.com/en-us/sql/relational-databases/performance-monitor/sql-server-databases-object?view=sql-server-2017)<br/>[`Win32_PerfRawData_MSSQLSERVER_SQLServerGeneralStatistics`](https://docs.microsoft.com/en-us/sql/relational-databases/performance-monitor/sql-server-general-statistics-object)<br/>[`Win32_PerfRawData_MSSQLSERVER_SQLServerLocks`](https://docs.microsoft.com/en-us/sql/relational-databases/performance-monitor/sql-server-locks-object)<br/>[`Win32_PerfRawData_MSSQLSERVER_SQLServerMemoryManager`](https://docs.microsoft.com/en-us/sql/relational-databases/performance-monitor/sql-server-memory-manager-object)<br/>[`Win32_PerfRawData_MSSQLSERVER_SQLServerSQLStatistics`](https://docs.microsoft.com/en-us/sql/relational-databases/performance-monitor/sql-server-sql-statistics-object)
|
Classes | [`Win32_PerfRawData_MSSQLSERVER_SQLServerAccessMethods`](https://docs.microsoft.com/en-us/sql/relational-databases/performance-monitor/sql-server-access-methods-object)<br/>[`Win32_PerfRawData_MSSQLSERVER_SQLServerAvailabilityReplica`](https://docs.microsoft.com/en-us/sql/relational-databases/performance-monitor/sql-server-availability-replica)<br/>[`Win32_PerfRawData_MSSQLSERVER_SQLServerBufferManager`](https://docs.microsoft.com/en-us/sql/relational-databases/performance-monitor/sql-server-buffer-manager-object)<br/>[`Win32_PerfRawData_MSSQLSERVER_SQLServerDatabaseReplica`](https://docs.microsoft.com/en-us/sql/relational-databases/performance-monitor/sql-server-database-replica)<br/>[`Win32_PerfRawData_MSSQLSERVER_SQLServerDatabases`](https://docs.microsoft.com/en-us/sql/relational-databases/performance-monitor/sql-server-databases-object?view=sql-server-2017)<br/>[`Win32_PerfRawData_MSSQLSERVER_SQLServerGeneralStatistics`](https://docs.microsoft.com/en-us/sql/relational-databases/performance-monitor/sql-server-general-statistics-object)<br/>[`Win32_PerfRawData_MSSQLSERVER_SQLServerLocks`](https://docs.microsoft.com/en-us/sql/relational-databases/performance-monitor/sql-server-locks-object)<br/>[`Win32_PerfRawData_MSSQLSERVER_SQLServerMemoryManager`](https://docs.microsoft.com/en-us/sql/relational-databases/performance-monitor/sql-server-memory-manager-object)<br/>[`Win32_PerfRawData_MSSQLSERVER_SQLServerSQLStatistics`](https://docs.microsoft.com/en-us/sql/relational-databases/performance-monitor/sql-server-sql-statistics-object)<br/>[`Win32_PerfRawData_MSSQLSERVER_SQLServerSQLErrors`](https://docs.microsoft.com/en-us/sql/relational-databases/performance-monitor/sql-server-sql-errors-object)<br/>[`Win32_PerfRawData_MSSQLSERVER_SQLServerTransactions`](https://docs.microsoft.com/en-us/sql/relational-databases/performance-monitor/sql-server-transactions-object)
|
||||||
Enabled by default? | No
|
Enabled by default? | No
|
||||||
|
|
||||||
## Flags
|
## Flags
|
||||||
|
|
||||||
### `--collectors.mssql.classes-enabled`
|
### `--collectors.mssql.classes-enabled`
|
||||||
|
|
||||||
Comma-separated list of MSSQL WMI classes to use. Supported values are `accessmethods`, `availreplica`, `bufman`, `databases`, `dbreplica`, `genstats`, `locks`, `memmgr` and `sqlstats`.
|
Comma-separated list of MSSQL WMI classes to use. Supported values are `accessmethods`, `availreplica`, `bufman`, `databases`, `dbreplica`, `genstats`, `locks`, `memmgr`, `sqlstats`, `sqlerrors` and `transactions`.
|
||||||
|
|
||||||
### `--collectors.mssql.class-print`
|
### `--collectors.mssql.class-print`
|
||||||
|
|
||||||
@@ -230,6 +230,20 @@ Name | Description | Type | Labels
|
|||||||
`wmi_mssql_sqlstats_sql_compilations` | _Not yet documented_ | counter | `instance`
|
`wmi_mssql_sqlstats_sql_compilations` | _Not yet documented_ | counter | `instance`
|
||||||
`wmi_mssql_sqlstats_sql_recompilations` | _Not yet documented_ | counter | `instance`
|
`wmi_mssql_sqlstats_sql_recompilations` | _Not yet documented_ | counter | `instance`
|
||||||
`wmi_mssql_sqlstats_unsafe_auto_parameterization_attempts` | _Not yet documented_ | counter | `instance`
|
`wmi_mssql_sqlstats_unsafe_auto_parameterization_attempts` | _Not yet documented_ | counter | `instance`
|
||||||
|
`wmi_mssql_sql_errors_total` | _Not yet documented_ | counter | `instance`, `resource`
|
||||||
|
`wmi_mssql_transactions_tempdb_free_space_bytes` | _Not yet documented_ | gauge | `instance`
|
||||||
|
`wmi_mssql_transactions_longest_transaction_running_seconds` | _Not yet documented_ | gauge | `instance`
|
||||||
|
`wmi_mssql_transactions_nonsnapshot_version_active_total` | _Not yet documented_ | counter | `instance`
|
||||||
|
`wmi_mssql_transactions_snapshot_active_total` | _Not yet documented_ | counter | `instance`
|
||||||
|
`wmi_mssql_transactions_active_total` | _Not yet documented_ | counter | `instance`
|
||||||
|
`wmi_mssql_transactions_update_conflicts_total` | _Not yet documented_ | counter | `instance`
|
||||||
|
`wmi_mssql_transactions_update_snapshot_active_total` | _Not yet documented_ | counter | `instance`
|
||||||
|
`wmi_mssql_transactions_version_cleanup_rate_bytes` | _Not yet documented_ | gauge | `instance`
|
||||||
|
`wmi_mssql_transactions_version_generation_rate_bytes` | _Not yet documented_ | gauge | `instance`
|
||||||
|
`wmi_mssql_transactions_version_store_size_bytes` | _Not yet documented_ | gauge | `instance`
|
||||||
|
`wmi_mssql_transactions_version_store_units` | _Not yet documented_ | counter | `instance`
|
||||||
|
`wmi_mssql_transactions_version_store_creation_units` | _Not yet documented_ | counter | `instance`
|
||||||
|
`wmi_mssql_transactions_version_store_truncation_units` | _Not yet documented_ | counter | `instance`
|
||||||
|
|
||||||
### Example metric
|
### Example metric
|
||||||
_This collector does not yet have explained examples, we would appreciate your help adding them!_
|
_This collector does not yet have explained examples, we would appreciate your help adding them!_
|
||||||
|
|||||||
@@ -66,10 +66,42 @@ A service can have any of the following statuses:
|
|||||||
Note that there is some overlap with service state.
|
Note that there is some overlap with service state.
|
||||||
|
|
||||||
### Example metric
|
### Example metric
|
||||||
_This collector does not yet have explained examples, we would appreciate your help adding them!_
|
Lists the services that have a 'disabled' start mode.
|
||||||
|
```
|
||||||
|
wmi_service_start_mode{exported_name=~"(mssqlserver|sqlserveragent)",start_mode="disabled"}
|
||||||
|
```
|
||||||
|
|
||||||
## Useful queries
|
## Useful queries
|
||||||
_This collector does not yet have any useful queries added, we would appreciate your help adding them!_
|
Counts the number of Microsoft SQL Server/Agent Processes
|
||||||
|
```
|
||||||
|
count(wmi_service_state{exported_name=~"(sqlserveragent|mssqlserver)",state="running"})
|
||||||
|
```
|
||||||
|
|
||||||
## Alerting examples
|
## Alerting examples
|
||||||
_This collector does not yet have alerting examples, we would appreciate your help adding them!_
|
**prometheus.rules**
|
||||||
|
```
|
||||||
|
groups:
|
||||||
|
- name: Microsoft SQL Server Alerts
|
||||||
|
rules:
|
||||||
|
|
||||||
|
# Sends an alert when the 'sqlserveragent' service is not in the running state for 3 minutes.
|
||||||
|
- alert: SQL Server Agent DOWN
|
||||||
|
expr: wmi_service_state{instance="SQL",exported_name="sqlserveragent",state="running"} == 0
|
||||||
|
for: 3m
|
||||||
|
labels:
|
||||||
|
severity: high
|
||||||
|
annotations:
|
||||||
|
summary: "Service {{ $labels.exported_name }} down"
|
||||||
|
description: "Service {{ $labels.exported_name }} on instance {{ $labels.instance }} has been down for more than 3 minutes."
|
||||||
|
|
||||||
|
# Sends an alert when the 'mssqlserver' service is not in the running state for 3 minutes.
|
||||||
|
- alert: SQL Server DOWN
|
||||||
|
expr: wmi_service_state{instance="SQL",exported_name="mssqlserver",state="running"} == 0
|
||||||
|
for: 3m
|
||||||
|
labels:
|
||||||
|
severity: high
|
||||||
|
annotations:
|
||||||
|
summary: "Service {{ $labels.exported_name }} down"
|
||||||
|
description: "Service {{ $labels.exported_name }} on instance {{ $labels.instance }} has been down for more than 3 minutes."
|
||||||
|
```
|
||||||
|
In this example, `instance` is the target label of the host. So each alert will be processed per host, which is then used in the alert description.
|
||||||
|
|||||||
32
docs/collector.thermalzone.md
Normal file
32
docs/collector.thermalzone.md
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# thermalzone collector
|
||||||
|
|
||||||
|
The thermalzone collector exposes metrics about system temps. Note that temperature is given in Kelvin
|
||||||
|
|
||||||
|
|||
|
||||||
|
-|-
|
||||||
|
Metric name prefix | `thermalzone`
|
||||||
|
Classes | [`Win32_PerfRawData_Counters_ThermalZoneInformation`](https://wutils.com/wmi/root/cimv2/win32_perfrawdata_counters_thermalzoneinformation/#temperature_properties)
|
||||||
|
Enabled by default? | No
|
||||||
|
|
||||||
|
## Flags
|
||||||
|
|
||||||
|
None
|
||||||
|
|
||||||
|
## Metrics
|
||||||
|
|
||||||
|
Name | Description | Type | Labels
|
||||||
|
-----|-------------|------|-------
|
||||||
|
`wmi_thermalzone_percent_passive_limit` | % Passive Limit is the current limit this thermal zone is placing on the devices it controls. A limit of 100% indicates the devices are unconstrained. A limit of 0% indicates the devices are fully constrained. | gauge | None
|
||||||
|
`wmi_thermalzone_temperature_celsius ` | Temperature of the thermal zone, in degrees Celsius. | gauge | None
|
||||||
|
`wmi_thermalzone_throttle_reasons ` | Throttle Reasons indicate reasons why the thermal zone is limiting performance of the devices it controls. 0x0 – The zone is not throttled. 0x1 – The zone is throttled for thermal reasons. 0x2 – The zone is throttled to limit electrical current. | gauge | None
|
||||||
|
|
||||||
|
[`Throttle reasons` source](https://docs.microsoft.com/en-us/windows-hardware/design/device-experiences/examples--requirements-and-diagnostics)
|
||||||
|
|
||||||
|
### Example metric
|
||||||
|
_This collector does not yet have explained examples, we would appreciate your help adding them!_
|
||||||
|
|
||||||
|
## Useful queries
|
||||||
|
_This collector does not yet have any useful queries added, we would appreciate your help adding them!_
|
||||||
|
|
||||||
|
## Alerting examples
|
||||||
|
_This collector does not yet have alerting examples, we would appreciate your help adding them!_
|
||||||
216
exporter.go
216
exporter.go
@@ -5,7 +5,9 @@ package main
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
@@ -23,7 +25,8 @@ import (
|
|||||||
|
|
||||||
// WmiCollector implements the prometheus.Collector interface.
|
// WmiCollector implements the prometheus.Collector interface.
|
||||||
type WmiCollector struct {
|
type WmiCollector struct {
|
||||||
collectors map[string]collector.Collector
|
maxScrapeDuration time.Duration
|
||||||
|
collectors map[string]collector.Collector
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -45,6 +48,18 @@ var (
|
|||||||
[]string{"collector"},
|
[]string{"collector"},
|
||||||
nil,
|
nil,
|
||||||
)
|
)
|
||||||
|
scrapeTimeoutDesc = prometheus.NewDesc(
|
||||||
|
prometheus.BuildFQName(collector.Namespace, "exporter", "collector_timeout"),
|
||||||
|
"wmi_exporter: Whether the collector timed out.",
|
||||||
|
[]string{"collector"},
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
snapshotDuration = prometheus.NewDesc(
|
||||||
|
prometheus.BuildFQName(collector.Namespace, "exporter", "perflib_snapshot_duration_seconds"),
|
||||||
|
"Duration of perflib snapshot capture",
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
|
||||||
// This can be removed when client_golang exposes this on Windows
|
// This can be removed when client_golang exposes this on Windows
|
||||||
// (See https://github.com/prometheus/client_golang/issues/376)
|
// (See https://github.com/prometheus/client_golang/issues/376)
|
||||||
@@ -64,25 +79,112 @@ func (coll WmiCollector) Describe(ch chan<- *prometheus.Desc) {
|
|||||||
ch <- scrapeSuccessDesc
|
ch <- scrapeSuccessDesc
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect sends the collected metrics from each of the collectors to
|
type collectorOutcome int
|
||||||
// prometheus. Collect could be called several times concurrently
|
|
||||||
// and thus its run is protected by a single mutex.
|
|
||||||
func (coll WmiCollector) Collect(ch chan<- prometheus.Metric) {
|
|
||||||
wg := sync.WaitGroup{}
|
|
||||||
wg.Add(len(coll.collectors))
|
|
||||||
for name, c := range coll.collectors {
|
|
||||||
go func(name string, c collector.Collector) {
|
|
||||||
execute(name, c, ch)
|
|
||||||
wg.Done()
|
|
||||||
}(name, c)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const (
|
||||||
|
pending collectorOutcome = iota
|
||||||
|
success
|
||||||
|
failed
|
||||||
|
)
|
||||||
|
|
||||||
|
// Collect sends the collected metrics from each of the collectors to
|
||||||
|
// prometheus.
|
||||||
|
func (coll WmiCollector) Collect(ch chan<- prometheus.Metric) {
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
startTimeDesc,
|
startTimeDesc,
|
||||||
prometheus.CounterValue,
|
prometheus.CounterValue,
|
||||||
startTime,
|
startTime,
|
||||||
)
|
)
|
||||||
wg.Wait()
|
|
||||||
|
t := time.Now()
|
||||||
|
scrapeContext, err := collector.PrepareScrapeContext()
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
snapshotDuration,
|
||||||
|
prometheus.GaugeValue,
|
||||||
|
time.Since(t).Seconds(),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
ch <- prometheus.NewInvalidMetric(scrapeSuccessDesc, fmt.Errorf("failed to prepare scrape: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
wg.Add(len(coll.collectors))
|
||||||
|
collectorOutcomes := make(map[string]collectorOutcome)
|
||||||
|
for name := range coll.collectors {
|
||||||
|
collectorOutcomes[name] = pending
|
||||||
|
}
|
||||||
|
|
||||||
|
metricsBuffer := make(chan prometheus.Metric)
|
||||||
|
l := sync.Mutex{}
|
||||||
|
finished := false
|
||||||
|
go func() {
|
||||||
|
for m := range metricsBuffer {
|
||||||
|
l.Lock()
|
||||||
|
if !finished {
|
||||||
|
ch <- m
|
||||||
|
}
|
||||||
|
l.Unlock()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
for name, c := range coll.collectors {
|
||||||
|
go func(name string, c collector.Collector) {
|
||||||
|
defer wg.Done()
|
||||||
|
outcome := execute(name, c, scrapeContext, metricsBuffer)
|
||||||
|
l.Lock()
|
||||||
|
if !finished {
|
||||||
|
collectorOutcomes[name] = outcome
|
||||||
|
}
|
||||||
|
l.Unlock()
|
||||||
|
}(name, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
allDone := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
wg.Wait()
|
||||||
|
close(allDone)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Wait until either all collectors finish, or timeout expires
|
||||||
|
select {
|
||||||
|
case <-allDone:
|
||||||
|
case <-time.After(coll.maxScrapeDuration):
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Lock()
|
||||||
|
finished = true
|
||||||
|
|
||||||
|
remainingCollectorNames := make([]string, 0)
|
||||||
|
for name, outcome := range collectorOutcomes {
|
||||||
|
var successValue, timeoutValue float64
|
||||||
|
if outcome == pending {
|
||||||
|
timeoutValue = 1.0
|
||||||
|
remainingCollectorNames = append(remainingCollectorNames, name)
|
||||||
|
}
|
||||||
|
if outcome == success {
|
||||||
|
successValue = 1.0
|
||||||
|
}
|
||||||
|
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
scrapeSuccessDesc,
|
||||||
|
prometheus.GaugeValue,
|
||||||
|
successValue,
|
||||||
|
name,
|
||||||
|
)
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
scrapeTimeoutDesc,
|
||||||
|
prometheus.GaugeValue,
|
||||||
|
timeoutValue,
|
||||||
|
name,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(remainingCollectorNames) > 0 {
|
||||||
|
log.Warn("Collection timed out, still waiting for ", remainingCollectorNames)
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func filterAvailableCollectors(collectors string) string {
|
func filterAvailableCollectors(collectors string) string {
|
||||||
@@ -96,31 +198,23 @@ func filterAvailableCollectors(collectors string) string {
|
|||||||
return strings.Join(availableCollectors, ",")
|
return strings.Join(availableCollectors, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
func execute(name string, c collector.Collector, ch chan<- prometheus.Metric) {
|
func execute(name string, c collector.Collector, ctx *collector.ScrapeContext, ch chan<- prometheus.Metric) collectorOutcome {
|
||||||
begin := time.Now()
|
t := time.Now()
|
||||||
err := c.Collect(ch)
|
err := c.Collect(ctx, ch)
|
||||||
duration := time.Since(begin)
|
duration := time.Since(t).Seconds()
|
||||||
var success float64
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("collector %s failed after %fs: %s", name, duration.Seconds(), err)
|
|
||||||
success = 0
|
|
||||||
} else {
|
|
||||||
log.Debugf("collector %s succeeded after %fs.", name, duration.Seconds())
|
|
||||||
success = 1
|
|
||||||
}
|
|
||||||
ch <- prometheus.MustNewConstMetric(
|
ch <- prometheus.MustNewConstMetric(
|
||||||
scrapeDurationDesc,
|
scrapeDurationDesc,
|
||||||
prometheus.GaugeValue,
|
prometheus.GaugeValue,
|
||||||
duration.Seconds(),
|
duration,
|
||||||
name,
|
|
||||||
)
|
|
||||||
ch <- prometheus.MustNewConstMetric(
|
|
||||||
scrapeSuccessDesc,
|
|
||||||
prometheus.GaugeValue,
|
|
||||||
success,
|
|
||||||
name,
|
name,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("collector %s failed after %fs: %s", name, duration, err)
|
||||||
|
return failed
|
||||||
|
}
|
||||||
|
log.Debugf("collector %s succeeded after %fs.", name, duration)
|
||||||
|
return success
|
||||||
}
|
}
|
||||||
|
|
||||||
func expandEnabledCollectors(enabled string) []string {
|
func expandEnabledCollectors(enabled string) []string {
|
||||||
@@ -157,10 +251,6 @@ func loadCollectors(list string) (map[string]collector.Collector, error) {
|
|||||||
return collectors, nil
|
return collectors, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
|
||||||
prometheus.MustRegister(version.NewCollector("wmi_exporter"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func initWbem() {
|
func initWbem() {
|
||||||
// This initialization prevents a memory leak on WMF 5+. See
|
// This initialization prevents a memory leak on WMF 5+. See
|
||||||
// https://github.com/martinlindhe/wmi_exporter/issues/77 and linked issues
|
// https://github.com/martinlindhe/wmi_exporter/issues/77 and linked issues
|
||||||
@@ -192,6 +282,10 @@ func main() {
|
|||||||
"collectors.print",
|
"collectors.print",
|
||||||
"If true, print available collectors and exit.",
|
"If true, print available collectors and exit.",
|
||||||
).Bool()
|
).Bool()
|
||||||
|
timeoutMargin = kingpin.Flag(
|
||||||
|
"scrape.timeout-margin",
|
||||||
|
"Seconds to subtract from the timeout allowed by the client. Tune to allow for overhead or high loads.",
|
||||||
|
).Default("0.5").Float64()
|
||||||
)
|
)
|
||||||
|
|
||||||
log.AddFlags(kingpin.CommandLine)
|
log.AddFlags(kingpin.CommandLine)
|
||||||
@@ -236,10 +330,17 @@ func main() {
|
|||||||
|
|
||||||
log.Infof("Enabled collectors: %v", strings.Join(keys(collectors), ", "))
|
log.Infof("Enabled collectors: %v", strings.Join(keys(collectors), ", "))
|
||||||
|
|
||||||
nodeCollector := WmiCollector{collectors: collectors}
|
h := &metricsHandler{
|
||||||
prometheus.MustRegister(nodeCollector)
|
timeoutMargin: *timeoutMargin,
|
||||||
|
collectorFactory: func(timeout time.Duration) *WmiCollector {
|
||||||
|
return &WmiCollector{
|
||||||
|
collectors: collectors,
|
||||||
|
maxScrapeDuration: timeout,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
http.Handle(*metricsPath, promhttp.Handler())
|
http.Handle(*metricsPath, h)
|
||||||
http.HandleFunc("/health", healthCheck)
|
http.HandleFunc("/health", healthCheck)
|
||||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
http.Redirect(w, r, *metricsPath, http.StatusMovedPermanently)
|
http.Redirect(w, r, *metricsPath, http.StatusMovedPermanently)
|
||||||
@@ -303,3 +404,36 @@ loop:
|
|||||||
changes <- svc.Status{State: svc.StopPending}
|
changes <- svc.Status{State: svc.StopPending}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type metricsHandler struct {
|
||||||
|
timeoutMargin float64
|
||||||
|
collectorFactory func(timeout time.Duration) *WmiCollector
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mh *metricsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
const defaultTimeout = 10.0
|
||||||
|
|
||||||
|
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 {
|
||||||
|
log.Warnf("Couldn't parse X-Prometheus-Scrape-Timeout-Seconds: %q. Defaulting timeout to %f", v, defaultTimeout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if timeoutSeconds == 0 {
|
||||||
|
timeoutSeconds = defaultTimeout
|
||||||
|
}
|
||||||
|
timeoutSeconds = timeoutSeconds - mh.timeoutMargin
|
||||||
|
|
||||||
|
reg := prometheus.NewRegistry()
|
||||||
|
reg.MustRegister(mh.collectorFactory(time.Duration(timeoutSeconds * float64(time.Second))))
|
||||||
|
reg.MustRegister(
|
||||||
|
prometheus.NewProcessCollector(os.Getpid(), ""),
|
||||||
|
prometheus.NewGoCollector(),
|
||||||
|
version.NewCollector("wmi_exporter"),
|
||||||
|
)
|
||||||
|
|
||||||
|
h := promhttp.HandlerFor(reg, promhttp.HandlerOpts{})
|
||||||
|
h.ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ else {
|
|||||||
$members = $wmiObject `
|
$members = $wmiObject `
|
||||||
| Get-Member -MemberType Properties `
|
| Get-Member -MemberType Properties `
|
||||||
| Where-Object { $_.Definition -Match '^u?int' -and $_.Name -NotMatch '_' } `
|
| Where-Object { $_.Definition -Match '^u?int' -and $_.Name -NotMatch '_' } `
|
||||||
| Select-Object Name, @{Name="Type";Expression={$_.Definition.Split(" ")[0]}}
|
| Select-Object Name, @{Name="Type";Expression={$_.Definition.Split(" ")[0]}})
|
||||||
$input = @{
|
$input = @{
|
||||||
"Class"=$Class;
|
"Class"=$Class;
|
||||||
"CollectorName"=$CollectorName;
|
"CollectorName"=$CollectorName;
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ func New{{ .CollectorName }}Collector() (Collector, error) {
|
|||||||
}
|
}
|
||||||
// Collect sends the metric values for each metric
|
// Collect sends the metric values for each metric
|
||||||
// to the provided prometheus Metric channel.
|
// to the provided prometheus Metric channel.
|
||||||
func (c *{{ .CollectorName }}Collector) Collect(ch chan<- prometheus.Metric) error {
|
func (c *{{ .CollectorName }}Collector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error {
|
||||||
if desc, err := c.collect(ch); err != nil {
|
if desc, err := c.collect(ch); err != nil {
|
||||||
log.Error("failed collecting {{ .CollectorName | toLower }} metrics:", desc, err)
|
log.Error("failed collecting {{ .CollectorName | toLower }} metrics:", desc, err)
|
||||||
return err
|
return err
|
||||||
|
|||||||
21
vendor/github.com/leoluk/perflib_exporter/LICENSE
generated
vendored
Normal file
21
vendor/github.com/leoluk/perflib_exporter/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2018 Leopold Schabel / The perflib_exporter authors
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
184
vendor/github.com/leoluk/perflib_exporter/collector/collector.go
generated
vendored
Normal file
184
vendor/github.com/leoluk/perflib_exporter/collector/collector.go
generated
vendored
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
package collector
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/leoluk/perflib_exporter/perflib"
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
"github.com/prometheus/common/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ...
|
||||||
|
const (
|
||||||
|
Namespace = "perflib"
|
||||||
|
|
||||||
|
// Conversion factors
|
||||||
|
hundredNsToSecondsScaleFactor = 1 / 1e7
|
||||||
|
)
|
||||||
|
|
||||||
|
// Collector is the interface a collector has to implement.
|
||||||
|
type Collector interface {
|
||||||
|
// Get new metrics and expose them via prometheus registry.
|
||||||
|
Collect(ch chan<- prometheus.Metric) (err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type CounterKey struct {
|
||||||
|
ObjectIndex uint
|
||||||
|
CounterIndex uint
|
||||||
|
CounterType uint32 // This is a bit mask
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCounterKey(object *perflib.PerfObject, def *perflib.PerfCounterDef) CounterKey {
|
||||||
|
return CounterKey{object.NameIndex, def.NameIndex, def.CounterType}
|
||||||
|
}
|
||||||
|
|
||||||
|
type PerflibCollector struct {
|
||||||
|
perflibQuery string
|
||||||
|
perflibDescs map[CounterKey]*prometheus.Desc
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPerflibCollector(query string) (c PerflibCollector) {
|
||||||
|
c.perflibQuery = query
|
||||||
|
|
||||||
|
objects, err := perflib.QueryPerformanceData(c.perflibQuery)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("Number of objects: %d", len(objects))
|
||||||
|
|
||||||
|
c.perflibDescs = make(map[CounterKey]*prometheus.Desc)
|
||||||
|
|
||||||
|
for _, object := range objects {
|
||||||
|
for _, def := range object.CounterDefs {
|
||||||
|
desc := descFromCounterDef(*object, *def)
|
||||||
|
|
||||||
|
key := NewCounterKey(object, def)
|
||||||
|
c.perflibDescs[key] = desc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c PerflibCollector) Collect(ch chan<- prometheus.Metric) (err error) {
|
||||||
|
// TODO QueryPerformanceData timing metric
|
||||||
|
objects, err := perflib.QueryPerformanceData(c.perflibQuery)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
// TODO - we shouldn't panic if a single call fails
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("Number of objects: %d", len(objects))
|
||||||
|
|
||||||
|
for _, object := range objects {
|
||||||
|
n := object.NameIndex
|
||||||
|
|
||||||
|
for _, instance := range object.Instances {
|
||||||
|
name := instance.Name
|
||||||
|
|
||||||
|
// _Total metrics do not fit into the Prometheus model - we try to merge similar
|
||||||
|
// metrics and give them labels, so you'd sum() them instead. Having a _Total label
|
||||||
|
// would make
|
||||||
|
if strings.HasSuffix(name, "_Total") || strings.HasPrefix(name, "Total") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, counter := range instance.Counters {
|
||||||
|
if IsDefPromotedLabel(n, counter.Def.NameIndex) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if counter == nil {
|
||||||
|
log.Debugf("nil counter for %s -> %s", object.Name, instance.Name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if counter.Def.NameIndex == 0 {
|
||||||
|
log.Debugf("null counter index for %s -> %s", object.Name, instance.Name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if counter.Def.Name == "" {
|
||||||
|
log.Debugf("no counter name for %s -> %s", object.Name, instance.Name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if counter.Def.Name == "No name" {
|
||||||
|
log.Debugf("no name counter %s -> %s -> %s", object.Name, instance.Name, counter.Def.Name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
key := NewCounterKey(object, counter.Def)
|
||||||
|
|
||||||
|
desc, ok := c.perflibDescs[key]
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
log.Debugf("missing metric description for counter %s -> %s -> %s", object.Name, instance.Name, counter.Def.Name)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
labels := []string{name}
|
||||||
|
|
||||||
|
if len(object.Instances) == 1 {
|
||||||
|
labels = []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if HasPromotedLabels(n) {
|
||||||
|
labels = append(labels, PromotedLabelValuesForInstance(n, instance)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO - Label merging needs to be fixed for [230] Process
|
||||||
|
//if HasMergedLabels(n) {
|
||||||
|
// _, value := MergedMetricForInstance(n, counter.Def.NameIndex)
|
||||||
|
//
|
||||||
|
// // Null string in definition means we should skip this metric (it's probably a sum)
|
||||||
|
// if value == "" {
|
||||||
|
// log.Debugf("Skipping %d -> %s (empty merge label)", n, counter.Def.NameIndex)
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// labels = append(labels, value)
|
||||||
|
//}
|
||||||
|
|
||||||
|
valueType, err := GetPrometheusValueType(counter.Def.CounterType)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
// TODO - Is this too verbose? There will always be counter types we don't support
|
||||||
|
log.Debug(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
value := float64(counter.Value)
|
||||||
|
|
||||||
|
if counter.Def.IsNanosecondCounter {
|
||||||
|
value = value * hundredNsToSecondsScaleFactor
|
||||||
|
}
|
||||||
|
|
||||||
|
if IsElapsedTime(counter.Def.CounterType) {
|
||||||
|
// convert from Windows timestamp (1 jan 1601) to unix timestamp (1 jan 1970)
|
||||||
|
value = float64(counter.Value-116444736000000000) / float64(object.Frequency)
|
||||||
|
}
|
||||||
|
|
||||||
|
metric := prometheus.MustNewConstMetric(
|
||||||
|
desc,
|
||||||
|
valueType,
|
||||||
|
value,
|
||||||
|
labels...,
|
||||||
|
)
|
||||||
|
|
||||||
|
ch <- metric
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*ch <- prometheus.MustNewConstMetric(
|
||||||
|
prometheus.NewDesc(),
|
||||||
|
prometheus.CounterValue,
|
||||||
|
float64(0),
|
||||||
|
"ds_client",
|
||||||
|
)*/
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
90
vendor/github.com/leoluk/perflib_exporter/collector/mangle.go
generated
vendored
Normal file
90
vendor/github.com/leoluk/perflib_exporter/collector/mangle.go
generated
vendored
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
package collector
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/leoluk/perflib_exporter/perflib"
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func manglePerflibName(s string) string {
|
||||||
|
s = strings.ToLower(s)
|
||||||
|
s = strings.Replace(s, " ", "_", -1)
|
||||||
|
s = strings.Replace(s, ".", "", -1)
|
||||||
|
s = strings.Replace(s, "(", "", -1)
|
||||||
|
s = strings.Replace(s, ")", "", -1)
|
||||||
|
s = strings.Replace(s, "+", "", -1)
|
||||||
|
s = strings.Replace(s, "-", "", -1)
|
||||||
|
s = strings.Replace(s, ",", "", -1)
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func manglePerflibCounterName(s string) string {
|
||||||
|
s = manglePerflibName(s)
|
||||||
|
|
||||||
|
s = strings.Replace(s, "total_", "", -1)
|
||||||
|
s = strings.Replace(s, "_total", "", -1)
|
||||||
|
s = strings.Replace(s, "/second", "", -1)
|
||||||
|
s = strings.Replace(s, "/sec", "", -1)
|
||||||
|
s = strings.Replace(s, "_%", "", -1)
|
||||||
|
s = strings.Replace(s, "%_", "", -1)
|
||||||
|
s = strings.Replace(s, "/", "_per_", -1)
|
||||||
|
s = strings.Replace(s, "&", "and", -1)
|
||||||
|
s = strings.Replace(s, "#_of_", "", -1)
|
||||||
|
s = strings.Replace(s, ":", "", -1)
|
||||||
|
s = strings.Replace(s, "__", "_", -1)
|
||||||
|
|
||||||
|
s = strings.Trim(s, " _")
|
||||||
|
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func MakePrometheusLabel(def *perflib.PerfCounterDef) (s string) {
|
||||||
|
s = manglePerflibCounterName(def.Name)
|
||||||
|
|
||||||
|
if len(s) > 0 {
|
||||||
|
if IsCounter(def.CounterType) {
|
||||||
|
s += "_total"
|
||||||
|
} else if IsBaseValue(def.CounterType) && !strings.HasSuffix(s, "_base") {
|
||||||
|
s += "_max"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func pdhNameFromCounterDef(obj perflib.PerfObject, def perflib.PerfCounterDef) string {
|
||||||
|
return fmt.Sprintf(`\%s(*)\%s`, obj.Name, def.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func descFromCounterDef(obj perflib.PerfObject, def perflib.PerfCounterDef) *prometheus.Desc {
|
||||||
|
subsystem := manglePerflibName(obj.Name)
|
||||||
|
counterName := MakePrometheusLabel(&def)
|
||||||
|
|
||||||
|
labels := []string{"name"}
|
||||||
|
|
||||||
|
if len(obj.Instances) == 1 {
|
||||||
|
labels = []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if HasPromotedLabels(obj.NameIndex) {
|
||||||
|
labels = append(labels, PromotedLabelsForObject(obj.NameIndex)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO - Label merging needs to be fixed for [230] Process
|
||||||
|
//if HasMergedLabels(obj.NameIndex) {
|
||||||
|
// s, labelsForObject := MergedLabelsForInstance(obj.NameIndex, def.NameIndex)
|
||||||
|
// counterName = s
|
||||||
|
// labels = append(labels, labelsForObject)
|
||||||
|
//}
|
||||||
|
|
||||||
|
return prometheus.NewDesc(
|
||||||
|
prometheus.BuildFQName(Namespace, subsystem, counterName),
|
||||||
|
fmt.Sprintf("perflib metric: %s (see /dump for docs) [%d]",
|
||||||
|
pdhNameFromCounterDef(obj, def), def.NameIndex),
|
||||||
|
labels,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
91
vendor/github.com/leoluk/perflib_exporter/collector/mapper.go
generated
vendored
Normal file
91
vendor/github.com/leoluk/perflib_exporter/collector/mapper.go
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
package collector
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PERF_COUNTER_RAWCOUNT_HEX = 0x00000000
|
||||||
|
PERF_COUNTER_LARGE_RAWCOUNT_HEX = 0x00000100
|
||||||
|
PERF_COUNTER_TEXT = 0x00000b00
|
||||||
|
PERF_COUNTER_RAWCOUNT = 0x00010000
|
||||||
|
PERF_COUNTER_LARGE_RAWCOUNT = 0x00010100
|
||||||
|
PERF_DOUBLE_RAW = 0x00012000
|
||||||
|
PERF_COUNTER_DELTA = 0x00400400
|
||||||
|
PERF_COUNTER_LARGE_DELTA = 0x00400500
|
||||||
|
PERF_SAMPLE_COUNTER = 0x00410400
|
||||||
|
PERF_COUNTER_QUEUELEN_TYPE = 0x00450400
|
||||||
|
PERF_COUNTER_LARGE_QUEUELEN_TYPE = 0x00450500
|
||||||
|
PERF_COUNTER_100NS_QUEUELEN_TYPE = 0x00550500
|
||||||
|
PERF_COUNTER_OBJ_TIME_QUEUELEN_TYPE = 0x00650500
|
||||||
|
PERF_COUNTER_COUNTER = 0x10410400
|
||||||
|
PERF_COUNTER_BULK_COUNT = 0x10410500
|
||||||
|
PERF_RAW_FRACTION = 0x20020400
|
||||||
|
PERF_LARGE_RAW_FRACTION = 0x20020500
|
||||||
|
PERF_COUNTER_TIMER = 0x20410500
|
||||||
|
PERF_PRECISION_SYSTEM_TIMER = 0x20470500
|
||||||
|
PERF_100NSEC_TIMER = 0x20510500
|
||||||
|
PERF_PRECISION_100NS_TIMER = 0x20570500
|
||||||
|
PERF_OBJ_TIME_TIMER = 0x20610500
|
||||||
|
PERF_PRECISION_OBJECT_TIMER = 0x20670500
|
||||||
|
PERF_SAMPLE_FRACTION = 0x20c20400
|
||||||
|
PERF_COUNTER_TIMER_INV = 0x21410500
|
||||||
|
PERF_100NSEC_TIMER_INV = 0x21510500
|
||||||
|
PERF_COUNTER_MULTI_TIMER = 0x22410500
|
||||||
|
PERF_100NSEC_MULTI_TIMER = 0x22510500
|
||||||
|
PERF_COUNTER_MULTI_TIMER_INV = 0x23410500
|
||||||
|
PERF_100NSEC_MULTI_TIMER_INV = 0x23510500
|
||||||
|
PERF_AVERAGE_TIMER = 0x30020400
|
||||||
|
PERF_ELAPSED_TIME = 0x30240500
|
||||||
|
PERF_COUNTER_NODATA = 0x40000200
|
||||||
|
PERF_AVERAGE_BULK = 0x40020500
|
||||||
|
PERF_SAMPLE_BASE = 0x40030401
|
||||||
|
PERF_AVERAGE_BASE = 0x40030402
|
||||||
|
PERF_RAW_BASE = 0x40030403
|
||||||
|
PERF_PRECISION_TIMESTAMP = 0x40030500
|
||||||
|
PERF_LARGE_RAW_BASE = 0x40030503
|
||||||
|
PERF_COUNTER_MULTI_BASE = 0x42030500
|
||||||
|
PERF_COUNTER_HISTOGRAM_TYPE = 0x80000000
|
||||||
|
)
|
||||||
|
|
||||||
|
var supportedCounterTypes = map[uint32]prometheus.ValueType{
|
||||||
|
PERF_COUNTER_RAWCOUNT_HEX: prometheus.GaugeValue,
|
||||||
|
PERF_COUNTER_LARGE_RAWCOUNT_HEX: prometheus.GaugeValue,
|
||||||
|
PERF_COUNTER_RAWCOUNT: prometheus.GaugeValue,
|
||||||
|
PERF_COUNTER_LARGE_RAWCOUNT: prometheus.GaugeValue,
|
||||||
|
PERF_COUNTER_DELTA: prometheus.CounterValue,
|
||||||
|
PERF_COUNTER_COUNTER: prometheus.CounterValue,
|
||||||
|
PERF_COUNTER_BULK_COUNT: prometheus.CounterValue,
|
||||||
|
PERF_RAW_FRACTION: prometheus.GaugeValue,
|
||||||
|
PERF_LARGE_RAW_FRACTION: prometheus.GaugeValue,
|
||||||
|
PERF_100NSEC_TIMER: prometheus.CounterValue,
|
||||||
|
PERF_PRECISION_100NS_TIMER: prometheus.CounterValue,
|
||||||
|
PERF_SAMPLE_FRACTION: prometheus.GaugeValue,
|
||||||
|
PERF_100NSEC_TIMER_INV: prometheus.CounterValue,
|
||||||
|
PERF_ELAPSED_TIME: prometheus.GaugeValue,
|
||||||
|
PERF_SAMPLE_BASE: prometheus.GaugeValue,
|
||||||
|
PERF_RAW_BASE: prometheus.GaugeValue,
|
||||||
|
PERF_LARGE_RAW_BASE: prometheus.GaugeValue,
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsCounter(counterType uint32) bool {
|
||||||
|
return supportedCounterTypes[counterType] == prometheus.CounterValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsBaseValue(counterType uint32) bool {
|
||||||
|
return counterType == PERF_SAMPLE_BASE || counterType == PERF_RAW_BASE || counterType == PERF_LARGE_RAW_BASE
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsElapsedTime(counterType uint32) bool {
|
||||||
|
return counterType == PERF_ELAPSED_TIME
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPrometheusValueType(counterType uint32) (prometheus.ValueType, error) {
|
||||||
|
val, ok := supportedCounterTypes[counterType]
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("counter type %#08x is not supported", counterType)
|
||||||
|
}
|
||||||
|
return val, nil
|
||||||
|
}
|
||||||
38
vendor/github.com/leoluk/perflib_exporter/collector/merge.go
generated
vendored
Normal file
38
vendor/github.com/leoluk/perflib_exporter/collector/merge.go
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
package collector
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
var mergedDefinitions = map[uint]map[string]map[uint]string{
|
||||||
|
230: {
|
||||||
|
"processor_time_total": {
|
||||||
|
0: "mode",
|
||||||
|
6: "", // Processor Time (drop)
|
||||||
|
142: "user", // User Time
|
||||||
|
144: "privileged", // Privileged Time
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return if a given object has merge definitions
|
||||||
|
func HasMergedLabels(index uint) bool {
|
||||||
|
_, ok := mergedDefinitions[index]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return a list of merged label names for an instance
|
||||||
|
func MergedLabelsForInstance(objIndex uint, def uint) (name string, labelName string) {
|
||||||
|
return MergedMetricForInstance(objIndex, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return merged metric name and label value for an instance
|
||||||
|
func MergedMetricForInstance(objIndex uint, def uint) (name string, label string) {
|
||||||
|
for k, v := range mergedDefinitions[objIndex] {
|
||||||
|
for n := range v {
|
||||||
|
if def == n {
|
||||||
|
return k, v[n]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
panic(fmt.Sprintf("No merge definition for obj %d, inst %d", objIndex, def))
|
||||||
|
}
|
||||||
56
vendor/github.com/leoluk/perflib_exporter/collector/promote.go
generated
vendored
Normal file
56
vendor/github.com/leoluk/perflib_exporter/collector/promote.go
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
package collector
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/leoluk/perflib_exporter/perflib"
|
||||||
|
)
|
||||||
|
|
||||||
|
var labelPromotionLabels = map[uint][]string{
|
||||||
|
230: {
|
||||||
|
"process_id",
|
||||||
|
"creating_process_id",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var labelPromotionValues = map[uint][]uint{
|
||||||
|
230: {
|
||||||
|
784, // process_id
|
||||||
|
1410, // creating_process_id
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a list of promoted labels for an object
|
||||||
|
func PromotedLabelsForObject(index uint) []string {
|
||||||
|
return labelPromotionLabels[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a list of label values for a given object and instance
|
||||||
|
func PromotedLabelValuesForInstance(index uint, instance *perflib.PerfInstance) []string {
|
||||||
|
values := make([]string, len(labelPromotionValues[index]))
|
||||||
|
|
||||||
|
for _, c := range instance.Counters {
|
||||||
|
for i, v := range labelPromotionValues[index] {
|
||||||
|
if c.Def.NameIndex == v {
|
||||||
|
values[i] = strconv.Itoa(int(c.Value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return if a given object has label promotion definitions
|
||||||
|
func HasPromotedLabels(index uint) bool {
|
||||||
|
_, ok := labelPromotionLabels[index]
|
||||||
|
return ok
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return if a given definition is a promoted label for an object
|
||||||
|
func IsDefPromotedLabel(objIndex uint, def uint) bool {
|
||||||
|
for _, v := range labelPromotionValues[objIndex] {
|
||||||
|
if v == def {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
75
vendor/github.com/leoluk/perflib_exporter/perflib/nametable.go
generated
vendored
Normal file
75
vendor/github.com/leoluk/perflib_exporter/perflib/nametable.go
generated
vendored
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
package perflib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type nameTableLookuper interface {
|
||||||
|
LookupName() string
|
||||||
|
LookupHelp() string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *perfObjectType) LookupName() string {
|
||||||
|
return counterNameTable.LookupString(p.ObjectNameTitleIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *perfObjectType) LookupHelp() string {
|
||||||
|
return helpNameTable.LookupString(p.ObjectHelpTitleIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
type NameTable struct {
|
||||||
|
byIndex map[uint32]string
|
||||||
|
byString map[string]uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *NameTable) LookupString(index uint32) string {
|
||||||
|
return t.byIndex[index]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *NameTable) LookupIndex(str string) uint32 {
|
||||||
|
return t.byString[str]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Query a perflib name table from the registry. Specify the type and the language
|
||||||
|
// code (i.e. "Counter 009" or "Help 009") for English language.
|
||||||
|
func QueryNameTable(tableName string) *NameTable {
|
||||||
|
nameTable := new(NameTable)
|
||||||
|
nameTable.byIndex = make(map[uint32]string)
|
||||||
|
|
||||||
|
buffer, err := queryRawData(tableName)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
r := bytes.NewReader(buffer)
|
||||||
|
for {
|
||||||
|
index, err := readUTF16String(r)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
desc, err := readUTF16String(r)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
indexInt, _ := strconv.Atoi(index)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprint("Invalid index ", index))
|
||||||
|
}
|
||||||
|
|
||||||
|
nameTable.byIndex[uint32(indexInt)] = desc
|
||||||
|
}
|
||||||
|
|
||||||
|
nameTable.byString = make(map[string]uint32)
|
||||||
|
|
||||||
|
for k, v := range nameTable.byIndex {
|
||||||
|
nameTable.byString[v] = k
|
||||||
|
}
|
||||||
|
|
||||||
|
return nameTable
|
||||||
|
}
|
||||||
460
vendor/github.com/leoluk/perflib_exporter/perflib/perflib.go
generated
vendored
Normal file
460
vendor/github.com/leoluk/perflib_exporter/perflib/perflib.go
generated
vendored
Normal file
@@ -0,0 +1,460 @@
|
|||||||
|
/*
|
||||||
|
Go bindings for the HKEY_PERFORMANCE_DATA perflib / Performance Counters interface.
|
||||||
|
|
||||||
|
Overview
|
||||||
|
|
||||||
|
HKEY_PERFORMANCE_DATA is a low-level alternative to the higher-level PDH library and WMI.
|
||||||
|
It operates on blocks of counters and only returns raw values without calculating rates
|
||||||
|
or formatting them, which is exactly what you want for, say, a Prometheus exporter
|
||||||
|
(not so much for a GUI like Windows Performance Monitor).
|
||||||
|
|
||||||
|
Its overhead is much lower than the high-level libraries.
|
||||||
|
|
||||||
|
It operates on the same set of perflib providers as PDH and WMI. See this document
|
||||||
|
for more details on the relationship between the different libraries:
|
||||||
|
https://msdn.microsoft.com/en-us/library/windows/desktop/aa371643(v=vs.85).aspx
|
||||||
|
|
||||||
|
Example C++ source code:
|
||||||
|
https://msdn.microsoft.com/de-de/library/windows/desktop/aa372138(v=vs.85).aspx
|
||||||
|
|
||||||
|
For now, the API is not stable and is probably going to change in future
|
||||||
|
perflib_exporter releases. If you want to use this library, send the author an email
|
||||||
|
so we can discuss your requirements and stabilize the API.
|
||||||
|
|
||||||
|
Names
|
||||||
|
|
||||||
|
Counter names and help texts are resolved by looking up an index in a name table.
|
||||||
|
Since Microsoft loves internalization, both names and help texts can be requested
|
||||||
|
any locally available language.
|
||||||
|
|
||||||
|
The library automatically loads the name tables and resolves all identifiers
|
||||||
|
in English ("Name" and "HelpText" struct members). You can manually resolve
|
||||||
|
identifiers in a different language by using the NameTable API.
|
||||||
|
|
||||||
|
Performance Counters intro
|
||||||
|
|
||||||
|
Windows has a system-wide performance counter mechanism. Most performance counters
|
||||||
|
are stored as actual counters, not gauges (with some exceptions).
|
||||||
|
There's additional metadata which defines how the counter should be presented to the user
|
||||||
|
(for example, as a calculated rate). This library disregards all of the display metadata.
|
||||||
|
|
||||||
|
At the top level, there's a number of performance counter objects.
|
||||||
|
Each object has counter definitions, which contain the metadata for a particular
|
||||||
|
counter, and either zero or multiple instances. We hide the fact that there are
|
||||||
|
objects with no instances, and simply return a single null instance.
|
||||||
|
|
||||||
|
There's one counter per counter definition and instance (or the object itself, if
|
||||||
|
there are no instances).
|
||||||
|
|
||||||
|
Behind the scenes, every perflib DLL provides one or more objects.
|
||||||
|
Perflib has a registry where DLLs are dynamically registered and
|
||||||
|
unregistered. Some third party applications like VMWare provide their own counters,
|
||||||
|
but this is, sadly, a rare occurrence.
|
||||||
|
|
||||||
|
Different Windows releases have different numbers of counters.
|
||||||
|
|
||||||
|
Objects and counters are identified by well-known indices.
|
||||||
|
|
||||||
|
Here's an example object with one instance:
|
||||||
|
|
||||||
|
4320 WSMan Quota Statistics [7 counters, 1 instance(s)]
|
||||||
|
`-- "WinRMService"
|
||||||
|
`-- Total Requests/Second [4322] = 59
|
||||||
|
`-- User Quota Violations/Second [4324] = 0
|
||||||
|
`-- System Quota Violations/Second [4326] = 0
|
||||||
|
`-- Active Shells [4328] = 0
|
||||||
|
`-- Active Operations [4330] = 0
|
||||||
|
`-- Active Users [4332] = 0
|
||||||
|
`-- Process ID [4334] = 928
|
||||||
|
|
||||||
|
All "per second" metrics are counters, the rest are gauges.
|
||||||
|
|
||||||
|
Another example, with no instance:
|
||||||
|
|
||||||
|
4600 Network QoS Policy [6 counters, 1 instance(s)]
|
||||||
|
`-- (default)
|
||||||
|
`-- Packets transmitted [4602] = 1744
|
||||||
|
`-- Packets transmitted/sec [4604] = 4852
|
||||||
|
`-- Bytes transmitted [4606] = 4853
|
||||||
|
`-- Bytes transmitted/sec [4608] = 180388626632
|
||||||
|
`-- Packets dropped [4610] = 0
|
||||||
|
`-- Packets dropped/sec [4612] = 0
|
||||||
|
|
||||||
|
You can access the same values using PowerShell's Get-Counter cmdlet
|
||||||
|
or the Performance Monitor.
|
||||||
|
|
||||||
|
> Get-Counter '\WSMan Quota Statistics(WinRMService)\Process ID'
|
||||||
|
|
||||||
|
Timestamp CounterSamples
|
||||||
|
--------- --------------
|
||||||
|
1/28/2018 10:18:00 PM \\DEV\wsman quota statistics(winrmservice)\process id :
|
||||||
|
928
|
||||||
|
|
||||||
|
> (Get-Counter '\Process(Idle)\% Processor Time').CounterSamples[0] | Format-List *
|
||||||
|
[..detailed output...]
|
||||||
|
|
||||||
|
Data for some of the objects is also available through WMI:
|
||||||
|
|
||||||
|
> Get-CimInstance Win32_PerfRawData_Counters_WSManQuotaStatistics
|
||||||
|
|
||||||
|
Name : WinRMService
|
||||||
|
[...]
|
||||||
|
ActiveOperations : 0
|
||||||
|
ActiveShells : 0
|
||||||
|
ActiveUsers : 0
|
||||||
|
ProcessID : 928
|
||||||
|
SystemQuotaViolationsPerSecond : 0
|
||||||
|
TotalRequestsPerSecond : 59
|
||||||
|
UserQuotaViolationsPerSecond : 0
|
||||||
|
|
||||||
|
*/
|
||||||
|
package perflib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: There's a LittleEndian field in the PERF header - we ought to check it
|
||||||
|
var bo = binary.LittleEndian
|
||||||
|
|
||||||
|
var counterNameTable NameTable
|
||||||
|
var helpNameTable NameTable
|
||||||
|
|
||||||
|
// Top-level performance object (like "Process").
|
||||||
|
type PerfObject struct {
|
||||||
|
Name string
|
||||||
|
// Same index you pass to QueryPerformanceData
|
||||||
|
NameIndex uint
|
||||||
|
HelpText string
|
||||||
|
HelpTextIndex uint
|
||||||
|
Instances []*PerfInstance
|
||||||
|
CounterDefs []*PerfCounterDef
|
||||||
|
|
||||||
|
Frequency int64
|
||||||
|
|
||||||
|
rawData *perfObjectType
|
||||||
|
}
|
||||||
|
|
||||||
|
// Each object can have multiple instances. For example,
|
||||||
|
// In case the object has no instances, we return one single PerfInstance with an empty name.
|
||||||
|
type PerfInstance struct {
|
||||||
|
// *not* resolved using a name table
|
||||||
|
Name string
|
||||||
|
Counters []*PerfCounter
|
||||||
|
|
||||||
|
rawData *perfInstanceDefinition
|
||||||
|
rawCounterBlock *perfCounterBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
type PerfCounterDef struct {
|
||||||
|
Name string
|
||||||
|
NameIndex uint
|
||||||
|
HelpText string
|
||||||
|
HelpTextIndex uint
|
||||||
|
|
||||||
|
// For debugging - subject to removal. CounterType is a perflib
|
||||||
|
// implementation detail (see perflib.h) and should not be used outside
|
||||||
|
// of this package. We export it so we can show it on /dump.
|
||||||
|
CounterType uint32
|
||||||
|
|
||||||
|
// PERF_TYPE_COUNTER (otherwise, it's a gauge)
|
||||||
|
IsCounter bool
|
||||||
|
// PERF_COUNTER_BASE (base value of a multi-value fraction)
|
||||||
|
IsBaseValue bool
|
||||||
|
// PERF_TIMER_100NS
|
||||||
|
IsNanosecondCounter bool
|
||||||
|
|
||||||
|
rawData *perfCounterDefinition
|
||||||
|
}
|
||||||
|
|
||||||
|
type PerfCounter struct {
|
||||||
|
Value int64
|
||||||
|
Def *PerfCounterDef
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error value returned by RegQueryValueEx if the buffer isn't sufficiently large
|
||||||
|
const errorMoreData = syscall.Errno(234)
|
||||||
|
|
||||||
|
var (
|
||||||
|
bufLenGlobal = uint32(400000)
|
||||||
|
bufLenCostly = uint32(2000000)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Queries the performance counter buffer using RegQueryValueEx, returning raw bytes. See:
|
||||||
|
// https://msdn.microsoft.com/de-de/library/windows/desktop/aa373219(v=vs.85).aspx
|
||||||
|
func queryRawData(query string) ([]byte, error) {
|
||||||
|
var (
|
||||||
|
valType uint32
|
||||||
|
buffer []byte
|
||||||
|
bufLen uint32
|
||||||
|
)
|
||||||
|
|
||||||
|
switch query {
|
||||||
|
case "Global":
|
||||||
|
bufLen = bufLenGlobal
|
||||||
|
case "Costly":
|
||||||
|
bufLen = bufLenCostly
|
||||||
|
default:
|
||||||
|
// TODO: depends on the number of values requested
|
||||||
|
// need make an educated guess
|
||||||
|
numCounters := len(strings.Split(query, " "))
|
||||||
|
bufLen = uint32(150000 * numCounters)
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer = make([]byte, bufLen)
|
||||||
|
|
||||||
|
name, err := syscall.UTF16PtrFromString(query)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to encode query string: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
bufLen := uint32(len(buffer))
|
||||||
|
|
||||||
|
err := syscall.RegQueryValueEx(
|
||||||
|
syscall.HKEY_PERFORMANCE_DATA,
|
||||||
|
name,
|
||||||
|
nil,
|
||||||
|
&valType,
|
||||||
|
(*byte)(unsafe.Pointer(&buffer[0])),
|
||||||
|
&bufLen)
|
||||||
|
|
||||||
|
if err == errorMoreData {
|
||||||
|
newBuffer := make([]byte, len(buffer)+16384)
|
||||||
|
copy(newBuffer, buffer)
|
||||||
|
buffer = newBuffer
|
||||||
|
continue
|
||||||
|
} else if err != nil {
|
||||||
|
if errno, ok := err.(syscall.Errno); ok {
|
||||||
|
return nil, fmt.Errorf("ReqQueryValueEx failed: %v errno %d", err, uint(errno))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer = buffer[:bufLen]
|
||||||
|
|
||||||
|
switch query {
|
||||||
|
case "Global":
|
||||||
|
if bufLen > bufLenGlobal {
|
||||||
|
bufLenGlobal = bufLen
|
||||||
|
}
|
||||||
|
case "Costly":
|
||||||
|
if bufLen > bufLenCostly {
|
||||||
|
bufLenCostly = bufLen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Initialize global name tables
|
||||||
|
// TODO: profiling, add option to disable name tables if necessary
|
||||||
|
// Not sure if we should resolve the names at all or just have the caller do it on demand
|
||||||
|
// (for many use cases the index is sufficient)
|
||||||
|
|
||||||
|
counterNameTable = *QueryNameTable("Counter 009")
|
||||||
|
helpNameTable = *QueryNameTable("Help 009")
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Query all performance counters that match a given query.
|
||||||
|
|
||||||
|
The query can be any of the following:
|
||||||
|
|
||||||
|
- "Global" (all performance counters except those Windows marked as costly)
|
||||||
|
|
||||||
|
- "Costly" (only the costly ones)
|
||||||
|
|
||||||
|
- One or more object indices, separated by spaces ("238 2 5")
|
||||||
|
|
||||||
|
Many objects have dependencies - if you query one of them, you often get back
|
||||||
|
more than you asked for.
|
||||||
|
*/
|
||||||
|
func QueryPerformanceData(query string) ([]*PerfObject, error) {
|
||||||
|
buffer, err := queryRawData(query)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
r := bytes.NewReader(buffer)
|
||||||
|
|
||||||
|
// Read global header
|
||||||
|
|
||||||
|
header := new(perfDataBlock)
|
||||||
|
err = header.BinaryReadFrom(r)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for "PERF" signature
|
||||||
|
if header.Signature != [4]uint16{80, 69, 82, 70} {
|
||||||
|
panic("Invalid performance block header")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the performance data
|
||||||
|
|
||||||
|
numObjects := int(header.NumObjectTypes)
|
||||||
|
objects := make([]*PerfObject, numObjects)
|
||||||
|
|
||||||
|
objOffset := int64(header.HeaderLength)
|
||||||
|
|
||||||
|
for i := 0; i < numObjects; i++ {
|
||||||
|
r.Seek(objOffset, io.SeekStart)
|
||||||
|
|
||||||
|
obj := new(perfObjectType)
|
||||||
|
obj.BinaryReadFrom(r)
|
||||||
|
|
||||||
|
numCounterDefs := int(obj.NumCounters)
|
||||||
|
numInstances := int(obj.NumInstances)
|
||||||
|
|
||||||
|
// Perf objects can have no instances. The perflib differentiates
|
||||||
|
// between objects with instances and without, but we just create
|
||||||
|
// an empty instance in order to simplify the interface.
|
||||||
|
if numInstances <= 0 {
|
||||||
|
numInstances = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
instances := make([]*PerfInstance, numInstances)
|
||||||
|
counterDefs := make([]*PerfCounterDef, numCounterDefs)
|
||||||
|
|
||||||
|
objects[i] = &PerfObject{
|
||||||
|
Name: obj.LookupName(),
|
||||||
|
NameIndex: uint(obj.ObjectNameTitleIndex),
|
||||||
|
HelpText: obj.LookupHelp(),
|
||||||
|
HelpTextIndex: uint(obj.ObjectHelpTitleIndex),
|
||||||
|
Instances: instances,
|
||||||
|
CounterDefs: counterDefs,
|
||||||
|
Frequency: obj.PerfFreq,
|
||||||
|
rawData: obj,
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < numCounterDefs; i++ {
|
||||||
|
def := new(perfCounterDefinition)
|
||||||
|
def.BinaryReadFrom(r)
|
||||||
|
|
||||||
|
counterDefs[i] = &PerfCounterDef{
|
||||||
|
Name: def.LookupName(),
|
||||||
|
NameIndex: uint(def.CounterNameTitleIndex),
|
||||||
|
HelpText: def.LookupHelp(),
|
||||||
|
HelpTextIndex: uint(def.CounterHelpTitleIndex),
|
||||||
|
rawData: def,
|
||||||
|
|
||||||
|
CounterType: def.CounterType,
|
||||||
|
|
||||||
|
IsCounter: def.CounterType&0x400 == 0x400,
|
||||||
|
IsBaseValue: def.CounterType&0x00030000 == 0x00030000,
|
||||||
|
IsNanosecondCounter: def.CounterType&0x00100000 == 0x00100000,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if obj.NumInstances <= 0 {
|
||||||
|
blockOffset := objOffset + int64(obj.DefinitionLength)
|
||||||
|
r.Seek(blockOffset, io.SeekStart)
|
||||||
|
|
||||||
|
_, counters := parseCounterBlock(buffer, r, blockOffset, counterDefs)
|
||||||
|
|
||||||
|
instances[0] = &PerfInstance{
|
||||||
|
Name: "",
|
||||||
|
Counters: counters,
|
||||||
|
rawData: nil,
|
||||||
|
rawCounterBlock: nil,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
instOffset := objOffset + int64(obj.DefinitionLength)
|
||||||
|
|
||||||
|
for i := 0; i < numInstances; i++ {
|
||||||
|
r.Seek(instOffset, io.SeekStart)
|
||||||
|
|
||||||
|
inst := new(perfInstanceDefinition)
|
||||||
|
inst.BinaryReadFrom(r)
|
||||||
|
|
||||||
|
name, _ := readUTF16StringAtPos(r, instOffset+int64(inst.NameOffset), inst.NameLength)
|
||||||
|
pos := instOffset + int64(inst.ByteLength)
|
||||||
|
offset, counters := parseCounterBlock(buffer, r, pos, counterDefs)
|
||||||
|
|
||||||
|
instances[i] = &PerfInstance{
|
||||||
|
Name: name,
|
||||||
|
Counters: counters,
|
||||||
|
rawData: inst,
|
||||||
|
}
|
||||||
|
|
||||||
|
instOffset = pos + offset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next perfObjectType
|
||||||
|
objOffset += int64(obj.TotalByteLength)
|
||||||
|
}
|
||||||
|
|
||||||
|
return objects, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseCounterBlock(b []byte, r io.ReadSeeker, pos int64, defs []*PerfCounterDef) (int64, []*PerfCounter) {
|
||||||
|
r.Seek(pos, io.SeekStart)
|
||||||
|
block := new(perfCounterBlock)
|
||||||
|
block.BinaryReadFrom(r)
|
||||||
|
|
||||||
|
counters := make([]*PerfCounter, len(defs))
|
||||||
|
|
||||||
|
for i, def := range defs {
|
||||||
|
valueOffset := pos + int64(def.rawData.CounterOffset)
|
||||||
|
value := convertCounterValue(def.rawData, b, valueOffset)
|
||||||
|
|
||||||
|
counters[i] = &PerfCounter{
|
||||||
|
Value: value,
|
||||||
|
Def: def,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return int64(block.ByteLength), counters
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertCounterValue(counterDef *perfCounterDefinition, buffer []byte, valueOffset int64) (value int64) {
|
||||||
|
/*
|
||||||
|
We can safely ignore the type since we're not interested in anything except the raw value.
|
||||||
|
We also ignore all of the other attributes (timestamp, presentation, multi counter values...)
|
||||||
|
|
||||||
|
See also: winperf.h.
|
||||||
|
|
||||||
|
Here's the most common value for CounterType:
|
||||||
|
|
||||||
|
65536 32bit counter
|
||||||
|
65792 64bit counter
|
||||||
|
272696320 32bit rate
|
||||||
|
272696576 64bit rate
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
switch counterDef.CounterSize {
|
||||||
|
case 4:
|
||||||
|
value = int64(bo.Uint32(buffer[valueOffset:(valueOffset + 4)]))
|
||||||
|
case 8:
|
||||||
|
value = int64(bo.Uint64(buffer[valueOffset:(valueOffset + 8)]))
|
||||||
|
default:
|
||||||
|
value = int64(bo.Uint32(buffer[valueOffset:(valueOffset + 4)]))
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort slice of objects by index. This is useful for displaying
|
||||||
|
// a human-readable list or dump, but unnecessary otherwise.
|
||||||
|
func SortObjects(p []*PerfObject) {
|
||||||
|
sort.Slice(p, func(i, j int) bool {
|
||||||
|
return p[i].NameIndex < p[j].NameIndex
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
180
vendor/github.com/leoluk/perflib_exporter/perflib/raw_types.go
generated
vendored
Normal file
180
vendor/github.com/leoluk/perflib_exporter/perflib/raw_types.go
generated
vendored
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
package perflib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
type binaryReaderFrom interface {
|
||||||
|
BinaryReadFrom(r io.Reader) error
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
https://msdn.microsoft.com/de-de/library/windows/desktop/aa373157(v=vs.85).aspx
|
||||||
|
|
||||||
|
typedef struct _PERF_DATA_BLOCK {
|
||||||
|
WCHAR Signature[4];
|
||||||
|
DWORD LittleEndian;
|
||||||
|
DWORD Version;
|
||||||
|
DWORD Revision;
|
||||||
|
DWORD TotalByteLength;
|
||||||
|
DWORD HeaderLength;
|
||||||
|
DWORD NumObjectTypes;
|
||||||
|
DWORD DefaultObject;
|
||||||
|
SYSTEMTIME SystemTime;
|
||||||
|
LARGE_INTEGER PerfTime;
|
||||||
|
LARGE_INTEGER PerfFreq;
|
||||||
|
LARGE_INTEGER PerfTime100nSec;
|
||||||
|
DWORD SystemNameLength;
|
||||||
|
DWORD SystemNameOffset;
|
||||||
|
} PERF_DATA_BLOCK;
|
||||||
|
*/
|
||||||
|
|
||||||
|
type perfDataBlock struct {
|
||||||
|
Signature [4]uint16
|
||||||
|
LittleEndian uint32
|
||||||
|
Version uint32
|
||||||
|
Revision uint32
|
||||||
|
TotalByteLength uint32
|
||||||
|
HeaderLength uint32
|
||||||
|
NumObjectTypes uint32
|
||||||
|
DefaultObject int32
|
||||||
|
SystemTime syscall.Systemtime
|
||||||
|
_ uint32 // TODO
|
||||||
|
PerfTime int64
|
||||||
|
PerfFreq int64
|
||||||
|
PerfTime100nSec int64
|
||||||
|
SystemNameLength uint32
|
||||||
|
SystemNameOffset uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *perfDataBlock) BinaryReadFrom(r io.Reader) error {
|
||||||
|
return binary.Read(r, bo, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
https://msdn.microsoft.com/en-us/library/windows/desktop/aa373160(v=vs.85).aspx
|
||||||
|
|
||||||
|
typedef struct _PERF_OBJECT_TYPE {
|
||||||
|
DWORD TotalByteLength;
|
||||||
|
DWORD DefinitionLength;
|
||||||
|
DWORD HeaderLength;
|
||||||
|
DWORD ObjectNameTitleIndex;
|
||||||
|
LPWSTR ObjectNameTitle;
|
||||||
|
DWORD ObjectHelpTitleIndex;
|
||||||
|
LPWSTR ObjectHelpTitle;
|
||||||
|
DWORD DetailLevel;
|
||||||
|
DWORD NumCounters;
|
||||||
|
DWORD DefaultCounter;
|
||||||
|
DWORD NumInstances;
|
||||||
|
DWORD CodePage;
|
||||||
|
LARGE_INTEGER PerfTime;
|
||||||
|
LARGE_INTEGER PerfFreq;
|
||||||
|
} PERF_OBJECT_TYPE;
|
||||||
|
*/
|
||||||
|
|
||||||
|
type perfObjectType struct {
|
||||||
|
TotalByteLength uint32
|
||||||
|
DefinitionLength uint32
|
||||||
|
HeaderLength uint32
|
||||||
|
ObjectNameTitleIndex uint32
|
||||||
|
ObjectNameTitle uint32
|
||||||
|
ObjectHelpTitleIndex uint32
|
||||||
|
ObjectHelpTitle uint32
|
||||||
|
DetailLevel uint32
|
||||||
|
NumCounters uint32
|
||||||
|
DefaultCounter int32
|
||||||
|
NumInstances int32
|
||||||
|
CodePage uint32
|
||||||
|
PerfTime int64
|
||||||
|
PerfFreq int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *perfObjectType) BinaryReadFrom(r io.Reader) error {
|
||||||
|
return binary.Read(r, bo, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
https://msdn.microsoft.com/en-us/library/windows/desktop/aa373150(v=vs.85).aspx
|
||||||
|
|
||||||
|
typedef struct _PERF_COUNTER_DEFINITION {
|
||||||
|
DWORD ByteLength;
|
||||||
|
DWORD CounterNameTitleIndex;
|
||||||
|
LPWSTR CounterNameTitle;
|
||||||
|
DWORD CounterHelpTitleIndex;
|
||||||
|
LPWSTR CounterHelpTitle;
|
||||||
|
LONG DefaultScale;
|
||||||
|
DWORD DetailLevel;
|
||||||
|
DWORD CounterType;
|
||||||
|
DWORD CounterSize;
|
||||||
|
DWORD CounterOffset;
|
||||||
|
} PERF_COUNTER_DEFINITION;
|
||||||
|
*/
|
||||||
|
|
||||||
|
type perfCounterDefinition struct {
|
||||||
|
ByteLength uint32
|
||||||
|
CounterNameTitleIndex uint32
|
||||||
|
CounterNameTitle uint32
|
||||||
|
CounterHelpTitleIndex uint32
|
||||||
|
CounterHelpTitle uint32
|
||||||
|
DefaultScale int32
|
||||||
|
DetailLevel uint32
|
||||||
|
CounterType uint32
|
||||||
|
CounterSize uint32
|
||||||
|
CounterOffset uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *perfCounterDefinition) BinaryReadFrom(r io.Reader) error {
|
||||||
|
return binary.Read(r, bo, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *perfCounterDefinition) LookupName() string {
|
||||||
|
return counterNameTable.LookupString(p.CounterNameTitleIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *perfCounterDefinition) LookupHelp() string {
|
||||||
|
return helpNameTable.LookupString(p.CounterHelpTitleIndex)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
https://msdn.microsoft.com/en-us/library/windows/desktop/aa373147(v=vs.85).aspx
|
||||||
|
|
||||||
|
typedef struct _PERF_COUNTER_BLOCK {
|
||||||
|
DWORD ByteLength;
|
||||||
|
} PERF_COUNTER_BLOCK;
|
||||||
|
*/
|
||||||
|
|
||||||
|
type perfCounterBlock struct {
|
||||||
|
ByteLength uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *perfCounterBlock) BinaryReadFrom(r io.Reader) error {
|
||||||
|
return binary.Read(r, bo, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
https://msdn.microsoft.com/en-us/library/windows/desktop/aa373159(v=vs.85).aspx
|
||||||
|
|
||||||
|
typedef struct _PERF_INSTANCE_DEFINITION {
|
||||||
|
DWORD ByteLength;
|
||||||
|
DWORD ParentObjectTitleIndex;
|
||||||
|
DWORD ParentObjectInstance;
|
||||||
|
DWORD UniqueID;
|
||||||
|
DWORD NameOffset;
|
||||||
|
DWORD NameLength;
|
||||||
|
} PERF_INSTANCE_DEFINITION;
|
||||||
|
*/
|
||||||
|
|
||||||
|
type perfInstanceDefinition struct {
|
||||||
|
ByteLength uint32
|
||||||
|
ParentObjectTitleIndex uint32
|
||||||
|
ParentObjectInstance uint32
|
||||||
|
UniqueID uint32
|
||||||
|
NameOffset uint32
|
||||||
|
NameLength uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *perfInstanceDefinition) BinaryReadFrom(r io.Reader) error {
|
||||||
|
return binary.Read(r, bo, p)
|
||||||
|
}
|
||||||
49
vendor/github.com/leoluk/perflib_exporter/perflib/utf16.go
generated
vendored
Normal file
49
vendor/github.com/leoluk/perflib_exporter/perflib/utf16.go
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package perflib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
err = binary.Read(r, bo, value)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return syscall.UTF16ToString(value), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reads a null-terminated UTF16 string at the current offset
|
||||||
|
func readUTF16String(r io.Reader) (string, error) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
b := make([]byte, 2)
|
||||||
|
out := make([]uint16, 0, 100)
|
||||||
|
|
||||||
|
for i := 0; err == nil; i += 2 {
|
||||||
|
_, err = r.Read(b)
|
||||||
|
|
||||||
|
if b[0] == 0 && b[1] == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
out = append(out, bo.Uint16(b))
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return syscall.UTF16ToString(out), nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user