Compare commits

..

1 Commits

Author SHA1 Message Date
Ashok Siyani
d9404c6cc2 added terminal services collector for number of session count 2020-03-23 09:40:40 +00:00
4 changed files with 177 additions and 126 deletions

View File

@@ -3,8 +3,6 @@
package collector package collector
import ( import (
"fmt"
"regexp"
"strconv" "strconv"
"strings" "strings"
@@ -15,21 +13,18 @@ import (
) )
func init() { func init() {
registerCollector("process", newProcessCollector, "Process") registerCollector("process", NewProcessCollector)
} }
var ( var (
processWhitelist = kingpin.Flag( processWhereClause = kingpin.Flag(
"collector.process.whitelist", "collector.process.processes-where",
"Regexp of processes to include. Process name must both match whitelist and not match blacklist to be included.", "WQL 'where' clause to use in WMI metrics query. Limits the response to the processes you specify and reduces the size of the response.",
).Default(".*").String()
processBlacklist = kingpin.Flag(
"collector.process.blacklist",
"Regexp of processes to exclude. Process name must both match whitelist and not match blacklist to be included.",
).Default("").String() ).Default("").String()
) )
type processCollector struct { // A ProcessCollector is a Prometheus collector for WMI Win32_PerfRawData_PerfProc_Process metrics
type ProcessCollector struct {
StartTime *prometheus.Desc StartTime *prometheus.Desc
CPUTimeTotal *prometheus.Desc CPUTimeTotal *prometheus.Desc
HandleCount *prometheus.Desc HandleCount *prometheus.Desc
@@ -44,19 +39,18 @@ type processCollector struct {
VirtualBytes *prometheus.Desc VirtualBytes *prometheus.Desc
WorkingSet *prometheus.Desc WorkingSet *prometheus.Desc
processWhitelistPattern *regexp.Regexp queryWhereClause string
processBlacklistPattern *regexp.Regexp
} }
// NewProcessCollector ... // NewProcessCollector ...
func newProcessCollector() (Collector, error) { func NewProcessCollector() (Collector, error) {
const subsystem = "process" const subsystem = "process"
if *processWhitelist == ".*" && *processBlacklist == "" { if *processWhereClause == "" {
log.Warn("No filters specified for process collector. This will generate a very large number of metrics!") log.Warn("No where-clause specified for process collector. This will generate a very large number of metrics!")
} }
return &processCollector{ return &ProcessCollector{
StartTime: prometheus.NewDesc( StartTime: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "start_time"), prometheus.BuildFQName(Namespace, subsystem, "start_time"),
"Time of process start.", "Time of process start.",
@@ -135,53 +129,66 @@ func newProcessCollector() (Collector, error) {
[]string{"process", "process_id", "creating_process_id"}, []string{"process", "process_id", "creating_process_id"},
nil, nil,
), ),
processWhitelistPattern: regexp.MustCompile(fmt.Sprintf("^(?:%s)$", *processWhitelist)), queryWhereClause: *processWhereClause,
processBlacklistPattern: regexp.MustCompile(fmt.Sprintf("^(?:%s)$", *processBlacklist)),
}, nil }, nil
} }
type perflibProcess struct { // Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *ProcessCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error {
if desc, err := c.collect(ch); err != nil {
log.Error("failed collecting process metrics:", desc, err)
return err
}
return nil
}
// Win32_PerfRawData_PerfProc_Process docs:
// - https://msdn.microsoft.com/en-us/library/aa394323(v=vs.85).aspx
type Win32_PerfRawData_PerfProc_Process struct {
Name string Name string
PercentProcessorTime float64 `perflib:"% Processor Time"` CreatingProcessID uint32
PercentPrivilegedTime float64 `perflib:"% Privileged Time"` ElapsedTime uint64
PercentUserTime float64 `perflib:"% User Time"` Frequency_Object uint64
CreatingProcessID float64 `perflib:"Creating Process ID"` HandleCount uint32
ElapsedTime float64 `perflib:"Elapsed Time"` IDProcess uint32
HandleCount float64 `perflib:"Handle Count"` IODataBytesPersec uint64
IDProcess float64 `perflib:"ID Process"` IODataOperationsPersec uint64
IODataBytesPerSec float64 `perflib:"IO Data Bytes/sec"` IOOtherBytesPersec uint64
IODataOperationsPerSec float64 `perflib:"IO Data Operations/sec"` IOOtherOperationsPersec uint64
IOOtherBytesPerSec float64 `perflib:"IO Other Bytes/sec"` IOReadBytesPersec uint64
IOOtherOperationsPerSec float64 `perflib:"IO Other Operations/sec"` IOReadOperationsPersec uint64
IOReadBytesPerSec float64 `perflib:"IO Read Bytes/sec"` IOWriteBytesPersec uint64
IOReadOperationsPerSec float64 `perflib:"IO Read Operations/sec"` IOWriteOperationsPersec uint64
IOWriteBytesPerSec float64 `perflib:"IO Write Bytes/sec"` PageFaultsPersec uint32
IOWriteOperationsPerSec float64 `perflib:"IO Write Operations/sec"` PageFileBytes uint64
PageFaultsPerSec float64 `perflib:"Page Faults/sec"` PageFileBytesPeak uint64
PageFileBytesPeak float64 `perflib:"Page File Bytes Peak"` PercentPrivilegedTime uint64
PageFileBytes float64 `perflib:"Page File Bytes"` PercentProcessorTime uint64
PoolNonpagedBytes float64 `perflib:"Pool Nonpaged Bytes"` PercentUserTime uint64
PoolPagedBytes float64 `perflib:"Pool Paged Bytes"` PoolNonpagedBytes uint32
PriorityBase float64 `perflib:"Priority Base"` PoolPagedBytes uint32
PrivateBytes float64 `perflib:"Private Bytes"` PriorityBase uint32
ThreadCount float64 `perflib:"Thread Count"` PrivateBytes uint64
VirtualBytesPeak float64 `perflib:"Virtual Bytes Peak"` ThreadCount uint32
VirtualBytes float64 `perflib:"Virtual Bytes"` Timestamp_Object uint64
WorkingSetPrivate float64 `perflib:"Working Set - Private"` VirtualBytes uint64
WorkingSetPeak float64 `perflib:"Working Set Peak"` VirtualBytesPeak uint64
WorkingSet float64 `perflib:"Working Set"` WorkingSet uint64
WorkingSetPeak uint64
WorkingSetPrivate uint64
} }
type WorkerProcess struct { type WorkerProcess struct {
AppPoolName string AppPoolName string
ProcessId uint64 ProcessId uint32
} }
func (c *processCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error { func (c *ProcessCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, error) {
data := make([]perflibProcess, 0) var dst []Win32_PerfRawData_PerfProc_Process
err := unmarshalObject(ctx.perfObjects["Process"], &data) q := queryAllWhere(&dst, c.queryWhereClause)
if err != nil { if err := wmi.Query(q, &dst); err != nil {
return err return nil, err
} }
var dst_wp []WorkerProcess var dst_wp []WorkerProcess
@@ -190,10 +197,9 @@ func (c *processCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metr
log.Debugf("Could not query WebAdministration namespace for IIS worker processes: %v. Skipping", err) log.Debugf("Could not query WebAdministration namespace for IIS worker processes: %v. Skipping", err)
} }
for _, process := range data { for _, process := range dst {
if process.Name == "_Total" ||
c.processBlacklistPattern.MatchString(process.Name) || if process.Name == "_Total" {
!c.processWhitelistPattern.MatchString(process.Name) {
continue continue
} }
// Duplicate processes are suffixed # and an index number. Remove those. // Duplicate processes are suffixed # and an index number. Remove those.
@@ -202,7 +208,7 @@ func (c *processCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metr
cpid := strconv.FormatUint(uint64(process.CreatingProcessID), 10) cpid := strconv.FormatUint(uint64(process.CreatingProcessID), 10)
for _, wp := range dst_wp { for _, wp := range dst_wp {
if wp.ProcessId == uint64(process.IDProcess) { if wp.ProcessId == process.IDProcess {
processName = strings.Join([]string{processName, wp.AppPoolName}, "_") processName = strings.Join([]string{processName, wp.AppPoolName}, "_")
break break
} }
@@ -211,7 +217,8 @@ func (c *processCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metr
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.StartTime, c.StartTime,
prometheus.GaugeValue, prometheus.GaugeValue,
process.ElapsedTime, // convert from Windows timestamp (1 jan 1601) to unix timestamp (1 jan 1970)
float64(process.ElapsedTime-116444736000000000)/float64(process.Frequency_Object),
processName, processName,
pid, pid,
cpid, cpid,
@@ -220,7 +227,7 @@ func (c *processCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metr
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.HandleCount, c.HandleCount,
prometheus.GaugeValue, prometheus.GaugeValue,
process.HandleCount, float64(process.HandleCount),
processName, processName,
pid, pid,
cpid, cpid,
@@ -229,7 +236,7 @@ func (c *processCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metr
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.CPUTimeTotal, c.CPUTimeTotal,
prometheus.CounterValue, prometheus.CounterValue,
process.PercentPrivilegedTime, float64(process.PercentPrivilegedTime)*ticksToSecondsScaleFactor,
processName, processName,
pid, pid,
cpid, cpid,
@@ -239,7 +246,7 @@ func (c *processCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metr
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.CPUTimeTotal, c.CPUTimeTotal,
prometheus.CounterValue, prometheus.CounterValue,
process.PercentUserTime, float64(process.PercentUserTime)*ticksToSecondsScaleFactor,
processName, processName,
pid, pid,
cpid, cpid,
@@ -249,7 +256,7 @@ func (c *processCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metr
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.IOBytesTotal, c.IOBytesTotal,
prometheus.CounterValue, prometheus.CounterValue,
process.IOOtherBytesPerSec, float64(process.IOOtherBytesPersec),
processName, processName,
pid, pid,
cpid, cpid,
@@ -259,7 +266,7 @@ func (c *processCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metr
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.IOOperationsTotal, c.IOOperationsTotal,
prometheus.CounterValue, prometheus.CounterValue,
process.IOOtherOperationsPerSec, float64(process.IOOtherOperationsPersec),
processName, processName,
pid, pid,
cpid, cpid,
@@ -269,7 +276,7 @@ func (c *processCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metr
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.IOBytesTotal, c.IOBytesTotal,
prometheus.CounterValue, prometheus.CounterValue,
process.IOReadBytesPerSec, float64(process.IOReadBytesPersec),
processName, processName,
pid, pid,
cpid, cpid,
@@ -279,7 +286,7 @@ func (c *processCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metr
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.IOOperationsTotal, c.IOOperationsTotal,
prometheus.CounterValue, prometheus.CounterValue,
process.IOReadOperationsPerSec, float64(process.IOReadOperationsPersec),
processName, processName,
pid, pid,
cpid, cpid,
@@ -289,7 +296,7 @@ func (c *processCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metr
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.IOBytesTotal, c.IOBytesTotal,
prometheus.CounterValue, prometheus.CounterValue,
process.IOWriteBytesPerSec, float64(process.IOWriteBytesPersec),
processName, processName,
pid, pid,
cpid, cpid,
@@ -299,7 +306,7 @@ func (c *processCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metr
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.IOOperationsTotal, c.IOOperationsTotal,
prometheus.CounterValue, prometheus.CounterValue,
process.IOWriteOperationsPerSec, float64(process.IOWriteOperationsPersec),
processName, processName,
pid, pid,
cpid, cpid,
@@ -309,7 +316,7 @@ func (c *processCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metr
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.PageFaultsTotal, c.PageFaultsTotal,
prometheus.CounterValue, prometheus.CounterValue,
process.PageFaultsPerSec, float64(process.PageFaultsPersec),
processName, processName,
pid, pid,
cpid, cpid,
@@ -318,7 +325,7 @@ func (c *processCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metr
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.PageFileBytes, c.PageFileBytes,
prometheus.GaugeValue, prometheus.GaugeValue,
process.PageFileBytes, float64(process.PageFileBytes),
processName, processName,
pid, pid,
cpid, cpid,
@@ -327,7 +334,7 @@ func (c *processCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metr
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.PoolBytes, c.PoolBytes,
prometheus.GaugeValue, prometheus.GaugeValue,
process.PoolNonpagedBytes, float64(process.PoolNonpagedBytes),
processName, processName,
pid, pid,
cpid, cpid,
@@ -337,7 +344,7 @@ func (c *processCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metr
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.PoolBytes, c.PoolBytes,
prometheus.GaugeValue, prometheus.GaugeValue,
process.PoolPagedBytes, float64(process.PoolPagedBytes),
processName, processName,
pid, pid,
cpid, cpid,
@@ -347,7 +354,7 @@ func (c *processCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metr
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.PriorityBase, c.PriorityBase,
prometheus.GaugeValue, prometheus.GaugeValue,
process.PriorityBase, float64(process.PriorityBase),
processName, processName,
pid, pid,
cpid, cpid,
@@ -356,7 +363,7 @@ func (c *processCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metr
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.PrivateBytes, c.PrivateBytes,
prometheus.GaugeValue, prometheus.GaugeValue,
process.PrivateBytes, float64(process.PrivateBytes),
processName, processName,
pid, pid,
cpid, cpid,
@@ -365,7 +372,7 @@ func (c *processCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metr
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.ThreadCount, c.ThreadCount,
prometheus.GaugeValue, prometheus.GaugeValue,
process.ThreadCount, float64(process.ThreadCount),
processName, processName,
pid, pid,
cpid, cpid,
@@ -374,7 +381,7 @@ func (c *processCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metr
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.VirtualBytes, c.VirtualBytes,
prometheus.GaugeValue, prometheus.GaugeValue,
process.VirtualBytes, float64(process.VirtualBytes),
processName, processName,
pid, pid,
cpid, cpid,
@@ -383,12 +390,12 @@ func (c *processCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metr
ch <- prometheus.MustNewConstMetric( ch <- prometheus.MustNewConstMetric(
c.WorkingSet, c.WorkingSet,
prometheus.GaugeValue, prometheus.GaugeValue,
process.WorkingSet, float64(process.WorkingSet),
processName, processName,
pid, pid,
cpid, cpid,
) )
} }
return nil return nil, nil
} }

View File

@@ -0,0 +1,84 @@
// +build windows
package collector
import (
"errors"
"github.com/StackExchange/wmi"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/log"
)
func init() {
registerCollector("terminal_services", NewTerminalServicesCollector)
}
// A TerminalServicesCollector is a Prometheus collector for WMI
// Win32_PerfRawData_LocalSessionManager_TerminalServices & Win32_PerfRawData_TermService_TerminalServicesSession metrics
type TerminalServicesCollector struct {
Local_session_count *prometheus.Desc
}
// NewTerminalServicesCollector ...
func NewTerminalServicesCollector() (Collector, error) {
const subsystem = "terminal_services"
return &TerminalServicesCollector{
Local_session_count: prometheus.NewDesc(
prometheus.BuildFQName(Namespace, subsystem, "local_session_count"),
"Number of Terminal Services sessions",
[]string{"session"},
nil,
),
}, nil
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *TerminalServicesCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error {
if desc, err := c.collectTSSessionCount(ch); err != nil {
log.Error("failed collecting terminal services session count metrics:", desc, err)
return err
}
return nil
}
type Win32_PerfRawData_LocalSessionManager_TerminalServices struct {
ActiveSessions uint32
InactiveSessions uint32
TotalSessions uint32
}
func (c *TerminalServicesCollector) collectTSSessionCount(ch chan<- prometheus.Metric) (*prometheus.Desc, error) {
var dst []Win32_PerfRawData_LocalSessionManager_TerminalServices
q := queryAll(&dst)
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")
}
ch <- prometheus.MustNewConstMetric(
c.Local_session_count,
prometheus.GaugeValue,
float64(dst[0].ActiveSessions),
"active",
)
ch <- prometheus.MustNewConstMetric(
c.Local_session_count,
prometheus.GaugeValue,
float64(dst[0].InactiveSessions),
"inactive",
)
ch <- prometheus.MustNewConstMetric(
c.Local_session_count,
prometheus.GaugeValue,
float64(dst[0].TotalSessions),
"total",
)
return nil, nil
}

View File

@@ -5,29 +5,18 @@ The process collector exposes metrics about processes
||| |||
-|- -|-
Metric name prefix | `process` Metric name prefix | `process`
Data source | Perflib Classes | [`Win32_PerfRawData_PerfProc_Process`](https://msdn.microsoft.com/en-us/library/aa394323(v=vs.85).aspx)
Counters | `Process`
Enabled by default? | No Enabled by default? | No
## Flags ## Flags
### `--collector.process.whitelist` ### `--collector.process.processes-where`
Regexp of processes to include. Process name must both match whitelist and not A WMI filter on which processes to include. Recommended to keep down number of returned metrics.
match blacklist to be included. Recommended to keep down number of returned
metrics.
### `--collector.process.blacklist` `%` is a wildcard, and can be used to match on substrings.
Regexp of processes to exclude. Process name must both match whitelist and not Example: `--collector.process.processes-where="Name LIKE 'firefox%'`
match blacklist to be included. Recommended to keep down number of returned
metrics.
### Example
To match all firefox processes: `--collector.process.whitelist="firefox.+"`.
Note that multiple processes with the same name will be disambiguated by
Windows by adding a number suffix, such as `firefox#2`. Your regexp must take
these suffixes into consideration.
## Metrics ## Metrics

View File

@@ -265,10 +265,6 @@ func main() {
"telemetry.path", "telemetry.path",
"URL path for surfacing collected metrics.", "URL path for surfacing collected metrics.",
).Default("/metrics").String() ).Default("/metrics").String()
maxRequests = kingpin.Flag(
"telemetry.max-requests",
"Maximum number of concurrent requests. 0 to disable.",
).Default("5").Int()
enabledCollectors = kingpin.Flag( enabledCollectors = kingpin.Flag(
"collectors.enabled", "collectors.enabled",
"Comma-separated list of collectors to use. Use '[defaults]' as a placeholder for all the collectors enabled by default."). "Comma-separated list of collectors to use. Use '[defaults]' as a placeholder for all the collectors enabled by default.").
@@ -336,16 +332,10 @@ func main() {
}, },
} }
http.HandleFunc(*metricsPath, withConcurrencyLimit(*maxRequests, h.ServeHTTP)) 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) {
_, _ = w.Write([]byte(`<html> http.Redirect(w, r, *metricsPath, http.StatusMovedPermanently)
<head><title>WMI Exporter</title></head>
<body>
<h1>WMI Exporter</h1>
<p><a href="` + *metricsPath + `">Metrics</a></p>
</body>
</html>`))
}) })
log.Infoln("Starting WMI exporter", version.Info()) log.Infoln("Starting WMI exporter", version.Info())
@@ -380,25 +370,6 @@ func keys(m map[string]collector.Collector) []string {
return ret return ret
} }
func withConcurrencyLimit(n int, next http.HandlerFunc) http.HandlerFunc {
if n <= 0 {
return next
}
sem := make(chan struct{}, n)
return func(w http.ResponseWriter, r *http.Request) {
select {
case sem <- struct{}{}:
defer func() { <-sem }()
default:
w.WriteHeader(http.StatusServiceUnavailable)
_, _ = w.Write([]byte("Too many concurrent requests"))
return
}
next(w, r)
}
}
type wmiExporterService struct { type wmiExporterService struct {
stopCh chan<- bool stopCh chan<- bool
} }