exchange: Use new collector interface (#1685)

This commit is contained in:
Jan-Otto Kröpke 2024-10-12 18:09:05 +02:00 committed by GitHub
parent 22fdb33b4c
commit a0159b333e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 1306 additions and 497 deletions

View File

@ -15,6 +15,7 @@ linters:
- gocognit
- goconst
- gocyclo
- godot
- gomnd
- paralleltest
- lll

View File

@ -3,42 +3,68 @@
package exchange
import (
"errors"
"fmt"
"log/slog"
"os"
"strings"
"github.com/alecthomas/kingpin/v2"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus-community/windows_exporter/internal/utils"
"github.com/prometheus/client_golang/prometheus"
"github.com/yusufpapurcu/wmi"
)
const Name = "exchange"
const (
adAccessProcesses = "ADAccessProcesses"
transportQueues = "TransportQueues"
httpProxy = "HttpProxy"
activeSync = "ActiveSync"
availabilityService = "AvailabilityService"
outlookWebAccess = "OutlookWebAccess"
autoDiscover = "Autodiscover"
workloadManagement = "WorkloadManagement"
rpcClientAccess = "RpcClientAccess"
mapiHttpEmsmdb = "MapiHttpEmsmdb"
)
type Config struct {
CollectorsEnabled []string `yaml:"collectors_enabled"`
}
var ConfigDefaults = Config{
CollectorsEnabled: []string{
"ADAccessProcesses",
"TransportQueues",
"HttpProxy",
"ActiveSync",
"AvailabilityService",
"OutlookWebAccess",
"Autodiscover",
"WorkloadManagement",
"RpcClientAccess",
"MapiHttpEmsmdb",
adAccessProcesses,
transportQueues,
httpProxy,
activeSync,
availabilityService,
outlookWebAccess,
autoDiscover,
workloadManagement,
rpcClientAccess,
mapiHttpEmsmdb,
},
}
type Collector struct {
config Config
perfDataCollectorADAccessProcesses perfdata.Collector
perfDataCollectorTransportQueues perfdata.Collector
perfDataCollectorHttpProxy perfdata.Collector
perfDataCollectorActiveSync perfdata.Collector
perfDataCollectorAvailabilityService perfdata.Collector
perfDataCollectorOWA perfdata.Collector
perfDataCollectorAutoDiscover perfdata.Collector
perfDataCollectorWorkloadManagementWorkloads perfdata.Collector
perfDataCollectorRpcClientAccess perfdata.Collector
perfDataCollectorMapiHttpEmsmdb perfdata.Collector
activeMailboxDeliveryQueueLength *prometheus.Desc
activeSyncRequestsPerSec *prometheus.Desc
activeTasks *prometheus.Desc
@ -118,16 +144,16 @@ func NewWithFlags(app *kingpin.Application) *Collector {
app.PreAction(func(*kingpin.ParseContext) error {
if listAllCollectors {
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",
"MapiHttpEmsmdb": "[26463] MSExchange MapiHttp Emsmdb",
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",
mapiHttpEmsmdb: "[26463] MSExchange MapiHttp Emsmdb",
}
sb := strings.Builder{}
@ -159,6 +185,10 @@ func (c *Collector) GetName() string {
}
func (c *Collector) GetPerfCounter(_ *slog.Logger) ([]string, error) {
if utils.PDHEnabled() {
return []string{}, nil
}
return []string{
"MSExchange ADAccess Processes",
"MSExchangeTransport Queues",
@ -178,10 +208,31 @@ func (c *Collector) Close(_ *slog.Logger) error {
}
func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
if utils.PDHEnabled() {
collectorFuncs := map[string]func() error{
adAccessProcesses: c.buildADAccessProcesses,
transportQueues: c.buildTransportQueues,
httpProxy: c.buildHTTPProxy,
activeSync: c.buildActiveSync,
availabilityService: c.buildAvailabilityService,
outlookWebAccess: c.buildOWA,
autoDiscover: c.buildAutoDiscover,
workloadManagement: c.buildWorkloadManagementWorkloads,
rpcClientAccess: c.buildRPC,
mapiHttpEmsmdb: c.buildMapiHttpEmsmdb,
}
for _, collectorName := range c.config.CollectorsEnabled {
if err := collectorFuncs[collectorName](); err != nil {
return err
}
}
}
// desc creates a new prometheus description
desc := func(metricName string, description string, labels ...string) *prometheus.Desc {
return prometheus.NewDesc(
prometheus.BuildFQName(types.Namespace, "exchange", metricName),
prometheus.BuildFQName(types.Namespace, Name, metricName),
description,
labels,
nil,
@ -232,18 +283,22 @@ func (c *Collector) Build(_ *slog.Logger, _ *wmi.Client) error {
// Collect collects exchange metrics and sends them to prometheus.
func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
if utils.PDHEnabled() {
return c.collectPDH(ch)
}
logger = logger.With(slog.String("collector", Name))
collectorFuncs := map[string]func(ctx *types.ScrapeContext, logger *slog.Logger, 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,
"MapiHttpEmsmdb": c.collectMapiHttpEmsmdb,
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,
mapiHttpEmsmdb: c.collectMapiHttpEmsmdb,
}
for _, collectorName := range c.config.CollectorsEnabled {
@ -259,476 +314,28 @@ func (c *Collector) Collect(ctx *types.ScrapeContext, logger *slog.Logger, ch ch
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 *Collector) collectADAccessProcesses(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
var data []perflibADAccessProcesses
if err := v1.UnmarshalObject(ctx.PerfObjects["MSExchange ADAccess Processes"], &data, logger); err != nil {
return err
// Collect collects exchange metrics and sends them to prometheus.
func (c *Collector) collectPDH(ch chan<- prometheus.Metric) error {
collectorFuncs := map[string]func(ch chan<- prometheus.Metric) error{
adAccessProcesses: c.collectPDHADAccessProcesses,
transportQueues: c.collectPDHTransportQueues,
httpProxy: c.collectPDHHTTPProxy,
activeSync: c.collectPDHActiveSync,
availabilityService: c.collectPDHAvailabilityService,
outlookWebAccess: c.collectPDHOWA,
autoDiscover: c.collectPDHAutoDiscover,
workloadManagement: c.collectPDHWorkloadManagementWorkloads,
rpcClientAccess: c.collectPDHRPC,
mapiHttpEmsmdb: c.collectPDHMapiHttpEmsmdb,
}
labelUseCount := make(map[string]int)
errs := make([]error, len(c.config.CollectorsEnabled))
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 occasional duplicate.
// This seems to affect about 4 instances only of 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,
)
for i, collectorName := range c.config.CollectorsEnabled {
errs[i] = collectorFuncs[collectorName](ch)
}
return nil
}
// Perflib: [24914] MSExchange Availability Service.
type perflibAvailabilityService struct {
RequestsSec float64 `perflib:"Availability Requests (sec)"`
}
func (c *Collector) collectAvailabilityService(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
var data []perflibAvailabilityService
if err := v1.UnmarshalObject(ctx.PerfObjects["MSExchange Availability Service"], &data, logger); 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 *Collector) collectHTTPProxy(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
var data []perflibHTTPProxy
if err := v1.UnmarshalObject(ctx.PerfObjects["MSExchange HttpProxy"], &data, logger); 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 *Collector) collectOWA(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
var data []perflibOWA
if err := v1.UnmarshalObject(ctx.PerfObjects["MSExchange OWA"], &data, logger); 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 *Collector) collectActiveSync(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
var data []perflibActiveSync
if err := v1.UnmarshalObject(ctx.PerfObjects["MSExchange ActiveSync"], &data, logger); 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 *Collector) collectRPC(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
var data []perflibRPCClientAccess
if err := v1.UnmarshalObject(ctx.PerfObjects["MSExchange RpcClientAccess"], &data, logger); 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 *Collector) collectTransportQueues(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
var data []perflibTransportQueues
if err := v1.UnmarshalObject(ctx.PerfObjects["MSExchangeTransport Queues"], &data, logger); 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 *Collector) collectWorkloadManagementWorkloads(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
var data []perflibWorkloadManagementWorkloads
if err := v1.UnmarshalObject(ctx.PerfObjects["MSExchange WorkloadManagement Workloads"], &data, logger); 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 *Collector) collectAutoDiscover(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
var data []perflibAutodiscover
if err := v1.UnmarshalObject(ctx.PerfObjects["MSExchangeAutodiscover"], &data, logger); err != nil {
return err
}
for _, autodisc := range data {
ch <- prometheus.MustNewConstMetric(
c.autoDiscoverRequestsPerSec,
prometheus.CounterValue,
autodisc.RequestsPerSec,
)
}
return nil
}
// perflib [26463] MSExchange MapiHttp Emsmdb.
type perflibMapiHttpEmsmdb struct {
ActiveUserCount float64 `perflib:"Active User Count"`
}
func (c *Collector) collectMapiHttpEmsmdb(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
logger = logger.With(slog.String("collector", Name))
var data []perflibMapiHttpEmsmdb
if err := v1.UnmarshalObject(ctx.PerfObjects["MSExchange MapiHttp Emsmdb"], &data, logger); err != nil {
return err
}
for _, mapihttp := range data {
ch <- prometheus.MustNewConstMetric(
c.activeUserCountMapiHttpEmsMDB,
prometheus.GaugeValue,
mapihttp.ActiveUserCount,
)
}
return nil
return errors.Join(errs...)
}
// toLabelName converts strings to lowercase and replaces all whitespaces and dots with underscores.

View File

@ -0,0 +1,101 @@
package exchange
import (
"errors"
"fmt"
"log/slog"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
)
const (
requestsPerSec = "Requests/sec"
pingCommandsPending = "Ping Commands Pending"
syncCommandsPerSec = "Sync Commands/sec"
)
// 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 *Collector) buildActiveSync() error {
counters := []string{
requestsPerSec,
pingCommandsPending,
syncCommandsPerSec,
}
var err error
c.perfDataCollectorActiveSync, err = perfdata.NewCollector(perfdata.V1, "MSExchange ActiveSync", perfdata.AllInstances, counters)
if err != nil {
return fmt.Errorf("failed to create MSExchange ActiveSync collector: %w", err)
}
return nil
}
func (c *Collector) collectActiveSync(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
var data []perflibActiveSync
if err := v1.UnmarshalObject(ctx.PerfObjects["MSExchange ActiveSync"], &data, logger); 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
}
func (c *Collector) collectPDHActiveSync(ch chan<- prometheus.Metric) error {
perfData, err := c.perfDataCollectorActiveSync.Collect()
if err != nil {
return fmt.Errorf("failed to collect MSExchange ActiveSync metrics: %w", err)
}
if len(perfData) == 0 {
return errors.New("perflib query for MSExchange ActiveSync returned empty result set")
}
for _, data := range perfData {
ch <- prometheus.MustNewConstMetric(
c.activeSyncRequestsPerSec,
prometheus.CounterValue,
data[requestsPerSec].FirstValue,
)
ch <- prometheus.MustNewConstMetric(
c.pingCommandsPending,
prometheus.GaugeValue,
data[pingCommandsPending].FirstValue,
)
ch <- prometheus.MustNewConstMetric(
c.syncCommandsPerSec,
prometheus.CounterValue,
data[syncCommandsPerSec].FirstValue,
)
}
return nil
}

View File

@ -0,0 +1,164 @@
package exchange
import (
"errors"
"fmt"
"log/slog"
"strings"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
)
const (
ldapReadTime = "LDAP Read Time"
ldapSearchTime = "LDAP Search Time"
ldapWriteTime = "LDAP Write Time"
ldapTimeoutErrorsPerSec = "LDAP Timeout Errors/sec"
longRunningLDAPOperationsPerMin = "Long Running LDAP Operations/min"
)
// 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 *Collector) buildADAccessProcesses() error {
counters := []string{
ldapReadTime,
ldapSearchTime,
ldapWriteTime,
ldapTimeoutErrorsPerSec,
longRunningLDAPOperationsPerMin,
}
var err error
c.perfDataCollectorADAccessProcesses, err = perfdata.NewCollector(perfdata.V1, "MSExchange ADAccess Processes", perfdata.AllInstances, counters)
if err != nil {
return fmt.Errorf("failed to create MSExchange ADAccess Processes collector: %w", err)
}
return nil
}
func (c *Collector) collectADAccessProcesses(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
var data []perflibADAccessProcesses
if err := v1.UnmarshalObject(ctx.PerfObjects["MSExchange ADAccess Processes"], &data, logger); 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 occasional duplicate.
// This seems to affect about 4 instances only of 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
}
func (c *Collector) collectPDHADAccessProcesses(ch chan<- prometheus.Metric) error {
perfData, err := c.perfDataCollectorADAccessProcesses.Collect()
if err != nil {
return fmt.Errorf("failed to collect MSExchange ADAccess Processes metrics: %w", err)
}
if len(perfData) == 0 {
return errors.New("perflib query for MSExchange ADAccess Processes returned empty result set")
}
labelUseCount := make(map[string]int)
for name, data := range perfData {
labelName := c.toLabelName(name)
// Since we're not including the PID suffix from the instance names in the label names, we get an occasional duplicate.
// This seems to affect about 4 instances only of 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(data[ldapReadTime].FirstValue),
labelName,
)
ch <- prometheus.MustNewConstMetric(
c.ldapSearchTime,
prometheus.CounterValue,
c.msToSec(data[ldapSearchTime].FirstValue),
labelName,
)
ch <- prometheus.MustNewConstMetric(
c.ldapWriteTime,
prometheus.CounterValue,
c.msToSec(data[ldapWriteTime].FirstValue),
labelName,
)
ch <- prometheus.MustNewConstMetric(
c.ldapTimeoutErrorsPerSec,
prometheus.CounterValue,
data[ldapTimeoutErrorsPerSec].FirstValue,
labelName,
)
ch <- prometheus.MustNewConstMetric(
c.longRunningLDAPOperationsPerMin,
prometheus.CounterValue,
data[longRunningLDAPOperationsPerMin].FirstValue*60,
labelName,
)
}
return nil
}

View File

@ -0,0 +1,71 @@
package exchange
import (
"errors"
"fmt"
"log/slog"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
)
// [29240] MSExchangeAutodiscover.
type perflibAutodiscover struct {
RequestsPerSec float64 `perflib:"Requests/sec"`
}
func (c *Collector) buildAutoDiscover() error {
counters := []string{
requestsPerSec,
}
var err error
c.perfDataCollectorAutoDiscover, err = perfdata.NewCollector(perfdata.V1, "MSExchange Autodiscover", perfdata.AllInstances, counters)
if err != nil {
return fmt.Errorf("failed to create MSExchange Autodiscover collector: %w", err)
}
return nil
}
func (c *Collector) collectAutoDiscover(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
var data []perflibAutodiscover
if err := v1.UnmarshalObject(ctx.PerfObjects["MSExchangeAutodiscover"], &data, logger); err != nil {
return err
}
for _, autodisc := range data {
ch <- prometheus.MustNewConstMetric(
c.autoDiscoverRequestsPerSec,
prometheus.CounterValue,
autodisc.RequestsPerSec,
)
}
return nil
}
func (c *Collector) collectPDHAutoDiscover(ch chan<- prometheus.Metric) error {
perfData, err := c.perfDataCollectorAutoDiscover.Collect()
if err != nil {
return fmt.Errorf("failed to collect MSExchange Autodiscover metrics: %w", err)
}
if len(perfData) == 0 {
return errors.New("perflib query for MSExchange Autodiscover returned empty result set")
}
for _, data := range perfData {
ch <- prometheus.MustNewConstMetric(
c.autoDiscoverRequestsPerSec,
prometheus.CounterValue,
data[requestsPerSec].FirstValue,
)
}
return nil
}

View File

@ -0,0 +1,69 @@
package exchange
import (
"errors"
"fmt"
"log/slog"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
)
// Perflib: [24914] MSExchange Availability Service.
type perflibAvailabilityService struct {
RequestsSec float64 `perflib:"Availability Requests (sec)"`
}
func (c *Collector) buildAvailabilityService() error {
counters := []string{}
var err error
c.perfDataCollectorAvailabilityService, err = perfdata.NewCollector(perfdata.V1, "MSExchange Availability Service", perfdata.AllInstances, counters)
if err != nil {
return fmt.Errorf("failed to create MSExchange Availability Service collector: %w", err)
}
return nil
}
func (c *Collector) collectAvailabilityService(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
var data []perflibAvailabilityService
if err := v1.UnmarshalObject(ctx.PerfObjects["MSExchange Availability Service"], &data, logger); err != nil {
return err
}
for _, availservice := range data {
ch <- prometheus.MustNewConstMetric(
c.availabilityRequestsSec,
prometheus.CounterValue,
availservice.RequestsSec,
)
}
return nil
}
func (c *Collector) collectPDHAvailabilityService(ch chan<- prometheus.Metric) error {
perfData, err := c.perfDataCollectorAvailabilityService.Collect()
if err != nil {
return fmt.Errorf("failed to collect MSExchange Availability Service metrics: %w", err)
}
if len(perfData) == 0 {
return errors.New("perflib query for MSExchange Availability Service returned empty result set")
}
for _, data := range perfData {
ch <- prometheus.MustNewConstMetric(
c.availabilityRequestsSec,
prometheus.CounterValue,
data[requestsPerSec].FirstValue,
)
}
return nil
}

View File

@ -0,0 +1,156 @@
package exchange
import (
"errors"
"fmt"
"log/slog"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
)
const (
mailboxServerLocatorAverageLatency = "MailboxServerLocator Average Latency (Moving Average)"
averageAuthenticationLatency = "Average Authentication Latency"
averageCASProcessingLatency = "Average ClientAccess Server Processing Latency"
mailboxServerProxyFailureRate = "Mailbox Server Proxy Failure Rate"
outstandingProxyRequests = "Outstanding Proxy Requests"
proxyRequestsPerSec = "Proxy Requests/Sec"
)
// 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 *Collector) buildHTTPProxy() error {
counters := []string{
mailboxServerLocatorAverageLatency,
averageAuthenticationLatency,
averageCASProcessingLatency,
mailboxServerProxyFailureRate,
outstandingProxyRequests,
proxyRequestsPerSec,
}
var err error
c.perfDataCollectorHttpProxy, err = perfdata.NewCollector(perfdata.V1, "MSExchange HttpProxy", perfdata.AllInstances, counters)
if err != nil {
return fmt.Errorf("failed to create MSExchange HttpProxy collector: %w", err)
}
return nil
}
func (c *Collector) collectHTTPProxy(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
var data []perflibHTTPProxy
if err := v1.UnmarshalObject(ctx.PerfObjects["MSExchange HttpProxy"], &data, logger); 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
}
func (c *Collector) collectPDHHTTPProxy(ch chan<- prometheus.Metric) error {
perfData, err := c.perfDataCollectorHttpProxy.Collect()
if err != nil {
return fmt.Errorf("failed to collect MSExchange HttpProxy Service metrics: %w", err)
}
if len(perfData) == 0 {
return errors.New("perflib query for MSExchange HttpProxy Service returned empty result set")
}
for name, data := range perfData {
labelName := c.toLabelName(name)
ch <- prometheus.MustNewConstMetric(
c.mailboxServerLocatorAverageLatency,
prometheus.GaugeValue,
c.msToSec(data[mailboxServerLocatorAverageLatency].FirstValue),
labelName,
)
ch <- prometheus.MustNewConstMetric(
c.averageAuthenticationLatency,
prometheus.GaugeValue,
data[averageAuthenticationLatency].FirstValue,
labelName,
)
ch <- prometheus.MustNewConstMetric(
c.averageCASProcessingLatency,
prometheus.GaugeValue,
c.msToSec(data[averageCASProcessingLatency].FirstValue),
labelName,
)
ch <- prometheus.MustNewConstMetric(
c.mailboxServerProxyFailureRate,
prometheus.GaugeValue,
data[mailboxServerProxyFailureRate].FirstValue,
labelName,
)
ch <- prometheus.MustNewConstMetric(
c.outstandingProxyRequests,
prometheus.GaugeValue,
data[outstandingProxyRequests].FirstValue,
labelName,
)
ch <- prometheus.MustNewConstMetric(
c.proxyRequestsPerSec,
prometheus.CounterValue,
data[proxyRequestsPerSec].FirstValue,
labelName,
)
}
return nil
}

View File

@ -0,0 +1,75 @@
package exchange
import (
"errors"
"fmt"
"log/slog"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
)
const (
activeUserCount = "Active User Count"
)
// perflib [26463] MSExchange MapiHttp Emsmdb.
type perflibMapiHttpEmsmdb struct {
ActiveUserCount float64 `perflib:"Active User Count"`
}
func (c *Collector) buildMapiHttpEmsmdb() error {
counters := []string{
activeUserCount,
}
var err error
c.perfDataCollectorMapiHttpEmsmdb, err = perfdata.NewCollector(perfdata.V1, "MSExchange MapiHttp Emsmdb", perfdata.AllInstances, counters)
if err != nil {
return fmt.Errorf("failed to create MSExchange MapiHttp Emsmdb: %w", err)
}
return nil
}
func (c *Collector) collectMapiHttpEmsmdb(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
var data []perflibMapiHttpEmsmdb
if err := v1.UnmarshalObject(ctx.PerfObjects["MSExchange MapiHttp Emsmdb"], &data, logger); err != nil {
return err
}
for _, mapihttp := range data {
ch <- prometheus.MustNewConstMetric(
c.activeUserCountMapiHttpEmsMDB,
prometheus.GaugeValue,
mapihttp.ActiveUserCount,
)
}
return nil
}
func (c *Collector) collectPDHMapiHttpEmsmdb(ch chan<- prometheus.Metric) error {
perfData, err := c.perfDataCollectorMapiHttpEmsmdb.Collect()
if err != nil {
return fmt.Errorf("failed to collect MSExchange MapiHttp Emsmdb metrics: %w", err)
}
if len(perfData) == 0 {
return errors.New("perflib query for MSExchange MapiHttp Emsmdb returned empty result set")
}
for _, data := range perfData {
ch <- prometheus.MustNewConstMetric(
c.activeUserCountMapiHttpEmsMDB,
prometheus.GaugeValue,
data[activeUserCount].FirstValue,
)
}
return nil
}

View File

@ -0,0 +1,88 @@
package exchange
import (
"errors"
"fmt"
"log/slog"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
)
const (
currentUniqueUsers = "Current Unique Users"
// requestsPerSec = "Requests/sec"
)
// Perflib: [24618] MSExchange OWA.
type perflibOWA struct {
CurrentUniqueUsers float64 `perflib:"Current Unique Users"`
RequestsPerSec float64 `perflib:"Requests/sec"`
}
func (c *Collector) buildOWA() error {
counters := []string{
currentUniqueUsers,
requestsPerSec,
}
var err error
c.perfDataCollectorOWA, err = perfdata.NewCollector(perfdata.V1, "MSExchange OWA", perfdata.AllInstances, counters)
if err != nil {
return fmt.Errorf("failed to create MSExchange OWA collector: %w", err)
}
return nil
}
func (c *Collector) collectOWA(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
var data []perflibOWA
if err := v1.UnmarshalObject(ctx.PerfObjects["MSExchange OWA"], &data, logger); 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
}
func (c *Collector) collectPDHOWA(ch chan<- prometheus.Metric) error {
perfData, err := c.perfDataCollectorOWA.Collect()
if err != nil {
return fmt.Errorf("failed to collect MSExchange OWA metrics: %w", err)
}
if len(perfData) == 0 {
return errors.New("perflib query for MSExchange OWA returned empty result set")
}
for _, data := range perfData {
ch <- prometheus.MustNewConstMetric(
c.currentUniqueUsers,
prometheus.GaugeValue,
data[currentUniqueUsers].FirstValue,
)
ch <- prometheus.MustNewConstMetric(
c.owaRequestsPerSec,
prometheus.CounterValue,
data[requestsPerSec].FirstValue,
)
}
return nil
}

View File

@ -0,0 +1,140 @@
package exchange
import (
"errors"
"fmt"
"log/slog"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
)
const (
rpcAveragedLatency = "RPC Averaged Latency"
rpcRequests = "RPC Requests"
// activeUserCount = "Active User Count"
connectionCount = "Connection Count"
rpcOperationsPerSec = "RPC Operations/sec"
userCount = "User Count"
)
// 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 *Collector) buildRPC() error {
counters := []string{
rpcAveragedLatency,
rpcRequests,
activeUserCount,
connectionCount,
rpcOperationsPerSec,
userCount,
}
var err error
c.perfDataCollectorRpcClientAccess, err = perfdata.NewCollector(perfdata.V1, "MSExchange RpcClientAccess", perfdata.AllInstances, counters)
if err != nil {
return fmt.Errorf("failed to create MSExchange RpcClientAccess collector: %w", err)
}
return nil
}
func (c *Collector) collectRPC(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
var data []perflibRPCClientAccess
if err := v1.UnmarshalObject(ctx.PerfObjects["MSExchange RpcClientAccess"], &data, logger); 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
}
func (c *Collector) collectPDHRPC(ch chan<- prometheus.Metric) error {
perfData, err := c.perfDataCollectorRpcClientAccess.Collect()
if err != nil {
return fmt.Errorf("failed to collect MSExchange RpcClientAccess: %w", err)
}
if len(perfData) == 0 {
return errors.New("perflib query for MSExchange RpcClientAccess returned empty result set")
}
for _, data := range perfData {
ch <- prometheus.MustNewConstMetric(
c.rpcAveragedLatency,
prometheus.GaugeValue,
c.msToSec(data[rpcAveragedLatency].FirstValue),
)
ch <- prometheus.MustNewConstMetric(
c.rpcRequests,
prometheus.GaugeValue,
data[rpcRequests].FirstValue,
)
ch <- prometheus.MustNewConstMetric(
c.activeUserCount,
prometheus.GaugeValue,
data[activeUserCount].FirstValue,
)
ch <- prometheus.MustNewConstMetric(
c.connectionCount,
prometheus.GaugeValue,
data[connectionCount].FirstValue,
)
ch <- prometheus.MustNewConstMetric(
c.rpcOperationsPerSec,
prometheus.CounterValue,
data[rpcOperationsPerSec].FirstValue,
)
ch <- prometheus.MustNewConstMetric(
c.userCount,
prometheus.GaugeValue,
data[userCount].FirstValue,
)
}
return nil
}

View File

@ -0,0 +1,191 @@
package exchange
import (
"errors"
"fmt"
"log/slog"
"strings"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
)
const (
externalActiveRemoteDeliveryQueueLength = "External Active Remote Delivery Queue Length"
internalActiveRemoteDeliveryQueueLength = "Internal Active Remote Delivery Queue Length"
activeMailboxDeliveryQueueLength = "Active Mailbox Delivery Queue Length"
retryMailboxDeliveryQueueLength = "Retry Mailbox Delivery Queue Length"
unreachableQueueLength = "Unreachable Queue Length"
externalLargestDeliveryQueueLength = "External Largest Delivery Queue Length"
internalLargestDeliveryQueueLength = "Internal Largest Delivery Queue Length"
poisonQueueLength = "Poison Queue Length"
)
// 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 *Collector) buildTransportQueues() error {
counters := []string{
externalActiveRemoteDeliveryQueueLength,
internalActiveRemoteDeliveryQueueLength,
activeMailboxDeliveryQueueLength,
retryMailboxDeliveryQueueLength,
unreachableQueueLength,
externalLargestDeliveryQueueLength,
internalLargestDeliveryQueueLength,
poisonQueueLength,
}
var err error
c.perfDataCollectorTransportQueues, err = perfdata.NewCollector(perfdata.V1, "MSExchangeTransport Queues", perfdata.AllInstances, counters)
if err != nil {
return fmt.Errorf("failed to create MSExchangeTransport Queues collector: %w", err)
}
return nil
}
func (c *Collector) collectTransportQueues(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
var data []perflibTransportQueues
if err := v1.UnmarshalObject(ctx.PerfObjects["MSExchangeTransport Queues"], &data, logger); 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
}
func (c *Collector) collectPDHTransportQueues(ch chan<- prometheus.Metric) error {
perfData, err := c.perfDataCollectorTransportQueues.Collect()
if err != nil {
return fmt.Errorf("failed to collect MSExchangeTransport Queues: %w", err)
}
if len(perfData) == 0 {
return errors.New("perflib query for MSExchangeTransport Queues returned empty result set")
}
for name, data := range perfData {
labelName := c.toLabelName(name)
ch <- prometheus.MustNewConstMetric(
c.externalActiveRemoteDeliveryQueueLength,
prometheus.GaugeValue,
data[externalActiveRemoteDeliveryQueueLength].FirstValue,
labelName,
)
ch <- prometheus.MustNewConstMetric(
c.internalActiveRemoteDeliveryQueueLength,
prometheus.GaugeValue,
data[internalActiveRemoteDeliveryQueueLength].FirstValue,
labelName,
)
ch <- prometheus.MustNewConstMetric(
c.activeMailboxDeliveryQueueLength,
prometheus.GaugeValue,
data[activeMailboxDeliveryQueueLength].FirstValue,
labelName,
)
ch <- prometheus.MustNewConstMetric(
c.retryMailboxDeliveryQueueLength,
prometheus.GaugeValue,
data[retryMailboxDeliveryQueueLength].FirstValue,
labelName,
)
ch <- prometheus.MustNewConstMetric(
c.unreachableQueueLength,
prometheus.GaugeValue,
data[unreachableQueueLength].FirstValue,
labelName,
)
ch <- prometheus.MustNewConstMetric(
c.externalLargestDeliveryQueueLength,
prometheus.GaugeValue,
data[externalLargestDeliveryQueueLength].FirstValue,
labelName,
)
ch <- prometheus.MustNewConstMetric(
c.internalLargestDeliveryQueueLength,
prometheus.GaugeValue,
data[internalLargestDeliveryQueueLength].FirstValue,
labelName,
)
ch <- prometheus.MustNewConstMetric(
c.poisonQueueLength,
prometheus.GaugeValue,
data[poisonQueueLength].FirstValue,
labelName,
)
}
return nil
}

View File

@ -0,0 +1,146 @@
package exchange
import (
"errors"
"fmt"
"log/slog"
"strings"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
v1 "github.com/prometheus-community/windows_exporter/internal/perfdata/v1"
"github.com/prometheus-community/windows_exporter/internal/types"
"github.com/prometheus/client_golang/prometheus"
)
const (
activeTasks = "ActiveTasks"
completedTasks = "CompletedTasks"
queuedTasks = "QueuedTasks"
yieldedTasks = "YieldedTasks"
isActive = "Active"
)
// 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 *Collector) buildWorkloadManagementWorkloads() error {
counters := []string{
activeTasks,
completedTasks,
queuedTasks,
yieldedTasks,
isActive,
}
var err error
c.perfDataCollectorWorkloadManagementWorkloads, err = perfdata.NewCollector(perfdata.V1, "MSExchange WorkloadManagement Workloads", perfdata.AllInstances, counters)
if err != nil {
return fmt.Errorf("failed to create MSExchange WorkloadManagement Workloads collector: %w", err)
}
return nil
}
func (c *Collector) collectWorkloadManagementWorkloads(ctx *types.ScrapeContext, logger *slog.Logger, ch chan<- prometheus.Metric) error {
var data []perflibWorkloadManagementWorkloads
if err := v1.UnmarshalObject(ctx.PerfObjects["MSExchange WorkloadManagement Workloads"], &data, logger); 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
}
func (c *Collector) collectPDHWorkloadManagementWorkloads(ch chan<- prometheus.Metric) error {
perfData, err := c.perfDataCollectorWorkloadManagementWorkloads.Collect()
if err != nil {
return fmt.Errorf("failed to collect MSExchange WorkloadManagement Workloads: %w", err)
}
if len(perfData) == 0 {
return errors.New("perflib query for MSExchange WorkloadManagement Workloads returned empty result set")
}
for name, data := range perfData {
labelName := c.toLabelName(name)
ch <- prometheus.MustNewConstMetric(
c.activeTasks,
prometheus.GaugeValue,
data[activeTasks].FirstValue,
labelName,
)
ch <- prometheus.MustNewConstMetric(
c.completedTasks,
prometheus.CounterValue,
data[completedTasks].FirstValue,
labelName,
)
ch <- prometheus.MustNewConstMetric(
c.queuedTasks,
prometheus.CounterValue,
data[queuedTasks].FirstValue,
labelName,
)
ch <- prometheus.MustNewConstMetric(
c.yieldedTasks,
prometheus.CounterValue,
data[yieldedTasks].FirstValue,
labelName,
)
ch <- prometheus.MustNewConstMetric(
c.isActive,
prometheus.GaugeValue,
data[isActive].FirstValue,
labelName,
)
}
return nil
}