Files
windows_exporter/internal/collector/exchange/exchange.go
Jan-Otto Kröpke 85845f4780 exchange: Extend Transport Queue metrics (#1749)
Signed-off-by: Jan-Otto Kröpke <mail@jkroepke.de>
2024-11-20 02:05:31 +01:00

259 lines
8.6 KiB
Go

//go:build windows
package exchange
import (
"errors"
"fmt"
"log/slog"
"os"
"strings"
"github.com/alecthomas/kingpin/v2"
"github.com/prometheus-community/windows_exporter/internal/mi"
"github.com/prometheus-community/windows_exporter/internal/perfdata"
"github.com/prometheus/client_golang/prometheus"
)
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,
},
}
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
activeUserCount *prometheus.Desc
activeUserCountMapiHttpEmsMDB *prometheus.Desc
autoDiscoverRequestsPerSec *prometheus.Desc
availabilityRequestsSec *prometheus.Desc
averageAuthenticationLatency *prometheus.Desc
averageCASProcessingLatency *prometheus.Desc
completedTasks *prometheus.Desc
connectionCount *prometheus.Desc
currentUniqueUsers *prometheus.Desc
externalActiveRemoteDeliveryQueueLength *prometheus.Desc
externalLargestDeliveryQueueLength *prometheus.Desc
internalActiveRemoteDeliveryQueueLength *prometheus.Desc
internalLargestDeliveryQueueLength *prometheus.Desc
isActive *prometheus.Desc
ldapReadTime *prometheus.Desc
ldapSearchTime *prometheus.Desc
ldapTimeoutErrorsPerSec *prometheus.Desc
ldapWriteTime *prometheus.Desc
longRunningLDAPOperationsPerMin *prometheus.Desc
mailboxServerLocatorAverageLatency *prometheus.Desc
mailboxServerProxyFailureRate *prometheus.Desc
outstandingProxyRequests *prometheus.Desc
owaRequestsPerSec *prometheus.Desc
pingCommandsPending *prometheus.Desc
poisonQueueLength *prometheus.Desc
proxyRequestsPerSec *prometheus.Desc
queuedTasks *prometheus.Desc
retryMailboxDeliveryQueueLength *prometheus.Desc
rpcAveragedLatency *prometheus.Desc
rpcOperationsPerSec *prometheus.Desc
rpcRequests *prometheus.Desc
syncCommandsPerSec *prometheus.Desc
unreachableQueueLength *prometheus.Desc
userCount *prometheus.Desc
yieldedTasks *prometheus.Desc
messagesQueuedForDeliveryTotal *prometheus.Desc
messagesSubmittedTotal *prometheus.Desc
messagesDelayedTotal *prometheus.Desc
messagesCompletedDeliveryTotal *prometheus.Desc
shadowQueueLength *prometheus.Desc
submissionQueueLength *prometheus.Desc
delayQueueLength *prometheus.Desc
itemsCompletedDeliveryTotal *prometheus.Desc
itemsQueuedForDeliveryExpiredTotal *prometheus.Desc
itemsQueuedForDeliveryTotal *prometheus.Desc
itemsResubmittedTotal *prometheus.Desc
}
func New(config *Config) *Collector {
if config == nil {
config = &ConfigDefaults
}
if config.CollectorsEnabled == nil {
config.CollectorsEnabled = ConfigDefaults.CollectorsEnabled
}
c := &Collector{
config: *config,
}
return c
}
func NewWithFlags(app *kingpin.Application) *Collector {
c := &Collector{
config: ConfigDefaults,
}
c.config.CollectorsEnabled = make([]string, 0)
var listAllCollectors bool
var collectorsEnabled string
app.Flag(
"collector.exchange.list",
"List the collectors along with their perflib object name/ids",
).BoolVar(&listAllCollectors)
app.Flag(
"collector.exchange.enabled",
"Comma-separated list of collectors to use. Defaults to all, if not specified.",
).Default(strings.Join(ConfigDefaults.CollectorsEnabled, ",")).StringVar(&collectorsEnabled)
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",
}
sb := strings.Builder{}
sb.WriteString(fmt.Sprintf("%-32s %-32s\n", "Collector Name", "[PerfID] Perflib Object"))
for _, cname := range ConfigDefaults.CollectorsEnabled {
sb.WriteString(fmt.Sprintf("%-32s %-32s\n", cname, collectorDesc[cname]))
}
app.UsageTemplate(sb.String()).Usage(nil)
os.Exit(0)
}
return nil
})
app.Action(func(*kingpin.ParseContext) error {
c.config.CollectorsEnabled = strings.Split(collectorsEnabled, ",")
return nil
})
return c
}
func (c *Collector) GetName() string {
return Name
}
func (c *Collector) Close() error {
return nil
}
func (c *Collector) Build(_ *slog.Logger, _ *mi.Session) error {
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
}
}
return nil
}
// Collect collects exchange metrics and sends them to prometheus.
func (c *Collector) Collect(ch chan<- prometheus.Metric) error {
collectorFuncs := map[string]func(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,
}
errs := make([]error, len(c.config.CollectorsEnabled))
for i, collectorName := range c.config.CollectorsEnabled {
errs[i] = collectorFuncs[collectorName](ch)
}
return errors.Join(errs...)
}
// toLabelName converts strings to lowercase and replaces all whitespaces and dots with underscores.
func (c *Collector) 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 *Collector) msToSec(t float64) float64 {
return t / 1000
}