From 3c98edaa2c4949a30b15ccd4d2304fcd0da828c3 Mon Sep 17 00:00:00 2001 From: rmyhren Date: Sun, 24 May 2020 18:05:27 +0200 Subject: [PATCH] Adds MS Exchange collector (#508) Adds MS Exchange collector Co-authored-by: Robin Eikaas Co-authored-by: Robert Myhren --- README.md | 1 + collector/exchange.go | 609 +++++++++++++++++++++++++++++++++++++ docs/collector.exchange.md | 67 ++++ go.sum | 27 +- 4 files changed, 681 insertions(+), 23 deletions(-) create mode 100644 collector/exchange.go create mode 100644 docs/collector.exchange.md diff --git a/README.md b/README.md index af49e8c8..f8f3afc2 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ Name | Description | Enabled by default [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 | +[exchange](docs/collector.exchange.md) | Exchange metrics | [hyperv](docs/collector.hyperv.md) | Hyper-V hosts | [iis](docs/collector.iis.md) | IIS sites and applications | [logical_disk](docs/collector.logical_disk.md) | Logical disks, disk I/O | ✓ diff --git a/collector/exchange.go b/collector/exchange.go new file mode 100644 index 00000000..90327d0c --- /dev/null +++ b/collector/exchange.go @@ -0,0 +1,609 @@ +// +build windows + +package collector + +import ( + "fmt" + "os" + "strings" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/common/log" + "gopkg.in/alecthomas/kingpin.v2" +) + +func init() { + registerCollector("exchange", newExchangeCollector, + "MSExchange ADAccess Processes", + "MSExchangeTransport Queues", + "MSExchange HttpProxy", + "MSExchange ActiveSync", + "MSExchange Availability Service", + "MSExchange OWA", + "MSExchangeAutodiscover", + "MSExchange WorkloadManagement Workloads", + "MSExchange RpcClientAccess", + ) +} + +type exchangeCollector struct { + LDAPReadTime *prometheus.Desc + LDAPSearchTime *prometheus.Desc + LDAPWriteTime *prometheus.Desc + LDAPTimeoutErrorsPerSec *prometheus.Desc + LongRunningLDAPOperationsPerMin *prometheus.Desc + ExternalActiveRemoteDeliveryQueueLength *prometheus.Desc + InternalActiveRemoteDeliveryQueueLength *prometheus.Desc + ActiveMailboxDeliveryQueueLength *prometheus.Desc + RetryMailboxDeliveryQueueLength *prometheus.Desc + UnreachableQueueLength *prometheus.Desc + ExternalLargestDeliveryQueueLength *prometheus.Desc + InternalLargestDeliveryQueueLength *prometheus.Desc + PoisonQueueLength *prometheus.Desc + MailboxServerLocatorAverageLatency *prometheus.Desc + AverageAuthenticationLatency *prometheus.Desc + AverageCASProcessingLatency *prometheus.Desc + MailboxServerProxyFailureRate *prometheus.Desc + OutstandingProxyRequests *prometheus.Desc + ProxyRequestsPerSec *prometheus.Desc + ActiveSyncRequestsPerSec *prometheus.Desc + PingCommandsPending *prometheus.Desc + SyncCommandsPerSec *prometheus.Desc + AvailabilityRequestsSec *prometheus.Desc + CurrentUniqueUsers *prometheus.Desc + OWARequestsPerSec *prometheus.Desc + AutodiscoverRequestsPerSec *prometheus.Desc + ActiveTasks *prometheus.Desc + CompletedTasks *prometheus.Desc + QueuedTasks *prometheus.Desc + YieldedTasks *prometheus.Desc + IsActive *prometheus.Desc + RPCAveragedLatency *prometheus.Desc + RPCRequests *prometheus.Desc + ActiveUserCount *prometheus.Desc + ConnectionCount *prometheus.Desc + RPCOperationsPerSec *prometheus.Desc + UserCount *prometheus.Desc + + ActiveCollFuncs []func(ctx *ScrapeContext, ch chan<- prometheus.Metric) error +} + +var ( + // All available collector functions + exchangeAllCollectorNames = []string{ + "ADAccessProcesses", + "TransportQueues", + "HttpProxy", + "ActiveSync", + "AvailabilityService", + "OutlookWebAccess", + "Autodiscover", + "WorkloadManagement", + "RpcClientAccess", + } + + argExchangeListAllCollectors = kingpin.Flag( + "collectors.exchange.list", + "List the collectors along with their perflib object name/ids", + ).Bool() +) + +// newExchangeCollector returns a new Collector +func newExchangeCollector() (Collector, error) { + + // desc creates a new prometheus description + desc := func(metricName string, description string, labels ...string) *prometheus.Desc { + return prometheus.NewDesc( + prometheus.BuildFQName(Namespace, "exchange", metricName), + description, + labels, + nil, + ) + } + + c := exchangeCollector{ + RPCAveragedLatency: desc("rpc_avg_latency_sec", "The latency (sec), averaged for the past 1024 packets"), + RPCRequests: desc("rpc_requests", "Number of client requests currently being processed by the RPC Client Access service"), + ActiveUserCount: desc("rpc_active_user_count", "Number of unique users that have shown some kind of activity in the last 2 minutes"), + ConnectionCount: desc("rpc_connection_count", "Total number of client connections maintained"), + RPCOperationsPerSec: desc("rpc_operations_total", "The rate at which RPC operations occur"), + UserCount: desc("rpc_user_count", "Number of users"), + LDAPReadTime: desc("ldap_read_time_sec", "Time (sec) to send an LDAP read request and receive a response", "name"), + LDAPSearchTime: desc("ldap_search_time_sec", "Time (sec) to send an LDAP search request and receive a response", "name"), + LDAPWriteTime: desc("ldap_write_time_sec", "Time (sec) to send an LDAP Add/Modify/Delete request and receive a response", "name"), + LDAPTimeoutErrorsPerSec: desc("ldap_timeout_errors_total", "Total number of LDAP timeout errors", "name"), + LongRunningLDAPOperationsPerMin: desc("ldap_long_running_ops_per_sec", "Long Running LDAP operations per second", "name"), + ExternalActiveRemoteDeliveryQueueLength: desc("transport_queues_external_active_remote_delivery", "External Active Remote Delivery Queue length", "name"), + InternalActiveRemoteDeliveryQueueLength: desc("transport_queues_internal_active_remote_delivery", "Internal Active Remote Delivery Queue length", "name"), + ActiveMailboxDeliveryQueueLength: desc("transport_queues_active_mailbox_delivery", "Active Mailbox Delivery Queue length", "name"), + RetryMailboxDeliveryQueueLength: desc("transport_queues_retry_mailbox_delivery", "Retry Mailbox Delivery Queue length", "name"), + UnreachableQueueLength: desc("transport_queues_unreachable", "Unreachable Queue length", "name"), + ExternalLargestDeliveryQueueLength: desc("transport_queues_external_largest_delivery", "External Largest Delivery Queue length", "name"), + InternalLargestDeliveryQueueLength: desc("transport_queues_internal_largest_delivery", "Internal Largest Delivery Queue length", "name"), + PoisonQueueLength: desc("transport_queues_poison", "Poison Queue length", "name"), + MailboxServerLocatorAverageLatency: desc("http_proxy_mailbox_server_locator_avg_latency_sec", "Average latency (sec) of MailboxServerLocator web service calls", "name"), + AverageAuthenticationLatency: desc("http_proxy_avg_auth_latency", "Average time spent authenticating CAS requests over the last 200 samples", "name"), + OutstandingProxyRequests: desc("http_proxy_outstanding_proxy_requests", "Number of concurrent outstanding proxy requests", "name"), + ProxyRequestsPerSec: desc("http_proxy_requests_total", "Number of proxy requests processed each second", "name"), + AvailabilityRequestsSec: desc("avail_service_requests_per_sec", "Number of requests serviced per second"), + CurrentUniqueUsers: desc("owa_current_unique_users", "Number of unique users currently logged on to Outlook Web App"), + OWARequestsPerSec: desc("owa_requests_total", "Number of requests handled by Outlook Web App per second"), + AutodiscoverRequestsPerSec: desc("autodiscover_requests_total", "Number of autodiscover service requests processed each second"), + ActiveTasks: desc("workload_active_tasks", "Number of active tasks currently running in the background for workload management", "name"), + CompletedTasks: desc("workload_completed_tasks", "Number of workload management tasks that have been completed", "name"), + QueuedTasks: desc("workload_queued_tasks", "Number of workload management tasks that are currently queued up waiting to be processed", "name"), + YieldedTasks: desc("workload_yielded_tasks", "The total number of tasks that have been yielded by a workload", "name"), + IsActive: desc("workload_is_active", "Active indicates whether the workload is in an active (1) or paused (0) state", "name"), + ActiveSyncRequestsPerSec: desc("activesync_requests_total", "Num HTTP requests received from the client via ASP.NET per sec. Shows Current user load"), + AverageCASProcessingLatency: desc("http_proxy_avg_cas_proccessing_latency_sec", "Average latency (sec) of CAS processing time over the last 200 reqs", "name"), + MailboxServerProxyFailureRate: desc("http_proxy_mailbox_proxy_failure_rate", "% of failures between this CAS and MBX servers over the last 200 samples", "name"), + PingCommandsPending: desc("activesync_ping_cmds_pending", "Number of ping commands currently pending in the queue"), + SyncCommandsPerSec: desc("activesync_sync_cmds_total", "Number of sync commands processed per second. Clients use this command to synchronize items within a folder"), + } + + collectorDesc := map[string]string{ + "ADAccessProcesses": "[19108] MSExchange ADAccess Processes", + "TransportQueues": "[20524] MSExchangeTransport Queues", + "HttpProxy": "[36934] MSExchange HttpProxy", + "ActiveSync": "[25138] MSExchange ActiveSync", + "AvailabilityService": "[24914] MSExchange Availability Service", + "OutlookWebAccess": "[24618] MSExchange OWA", + "Autodiscover": "[29240] MSExchange Autodiscover", + "WorkloadManagement": "[19430] MSExchange WorkloadManagement Workloads", + "RpcClientAccess": "[29336] MSExchange RpcClientAccess", + } + + if *argExchangeListAllCollectors { + fmt.Printf("%-32s %-32s\n", "Collector Name", "[PerfID] Perflib Object") + for _, cname := range exchangeAllCollectorNames { + fmt.Printf("%-32s %-32s\n", cname, collectorDesc[cname]) + } + os.Exit(0) + } + + return &c, nil +} + +// Collect collects exchange metrics and sends them to prometheus +func (c *exchangeCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error { + for collectorName, collectorFunc := range map[string]func(ctx *ScrapeContext, ch chan<- prometheus.Metric) error{ + "ADAccessProcesses": c.collectADAccessProcesses, + "TransportQueues": c.collectTransportQueues, + "HttpProxy": c.collectHTTPProxy, + "ActiveSync": c.collectActiveSync, + "AvailabilityService": c.collectAvailabilityService, + "OutlookWebAccess": c.collectOWA, + "Autodiscover": c.collectAutoDiscover, + "WorkloadManagement": c.collectWorkloadManagementWorkloads, + "RpcClientAccess": c.collectRPC, + } { + if err := collectorFunc(ctx, ch); err != nil { + log.Errorf("Error in %s: %s", collectorName, err) + return err + } + } + return nil +} + +// Perflib: [19108] MSExchange ADAccess Processes +type perflibADAccessProcesses struct { + Name string + + LDAPReadTime float64 `perflib:"LDAP Read Time"` + LDAPSearchTime float64 `perflib:"LDAP Search Time"` + LDAPWriteTime float64 `perflib:"LDAP Write Time"` + LDAPTimeoutErrorsPerSec float64 `perflib:"LDAP Timeout Errors/sec"` + LongRunningLDAPOperationsPerMin float64 `perflib:"Long Running LDAP Operations/min"` +} + +func (c *exchangeCollector) collectADAccessProcesses(ctx *ScrapeContext, ch chan<- prometheus.Metric) error { + var data []perflibADAccessProcesses + if err := unmarshalObject(ctx.perfObjects["MSExchange ADAccess Processes"], &data); err != nil { + return err + } + + labelUseCount := make(map[string]int) + for _, proc := range data { + labelName := c.toLabelName(proc.Name) + if strings.HasSuffix(labelName, "_total") { + continue + } + + // since we're not including the PID suffix from the instance names in the label names, + // we get an occational duplicate. This seems to affect about 4 instances only on this object. + labelUseCount[labelName]++ + if labelUseCount[labelName] > 1 { + labelName = fmt.Sprintf("%s_%d", labelName, labelUseCount[labelName]) + } + ch <- prometheus.MustNewConstMetric( + c.LDAPReadTime, + prometheus.CounterValue, + c.msToSec(proc.LDAPReadTime), + labelName, + ) + ch <- prometheus.MustNewConstMetric( + c.LDAPSearchTime, + prometheus.CounterValue, + c.msToSec(proc.LDAPSearchTime), + labelName, + ) + ch <- prometheus.MustNewConstMetric( + c.LDAPWriteTime, + prometheus.CounterValue, + c.msToSec(proc.LDAPWriteTime), + labelName, + ) + ch <- prometheus.MustNewConstMetric( + c.LDAPTimeoutErrorsPerSec, + prometheus.CounterValue, + proc.LDAPTimeoutErrorsPerSec, + labelName, + ) + ch <- prometheus.MustNewConstMetric( + c.LongRunningLDAPOperationsPerMin, + prometheus.CounterValue, + proc.LongRunningLDAPOperationsPerMin*60, + labelName, + ) + } + return nil +} + +// Perflib: [24914] MSExchange Availability Service +type perflibAvailabilityService struct { + RequestsSec float64 `perflib:"Availability Requests (sec)"` +} + +func (c *exchangeCollector) collectAvailabilityService(ctx *ScrapeContext, ch chan<- prometheus.Metric) error { + var data []perflibAvailabilityService + if err := unmarshalObject(ctx.perfObjects["MSExchange Availability Service"], &data); err != nil { + return err + } + + for _, availservice := range data { + ch <- prometheus.MustNewConstMetric( + c.AvailabilityRequestsSec, + prometheus.CounterValue, + availservice.RequestsSec, + ) + } + return nil +} + +// Perflib: [36934] MSExchange HttpProxy +type perflibHTTPProxy struct { + Name string + + MailboxServerLocatorAverageLatency float64 `perflib:"MailboxServerLocator Average Latency (Moving Average)"` + AverageAuthenticationLatency float64 `perflib:"Average Authentication Latency"` + AverageCASProcessingLatency float64 `perflib:"Average ClientAccess Server Processing Latency"` + MailboxServerProxyFailureRate float64 `perflib:"Mailbox Server Proxy Failure Rate"` + OutstandingProxyRequests float64 `perflib:"Outstanding Proxy Requests"` + ProxyRequestsPerSec float64 `perflib:"Proxy Requests/Sec"` +} + +func (c *exchangeCollector) collectHTTPProxy(ctx *ScrapeContext, ch chan<- prometheus.Metric) error { + var data []perflibHTTPProxy + if err := unmarshalObject(ctx.perfObjects["MSExchange HttpProxy"], &data); err != nil { + return err + } + + for _, instance := range data { + labelName := c.toLabelName(instance.Name) + ch <- prometheus.MustNewConstMetric( + c.MailboxServerLocatorAverageLatency, + prometheus.GaugeValue, + c.msToSec(instance.MailboxServerLocatorAverageLatency), + labelName, + ) + ch <- prometheus.MustNewConstMetric( + c.AverageAuthenticationLatency, + prometheus.GaugeValue, + instance.AverageAuthenticationLatency, + labelName, + ) + ch <- prometheus.MustNewConstMetric( + c.AverageCASProcessingLatency, + prometheus.GaugeValue, + c.msToSec(instance.AverageCASProcessingLatency), + labelName, + ) + ch <- prometheus.MustNewConstMetric( + c.MailboxServerProxyFailureRate, + prometheus.GaugeValue, + instance.MailboxServerProxyFailureRate, + labelName, + ) + ch <- prometheus.MustNewConstMetric( + c.OutstandingProxyRequests, + prometheus.GaugeValue, + instance.OutstandingProxyRequests, + labelName, + ) + ch <- prometheus.MustNewConstMetric( + c.ProxyRequestsPerSec, + prometheus.CounterValue, + instance.ProxyRequestsPerSec, + labelName, + ) + } + return nil +} + +// Perflib: [24618] MSExchange OWA +type perflibOWA struct { + CurrentUniqueUsers float64 `perflib:"Current Unique Users"` + RequestsPerSec float64 `perflib:"Requests/sec"` +} + +func (c *exchangeCollector) collectOWA(ctx *ScrapeContext, ch chan<- prometheus.Metric) error { + var data []perflibOWA + if err := unmarshalObject(ctx.perfObjects["MSExchange OWA"], &data); err != nil { + return err + } + + for _, owa := range data { + ch <- prometheus.MustNewConstMetric( + c.CurrentUniqueUsers, + prometheus.GaugeValue, + owa.CurrentUniqueUsers, + ) + ch <- prometheus.MustNewConstMetric( + c.OWARequestsPerSec, + prometheus.CounterValue, + owa.RequestsPerSec, + ) + } + return nil +} + +// Perflib: [25138] MSExchange ActiveSync +type perflibActiveSync struct { + RequestsPerSec float64 `perflib:"Requests/sec"` + PingCommandsPending float64 `perflib:"Ping Commands Pending"` + SyncCommandsPerSec float64 `perflib:"Sync Commands/sec"` +} + +func (c *exchangeCollector) collectActiveSync(ctx *ScrapeContext, ch chan<- prometheus.Metric) error { + var data []perflibActiveSync + if err := unmarshalObject(ctx.perfObjects["MSExchange ActiveSync"], &data); err != nil { + return err + } + + for _, instance := range data { + ch <- prometheus.MustNewConstMetric( + c.ActiveSyncRequestsPerSec, + prometheus.CounterValue, + instance.RequestsPerSec, + ) + ch <- prometheus.MustNewConstMetric( + c.PingCommandsPending, + prometheus.GaugeValue, + instance.PingCommandsPending, + ) + ch <- prometheus.MustNewConstMetric( + c.SyncCommandsPerSec, + prometheus.CounterValue, + instance.SyncCommandsPerSec, + ) + } + return nil +} + +// Perflib: [29366] MSExchange RpcClientAccess +type perflibRPCClientAccess struct { + RPCAveragedLatency float64 `perflib:"RPC Averaged Latency"` + RPCRequests float64 `perflib:"RPC Requests"` + ActiveUserCount float64 `perflib:"Active User Count"` + ConnectionCount float64 `perflib:"Connection Count"` + RPCOperationsPerSec float64 `perflib:"RPC Operations/sec"` + UserCount float64 `perflib:"User Count"` +} + +func (c *exchangeCollector) collectRPC(ctx *ScrapeContext, ch chan<- prometheus.Metric) error { + var data []perflibRPCClientAccess + if err := unmarshalObject(ctx.perfObjects["MSExchange RpcClientAccess"], &data); err != nil { + return err + } + + for _, rpc := range data { + ch <- prometheus.MustNewConstMetric( + c.RPCAveragedLatency, + prometheus.GaugeValue, + c.msToSec(rpc.RPCAveragedLatency), + ) + ch <- prometheus.MustNewConstMetric( + c.RPCRequests, + prometheus.GaugeValue, + rpc.RPCRequests, + ) + ch <- prometheus.MustNewConstMetric( + c.ActiveUserCount, + prometheus.GaugeValue, + rpc.ActiveUserCount, + ) + ch <- prometheus.MustNewConstMetric( + c.ConnectionCount, + prometheus.GaugeValue, + rpc.ConnectionCount, + ) + ch <- prometheus.MustNewConstMetric( + c.RPCOperationsPerSec, + prometheus.CounterValue, + rpc.RPCOperationsPerSec, + ) + ch <- prometheus.MustNewConstMetric( + c.UserCount, + prometheus.GaugeValue, + rpc.UserCount, + ) + } + + return nil +} + +// Perflib: [20524] MSExchangeTransport Queues +type perflibTransportQueues struct { + Name string + + ExternalActiveRemoteDeliveryQueueLength float64 `perflib:"External Active Remote Delivery Queue Length"` + InternalActiveRemoteDeliveryQueueLength float64 `perflib:"Internal Active Remote Delivery Queue Length"` + ActiveMailboxDeliveryQueueLength float64 `perflib:"Active Mailbox Delivery Queue Length"` + RetryMailboxDeliveryQueueLength float64 `perflib:"Retry Mailbox Delivery Queue Length"` + UnreachableQueueLength float64 `perflib:"Unreachable Queue Length"` + ExternalLargestDeliveryQueueLength float64 `perflib:"External Largest Delivery Queue Length"` + InternalLargestDeliveryQueueLength float64 `perflib:"Internal Largest Delivery Queue Length"` + PoisonQueueLength float64 `perflib:"Poison Queue Length"` +} + +func (c *exchangeCollector) collectTransportQueues(ctx *ScrapeContext, ch chan<- prometheus.Metric) error { + var data []perflibTransportQueues + if err := unmarshalObject(ctx.perfObjects["MSExchangeTransport Queues"], &data); err != nil { + return err + } + + for _, queue := range data { + labelName := c.toLabelName(queue.Name) + if strings.HasSuffix(labelName, "_total") { + continue + } + ch <- prometheus.MustNewConstMetric( + c.ExternalActiveRemoteDeliveryQueueLength, + prometheus.GaugeValue, + queue.ExternalActiveRemoteDeliveryQueueLength, + labelName, + ) + ch <- prometheus.MustNewConstMetric( + c.InternalActiveRemoteDeliveryQueueLength, + prometheus.GaugeValue, + queue.InternalActiveRemoteDeliveryQueueLength, + labelName, + ) + ch <- prometheus.MustNewConstMetric( + c.ActiveMailboxDeliveryQueueLength, + prometheus.GaugeValue, + queue.ActiveMailboxDeliveryQueueLength, + labelName, + ) + ch <- prometheus.MustNewConstMetric( + c.RetryMailboxDeliveryQueueLength, + prometheus.GaugeValue, + queue.RetryMailboxDeliveryQueueLength, + labelName, + ) + ch <- prometheus.MustNewConstMetric( + c.UnreachableQueueLength, + prometheus.GaugeValue, + queue.UnreachableQueueLength, + labelName, + ) + ch <- prometheus.MustNewConstMetric( + c.ExternalLargestDeliveryQueueLength, + prometheus.GaugeValue, + queue.ExternalLargestDeliveryQueueLength, + labelName, + ) + ch <- prometheus.MustNewConstMetric( + c.InternalLargestDeliveryQueueLength, + prometheus.GaugeValue, + queue.InternalLargestDeliveryQueueLength, + labelName, + ) + ch <- prometheus.MustNewConstMetric( + c.PoisonQueueLength, + prometheus.GaugeValue, + queue.PoisonQueueLength, + labelName, + ) + } + return nil +} + +// Perflib: [19430] MSExchange WorkloadManagement Workloads +type perflibWorkloadManagementWorkloads struct { + Name string + + ActiveTasks float64 `perflib:"ActiveTasks"` + CompletedTasks float64 `perflib:"CompletedTasks"` + QueuedTasks float64 `perflib:"QueuedTasks"` + YieldedTasks float64 `perflib:"YieldedTasks"` + IsActive float64 `perflib:"Active"` +} + +func (c *exchangeCollector) collectWorkloadManagementWorkloads(ctx *ScrapeContext, ch chan<- prometheus.Metric) error { + var data []perflibWorkloadManagementWorkloads + if err := unmarshalObject(ctx.perfObjects["MSExchange WorkloadManagement Workloads"], &data); err != nil { + return err + } + + for _, instance := range data { + labelName := c.toLabelName(instance.Name) + if strings.HasSuffix(labelName, "_total") { + continue + } + ch <- prometheus.MustNewConstMetric( + c.ActiveTasks, + prometheus.GaugeValue, + instance.ActiveTasks, + labelName, + ) + ch <- prometheus.MustNewConstMetric( + c.CompletedTasks, + prometheus.CounterValue, + instance.CompletedTasks, + labelName, + ) + ch <- prometheus.MustNewConstMetric( + c.QueuedTasks, + prometheus.CounterValue, + instance.QueuedTasks, + labelName, + ) + ch <- prometheus.MustNewConstMetric( + c.YieldedTasks, + prometheus.CounterValue, + instance.YieldedTasks, + labelName, + ) + ch <- prometheus.MustNewConstMetric( + c.IsActive, + prometheus.GaugeValue, + instance.IsActive, + labelName, + ) + } + + return nil +} + +// [29240] MSExchangeAutodiscover +type perflibAutodiscover struct { + RequestsPerSec float64 `perflib:"Requests/sec"` +} + +func (c *exchangeCollector) collectAutoDiscover(ctx *ScrapeContext, ch chan<- prometheus.Metric) error { + var data []perflibAutodiscover + if err := unmarshalObject(ctx.perfObjects["MSExchangeAutodiscover"], &data); err != nil { + return err + } + for _, autodisc := range data { + ch <- prometheus.MustNewConstMetric( + c.AutodiscoverRequestsPerSec, + prometheus.CounterValue, + autodisc.RequestsPerSec, + ) + } + return nil +} + +// toLabelName converts strings to lowercase and replaces all whitespace and dots with underscores +func (c *exchangeCollector) toLabelName(name string) string { + s := strings.ReplaceAll(strings.Join(strings.Fields(strings.ToLower(name)), "_"), ".", "_") + s = strings.ReplaceAll(s, "__", "_") + return s +} + +// msToSec converts from ms to seconds +func (c *exchangeCollector) msToSec(t float64) float64 { + return t / 1000 +} diff --git a/docs/collector.exchange.md b/docs/collector.exchange.md new file mode 100644 index 00000000..ccf3d586 --- /dev/null +++ b/docs/collector.exchange.md @@ -0,0 +1,67 @@ +# exchange collector + +The exchange collector collects metrics from MS Exchange hosts through perflib +======= + + +||| +-|- +Metric name prefix | `exchange` +Classes | [Win32_PerfRawData_MSExchangeADAccess_MSExchangeADAccessProcesses](https://docs.microsoft.com/en-us/exchange/)
[Win32_PerfRawData_MSExchangeTransportQueues_MSExchangeTransportueues](https://docs.microsoft.com/en-us/exchange/)
[Win32_PerfRawData_ESE_MSExchangeDatabaseInstances](https://docs.microsoft.com/en-us/exchange/)
[Win32_PerfRawData_MSExchangeHttpProxy_MSExchangeHttpProxy](https://docs.microsoft.com/en-us/exchange/)
[Win32_PerfRawData_MSExchangeActiveSync_MSExchangeActiveSync](https://docs.microsoft.com/en-us/exchange/)
[Win32_PerfRawData_MSExchangeAvailabilityService_MSExchangeAvailabilityService](https://docs.microsoft.com/en-us/exchange/)
[Win32_PerfRawData_MSExchangeOWA_MSExchangeOWA](https://docs.microsoft.com/en-us/exchange/)
[Win32_PerfRawData_MSExchangeAutodiscover_MSExchangeAutodiscover](https://docs.microsoft.com/en-us/exchange/)
[Win32_PerfRawData_MSExchangeWorkloadManagementWorkloads_MSExchangeWorkloadManagementWorkloads](https://docs.microsoft.com/en-us/exchange/)
[Win32_PerfRawData_MSExchangeRpcClientAccess_MSExchangeRpcClientAccess](https://docs.microsoft.com/en-us/exchange/)
+Enabled by default? | No + +## Flags + +### `--collectors.exchange.list` +Lists the Perflib Objects that are queried for data along with the perlfib object id + +## Metrics +Name | Description +--------------|--------------- +`wmi_exchange_rpc_avg_latency_sec` | The latency (sec), averaged for the past 1024 packets +`wmi_exchange_rpc_requests` | Number of client requests currently being processed by the RPC Client Access service +`wmi_exchange_rpc_active_user_count` | Number of unique users that have shown some kind of activity in the last 2 minutes +`wmi_exchange_rpc_connection_count` | Total number of client connections maintained +`wmi_exchange_rpc_operations_total` | The rate at which RPC operations occur +`wmi_exchange_rpc_user_count` | Number of users +`wmi_exchange_ldap_read_time_sec` | Time (sec) to send an LDAP read request and receive a response +`wmi_exchange_ldap_search_time_sec` | Time (sec) to send an LDAP search request and receive a response +`wmi_exchange_ldap_write_time_sec` | Time (sec) to send an LDAP Add/Modify/Delete request and receive a response +`wmi_exchange_ldap_timeout_errors_total` | Total number of LDAP timeout errors +`wmi_exchange_ldap_long_running_ops_per_sec` | Long Running LDAP operations per second +`wmi_exchange_transport_queues_external_active_remote_delivery` | External Active Remote Delivery Queue length +`wmi_exchange_transport_queues_internal_active_remote_delivery` | Internal Active Remote Delivery Queue length +`wmi_exchange_transport_queues_active_mailbox_delivery` | Active Mailbox Delivery Queue length +`wmi_exchange_transport_queues_retry_mailbox_delivery` | Retry Mailbox Delivery Queue length +`wmi_exchange_transport_queues_unreachable` | Unreachable Queue lengt +`wmi_exchange_transport_queues_external_largest_delivery` | External Largest Delivery Queue length +`wmi_exchange_transport_queues_internal_largest_delivery` | Internal Largest Delivery Queue length +`wmi_exchange_transport_queues_poison` | Poison Queue length +`wmi_exchange_http_proxy_mailbox_server_locator_avg_latency_sec` | Average latency (sec) of MailboxServerLocator web service calls +`wmi_exchange_http_proxy_avg_auth_latency` | Average time spent authenticating CAS requests over the last 200 samples +`wmi_exchange_http_proxy_outstanding_proxy_requests` | Number of concurrent outstanding proxy requests +`wmi_exchange_http_proxy_requests_total` | Number of proxy requests processed each second +`wmi_exchange_avail_service_requests_per_sec` | Number of requests serviced per second +`wmi_exchange_owa_current_unique_users` | Number of unique users currently logged on to Outlook Web App +`wmi_exchange_owa_requests_total` | Number of requests handled by Outlook Web App per second +`wmi_exchange_autodiscover_requests_total` | Number of autodiscover service requests processed each second +`wmi_exchange_workload_active_tasks` | Number of active tasks currently running in the background for workload management +`wmi_exchange_workload_completed_tasks` | Number of workload management tasks that have been completed +`wmi_exchange_workload_queued_tasks` | Number of workload management tasks that are currently queued up waiting to be processed +`wmi_exchange_workload_yielded_tasks` | The total number of tasks that have been yielded by a workload +`wmi_exchange_workload_is_active` | Active indicates whether the workload is in an active (1) or paused (0) state +`wmi_exchange_activesync_requests_total` | Num HTTP requests received from the client via ASP.NET per sec. Shows Current user load +`wmi_exchange_http_proxy_avg_cas_proccessing_latency_sec` | Average latency (sec) of CAS processing time over the last 200 reqs +`wmi_exchange_http_proxy_mailbox_proxy_failure_rate` | % of failures between this CAS and MBX servers over the last 200 sample +`wmi_exchange_activesync_ping_cmds_pending` | Number of ping commands currently pending in the queue +`wmi_exchange_activesync_sync_cmds_total` | Number of sync commands processed per second. Clients use this command to synchronize items within a folder + +### 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!_ + diff --git a/go.sum b/go.sum index 66b567af..d20b9d92 100644 --- a/go.sum +++ b/go.sum @@ -4,15 +4,13 @@ github.com/Microsoft/hcsshim v0.8.6 h1:ZfF0+zZeYdzMIVMZHKtDKJvLHj76XCuVae/jNkjj0 github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= -github.com/alecthomas/kingpin v2.2.6+incompatible/go.mod h1:59OFYbFVLKQKq+mqrL6Rw5bR0c3ACQaawgXx0QYndlE= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc h1:cAKDfWh5VpdgMhJosfJnn5/FoN2SRZ4p7fJNX58YPaU= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a h1:BtpsbiV638WQZwhA98cEZw2BsbnQJrbd0BI7tsy0W1c= -github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TRo4= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= @@ -22,61 +20,44 @@ github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E= github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/golang/protobuf v1.0.0 h1:lsek0oXi8iFE9L+EXARyHIjU5rlWIhhTkjDz3vHhWWQ= -github.com/golang/protobuf v1.0.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/leoluk/perflib_exporter v0.0.1 h1:MRwP1Ohh/mVevUy4ZUzvSlxnJtm9/NWHeM3aROxnRiQ= -github.com/leoluk/perflib_exporter v0.0.1/go.mod h1:4APOQriqHobMzovXV7guPQv0ynKH6vZD3XNmT2MBc6w= github.com/leoluk/perflib_exporter v0.1.0 h1:fXe/mDaf9jR+Zk8FjFlcCSksACuIj2VNN4GyKHmQqtA= github.com/leoluk/perflib_exporter v0.1.0/go.mod h1:rpV0lYj7lemdTm31t7zpCqYqPnw7xs86f+BaaNBVYFM= -github.com/matttproud/golang_protobuf_extensions v1.0.0 h1:YNOwxxSJzSUARoD9KRZLzM9Y858MNGCOACTvCW9TSAc= -github.com/matttproud/golang_protobuf_extensions v1.0.0/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.8.0 h1:1921Yw9Gc3iSc4VQh3PIoOqgPCZS7G/4xQNVUp8Mda8= -github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.2 h1:awm861/B8OKDd2I/6o1dy3ra4BamzKhYOiGItCeZ740= github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM= -github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5 h1:cLL6NowurKLMfCeQy4tIeph12XNQWgANCNvdyrOYKV4= -github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/common v0.0.0-20180312112859-e4aa40a9169a h1:JLXgXKi9RCmLk8DMn8+PCvN++iwpD3KptUbVvHBsKtU= -github.com/prometheus/common v0.0.0-20180312112859-e4aa40a9169a/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= github.com/prometheus/common v0.2.0 h1:kUZDBDTdBVBYBj5Tmh2NZLlF60mfjA27rM34b+cVwNU= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/procfs v0.0.0-20180310141954-54d17b57dd7d h1:iF+U2tTdys559fmqt0MNaC8QLIJh1twxIIOylDGhswM= -github.com/prometheus/procfs v0.0.0-20180310141954-54d17b57dd7d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a h1:9a8MnZMP0X2nLJdBg+pBmGgkJlSaKC2KaQmTCk1XDtE= github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/sirupsen/logrus v1.0.5 h1:8c8b5uO0zS4X6RPl/sd1ENwSkIc0/H2PaHxE3udaE8I= -github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -golang.org/x/crypto v0.0.0-20180312195533-182114d58262 h1:1NLVUmR8SQ7cNNA5Vo7ronpXbR+5A+9IwIC/bLE7D8Y= -golang.org/x/crypto v0.0.0-20180312195533-182114d58262/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f h1:Bl/8QSvNqXvPGPGXa2z5xUTmV7VDcZyvRZ+QQXkXTZQ= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180313075820-8c0ece68c283 h1:DE/w7won1Ns86VoWjUZ4cJS6//TObJntGkxuZ63asRc= -golang.org/x/sys v0.0.0-20180313075820-8c0ece68c283/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67 h1:1Fzlr8kkDLQwqMP8GxrhptBLqZG/EDpiATneiZHY998=