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=