From f4195aa435190066b8d4667dfc173e3bf5a1bfd5 Mon Sep 17 00:00:00 2001 From: Calle Pettersson Date: Sat, 15 Jul 2017 13:56:09 +0100 Subject: [PATCH] Conditional query for wmi fields added in IIS 8 --- collector/iis.go | 235 +++++++++++++++++++++++++++++------------------ collector/wmi.go | 35 ++++++- 2 files changed, 180 insertions(+), 90 deletions(-) diff --git a/collector/iis.go b/collector/iis.go index a9f17940..860c46d3 100644 --- a/collector/iis.go +++ b/collector/iis.go @@ -9,15 +9,50 @@ package collector import ( "flag" "fmt" - "log" "regexp" + "golang.org/x/sys/windows/registry" + "github.com/StackExchange/wmi" "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/common/log" ) func init() { Factories["iis"] = NewIISCollector + iis_version = getIISVersion() +} + +type simple_version struct { + major uint64 + minor uint64 +} + +func getIISVersion() simple_version { + k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\InetStp\`, registry.QUERY_VALUE) + if err != nil { + log.Warnf("Couldn't open registry to determine IIS version: %v\n", err) + return simple_version{} + } + defer k.Close() + + major, _, err := k.GetIntegerValue("MajorVersion") + if err != nil { + log.Warnf("Couldn't open registry to determine IIS version: %v\n", err) + return simple_version{} + } + minor, _, err := k.GetIntegerValue("MinorVersion") + if err != nil { + log.Warnf("Couldn't open registry to determine IIS version: %v\n", err) + return simple_version{} + } + + log.Debugf("Detected IIS %d.%d\n", major, minor) + + return simple_version{ + major: major, + minor: minor, + } } var ( @@ -25,6 +60,8 @@ var ( siteBlacklist = flag.String("collector.iis.site-blacklist", "", "Regexp of sites to blacklist. Site name must both match whitelist and not match blacklist to be included.") appWhitelist = flag.String("collector.iis.app-whitelist", ".+", "Regexp of apps to whitelist. App name must both match whitelist and not match blacklist to be included.") appBlacklist = flag.String("collector.iis.app-blacklist", "", "Regexp of apps to blacklist. App name must both match whitelist and not match blacklist to be included.") + + iis_version = simple_version{} ) type IISCollector struct { @@ -778,7 +815,7 @@ func NewIISCollector() (Collector, error) { // to the provided prometheus Metric channel. func (c *IISCollector) Collect(ch chan<- prometheus.Metric) error { if desc, err := c.collect(ch); err != nil { - log.Println("[ERROR] failed collecting iis metrics:", desc, err) + log.Errorf("[ERROR] failed collecting iis metrics:", desc, err) return err } return nil @@ -851,41 +888,45 @@ type Win32_PerfRawData_APPPOOLCountersProvider_APPPOOLWAS struct { type Win32_PerfRawData_W3SVCW3WPCounterProvider_W3SVCW3WP struct { Name string - ActiveFlushedEntries uint64 - CurrentFileCacheMemoryUsage uint64 - CurrentFilesCached uint64 - CurrentMetadataCached uint64 - CurrentURIsCached uint64 - FileCacheFlushes uint64 - FileCacheHits uint64 - FileCacheMisses uint64 - MaximumFileCacheMemoryUsage uint64 - MetadataCacheFlushes uint64 - MetadataCacheHits uint64 - MetadataCacheMisses uint64 - OutputCacheCurrentFlushedItems uint64 - OutputCacheCurrentItems uint64 - OutputCacheCurrentMemoryUsage uint64 - OutputCacheHitsPersec uint64 - OutputCacheMissesPersec uint64 - OutputCacheTotalFlushedItems uint64 - OutputCacheTotalFlushes uint64 - OutputCacheTotalHits uint64 - OutputCacheTotalMisses uint64 - TotalFilesCached uint64 - TotalFlushedFiles uint64 - TotalFlushedMetadata uint64 - TotalFlushedURIs uint64 - TotalMetadataCached uint64 - TotalURIsCached uint64 - URICacheFlushes uint64 - URICacheHits uint64 - URICacheMisses uint64 - ActiveThreadsCount uint64 - TotalThreads uint64 - MaximumThreadsCount uint64 - TotalHTTPRequestsServed uint64 - ActiveRequests uint64 + ActiveFlushedEntries uint64 + CurrentFileCacheMemoryUsage uint64 + CurrentFilesCached uint64 + CurrentMetadataCached uint64 + CurrentURIsCached uint64 + FileCacheFlushes uint64 + FileCacheHits uint64 + FileCacheMisses uint64 + MaximumFileCacheMemoryUsage uint64 + MetadataCacheFlushes uint64 + MetadataCacheHits uint64 + MetadataCacheMisses uint64 + OutputCacheCurrentFlushedItems uint64 + OutputCacheCurrentItems uint64 + OutputCacheCurrentMemoryUsage uint64 + OutputCacheHitsPersec uint64 + OutputCacheMissesPersec uint64 + OutputCacheTotalFlushedItems uint64 + OutputCacheTotalFlushes uint64 + OutputCacheTotalHits uint64 + OutputCacheTotalMisses uint64 + TotalFilesCached uint64 + TotalFlushedFiles uint64 + TotalFlushedMetadata uint64 + TotalFlushedURIs uint64 + TotalMetadataCached uint64 + TotalURIsCached uint64 + URICacheFlushes uint64 + URICacheHits uint64 + URICacheMisses uint64 + ActiveThreadsCount uint64 + TotalThreads uint64 + MaximumThreadsCount uint64 + TotalHTTPRequestsServed uint64 + ActiveRequests uint64 +} +type Win32_PerfRawData_W3SVCW3WPCounterProvider_W3SVCW3WP_IIS8 struct { + Name string + Percent401HTTPResponseSent uint64 Percent403HTTPResponseSent uint64 Percent404HTTPResponseSent uint64 @@ -1567,64 +1608,80 @@ func (c *IISCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, e float64(app.ActiveRequests), name, ) + } - ch <- prometheus.MustNewConstMetric( - c.RequestErrorsTotal, - prometheus.CounterValue, - float64(app.Percent401HTTPResponseSent), - name, - "401", - ) - ch <- prometheus.MustNewConstMetric( - c.RequestErrorsTotal, - prometheus.CounterValue, - float64(app.Percent403HTTPResponseSent), - name, - "403", - ) - ch <- prometheus.MustNewConstMetric( - c.RequestErrorsTotal, - prometheus.CounterValue, - float64(app.Percent404HTTPResponseSent), - name, - "404", - ) - ch <- prometheus.MustNewConstMetric( - c.RequestErrorsTotal, - prometheus.CounterValue, - float64(app.Percent500HTTPResponseSent), - name, - "500", - ) + if iis_version.major >= 8 { + var dst_worker_iis8 []Win32_PerfRawData_W3SVCW3WPCounterProvider_W3SVCW3WP_IIS8 + q = createQuery(&dst_worker_iis8, "Win32_PerfRawData_W3SVCW3WPCounterProvider_W3SVCW3WP", "") + if err := wmi.Query(q, &dst_worker_iis8); err != nil { + return nil, err + } + for _, app := range dst_worker_iis8 { + // Extract the apppool name from the format _ + name := workerProcessNameExtractor.ReplaceAllString(app.Name, "$1") + if name == "_Total" || + c.appBlacklistPattern.MatchString(name) || + !c.appWhitelistPattern.MatchString(name) { + continue + } - ch <- prometheus.MustNewConstMetric( - c.WebSocketRequestsActive, - prometheus.CounterValue, - float64(app.WebSocketActiveRequests), - name, - ) + ch <- prometheus.MustNewConstMetric( + c.RequestErrorsTotal, + prometheus.CounterValue, + float64(app.Percent401HTTPResponseSent), + name, + "401", + ) + ch <- prometheus.MustNewConstMetric( + c.RequestErrorsTotal, + prometheus.CounterValue, + float64(app.Percent403HTTPResponseSent), + name, + "403", + ) + ch <- prometheus.MustNewConstMetric( + c.RequestErrorsTotal, + prometheus.CounterValue, + float64(app.Percent404HTTPResponseSent), + name, + "404", + ) + ch <- prometheus.MustNewConstMetric( + c.RequestErrorsTotal, + prometheus.CounterValue, + float64(app.Percent500HTTPResponseSent), + name, + "500", + ) - ch <- prometheus.MustNewConstMetric( - c.WebSocketConnectionAttempts, - prometheus.CounterValue, - float64(app.WebSocketConnectionAttemptsPerSec), - name, - ) + ch <- prometheus.MustNewConstMetric( + c.WebSocketRequestsActive, + prometheus.CounterValue, + float64(app.WebSocketActiveRequests), + name, + ) - ch <- prometheus.MustNewConstMetric( - c.WebSocketConnectionsAccepted, - prometheus.CounterValue, - float64(app.WebSocketConnectionsAcceptedPerSec), - name, - ) + ch <- prometheus.MustNewConstMetric( + c.WebSocketConnectionAttempts, + prometheus.CounterValue, + float64(app.WebSocketConnectionAttemptsPerSec), + name, + ) - ch <- prometheus.MustNewConstMetric( - c.WebSocketConnectionsRejected, - prometheus.CounterValue, - float64(app.WebSocketConnectionsRejectedPerSec), - name, - ) + ch <- prometheus.MustNewConstMetric( + c.WebSocketConnectionsAccepted, + prometheus.CounterValue, + float64(app.WebSocketConnectionsAcceptedPerSec), + name, + ) + ch <- prometheus.MustNewConstMetric( + c.WebSocketConnectionsRejected, + prometheus.CounterValue, + float64(app.WebSocketConnectionsRejectedPerSec), + name, + ) + } } var dst_cache []Win32_PerfRawData_W3SVC_WebServiceCache diff --git a/collector/wmi.go b/collector/wmi.go index 40255a7f..47dbe59b 100644 --- a/collector/wmi.go +++ b/collector/wmi.go @@ -1,6 +1,12 @@ package collector -import "github.com/prometheus/client_golang/prometheus" +import ( + "bytes" + "reflect" + "strings" + + "github.com/prometheus/client_golang/prometheus" +) // ... const ( @@ -18,3 +24,30 @@ type Collector interface { // Get new metrics and expose them via prometheus registry. Collect(ch chan<- prometheus.Metric) (err error) } + +// This is adapted from StackExchange/wmi/wmi.go, and lets us change the class +// name being queried for: +// CreateQuery returns a WQL query string that queries all columns of src. where +// is an optional string that is appended to the query, to be used with WHERE +// clauses. In such a case, the "WHERE" string should appear at the beginning. +func createQuery(src interface{}, class, where string) string { + var b bytes.Buffer + b.WriteString("SELECT ") + s := reflect.Indirect(reflect.ValueOf(src)) + t := s.Type() + if s.Kind() == reflect.Slice { + t = t.Elem() + } + if t.Kind() != reflect.Struct { + return "" + } + var fields []string + for i := 0; i < t.NumField(); i++ { + fields = append(fields, t.Field(i).Name) + } + b.WriteString(strings.Join(fields, ", ")) + b.WriteString(" FROM ") + b.WriteString(class) + b.WriteString(" " + where) + return b.String() +}