mirror of
https://github.com/prometheus-community/windows_exporter
synced 2025-04-10 19:41:18 +00:00
Collect services using windows api
Signed-off-by: Carlos Castro <ccastro@newrelic.com>
This commit is contained in:
parent
1b96bb6d08
commit
a9ac2d4672
@ -3,12 +3,13 @@
|
|||||||
package collector
|
package collector
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/StackExchange/wmi"
|
"github.com/newrelic-forks/windows_exporter/log"
|
||||||
"github.com/prometheus-community/windows_exporter/log"
|
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
"golang.org/x/sys/windows/svc/mgr"
|
||||||
"gopkg.in/alecthomas/kingpin.v2"
|
"gopkg.in/alecthomas/kingpin.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -21,6 +22,10 @@ var (
|
|||||||
"collector.service.services-where",
|
"collector.service.services-where",
|
||||||
"WQL 'where' clause to use in WMI metrics query. Limits the response to the services you specify and reduces the size of the response.",
|
"WQL 'where' clause to use in WMI metrics query. Limits the response to the services you specify and reduces the size of the response.",
|
||||||
).Default("").String()
|
).Default("").String()
|
||||||
|
useAPI = kingpin.Flag(
|
||||||
|
"collector.service.use-api",
|
||||||
|
"Use API calls to collect service data instead of WMI. Flag 'collector.service.services-where' won't be effective.",
|
||||||
|
).Default("false").Bool()
|
||||||
)
|
)
|
||||||
|
|
||||||
// A serviceCollector is a Prometheus collector for WMI Win32_Service metrics
|
// A serviceCollector is a Prometheus collector for WMI Win32_Service metrics
|
||||||
@ -40,6 +45,9 @@ func NewserviceCollector() (Collector, error) {
|
|||||||
if *serviceWhereClause == "" {
|
if *serviceWhereClause == "" {
|
||||||
log.Warn("No where-clause specified for service collector. This will generate a very large number of metrics!")
|
log.Warn("No where-clause specified for service collector. This will generate a very large number of metrics!")
|
||||||
}
|
}
|
||||||
|
if *useAPI {
|
||||||
|
log.Warn("API collection is enabled.")
|
||||||
|
}
|
||||||
|
|
||||||
return &serviceCollector{
|
return &serviceCollector{
|
||||||
Information: prometheus.NewDesc(
|
Information: prometheus.NewDesc(
|
||||||
@ -73,10 +81,17 @@ 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(ctx *ScrapeContext, 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 *useAPI {
|
||||||
log.Error("failed collecting service metrics:", desc, err)
|
if err := c.collectAPI(ch); err != nil {
|
||||||
|
log.Error("failed collecting API service metrics:", err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if err := c.collectWMI(ch); err != nil {
|
||||||
|
log.Error("failed collecting WMI service metrics:", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,6 +118,15 @@ var (
|
|||||||
"paused",
|
"paused",
|
||||||
"unknown",
|
"unknown",
|
||||||
}
|
}
|
||||||
|
apiStateValues = map[uint]string{
|
||||||
|
windows.SERVICE_CONTINUE_PENDING: "continue pending",
|
||||||
|
windows.SERVICE_PAUSE_PENDING: "pause pending",
|
||||||
|
windows.SERVICE_PAUSED: "paused",
|
||||||
|
windows.SERVICE_RUNNING: "running",
|
||||||
|
windows.SERVICE_START_PENDING: "start pending",
|
||||||
|
windows.SERVICE_STOP_PENDING: "stop pending",
|
||||||
|
windows.SERVICE_STOPPED: "stopped",
|
||||||
|
}
|
||||||
allStartModes = []string{
|
allStartModes = []string{
|
||||||
"boot",
|
"boot",
|
||||||
"system",
|
"system",
|
||||||
@ -110,6 +134,13 @@ var (
|
|||||||
"manual",
|
"manual",
|
||||||
"disabled",
|
"disabled",
|
||||||
}
|
}
|
||||||
|
apiStartModeValues = map[uint32]string{
|
||||||
|
windows.SERVICE_AUTO_START: "auto",
|
||||||
|
windows.SERVICE_BOOT_START: "boot",
|
||||||
|
windows.SERVICE_DEMAND_START: "manual",
|
||||||
|
windows.SERVICE_DISABLED: "disabled",
|
||||||
|
windows.SERVICE_SYSTEM_START: "system",
|
||||||
|
}
|
||||||
allStatuses = []string{
|
allStatuses = []string{
|
||||||
"ok",
|
"ok",
|
||||||
"error",
|
"error",
|
||||||
@ -126,14 +157,14 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *serviceCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, error) {
|
func (c *serviceCollector) collectWMI(ch chan<- prometheus.Metric) error {
|
||||||
var dst []Win32_Service
|
var dst []Win32_Service
|
||||||
q := queryAllWhere(&dst, c.queryWhereClause)
|
q := queryAllWhere(&dst, c.queryWhereClause)
|
||||||
if err := wmi.Query(q, &dst); err != nil {
|
if err := wmi.Query(q, &dst); err != nil {
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
for _, service := range dst {
|
for _, service := range dst {
|
||||||
pid := strconv.FormatUint(uint64(service.ProcessId), 10)
|
pid := fmt.Sprintf("%d", uint64(service.ProcessId))
|
||||||
|
|
||||||
runAs := ""
|
runAs := ""
|
||||||
if service.StartName != nil {
|
if service.StartName != nil {
|
||||||
@ -191,5 +222,83 @@ func (c *serviceCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Des
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *serviceCollector) collectAPI(ch chan<- prometheus.Metric) error {
|
||||||
|
svcmgrConnection, err := mgr.Connect()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer svcmgrConnection.Disconnect()
|
||||||
|
|
||||||
|
// List All Services from the Services Manager
|
||||||
|
serviceList, err := svcmgrConnection.ListServices()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate through the Services List
|
||||||
|
for _, service := range serviceList {
|
||||||
|
// Retrieve handle for each service
|
||||||
|
serviceHandle, err := svcmgrConnection.OpenService(service)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get Service Configuration
|
||||||
|
serviceConfig, err := serviceHandle.Config()
|
||||||
|
if err != nil {
|
||||||
|
_ = serviceHandle.Close()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get Service Current Status
|
||||||
|
serviceStatus, err := serviceHandle.Query()
|
||||||
|
if err != nil {
|
||||||
|
_ = serviceHandle.Close()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
pid := fmt.Sprintf("%d", uint64(serviceStatus.ProcessId))
|
||||||
|
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
c.Information,
|
||||||
|
prometheus.GaugeValue,
|
||||||
|
1.0,
|
||||||
|
strings.ToLower(service),
|
||||||
|
serviceConfig.DisplayName,
|
||||||
|
pid,
|
||||||
|
serviceConfig.ServiceStartName,
|
||||||
|
)
|
||||||
|
|
||||||
|
for _, state := range apiStateValues {
|
||||||
|
isCurrentState := 0.0
|
||||||
|
if state == apiStateValues[uint(serviceStatus.State)] {
|
||||||
|
isCurrentState = 1.0
|
||||||
|
}
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
c.State,
|
||||||
|
prometheus.GaugeValue,
|
||||||
|
isCurrentState,
|
||||||
|
strings.ToLower(service),
|
||||||
|
state,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, startMode := range apiStartModeValues {
|
||||||
|
isCurrentStartMode := 0.0
|
||||||
|
if startMode == apiStartModeValues[serviceConfig.StartType] {
|
||||||
|
isCurrentStartMode = 1.0
|
||||||
|
}
|
||||||
|
ch <- prometheus.MustNewConstMetric(
|
||||||
|
c.StartMode,
|
||||||
|
prometheus.GaugeValue,
|
||||||
|
isCurrentStartMode,
|
||||||
|
strings.ToLower(service),
|
||||||
|
startMode,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,10 @@ Example: `--collector.service.services-where="Name='windows_exporter'"`
|
|||||||
|
|
||||||
Example config win_exporter.yml for multiple services: `services-where: Name='SQLServer' OR Name='Couchbase' OR Name='Spooler' OR Name='ActiveMQ'`
|
Example config win_exporter.yml for multiple services: `services-where: Name='SQLServer' OR Name='Couchbase' OR Name='Spooler' OR Name='ActiveMQ'`
|
||||||
|
|
||||||
|
### `--collector.service.use-api`
|
||||||
|
|
||||||
|
Uses API calls instead of WMI for performance optimization. **Note** the previous flag (`--collector.service.services-where`) won't have any effect on this mode.
|
||||||
|
|
||||||
## Metrics
|
## Metrics
|
||||||
|
|
||||||
Name | Description | Type | Labels
|
Name | Description | Type | Labels
|
||||||
@ -50,7 +54,7 @@ A service can have the following start modes:
|
|||||||
- `manual`
|
- `manual`
|
||||||
- `disabled`
|
- `disabled`
|
||||||
|
|
||||||
### Status
|
### Status (not available in API mode)
|
||||||
|
|
||||||
A service can have any of the following statuses:
|
A service can have any of the following statuses:
|
||||||
- `ok`
|
- `ok`
|
||||||
|
Loading…
Reference in New Issue
Block a user