mirror of
https://github.com/prometheus-community/windows_exporter.git
synced 2026-02-15 09:26:36 +00:00
Perflib often exposes duplicate IIS entries, suffixed with '#' and a number (I.E. iis_site_name#1). These duplicate entries were causing the exporter to fail scraping due to duplicate metrics. Based on user feedback, the entry with the highest suffix value is kept, with other duplicate entries discarded. E.G. Given the following list of site names, "Site_B" would be discarded, and "Site_B#2" would be kept and presented as "Site_B" in the collector metrics. [ "Site_A", "Site_B", "Site_C", "Site_B#2" ] Signed-off-by: Ben Reedy <breed808@breed808.com>
2083 lines
70 KiB
Go
2083 lines
70 KiB
Go
//go:build windows
|
|
// +build windows
|
|
|
|
package collector
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"regexp"
|
|
"sort"
|
|
"strings"
|
|
|
|
"github.com/alecthomas/kingpin/v2"
|
|
"github.com/prometheus-community/windows_exporter/log"
|
|
"github.com/prometheus/client_golang/prometheus"
|
|
"golang.org/x/sys/windows/registry"
|
|
)
|
|
|
|
const (
|
|
FlagIISSiteOldExclude = "collector.iis.site-blacklist"
|
|
FlagIISSiteOldInclude = "collector.iis.site-whitelist"
|
|
FlagIISAppOldExclude = "collector.iis.app-blacklist"
|
|
FlagIISAppOldInclude = "collector.iis.app-whitelist"
|
|
|
|
FlagIISSiteExclude = "collector.iis.site-exclude"
|
|
FlagIISSiteInclude = "collector.iis.site-include"
|
|
FlagIISAppExclude = "collector.iis.app-exclude"
|
|
FlagIISAppInclude = "collector.iis.app-include"
|
|
)
|
|
|
|
var (
|
|
oldSiteInclude *string
|
|
oldSiteExclude *string
|
|
oldAppInclude *string
|
|
oldAppExclude *string
|
|
|
|
siteInclude *string
|
|
siteExclude *string
|
|
appInclude *string
|
|
appExclude *string
|
|
|
|
siteIncludeSet bool
|
|
siteExcludeSet bool
|
|
appIncludeSet bool
|
|
appExcludeSet bool
|
|
)
|
|
|
|
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.Warn("Couldn't open registry to determine IIS version:", err)
|
|
return simple_version{}
|
|
}
|
|
defer func() {
|
|
err = k.Close()
|
|
if err != nil {
|
|
log.Warnf("Failed to close registry key: %v", err)
|
|
}
|
|
}()
|
|
|
|
major, _, err := k.GetIntegerValue("MajorVersion")
|
|
if err != nil {
|
|
log.Warn("Couldn't open registry to determine IIS version:", err)
|
|
return simple_version{}
|
|
}
|
|
minor, _, err := k.GetIntegerValue("MinorVersion")
|
|
if err != nil {
|
|
log.Warn("Couldn't open registry to determine IIS version:", err)
|
|
return simple_version{}
|
|
}
|
|
|
|
log.Debugf("Detected IIS %d.%d\n", major, minor)
|
|
|
|
return simple_version{
|
|
major: major,
|
|
minor: minor,
|
|
}
|
|
}
|
|
|
|
type IISCollector struct {
|
|
// Web Service
|
|
CurrentAnonymousUsers *prometheus.Desc
|
|
CurrentBlockedAsyncIORequests *prometheus.Desc
|
|
CurrentCGIRequests *prometheus.Desc
|
|
CurrentConnections *prometheus.Desc
|
|
CurrentISAPIExtensionRequests *prometheus.Desc
|
|
CurrentNonAnonymousUsers *prometheus.Desc
|
|
ServiceUptime *prometheus.Desc
|
|
TotalBytesReceived *prometheus.Desc
|
|
TotalBytesSent *prometheus.Desc
|
|
TotalAnonymousUsers *prometheus.Desc
|
|
TotalBlockedAsyncIORequests *prometheus.Desc
|
|
TotalCGIRequests *prometheus.Desc
|
|
TotalConnectionAttemptsAllInstances *prometheus.Desc
|
|
TotalRequests *prometheus.Desc
|
|
TotalFilesReceived *prometheus.Desc
|
|
TotalFilesSent *prometheus.Desc
|
|
TotalISAPIExtensionRequests *prometheus.Desc
|
|
TotalLockedErrors *prometheus.Desc
|
|
TotalLogonAttempts *prometheus.Desc
|
|
TotalNonAnonymousUsers *prometheus.Desc
|
|
TotalNotFoundErrors *prometheus.Desc
|
|
TotalRejectedAsyncIORequests *prometheus.Desc
|
|
|
|
siteIncludePattern *regexp.Regexp
|
|
siteExcludePattern *regexp.Regexp
|
|
|
|
// APP_POOL_WAS
|
|
CurrentApplicationPoolState *prometheus.Desc
|
|
CurrentApplicationPoolUptime *prometheus.Desc
|
|
CurrentWorkerProcesses *prometheus.Desc
|
|
MaximumWorkerProcesses *prometheus.Desc
|
|
RecentWorkerProcessFailures *prometheus.Desc
|
|
TimeSinceLastWorkerProcessFailure *prometheus.Desc
|
|
TotalApplicationPoolRecycles *prometheus.Desc
|
|
TotalApplicationPoolUptime *prometheus.Desc
|
|
TotalWorkerProcessesCreated *prometheus.Desc
|
|
TotalWorkerProcessFailures *prometheus.Desc
|
|
TotalWorkerProcessPingFailures *prometheus.Desc
|
|
TotalWorkerProcessShutdownFailures *prometheus.Desc
|
|
TotalWorkerProcessStartupFailures *prometheus.Desc
|
|
|
|
// W3SVC_W3WP
|
|
Threads *prometheus.Desc
|
|
MaximumThreads *prometheus.Desc
|
|
|
|
RequestsTotal *prometheus.Desc
|
|
RequestsActive *prometheus.Desc
|
|
|
|
ActiveFlushedEntries *prometheus.Desc
|
|
|
|
CurrentFileCacheMemoryUsage *prometheus.Desc
|
|
MaximumFileCacheMemoryUsage *prometheus.Desc
|
|
FileCacheFlushesTotal *prometheus.Desc
|
|
FileCacheQueriesTotal *prometheus.Desc
|
|
FilesCachedMissesTotal *prometheus.Desc
|
|
FileCacheHitsTotal *prometheus.Desc
|
|
FilesCached *prometheus.Desc
|
|
FilesCachedTotal *prometheus.Desc
|
|
FilesFlushedTotal *prometheus.Desc
|
|
|
|
URICacheFlushesTotal *prometheus.Desc
|
|
URICacheQueriesTotal *prometheus.Desc
|
|
URICacheHitsTotal *prometheus.Desc
|
|
URICacheMissesTotal *prometheus.Desc
|
|
URIsCached *prometheus.Desc
|
|
URIsCachedTotal *prometheus.Desc
|
|
URIsFlushedTotal *prometheus.Desc
|
|
|
|
MetadataCached *prometheus.Desc
|
|
MetadataCacheFlushes *prometheus.Desc
|
|
MetadataCacheQueriesTotal *prometheus.Desc
|
|
MetadataCacheHitsTotal *prometheus.Desc
|
|
MetadataCacheMissesTotal *prometheus.Desc
|
|
MetadataCachedTotal *prometheus.Desc
|
|
MetadataFlushedTotal *prometheus.Desc
|
|
|
|
OutputCacheActiveFlushedItems *prometheus.Desc
|
|
OutputCacheItems *prometheus.Desc
|
|
OutputCacheMemoryUsage *prometheus.Desc
|
|
OutputCacheQueriesTotal *prometheus.Desc
|
|
OutputCacheHitsTotal *prometheus.Desc
|
|
OutputCacheMissesTotal *prometheus.Desc
|
|
OutputCacheFlushedItemsTotal *prometheus.Desc
|
|
OutputCacheFlushesTotal *prometheus.Desc
|
|
|
|
// IIS 8+ Only
|
|
RequestErrorsTotal *prometheus.Desc
|
|
WebSocketRequestsActive *prometheus.Desc
|
|
WebSocketConnectionAttempts *prometheus.Desc
|
|
WebSocketConnectionsAccepted *prometheus.Desc
|
|
WebSocketConnectionsRejected *prometheus.Desc
|
|
|
|
// Web Service Cache
|
|
ServiceCache_ActiveFlushedEntries *prometheus.Desc
|
|
|
|
ServiceCache_CurrentFileCacheMemoryUsage *prometheus.Desc
|
|
ServiceCache_MaximumFileCacheMemoryUsage *prometheus.Desc
|
|
ServiceCache_FileCacheFlushesTotal *prometheus.Desc
|
|
ServiceCache_FileCacheQueriesTotal *prometheus.Desc
|
|
ServiceCache_FileCacheHitsTotal *prometheus.Desc
|
|
ServiceCache_FilesCached *prometheus.Desc
|
|
ServiceCache_FilesCachedTotal *prometheus.Desc
|
|
ServiceCache_FilesFlushedTotal *prometheus.Desc
|
|
|
|
ServiceCache_URICacheFlushesTotal *prometheus.Desc
|
|
ServiceCache_URICacheQueriesTotal *prometheus.Desc
|
|
ServiceCache_URICacheHitsTotal *prometheus.Desc
|
|
ServiceCache_URIsCached *prometheus.Desc
|
|
ServiceCache_URIsCachedTotal *prometheus.Desc
|
|
ServiceCache_URIsFlushedTotal *prometheus.Desc
|
|
|
|
ServiceCache_MetadataCached *prometheus.Desc
|
|
ServiceCache_MetadataCacheFlushes *prometheus.Desc
|
|
ServiceCache_MetadataCacheQueriesTotal *prometheus.Desc
|
|
ServiceCache_MetadataCacheHitsTotal *prometheus.Desc
|
|
ServiceCache_MetadataCachedTotal *prometheus.Desc
|
|
ServiceCache_MetadataFlushedTotal *prometheus.Desc
|
|
|
|
ServiceCache_OutputCacheActiveFlushedItems *prometheus.Desc
|
|
ServiceCache_OutputCacheItems *prometheus.Desc
|
|
ServiceCache_OutputCacheMemoryUsage *prometheus.Desc
|
|
ServiceCache_OutputCacheQueriesTotal *prometheus.Desc
|
|
ServiceCache_OutputCacheHitsTotal *prometheus.Desc
|
|
ServiceCache_OutputCacheFlushedItemsTotal *prometheus.Desc
|
|
ServiceCache_OutputCacheFlushesTotal *prometheus.Desc
|
|
|
|
appIncludePattern *regexp.Regexp
|
|
appExcludePattern *regexp.Regexp
|
|
|
|
iis_version simple_version
|
|
}
|
|
|
|
func newIISCollectorFlags(app *kingpin.Application) {
|
|
oldSiteInclude = app.Flag(FlagIISSiteOldInclude, "DEPRECATED: Use --collector.iis.site-include").Default(".+").Hidden().String()
|
|
oldSiteExclude = app.Flag(FlagIISSiteOldExclude, "DEPRECATED: Use --collector.iis.site-exclude").Hidden().String()
|
|
oldAppInclude = app.Flag(FlagIISAppOldInclude, "DEPRECATED: Use --collector.iis.app-include").Hidden().String()
|
|
oldAppExclude = app.Flag(FlagIISAppOldExclude, "DEPRECATED: Use --collector.iis.app-exclude").Hidden().String()
|
|
|
|
siteInclude = app.Flag(
|
|
FlagIISSiteInclude,
|
|
"Regexp of sites to include. Site name must both match include and not match exclude to be included.",
|
|
).Default(".+").PreAction(func(c *kingpin.ParseContext) error {
|
|
siteIncludeSet = true
|
|
return nil
|
|
}).String()
|
|
|
|
siteExclude = app.Flag(
|
|
FlagIISSiteExclude,
|
|
"Regexp of sites to exclude. Site name must both match include and not match exclude to be included.",
|
|
).Default("").PreAction(func(c *kingpin.ParseContext) error {
|
|
siteExcludeSet = true
|
|
return nil
|
|
}).String()
|
|
|
|
appInclude = app.Flag(
|
|
FlagIISAppInclude,
|
|
"Regexp of apps to include. App name must both match include and not match exclude to be included.",
|
|
).Default(".+").PreAction(func(c *kingpin.ParseContext) error {
|
|
appIncludeSet = true
|
|
return nil
|
|
}).String()
|
|
|
|
appExclude = app.Flag(
|
|
FlagIISAppExclude,
|
|
"Regexp of apps to include. App name must both match include and not match exclude to be included.",
|
|
).Default("").PreAction(func(c *kingpin.ParseContext) error {
|
|
siteExcludeSet = true
|
|
return nil
|
|
}).String()
|
|
}
|
|
|
|
func newIISCollector() (Collector, error) {
|
|
if *oldSiteExclude != "" {
|
|
if !siteExcludeSet {
|
|
log.Warnln("msg", "--collector.iis.site-blacklist is DEPRECATED and will be removed in a future release, use --collector.iis.site-exclude")
|
|
*siteExclude = *oldSiteExclude
|
|
} else {
|
|
return nil, errors.New("--collector.iis.site-blacklist and --collector.iis.site-exclude are mutually exclusive")
|
|
}
|
|
}
|
|
if *oldSiteInclude != "" {
|
|
if !siteIncludeSet {
|
|
log.Warnln("msg", "--collector.iis.site-whitelist is DEPRECATED and will be removed in a future release, use --collector.iis.site-include")
|
|
*siteInclude = *oldSiteInclude
|
|
} else {
|
|
return nil, errors.New("--collector.iis.site-whitelist and --collector.iis.site-include are mutually exclusive")
|
|
}
|
|
}
|
|
|
|
if *oldAppExclude != "" {
|
|
if !appExcludeSet {
|
|
log.Warnln("msg", "--collector.iis.app-blacklist is DEPRECATED and will be removed in a future release, use --collector.iis.app-exclude")
|
|
*appExclude = *oldAppExclude
|
|
} else {
|
|
return nil, errors.New("--collector.iis.app-blacklist and --collector.iis.app-exclude are mutually exclusive")
|
|
}
|
|
}
|
|
if *oldAppInclude != "" {
|
|
if !appIncludeSet {
|
|
log.Warnln("msg", "--collector.iis.app-whitelist is DEPRECATED and will be removed in a future release, use --collector.iis.app-include")
|
|
*appInclude = *oldAppInclude
|
|
} else {
|
|
return nil, errors.New("--collector.iis.app-whitelist and --collector.iis.app-include are mutually exclusive")
|
|
}
|
|
}
|
|
|
|
const subsystem = "iis"
|
|
return &IISCollector{
|
|
iis_version: getIISVersion(),
|
|
|
|
siteIncludePattern: regexp.MustCompile(fmt.Sprintf("^(?:%s)$", *siteInclude)),
|
|
siteExcludePattern: regexp.MustCompile(fmt.Sprintf("^(?:%s)$", *siteExclude)),
|
|
appIncludePattern: regexp.MustCompile(fmt.Sprintf("^(?:%s)$", *appInclude)),
|
|
appExcludePattern: regexp.MustCompile(fmt.Sprintf("^(?:%s)$", *appExclude)),
|
|
|
|
// Web Service
|
|
CurrentAnonymousUsers: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "current_anonymous_users"),
|
|
"Number of users who currently have an anonymous connection using the Web service (WebService.CurrentAnonymousUsers)",
|
|
[]string{"site"},
|
|
nil,
|
|
),
|
|
CurrentBlockedAsyncIORequests: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "current_blocked_async_io_requests"),
|
|
"Current requests temporarily blocked due to bandwidth throttling settings (WebService.CurrentBlockedAsyncIORequests)",
|
|
[]string{"site"},
|
|
nil,
|
|
),
|
|
CurrentCGIRequests: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "current_cgi_requests"),
|
|
"Current number of CGI requests being simultaneously processed by the Web service (WebService.CurrentCGIRequests)",
|
|
[]string{"site"},
|
|
nil,
|
|
),
|
|
CurrentConnections: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "current_connections"),
|
|
"Current number of connections established with the Web service (WebService.CurrentConnections)",
|
|
[]string{"site"},
|
|
nil,
|
|
),
|
|
CurrentISAPIExtensionRequests: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "current_isapi_extension_requests"),
|
|
"Current number of ISAPI requests being simultaneously processed by the Web service (WebService.CurrentISAPIExtensionRequests)",
|
|
[]string{"site"},
|
|
nil,
|
|
),
|
|
CurrentNonAnonymousUsers: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "current_non_anonymous_users"),
|
|
"Number of users who currently have a non-anonymous connection using the Web service (WebService.CurrentNonAnonymousUsers)",
|
|
[]string{"site"},
|
|
nil,
|
|
),
|
|
ServiceUptime: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "service_uptime"),
|
|
"Number of seconds the WebService is up (WebService.ServiceUptime)",
|
|
[]string{"site"},
|
|
nil,
|
|
),
|
|
TotalBytesReceived: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "received_bytes_total"),
|
|
"Number of data bytes that have been received by the Web service (WebService.TotalBytesReceived)",
|
|
[]string{"site"},
|
|
nil,
|
|
),
|
|
TotalBytesSent: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "sent_bytes_total"),
|
|
"Number of data bytes that have been sent by the Web service (WebService.TotalBytesSent)",
|
|
[]string{"site"},
|
|
nil,
|
|
),
|
|
TotalAnonymousUsers: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "anonymous_users_total"),
|
|
"Total number of users who established an anonymous connection with the Web service (WebService.TotalAnonymousUsers)",
|
|
[]string{"site"},
|
|
nil,
|
|
),
|
|
TotalBlockedAsyncIORequests: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "blocked_async_io_requests_total"),
|
|
"Total requests temporarily blocked due to bandwidth throttling settings (WebService.TotalBlockedAsyncIORequests)",
|
|
[]string{"site"},
|
|
nil,
|
|
),
|
|
TotalCGIRequests: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "cgi_requests_total"),
|
|
"Total CGI requests is the total number of CGI requests (WebService.TotalCGIRequests)",
|
|
[]string{"site"},
|
|
nil,
|
|
),
|
|
TotalConnectionAttemptsAllInstances: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "connection_attempts_all_instances_total"),
|
|
"Number of connections that have been attempted using the Web service (WebService.TotalConnectionAttemptsAllInstances)",
|
|
[]string{"site"},
|
|
nil,
|
|
),
|
|
TotalRequests: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "requests_total"),
|
|
"Number of HTTP requests (WebService.TotalRequests)",
|
|
[]string{"site", "method"},
|
|
nil,
|
|
),
|
|
TotalFilesReceived: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "files_received_total"),
|
|
"Number of files received by the Web service (WebService.TotalFilesReceived)",
|
|
[]string{"site"},
|
|
nil,
|
|
),
|
|
TotalFilesSent: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "files_sent_total"),
|
|
"Number of files sent by the Web service (WebService.TotalFilesSent)",
|
|
[]string{"site"},
|
|
nil,
|
|
),
|
|
TotalISAPIExtensionRequests: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "ipapi_extension_requests_total"),
|
|
"ISAPI Extension Requests received (WebService.TotalISAPIExtensionRequests)",
|
|
[]string{"site"},
|
|
nil,
|
|
),
|
|
TotalLockedErrors: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "locked_errors_total"),
|
|
"Number of requests that couldn't be satisfied by the server because the requested resource was locked (WebService.TotalLockedErrors)",
|
|
[]string{"site"},
|
|
nil,
|
|
),
|
|
TotalLogonAttempts: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "logon_attempts_total"),
|
|
"Number of logons attempts to the Web Service (WebService.TotalLogonAttempts)",
|
|
[]string{"site"},
|
|
nil,
|
|
),
|
|
TotalNonAnonymousUsers: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "non_anonymous_users_total"),
|
|
"Number of users who established a non-anonymous connection with the Web service (WebService.TotalNonAnonymousUsers)",
|
|
[]string{"site"},
|
|
nil,
|
|
),
|
|
TotalNotFoundErrors: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "not_found_errors_total"),
|
|
"Number of requests that couldn't be satisfied by the server because the requested document could not be found (WebService.TotalNotFoundErrors)",
|
|
[]string{"site"},
|
|
nil,
|
|
),
|
|
TotalRejectedAsyncIORequests: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "rejected_async_io_requests_total"),
|
|
"Requests rejected due to bandwidth throttling settings (WebService.TotalRejectedAsyncIORequests)",
|
|
[]string{"site"},
|
|
nil,
|
|
),
|
|
|
|
// APP_POOL_WAS
|
|
CurrentApplicationPoolState: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "current_application_pool_state"),
|
|
"The current status of the application pool (1 - Uninitialized, 2 - Initialized, 3 - Running, 4 - Disabling, 5 - Disabled, 6 - Shutdown Pending, 7 - Delete Pending) (CurrentApplicationPoolState)",
|
|
[]string{"app", "state"},
|
|
nil,
|
|
),
|
|
CurrentApplicationPoolUptime: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "current_application_pool_start_time"),
|
|
"The unix timestamp for the application pool start time (CurrentApplicationPoolUptime)",
|
|
[]string{"app"},
|
|
nil,
|
|
),
|
|
CurrentWorkerProcesses: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "current_worker_processes"),
|
|
"The current number of worker processes that are running in the application pool (CurrentWorkerProcesses)",
|
|
[]string{"app"},
|
|
nil,
|
|
),
|
|
MaximumWorkerProcesses: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "maximum_worker_processes"),
|
|
"The maximum number of worker processes that have been created for the application pool since Windows Process Activation Service (WAS) started (MaximumWorkerProcesses)",
|
|
[]string{"app"},
|
|
nil,
|
|
),
|
|
RecentWorkerProcessFailures: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "recent_worker_process_failures"),
|
|
"The number of times that worker processes for the application pool failed during the rapid-fail protection interval (RecentWorkerProcessFailures)",
|
|
[]string{"app"},
|
|
nil,
|
|
),
|
|
TimeSinceLastWorkerProcessFailure: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "time_since_last_worker_process_failure"),
|
|
"The length of time, in seconds, since the last worker process failure occurred for the application pool (TimeSinceLastWorkerProcessFailure)",
|
|
[]string{"app"},
|
|
nil,
|
|
),
|
|
TotalApplicationPoolRecycles: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "total_application_pool_recycles"),
|
|
"The number of times that the application pool has been recycled since Windows Process Activation Service (WAS) started (TotalApplicationPoolRecycles)",
|
|
[]string{"app"},
|
|
nil,
|
|
),
|
|
TotalApplicationPoolUptime: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "total_application_pool_start_time"),
|
|
"The unix timestamp for the application pool of when the Windows Process Activation Service (WAS) started (TotalApplicationPoolUptime)",
|
|
[]string{"app"},
|
|
nil,
|
|
),
|
|
TotalWorkerProcessesCreated: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "total_worker_processes_created"),
|
|
"The number of worker processes created for the application pool since Windows Process Activation Service (WAS) started (TotalWorkerProcessesCreated)",
|
|
[]string{"app"},
|
|
nil,
|
|
),
|
|
TotalWorkerProcessFailures: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "total_worker_process_failures"),
|
|
"The number of times that worker processes have crashed since the application pool was started (TotalWorkerProcessFailures)",
|
|
[]string{"app"},
|
|
nil,
|
|
),
|
|
TotalWorkerProcessPingFailures: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "total_worker_process_ping_failures"),
|
|
"The number of times that Windows Process Activation Service (WAS) did not receive a response to ping messages sent to a worker process (TotalWorkerProcessPingFailures)",
|
|
[]string{"app"},
|
|
nil,
|
|
),
|
|
TotalWorkerProcessShutdownFailures: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "total_worker_process_shutdown_failures"),
|
|
"The number of times that Windows Process Activation Service (WAS) failed to shut down a worker process (TotalWorkerProcessShutdownFailures)",
|
|
[]string{"app"},
|
|
nil,
|
|
),
|
|
TotalWorkerProcessStartupFailures: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "total_worker_process_startup_failures"),
|
|
"The number of times that Windows Process Activation Service (WAS) failed to start a worker process (TotalWorkerProcessStartupFailures)",
|
|
[]string{"app"},
|
|
nil,
|
|
),
|
|
|
|
// W3SVC_W3WP
|
|
Threads: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "worker_threads"),
|
|
"Number of threads actively processing requests in the worker process",
|
|
[]string{"app", "pid", "state"},
|
|
nil,
|
|
),
|
|
MaximumThreads: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "worker_max_threads"),
|
|
"Maximum number of threads to which the thread pool can grow as needed",
|
|
[]string{"app", "pid"},
|
|
nil,
|
|
),
|
|
RequestsTotal: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "worker_requests_total"),
|
|
"Total number of HTTP requests served by the worker process",
|
|
[]string{"app", "pid"},
|
|
nil,
|
|
),
|
|
RequestsActive: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "worker_current_requests"),
|
|
"Current number of requests being processed by the worker process",
|
|
[]string{"app", "pid"},
|
|
nil,
|
|
),
|
|
ActiveFlushedEntries: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "worker_cache_active_flushed_entries"),
|
|
"Number of file handles cached in user-mode that will be closed when all current transfers complete.",
|
|
[]string{"app", "pid"},
|
|
nil,
|
|
),
|
|
CurrentFileCacheMemoryUsage: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "worker_file_cache_memory_bytes"),
|
|
"Current number of bytes used by user-mode file cache",
|
|
[]string{"app", "pid"},
|
|
nil,
|
|
),
|
|
MaximumFileCacheMemoryUsage: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "worker_file_cache_max_memory_bytes"),
|
|
"Maximum number of bytes used by user-mode file cache",
|
|
[]string{"app", "pid"},
|
|
nil,
|
|
),
|
|
FileCacheFlushesTotal: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "worker_file_cache_flushes_total"),
|
|
"Total number of files removed from the user-mode cache",
|
|
[]string{"app", "pid"},
|
|
nil,
|
|
),
|
|
FileCacheQueriesTotal: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "worker_file_cache_queries_total"),
|
|
"Total file cache queries (hits + misses)",
|
|
[]string{"app", "pid"},
|
|
nil,
|
|
),
|
|
FileCacheHitsTotal: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "worker_file_cache_hits_total"),
|
|
"Total number of successful lookups in the user-mode file cache",
|
|
[]string{"app", "pid"},
|
|
nil,
|
|
),
|
|
FilesCached: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "worker_file_cache_items"),
|
|
"Current number of files whose contents are present in user-mode cache",
|
|
[]string{"app", "pid"},
|
|
nil,
|
|
),
|
|
FilesCachedTotal: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "worker_file_cache_items_total"),
|
|
"Total number of files whose contents were ever added to the user-mode cache (since service startup)",
|
|
[]string{"app", "pid"},
|
|
nil,
|
|
),
|
|
FilesFlushedTotal: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "worker_file_cache_items_flushed_total"),
|
|
"Total number of file handles that have been removed from the user-mode cache (since service startup)",
|
|
[]string{"app", "pid"},
|
|
nil,
|
|
),
|
|
URICacheFlushesTotal: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "worker_uri_cache_flushes_total"),
|
|
"Total number of URI cache flushes (since service startup)",
|
|
[]string{"app", "pid"},
|
|
nil,
|
|
),
|
|
URICacheQueriesTotal: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "worker_uri_cache_queries_total"),
|
|
"Total number of uri cache queries (hits + misses)",
|
|
[]string{"app", "pid"},
|
|
nil,
|
|
),
|
|
URICacheHitsTotal: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "worker_uri_cache_hits_total"),
|
|
"Total number of successful lookups in the user-mode URI cache (since service startup)",
|
|
[]string{"app", "pid"},
|
|
nil,
|
|
),
|
|
URIsCached: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "worker_uri_cache_items"),
|
|
"Number of URI information blocks currently in the user-mode cache",
|
|
[]string{"app", "pid"},
|
|
nil,
|
|
),
|
|
URIsCachedTotal: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "worker_uri_cache_items_total"),
|
|
"Total number of URI information blocks added to the user-mode cache (since service startup)",
|
|
[]string{"app", "pid"},
|
|
nil,
|
|
),
|
|
URIsFlushedTotal: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "worker_uri_cache_items_flushed_total"),
|
|
"The number of URI information blocks that have been removed from the user-mode cache (since service startup)",
|
|
[]string{"app", "pid"},
|
|
nil,
|
|
),
|
|
MetadataCached: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "worker_metadata_cache_items"),
|
|
"Number of metadata information blocks currently present in user-mode cache",
|
|
[]string{"app", "pid"},
|
|
nil,
|
|
),
|
|
MetadataCacheFlushes: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "worker_metadata_cache_flushes_total"),
|
|
"Total number of user-mode metadata cache flushes (since service startup)",
|
|
[]string{"app", "pid"},
|
|
nil,
|
|
),
|
|
MetadataCacheQueriesTotal: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "worker_metadata_cache_queries_total"),
|
|
"Total metadata cache queries (hits + misses)",
|
|
[]string{"app", "pid"},
|
|
nil,
|
|
),
|
|
MetadataCacheHitsTotal: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "worker_metadata_cache_hits_total"),
|
|
"Total number of successful lookups in the user-mode metadata cache (since service startup)",
|
|
[]string{"app", "pid"},
|
|
nil,
|
|
),
|
|
MetadataCachedTotal: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "worker_metadata_cache_items_cached_total"),
|
|
"Total number of metadata information blocks added to the user-mode cache (since service startup)",
|
|
[]string{"app", "pid"},
|
|
nil,
|
|
),
|
|
MetadataFlushedTotal: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "worker_metadata_cache_items_flushed_total"),
|
|
"Total number of metadata information blocks removed from the user-mode cache (since service startup)",
|
|
[]string{"app", "pid"},
|
|
nil,
|
|
),
|
|
OutputCacheActiveFlushedItems: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "worker_output_cache_active_flushed_items"),
|
|
"",
|
|
[]string{"app", "pid"},
|
|
nil,
|
|
),
|
|
OutputCacheItems: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "worker_output_cache_items"),
|
|
"Number of items current present in output cache",
|
|
[]string{"app", "pid"},
|
|
nil,
|
|
),
|
|
OutputCacheMemoryUsage: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "worker_output_cache_memory_bytes"),
|
|
"Current number of bytes used by output cache",
|
|
[]string{"app", "pid"},
|
|
nil,
|
|
),
|
|
OutputCacheQueriesTotal: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "worker_output_queries_total"),
|
|
"Total number of output cache queries (hits + misses)",
|
|
[]string{"app", "pid"},
|
|
nil,
|
|
),
|
|
OutputCacheHitsTotal: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "worker_output_cache_hits_total"),
|
|
"Total number of successful lookups in output cache (since service startup)",
|
|
[]string{"app", "pid"},
|
|
nil,
|
|
),
|
|
OutputCacheFlushedItemsTotal: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "worker_output_cache_items_flushed_total"),
|
|
"Total number of items flushed from output cache (since service startup)",
|
|
[]string{"app", "pid"},
|
|
nil,
|
|
),
|
|
OutputCacheFlushesTotal: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "worker_output_cache_flushes_total"),
|
|
"Total number of flushes of output cache (since service startup)",
|
|
[]string{"app", "pid"},
|
|
nil,
|
|
),
|
|
// W3SVC_W3WP_IIS8
|
|
RequestErrorsTotal: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "worker_request_errors_total"),
|
|
"Total number of requests that returned an error",
|
|
[]string{"app", "pid", "status_code"},
|
|
nil,
|
|
),
|
|
WebSocketRequestsActive: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "worker_current_websocket_requests"),
|
|
"",
|
|
[]string{"app", "pid"},
|
|
nil,
|
|
),
|
|
WebSocketConnectionAttempts: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "worker_websocket_connection_attempts_total"),
|
|
"",
|
|
[]string{"app", "pid"},
|
|
nil,
|
|
),
|
|
WebSocketConnectionsAccepted: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "worker_websocket_connection_accepted_total"),
|
|
"",
|
|
[]string{"app", "pid"},
|
|
nil,
|
|
),
|
|
WebSocketConnectionsRejected: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "worker_websocket_connection_rejected_total"),
|
|
"",
|
|
[]string{"app", "pid"},
|
|
nil,
|
|
),
|
|
|
|
// Web Service Cache
|
|
ServiceCache_ActiveFlushedEntries: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "server_cache_active_flushed_entries"),
|
|
"Number of file handles cached that will be closed when all current transfers complete.",
|
|
nil,
|
|
nil,
|
|
),
|
|
ServiceCache_CurrentFileCacheMemoryUsage: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "server_file_cache_memory_bytes"),
|
|
"Current number of bytes used by file cache",
|
|
nil,
|
|
nil,
|
|
),
|
|
ServiceCache_MaximumFileCacheMemoryUsage: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "server_file_cache_max_memory_bytes"),
|
|
"Maximum number of bytes used by file cache",
|
|
nil,
|
|
nil,
|
|
),
|
|
ServiceCache_FileCacheFlushesTotal: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "server_file_cache_flushes_total"),
|
|
"Total number of file cache flushes (since service startup)",
|
|
nil,
|
|
nil,
|
|
),
|
|
ServiceCache_FileCacheQueriesTotal: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "server_file_cache_queries_total"),
|
|
"Total number of file cache queries (hits + misses)",
|
|
nil,
|
|
nil,
|
|
),
|
|
ServiceCache_FileCacheHitsTotal: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "server_file_cache_hits_total"),
|
|
"Total number of successful lookups in the user-mode file cache",
|
|
nil,
|
|
nil,
|
|
),
|
|
ServiceCache_FilesCached: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "server_file_cache_items"),
|
|
"Current number of files whose contents are present in cache",
|
|
nil,
|
|
nil,
|
|
),
|
|
ServiceCache_FilesCachedTotal: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "server_file_cache_items_total"),
|
|
"Total number of files whose contents were ever added to the cache (since service startup)",
|
|
nil,
|
|
nil,
|
|
),
|
|
ServiceCache_FilesFlushedTotal: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "server_file_cache_items_flushed_total"),
|
|
"Total number of file handles that have been removed from the cache (since service startup)",
|
|
nil,
|
|
nil,
|
|
),
|
|
ServiceCache_URICacheFlushesTotal: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "server_uri_cache_flushes_total"),
|
|
"Total number of URI cache flushes (since service startup)",
|
|
[]string{"mode"},
|
|
nil,
|
|
),
|
|
ServiceCache_URICacheQueriesTotal: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "server_uri_cache_queries_total"),
|
|
"Total number of uri cache queries (hits + misses)",
|
|
[]string{"mode"},
|
|
nil,
|
|
),
|
|
ServiceCache_URICacheHitsTotal: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "server_uri_cache_hits_total"),
|
|
"Total number of successful lookups in the URI cache (since service startup)",
|
|
[]string{"mode"},
|
|
nil,
|
|
),
|
|
ServiceCache_URIsCached: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "server_uri_cache_items"),
|
|
"Number of URI information blocks currently in the cache",
|
|
[]string{"mode"},
|
|
nil,
|
|
),
|
|
ServiceCache_URIsCachedTotal: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "server_uri_cache_items_total"),
|
|
"Total number of URI information blocks added to the cache (since service startup)",
|
|
[]string{"mode"},
|
|
nil,
|
|
),
|
|
ServiceCache_URIsFlushedTotal: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "server_uri_cache_items_flushed_total"),
|
|
"The number of URI information blocks that have been removed from the cache (since service startup)",
|
|
[]string{"mode"},
|
|
nil,
|
|
),
|
|
ServiceCache_MetadataCached: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "server_metadata_cache_items"),
|
|
"Number of metadata information blocks currently present in cache",
|
|
nil,
|
|
nil,
|
|
),
|
|
ServiceCache_MetadataCacheFlushes: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "server_metadata_cache_flushes_total"),
|
|
"Total number of metadata cache flushes (since service startup)",
|
|
nil,
|
|
nil,
|
|
),
|
|
ServiceCache_MetadataCacheQueriesTotal: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "server_metadata_cache_queries_total"),
|
|
"Total metadata cache queries (hits + misses)",
|
|
nil,
|
|
nil,
|
|
),
|
|
ServiceCache_MetadataCacheHitsTotal: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "server_metadata_cache_hits_total"),
|
|
"Total number of successful lookups in the metadata cache (since service startup)",
|
|
nil,
|
|
nil,
|
|
),
|
|
ServiceCache_MetadataCachedTotal: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "server_metadata_cache_items_cached_total"),
|
|
"Total number of metadata information blocks added to the cache (since service startup)",
|
|
nil,
|
|
nil,
|
|
),
|
|
ServiceCache_MetadataFlushedTotal: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "server_metadata_cache_items_flushed_total"),
|
|
"Total number of metadata information blocks removed from the cache (since service startup)",
|
|
nil,
|
|
nil,
|
|
),
|
|
ServiceCache_OutputCacheActiveFlushedItems: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "server_output_cache_active_flushed_items"),
|
|
"",
|
|
nil,
|
|
nil,
|
|
),
|
|
ServiceCache_OutputCacheItems: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "server_output_cache_items"),
|
|
"Number of items current present in output cache",
|
|
nil,
|
|
nil,
|
|
),
|
|
ServiceCache_OutputCacheMemoryUsage: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "server_output_cache_memory_bytes"),
|
|
"Current number of bytes used by output cache",
|
|
nil,
|
|
nil,
|
|
),
|
|
ServiceCache_OutputCacheQueriesTotal: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "server_output_cache_queries_total"),
|
|
"Total output cache queries (hits + misses)",
|
|
nil,
|
|
nil,
|
|
),
|
|
ServiceCache_OutputCacheHitsTotal: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "server_output_cache_hits_total"),
|
|
"Total number of successful lookups in output cache (since service startup)",
|
|
nil,
|
|
nil,
|
|
),
|
|
ServiceCache_OutputCacheFlushedItemsTotal: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "server_output_cache_items_flushed_total"),
|
|
"Total number of items flushed from output cache (since service startup)",
|
|
nil,
|
|
nil,
|
|
),
|
|
ServiceCache_OutputCacheFlushesTotal: prometheus.NewDesc(
|
|
prometheus.BuildFQName(Namespace, subsystem, "server_output_cache_flushes_total"),
|
|
"Total number of flushes of output cache (since service startup)",
|
|
nil,
|
|
nil,
|
|
),
|
|
}, nil
|
|
}
|
|
|
|
// Collect sends the metric values for each metric
|
|
// to the provided prometheus Metric channel.
|
|
func (c *IISCollector) Collect(ctx *ScrapeContext, ch chan<- prometheus.Metric) error {
|
|
if desc, err := c.collectWebService(ctx, ch); err != nil {
|
|
log.Error("failed collecting iis metrics:", desc, err)
|
|
return err
|
|
}
|
|
|
|
if desc, err := c.collectAPP_POOL_WAS(ctx, ch); err != nil {
|
|
log.Error("failed collecting iis metrics:", desc, err)
|
|
return err
|
|
}
|
|
|
|
if desc, err := c.collectW3SVC_W3WP(ctx, ch); err != nil {
|
|
log.Error("failed collecting iis metrics:", desc, err)
|
|
return err
|
|
}
|
|
|
|
if desc, err := c.collectWebServiceCache(ctx, ch); err != nil {
|
|
log.Error("failed collecting iis metrics:", desc, err)
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type perflibWebService struct {
|
|
Name string
|
|
|
|
CurrentAnonymousUsers float64 `perflib:"Current Anonymous Users"`
|
|
CurrentBlockedAsyncIORequests float64 `perflib:"Current Blocked Async I/O Requests"`
|
|
CurrentCGIRequests float64 `perflib:"Current CGI Requests"`
|
|
CurrentConnections float64 `perflib:"Current Connections"`
|
|
CurrentISAPIExtensionRequests float64 `perflib:"Current ISAPI Extension Requests"`
|
|
CurrentNonAnonymousUsers float64 `perflib:"Current NonAnonymous Users"`
|
|
ServiceUptime float64 `perflib:"Service Uptime"`
|
|
|
|
TotalBytesReceived float64 `perflib:"Total Bytes Received"`
|
|
TotalBytesSent float64 `perflib:"Total Bytes Sent"`
|
|
TotalAnonymousUsers float64 `perflib:"Total Anonymous Users"`
|
|
TotalBlockedAsyncIORequests float64 `perflib:"Total Blocked Async I/O Requests"`
|
|
TotalCGIRequests float64 `perflib:"Total CGI Requests"`
|
|
TotalConnectionAttemptsAllInstances float64 `perflib:"Total Connection Attempts (all instances)"`
|
|
TotalFilesReceived float64 `perflib:"Total Files Received"`
|
|
TotalFilesSent float64 `perflib:"Total Files Sent"`
|
|
TotalISAPIExtensionRequests float64 `perflib:"Total ISAPI Extension Requests"`
|
|
TotalLockedErrors float64 `perflib:"Total Locked Errors"`
|
|
TotalLogonAttempts float64 `perflib:"Total Logon Attempts"`
|
|
TotalNonAnonymousUsers float64 `perflib:"Total NonAnonymous Users"`
|
|
TotalNotFoundErrors float64 `perflib:"Total Not Found Errors"`
|
|
TotalRejectedAsyncIORequests float64 `perflib:"Total Rejected Async I/O Requests"`
|
|
TotalCopyRequests float64 `perflib:"Total Copy Requests"`
|
|
TotalDeleteRequests float64 `perflib:"Total Delete Requests"`
|
|
TotalGetRequests float64 `perflib:"Total Get Requests"`
|
|
TotalHeadRequests float64 `perflib:"Total Head Requests"`
|
|
TotalLockRequests float64 `perflib:"Total Lock Requests"`
|
|
TotalMkcolRequests float64 `perflib:"Total Mkcol Requests"`
|
|
TotalMoveRequests float64 `perflib:"Total Move Requests"`
|
|
TotalOptionsRequests float64 `perflib:"Total Options Requests"`
|
|
TotalOtherRequests float64 `perflib:"Total Other Request Methods"`
|
|
TotalPostRequests float64 `perflib:"Total Post Requests"`
|
|
TotalPropfindRequests float64 `perflib:"Total Propfind Requests"`
|
|
TotalProppatchRequests float64 `perflib:"Total Proppatch Requests"`
|
|
TotalPutRequests float64 `perflib:"Total Put Requests"`
|
|
TotalSearchRequests float64 `perflib:"Total Search Requests"`
|
|
TotalTraceRequests float64 `perflib:"Total Trace Requests"`
|
|
TotalUnlockRequests float64 `perflib:"Total Unlock Requests"`
|
|
}
|
|
|
|
// Fulfill the hasGetIISName interface
|
|
func (p perflibWebService) getIISName() string {
|
|
return p.Name
|
|
}
|
|
|
|
// Fulfill the hasGetIISName interface
|
|
func (p perflibAPP_POOL_WAS) getIISName() string {
|
|
return p.Name
|
|
}
|
|
|
|
// Fulfill the hasGetIISName interface
|
|
func (p perflibW3SVC_W3WP) getIISName() string {
|
|
return p.Name
|
|
}
|
|
|
|
// Fulfill the hasGetIISName interface
|
|
func (p perflibW3SVC_W3WP_IIS8) getIISName() string {
|
|
return p.Name
|
|
}
|
|
|
|
// Required as Golang doesn't allow access to struct fields in generic functions. That restriction may be removed in a future release.
|
|
type hasGetIISName interface {
|
|
getIISName() string
|
|
}
|
|
|
|
// Deduplicate IIS site names from various IIS perflib objects.
|
|
//
|
|
// E.G. Given the following list of site names, "Site_B" would be
|
|
// discarded, and "Site_B#2" would be kept and presented as "Site_B" in the
|
|
// collector metrics.
|
|
// [ "Site_A", "Site_B", "Site_C", "Site_B#2" ]
|
|
func dedupIISNames[V hasGetIISName](services []V) map[string]V {
|
|
// Ensure IIS entry with the highest suffix occurs last
|
|
sort.SliceStable(services, func(i, j int) bool {
|
|
return services[i].getIISName() < services[j].getIISName()
|
|
})
|
|
|
|
var webServiceDeDuplicated = make(map[string]V)
|
|
|
|
// Use map to deduplicate IIS entries
|
|
for _, entry := range services {
|
|
name := strings.Split(entry.getIISName(), "#")[0]
|
|
webServiceDeDuplicated[name] = entry
|
|
}
|
|
return webServiceDeDuplicated
|
|
}
|
|
|
|
func (c *IISCollector) collectWebService(ctx *ScrapeContext, ch chan<- prometheus.Metric) (*prometheus.Desc, error) {
|
|
var webService []perflibWebService
|
|
if err := unmarshalObject(ctx.perfObjects["Web Service"], &webService); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
webServiceDeDuplicated := dedupIISNames(webService)
|
|
|
|
for name, app := range webServiceDeDuplicated {
|
|
if name == "_Total" || c.siteExcludePattern.MatchString(name) || !c.siteIncludePattern.MatchString(name) {
|
|
continue
|
|
}
|
|
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.CurrentAnonymousUsers,
|
|
prometheus.GaugeValue,
|
|
app.CurrentAnonymousUsers,
|
|
name,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.CurrentBlockedAsyncIORequests,
|
|
prometheus.GaugeValue,
|
|
app.CurrentBlockedAsyncIORequests,
|
|
name,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.CurrentCGIRequests,
|
|
prometheus.GaugeValue,
|
|
app.CurrentCGIRequests,
|
|
name,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.CurrentConnections,
|
|
prometheus.GaugeValue,
|
|
app.CurrentConnections,
|
|
name,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.CurrentISAPIExtensionRequests,
|
|
prometheus.GaugeValue,
|
|
app.CurrentISAPIExtensionRequests,
|
|
name,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.CurrentNonAnonymousUsers,
|
|
prometheus.GaugeValue,
|
|
app.CurrentNonAnonymousUsers,
|
|
name,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.ServiceUptime,
|
|
prometheus.GaugeValue,
|
|
app.ServiceUptime,
|
|
name,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.TotalBytesReceived,
|
|
prometheus.CounterValue,
|
|
app.TotalBytesReceived,
|
|
name,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.TotalBytesSent,
|
|
prometheus.CounterValue,
|
|
app.TotalBytesSent,
|
|
name,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.TotalAnonymousUsers,
|
|
prometheus.CounterValue,
|
|
app.TotalAnonymousUsers,
|
|
name,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.TotalBlockedAsyncIORequests,
|
|
prometheus.CounterValue,
|
|
app.TotalBlockedAsyncIORequests,
|
|
name,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.TotalCGIRequests,
|
|
prometheus.CounterValue,
|
|
app.TotalCGIRequests,
|
|
name,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.TotalConnectionAttemptsAllInstances,
|
|
prometheus.CounterValue,
|
|
app.TotalConnectionAttemptsAllInstances,
|
|
name,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.TotalFilesReceived,
|
|
prometheus.CounterValue,
|
|
app.TotalFilesReceived,
|
|
name,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.TotalFilesSent,
|
|
prometheus.CounterValue,
|
|
app.TotalFilesSent,
|
|
name,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.TotalISAPIExtensionRequests,
|
|
prometheus.CounterValue,
|
|
app.TotalISAPIExtensionRequests,
|
|
name,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.TotalLockedErrors,
|
|
prometheus.CounterValue,
|
|
app.TotalLockedErrors,
|
|
name,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.TotalLogonAttempts,
|
|
prometheus.CounterValue,
|
|
app.TotalLogonAttempts,
|
|
name,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.TotalNonAnonymousUsers,
|
|
prometheus.CounterValue,
|
|
app.TotalNonAnonymousUsers,
|
|
name,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.TotalNotFoundErrors,
|
|
prometheus.CounterValue,
|
|
app.TotalNotFoundErrors,
|
|
name,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.TotalRejectedAsyncIORequests,
|
|
prometheus.CounterValue,
|
|
app.TotalRejectedAsyncIORequests,
|
|
name,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.TotalRequests,
|
|
prometheus.CounterValue,
|
|
app.TotalOtherRequests,
|
|
name,
|
|
"other",
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.TotalRequests,
|
|
prometheus.CounterValue,
|
|
app.TotalCopyRequests,
|
|
name,
|
|
"COPY",
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.TotalRequests,
|
|
prometheus.CounterValue,
|
|
app.TotalDeleteRequests,
|
|
name,
|
|
"DELETE",
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.TotalRequests,
|
|
prometheus.CounterValue,
|
|
app.TotalGetRequests,
|
|
name,
|
|
"GET",
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.TotalRequests,
|
|
prometheus.CounterValue,
|
|
app.TotalHeadRequests,
|
|
name,
|
|
"HEAD",
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.TotalRequests,
|
|
prometheus.CounterValue,
|
|
app.TotalLockRequests,
|
|
name,
|
|
"LOCK",
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.TotalRequests,
|
|
prometheus.CounterValue,
|
|
app.TotalMkcolRequests,
|
|
name,
|
|
"MKCOL",
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.TotalRequests,
|
|
prometheus.CounterValue,
|
|
app.TotalMoveRequests,
|
|
name,
|
|
"MOVE",
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.TotalRequests,
|
|
prometheus.CounterValue,
|
|
app.TotalOptionsRequests,
|
|
name,
|
|
"OPTIONS",
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.TotalRequests,
|
|
prometheus.CounterValue,
|
|
app.TotalPostRequests,
|
|
name,
|
|
"POST",
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.TotalRequests,
|
|
prometheus.CounterValue,
|
|
app.TotalPropfindRequests,
|
|
name,
|
|
"PROPFIND",
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.TotalRequests,
|
|
prometheus.CounterValue,
|
|
app.TotalProppatchRequests,
|
|
name,
|
|
"PROPPATCH",
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.TotalRequests,
|
|
prometheus.CounterValue,
|
|
app.TotalPutRequests,
|
|
name,
|
|
"PUT",
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.TotalRequests,
|
|
prometheus.CounterValue,
|
|
app.TotalSearchRequests,
|
|
name,
|
|
"SEARCH",
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.TotalRequests,
|
|
prometheus.CounterValue,
|
|
app.TotalTraceRequests,
|
|
name,
|
|
"TRACE",
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.TotalRequests,
|
|
prometheus.CounterValue,
|
|
app.TotalUnlockRequests,
|
|
name,
|
|
"UNLOCK",
|
|
)
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
type perflibAPP_POOL_WAS struct {
|
|
Name string
|
|
Frequency_Object uint64
|
|
Timestamp_Object uint64
|
|
|
|
CurrentApplicationPoolState float64 `perflib:"Current Application Pool State"`
|
|
CurrentApplicationPoolUptime float64 `perflib:"Current Application Pool Uptime"`
|
|
CurrentWorkerProcesses float64 `perflib:"Current Worker Processes"`
|
|
MaximumWorkerProcesses float64 `perflib:"Maximum Worker Processes"`
|
|
RecentWorkerProcessFailures float64 `perflib:"Recent Worker Process Failures"`
|
|
TimeSinceLastWorkerProcessFailure float64 `perflib:"Time Since Last Worker Process Failure"`
|
|
TotalApplicationPoolRecycles float64 `perflib:"Total Application Pool Recycles"`
|
|
TotalApplicationPoolUptime float64 `perflib:"Total Application Pool Uptime"`
|
|
TotalWorkerProcessesCreated float64 `perflib:"Total Worker Processes Created"`
|
|
TotalWorkerProcessFailures float64 `perflib:"Total Worker Process Failures"`
|
|
TotalWorkerProcessPingFailures float64 `perflib:"Total Worker Process Ping Failures"`
|
|
TotalWorkerProcessShutdownFailures float64 `perflib:"Total Worker Process Shutdown Failures"`
|
|
TotalWorkerProcessStartupFailures float64 `perflib:"Total Worker Process Startup Failures"`
|
|
}
|
|
|
|
var applicationStates = map[uint32]string{
|
|
1: "Uninitialized",
|
|
2: "Initialized",
|
|
3: "Running",
|
|
4: "Disabling",
|
|
5: "Disabled",
|
|
6: "Shutdown Pending",
|
|
7: "Delete Pending",
|
|
}
|
|
|
|
func (c *IISCollector) collectAPP_POOL_WAS(ctx *ScrapeContext, ch chan<- prometheus.Metric) (*prometheus.Desc, error) {
|
|
var APP_POOL_WAS []perflibAPP_POOL_WAS
|
|
if err := unmarshalObject(ctx.perfObjects["APP_POOL_WAS"], &APP_POOL_WAS); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
appPoolDeDuplicated := dedupIISNames(APP_POOL_WAS)
|
|
|
|
for name, app := range appPoolDeDuplicated {
|
|
if name == "_Total" ||
|
|
c.appExcludePattern.MatchString(name) ||
|
|
!c.appIncludePattern.MatchString(name) {
|
|
continue
|
|
}
|
|
|
|
for key, label := range applicationStates {
|
|
isCurrentState := 0.0
|
|
if key == uint32(app.CurrentApplicationPoolState) {
|
|
isCurrentState = 1.0
|
|
}
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.CurrentApplicationPoolState,
|
|
prometheus.GaugeValue,
|
|
isCurrentState,
|
|
name,
|
|
label,
|
|
)
|
|
}
|
|
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.CurrentApplicationPoolUptime,
|
|
prometheus.GaugeValue,
|
|
app.CurrentApplicationPoolUptime,
|
|
name,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.CurrentWorkerProcesses,
|
|
prometheus.GaugeValue,
|
|
app.CurrentWorkerProcesses,
|
|
name,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.MaximumWorkerProcesses,
|
|
prometheus.GaugeValue,
|
|
app.MaximumWorkerProcesses,
|
|
name,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.RecentWorkerProcessFailures,
|
|
prometheus.GaugeValue,
|
|
app.RecentWorkerProcessFailures,
|
|
name,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.TimeSinceLastWorkerProcessFailure,
|
|
prometheus.GaugeValue,
|
|
app.TimeSinceLastWorkerProcessFailure,
|
|
name,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.TotalApplicationPoolRecycles,
|
|
prometheus.CounterValue,
|
|
app.TotalApplicationPoolRecycles,
|
|
name,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.TotalApplicationPoolUptime,
|
|
prometheus.CounterValue,
|
|
app.TotalApplicationPoolUptime,
|
|
name,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.TotalWorkerProcessesCreated,
|
|
prometheus.CounterValue,
|
|
app.TotalWorkerProcessesCreated,
|
|
name,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.TotalWorkerProcessFailures,
|
|
prometheus.CounterValue,
|
|
app.TotalWorkerProcessFailures,
|
|
name,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.TotalWorkerProcessPingFailures,
|
|
prometheus.CounterValue,
|
|
app.TotalWorkerProcessPingFailures,
|
|
name,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.TotalWorkerProcessShutdownFailures,
|
|
prometheus.CounterValue,
|
|
app.TotalWorkerProcessShutdownFailures,
|
|
name,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.TotalWorkerProcessStartupFailures,
|
|
prometheus.CounterValue,
|
|
app.TotalWorkerProcessStartupFailures,
|
|
name,
|
|
)
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
var workerProcessNameExtractor = regexp.MustCompile(`^(\d+)_(.+)$`)
|
|
|
|
type perflibW3SVC_W3WP struct {
|
|
Name string
|
|
|
|
Threads float64 `perflib:"Active Threads Count"`
|
|
MaximumThreads float64 `perflib:"Maximum Threads Count"`
|
|
|
|
RequestsTotal float64 `perflib:"Total HTTP Requests Served"`
|
|
RequestsActive float64 `perflib:"Active Requests"`
|
|
|
|
ActiveFlushedEntries float64 `perflib:"Active Flushed Entries"`
|
|
|
|
CurrentFileCacheMemoryUsage float64 `perflib:"Current File Cache Memory Usage"`
|
|
MaximumFileCacheMemoryUsage float64 `perflib:"Maximum File Cache Memory Usage"`
|
|
FileCacheFlushesTotal float64 `perflib:"File Cache Flushes"`
|
|
FileCacheHitsTotal float64 `perflib:"File Cache Hits"`
|
|
FileCacheMissesTotal float64 `perflib:"File Cache Misses"`
|
|
FilesCached float64 `perflib:"Current Files Cached"`
|
|
FilesCachedTotal float64 `perflib:"Total Files Cached"`
|
|
FilesFlushedTotal float64 `perflib:"Total Flushed Files"`
|
|
FileCacheQueriesTotal float64
|
|
|
|
URICacheFlushesTotal float64 `perflib:"Total Flushed URIs"`
|
|
URICacheFlushesTotalKernel float64 `perflib:"Total Flushed URIs"`
|
|
URIsFlushedTotalKernel float64 `perflib:"Kernel\: Total Flushed URIs"` //nolint:govet
|
|
URICacheHitsTotal float64 `perflib:"URI Cache Hits"`
|
|
URICacheHitsTotalKernel float64 `perflib:"Kernel\: URI Cache Hits"` //nolint:govet
|
|
URICacheMissesTotal float64 `perflib:"URI Cache Misses"`
|
|
URICacheMissesTotalKernel float64 `perflib:"Kernel\: URI Cache Misses"` //nolint:govet
|
|
URIsCached float64 `perflib:"Current URIs Cached"`
|
|
URIsCachedKernel float64 `perflib:"Kernel\: Current URIs Cached"` //nolint:govet
|
|
URIsCachedTotal float64 `perflib:"Total URIs Cached"`
|
|
URIsCachedTotalKernel float64 `perflib:"Total URIs Cached"`
|
|
URIsFlushedTotal float64 `perflib:"Total Flushed URIs"`
|
|
URICacheQueriesTotal float64
|
|
|
|
MetaDataCacheHits float64 `perflib:"Metadata Cache Hits"`
|
|
MetaDataCacheMisses float64 `perflib:"Metadata Cache Misses"`
|
|
MetadataCached float64 `perflib:"Current Metadata Cached"`
|
|
MetadataCacheFlushes float64 `perflib:"Metadata Cache Flushes"`
|
|
MetadataCachedTotal float64 `perflib:"Total Metadata Cached"`
|
|
MetadataFlushedTotal float64 `perflib:"Total Flushed Metadata"`
|
|
MetadataCacheQueriesTotal float64
|
|
|
|
OutputCacheActiveFlushedItems float64 `perflib:"Output Cache Current Flushed Items"`
|
|
OutputCacheItems float64 `perflib:"Output Cache Current Items"`
|
|
OutputCacheMemoryUsage float64 `perflib:"Output Cache Current Memory Usage"`
|
|
OutputCacheHitsTotal float64 `perflib:"Output Cache Total Hits"`
|
|
OutputCacheMissesTotal float64 `perflib:"Output Cache Total Misses"`
|
|
OutputCacheFlushedItemsTotal float64 `perflib:"Output Cache Total Flushed Items"`
|
|
OutputCacheFlushesTotal float64 `perflib:"Output Cache Total Flushes"`
|
|
OutputCacheQueriesTotal float64
|
|
}
|
|
|
|
type perflibW3SVC_W3WP_IIS8 struct {
|
|
Name string
|
|
|
|
RequestErrorsTotal float64
|
|
RequestErrors500 float64 `perflib:"% 500 HTTP Response Sent"`
|
|
RequestErrors503 float64 `perflib:"% 503 HTTP Response Sent"`
|
|
RequestErrors404 float64 `perflib:"% 404 HTTP Response Sent"`
|
|
RequestErrors403 float64 `perflib:"% 403 HTTP Response Sent"`
|
|
RequestErrors401 float64 `perflib:"% 401 HTTP Response Sent"`
|
|
|
|
WebSocketRequestsActive float64 `perflib:"WebSocket Active Requests"`
|
|
WebSocketConnectionAttempts float64 `perflib:"WebSocket Connection Attempts / Sec"`
|
|
WebSocketConnectionsAccepted float64 `perflib:"WebSocket Connections Accepted / Sec"`
|
|
WebSocketConnectionsRejected float64 `perflib:"WebSocket Connections Rejected / Sec"`
|
|
}
|
|
|
|
func (c *IISCollector) collectW3SVC_W3WP(ctx *ScrapeContext, ch chan<- prometheus.Metric) (*prometheus.Desc, error) {
|
|
var W3SVC_W3WP []perflibW3SVC_W3WP
|
|
if err := unmarshalObject(ctx.perfObjects["W3SVC_W3WP"], &W3SVC_W3WP); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
w3svcW3WPDeduplicated := dedupIISNames(W3SVC_W3WP)
|
|
|
|
for w3Name, app := range w3svcW3WPDeduplicated {
|
|
// Extract the apppool name from the format <PID>_<NAME>
|
|
pid := workerProcessNameExtractor.ReplaceAllString(w3Name, "$1")
|
|
name := workerProcessNameExtractor.ReplaceAllString(w3Name, "$2")
|
|
if name == "" || name == "_Total" ||
|
|
c.appExcludePattern.MatchString(name) ||
|
|
!c.appIncludePattern.MatchString(name) {
|
|
continue
|
|
}
|
|
|
|
// Duplicate instances are suffixed # with an index number. These should be ignored
|
|
if strings.Contains(app.Name, "#") {
|
|
continue
|
|
}
|
|
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.Threads,
|
|
prometheus.GaugeValue,
|
|
app.Threads,
|
|
name,
|
|
pid,
|
|
"busy",
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.Threads,
|
|
prometheus.GaugeValue,
|
|
app.Threads,
|
|
name,
|
|
pid,
|
|
"idle",
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.MaximumThreads,
|
|
prometheus.CounterValue,
|
|
app.MaximumThreads,
|
|
name,
|
|
pid,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.RequestsTotal,
|
|
prometheus.CounterValue,
|
|
app.RequestsTotal,
|
|
name,
|
|
pid,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.RequestsActive,
|
|
prometheus.CounterValue,
|
|
app.RequestsActive,
|
|
name,
|
|
pid,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.ActiveFlushedEntries,
|
|
prometheus.GaugeValue,
|
|
app.ActiveFlushedEntries,
|
|
name,
|
|
pid,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.CurrentFileCacheMemoryUsage,
|
|
prometheus.GaugeValue,
|
|
app.CurrentFileCacheMemoryUsage,
|
|
name,
|
|
pid,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.MaximumFileCacheMemoryUsage,
|
|
prometheus.CounterValue,
|
|
app.MaximumFileCacheMemoryUsage,
|
|
name,
|
|
pid,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.FileCacheFlushesTotal,
|
|
prometheus.CounterValue,
|
|
app.FileCacheFlushesTotal,
|
|
name,
|
|
pid,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.FileCacheQueriesTotal,
|
|
prometheus.CounterValue,
|
|
app.FileCacheHitsTotal+app.FileCacheMissesTotal,
|
|
name,
|
|
pid,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.FileCacheHitsTotal,
|
|
prometheus.CounterValue,
|
|
app.FileCacheHitsTotal,
|
|
name,
|
|
pid,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.FilesCached,
|
|
prometheus.GaugeValue,
|
|
app.FilesCached,
|
|
name,
|
|
pid,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.FilesCachedTotal,
|
|
prometheus.CounterValue,
|
|
app.FilesCachedTotal,
|
|
name,
|
|
pid,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.FilesFlushedTotal,
|
|
prometheus.CounterValue,
|
|
app.FilesFlushedTotal,
|
|
name,
|
|
pid,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.URICacheFlushesTotal,
|
|
prometheus.CounterValue,
|
|
app.URICacheFlushesTotal,
|
|
name,
|
|
pid,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.URICacheQueriesTotal,
|
|
prometheus.CounterValue,
|
|
app.URICacheHitsTotal+app.URICacheMissesTotal,
|
|
name,
|
|
pid,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.URICacheHitsTotal,
|
|
prometheus.CounterValue,
|
|
app.URICacheHitsTotal,
|
|
name,
|
|
pid,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.URIsCached,
|
|
prometheus.GaugeValue,
|
|
app.URIsCached,
|
|
name,
|
|
pid,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.URIsCachedTotal,
|
|
prometheus.CounterValue,
|
|
app.URIsCachedTotal,
|
|
name,
|
|
pid,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.URIsFlushedTotal,
|
|
prometheus.CounterValue,
|
|
app.URIsFlushedTotal,
|
|
name,
|
|
pid,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.MetadataCached,
|
|
prometheus.GaugeValue,
|
|
app.MetadataCached,
|
|
name,
|
|
pid,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.MetadataCacheFlushes,
|
|
prometheus.CounterValue,
|
|
app.MetadataCacheFlushes,
|
|
name,
|
|
pid,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.MetadataCacheQueriesTotal,
|
|
prometheus.CounterValue,
|
|
app.MetaDataCacheHits+app.MetaDataCacheMisses,
|
|
name,
|
|
pid,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.MetadataCacheHitsTotal,
|
|
prometheus.CounterValue,
|
|
app.MetaDataCacheHits,
|
|
name,
|
|
pid,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.MetadataCachedTotal,
|
|
prometheus.CounterValue,
|
|
app.MetadataCachedTotal,
|
|
name,
|
|
pid,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.MetadataFlushedTotal,
|
|
prometheus.CounterValue,
|
|
app.MetadataFlushedTotal,
|
|
name,
|
|
pid,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.OutputCacheActiveFlushedItems,
|
|
prometheus.CounterValue,
|
|
app.OutputCacheActiveFlushedItems,
|
|
name,
|
|
pid,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.OutputCacheItems,
|
|
prometheus.CounterValue,
|
|
app.OutputCacheItems,
|
|
name,
|
|
pid,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.OutputCacheMemoryUsage,
|
|
prometheus.CounterValue,
|
|
app.OutputCacheMemoryUsage,
|
|
name,
|
|
pid,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.OutputCacheQueriesTotal,
|
|
prometheus.CounterValue,
|
|
app.OutputCacheHitsTotal+app.OutputCacheMissesTotal,
|
|
name,
|
|
pid,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.OutputCacheHitsTotal,
|
|
prometheus.CounterValue,
|
|
app.OutputCacheHitsTotal,
|
|
name,
|
|
pid,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.OutputCacheFlushedItemsTotal,
|
|
prometheus.CounterValue,
|
|
app.OutputCacheFlushedItemsTotal,
|
|
name,
|
|
pid,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.OutputCacheFlushesTotal,
|
|
prometheus.CounterValue,
|
|
app.OutputCacheFlushesTotal,
|
|
name,
|
|
pid,
|
|
)
|
|
|
|
}
|
|
|
|
if c.iis_version.major >= 8 {
|
|
var W3SVC_W3WP_IIS8 []perflibW3SVC_W3WP_IIS8
|
|
if err := unmarshalObject(ctx.perfObjects["W3SVC_W3WP"], &W3SVC_W3WP_IIS8); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
w3svcW3WPIIS8Deduplicated := dedupIISNames(W3SVC_W3WP_IIS8)
|
|
|
|
for w3Name, app := range w3svcW3WPIIS8Deduplicated {
|
|
// Extract the apppool name from the format <PID>_<NAME>
|
|
pid := workerProcessNameExtractor.ReplaceAllString(w3Name, "$1")
|
|
name := workerProcessNameExtractor.ReplaceAllString(w3Name, "$2")
|
|
if name == "" || name == "_Total" ||
|
|
c.appExcludePattern.MatchString(name) ||
|
|
!c.appIncludePattern.MatchString(name) {
|
|
continue
|
|
}
|
|
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.RequestErrorsTotal,
|
|
prometheus.CounterValue,
|
|
app.RequestErrors401,
|
|
name,
|
|
pid,
|
|
"401",
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.RequestErrorsTotal,
|
|
prometheus.CounterValue,
|
|
app.RequestErrors403,
|
|
name,
|
|
pid,
|
|
"403",
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.RequestErrorsTotal,
|
|
prometheus.CounterValue,
|
|
app.RequestErrors404,
|
|
name,
|
|
pid,
|
|
"404",
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.RequestErrorsTotal,
|
|
prometheus.CounterValue,
|
|
app.RequestErrors500,
|
|
name,
|
|
pid,
|
|
"500",
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.RequestErrorsTotal,
|
|
prometheus.CounterValue,
|
|
app.RequestErrors503,
|
|
name,
|
|
pid,
|
|
"503",
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.WebSocketRequestsActive,
|
|
prometheus.CounterValue,
|
|
app.WebSocketRequestsActive,
|
|
name,
|
|
pid,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.WebSocketConnectionAttempts,
|
|
prometheus.CounterValue,
|
|
app.WebSocketConnectionAttempts,
|
|
name,
|
|
pid,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.WebSocketConnectionsAccepted,
|
|
prometheus.CounterValue,
|
|
app.WebSocketConnectionsAccepted,
|
|
name,
|
|
pid,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.WebSocketConnectionsRejected,
|
|
prometheus.CounterValue,
|
|
app.WebSocketConnectionsRejected,
|
|
name,
|
|
pid,
|
|
)
|
|
}
|
|
}
|
|
|
|
return nil, nil
|
|
}
|
|
|
|
type perflibWebServiceCache struct {
|
|
Name string
|
|
|
|
ServiceCache_ActiveFlushedEntries float64 `perflib:"Active Flushed Entries"`
|
|
|
|
ServiceCache_CurrentFileCacheMemoryUsage float64 `perflib:"Current File Cache Memory Usage"`
|
|
ServiceCache_MaximumFileCacheMemoryUsage float64 `perflib:"Maximum File Cache Memory Usage"`
|
|
ServiceCache_FileCacheFlushesTotal float64 `perflib:"File Cache Flushes"`
|
|
ServiceCache_FileCacheHitsTotal float64 `perflib:"File Cache Hits"`
|
|
ServiceCache_FileCacheMissesTotal float64 `perflib:"File Cache Misses"`
|
|
ServiceCache_FilesCached float64 `perflib:"Current Files Cached"`
|
|
ServiceCache_FilesCachedTotal float64 `perflib:"Total Files Cached"`
|
|
ServiceCache_FilesFlushedTotal float64 `perflib:"Total Flushed Files"`
|
|
ServiceCache_FileCacheQueriesTotal float64
|
|
|
|
ServiceCache_URICacheFlushesTotal float64 `perflib:"Total Flushed URIs"`
|
|
ServiceCache_URICacheFlushesTotalKernel float64 `perflib:"Total Flushed URIs"`
|
|
ServiceCache_URIsFlushedTotalKernel float64 `perflib:"Kernel: Total Flushed URIs"`
|
|
ServiceCache_URICacheHitsTotal float64 `perflib:"URI Cache Hits"`
|
|
ServiceCache_URICacheHitsTotalKernel float64 `perflib:"Kernel: URI Cache Hits"`
|
|
ServiceCache_URICacheMissesTotal float64 `perflib:"URI Cache Misses"`
|
|
ServiceCache_URICacheMissesTotalKernel float64 `perflib:"Kernel: URI Cache Misses"`
|
|
ServiceCache_URIsCached float64 `perflib:"Current URIs Cached"`
|
|
ServiceCache_URIsCachedKernel float64 `perflib:"Kernel: Current URIs Cached"`
|
|
ServiceCache_URIsCachedTotal float64 `perflib:"Total URIs Cached"`
|
|
ServiceCache_URIsCachedTotalKernel float64 `perflib:"Total URIs Cached"`
|
|
ServiceCache_URIsFlushedTotal float64 `perflib:"Total Flushed URIs"`
|
|
ServiceCache_URICacheQueriesTotal float64
|
|
|
|
ServiceCache_MetaDataCacheHits float64 `perflib:"Metadata Cache Hits"`
|
|
ServiceCache_MetaDataCacheMisses float64 `perflib:"Metadata Cache Misses"`
|
|
ServiceCache_MetadataCached float64 `perflib:"Current Metadata Cached"`
|
|
ServiceCache_MetadataCacheFlushes float64 `perflib:"Metadata Cache Flushes"`
|
|
ServiceCache_MetadataCachedTotal float64 `perflib:"Total Metadata Cached"`
|
|
ServiceCache_MetadataFlushedTotal float64 `perflib:"Total Flushed Metadata"`
|
|
ServiceCache_MetadataCacheQueriesTotal float64
|
|
|
|
ServiceCache_OutputCacheActiveFlushedItems float64 `perflib:"Output Cache Current Flushed Items"`
|
|
ServiceCache_OutputCacheItems float64 `perflib:"Output Cache Current Items"`
|
|
ServiceCache_OutputCacheMemoryUsage float64 `perflib:"Output Cache Current Memory Usage"`
|
|
ServiceCache_OutputCacheHitsTotal float64 `perflib:"Output Cache Total Hits"`
|
|
ServiceCache_OutputCacheMissesTotal float64 `perflib:"Output Cache Total Misses"`
|
|
ServiceCache_OutputCacheFlushedItemsTotal float64 `perflib:"Output Cache Total Flushed Items"`
|
|
ServiceCache_OutputCacheFlushesTotal float64 `perflib:"Output Cache Total Flushes"`
|
|
ServiceCache_OutputCacheQueriesTotal float64
|
|
}
|
|
|
|
func (c *IISCollector) collectWebServiceCache(ctx *ScrapeContext, ch chan<- prometheus.Metric) (*prometheus.Desc, error) {
|
|
var WebServiceCache []perflibWebServiceCache
|
|
if err := unmarshalObject(ctx.perfObjects["Web Service Cache"], &WebServiceCache); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, app := range WebServiceCache {
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.ServiceCache_ActiveFlushedEntries,
|
|
prometheus.GaugeValue,
|
|
app.ServiceCache_ActiveFlushedEntries,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.ServiceCache_CurrentFileCacheMemoryUsage,
|
|
prometheus.GaugeValue,
|
|
app.ServiceCache_CurrentFileCacheMemoryUsage,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.ServiceCache_MaximumFileCacheMemoryUsage,
|
|
prometheus.CounterValue,
|
|
app.ServiceCache_MaximumFileCacheMemoryUsage,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.ServiceCache_FileCacheFlushesTotal,
|
|
prometheus.CounterValue,
|
|
app.ServiceCache_FileCacheFlushesTotal,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.ServiceCache_FileCacheQueriesTotal,
|
|
prometheus.CounterValue,
|
|
app.ServiceCache_FileCacheHitsTotal+app.ServiceCache_FileCacheMissesTotal,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.ServiceCache_FileCacheHitsTotal,
|
|
prometheus.CounterValue,
|
|
app.ServiceCache_FileCacheHitsTotal,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.ServiceCache_FilesCached,
|
|
prometheus.GaugeValue,
|
|
app.ServiceCache_FilesCached,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.ServiceCache_FilesCachedTotal,
|
|
prometheus.CounterValue,
|
|
app.ServiceCache_FilesCachedTotal,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.ServiceCache_FilesFlushedTotal,
|
|
prometheus.CounterValue,
|
|
app.ServiceCache_FilesFlushedTotal,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.ServiceCache_URICacheFlushesTotal,
|
|
prometheus.CounterValue,
|
|
app.ServiceCache_URICacheFlushesTotal,
|
|
"user",
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.ServiceCache_URICacheFlushesTotal,
|
|
prometheus.CounterValue,
|
|
app.ServiceCache_URICacheFlushesTotalKernel,
|
|
"kernel",
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.ServiceCache_URICacheQueriesTotal,
|
|
prometheus.CounterValue,
|
|
app.ServiceCache_URICacheHitsTotal+app.ServiceCache_URICacheMissesTotal,
|
|
"user",
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.ServiceCache_URICacheQueriesTotal,
|
|
prometheus.CounterValue,
|
|
app.ServiceCache_URICacheHitsTotalKernel+app.ServiceCache_URICacheMissesTotalKernel,
|
|
"kernel",
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.ServiceCache_URICacheHitsTotal,
|
|
prometheus.CounterValue,
|
|
app.ServiceCache_URICacheHitsTotal,
|
|
"user",
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.ServiceCache_URICacheHitsTotal,
|
|
prometheus.CounterValue,
|
|
app.ServiceCache_URICacheHitsTotalKernel,
|
|
"kernel",
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.ServiceCache_URIsCached,
|
|
prometheus.GaugeValue,
|
|
app.ServiceCache_URIsCached,
|
|
"user",
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.ServiceCache_URIsCached,
|
|
prometheus.GaugeValue,
|
|
app.ServiceCache_URIsCachedKernel,
|
|
"kernel",
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.ServiceCache_URIsCachedTotal,
|
|
prometheus.CounterValue,
|
|
app.ServiceCache_URIsCachedTotal,
|
|
"user",
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.ServiceCache_URIsCachedTotal,
|
|
prometheus.CounterValue,
|
|
app.ServiceCache_URIsCachedTotalKernel,
|
|
"kernel",
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.ServiceCache_URIsFlushedTotal,
|
|
prometheus.CounterValue,
|
|
app.ServiceCache_URIsFlushedTotal,
|
|
"user",
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.ServiceCache_URIsFlushedTotal,
|
|
prometheus.CounterValue,
|
|
app.ServiceCache_URIsFlushedTotalKernel,
|
|
"kernel",
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.ServiceCache_MetadataCached,
|
|
prometheus.GaugeValue,
|
|
app.ServiceCache_MetadataCached,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.ServiceCache_MetadataCacheFlushes,
|
|
prometheus.CounterValue,
|
|
app.ServiceCache_MetadataCacheFlushes,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.ServiceCache_MetadataCacheQueriesTotal,
|
|
prometheus.CounterValue,
|
|
app.ServiceCache_MetaDataCacheHits+app.ServiceCache_MetaDataCacheMisses,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.ServiceCache_MetadataCacheHitsTotal,
|
|
prometheus.CounterValue,
|
|
app.ServiceCache_MetaDataCacheHits,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.ServiceCache_MetadataCachedTotal,
|
|
prometheus.CounterValue,
|
|
app.ServiceCache_MetadataCachedTotal,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.ServiceCache_MetadataFlushedTotal,
|
|
prometheus.CounterValue,
|
|
app.ServiceCache_MetadataFlushedTotal,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.ServiceCache_OutputCacheActiveFlushedItems,
|
|
prometheus.CounterValue,
|
|
app.ServiceCache_OutputCacheActiveFlushedItems,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.ServiceCache_OutputCacheItems,
|
|
prometheus.CounterValue,
|
|
app.ServiceCache_OutputCacheItems,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.ServiceCache_OutputCacheMemoryUsage,
|
|
prometheus.CounterValue,
|
|
app.ServiceCache_OutputCacheMemoryUsage,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.ServiceCache_OutputCacheQueriesTotal,
|
|
prometheus.CounterValue,
|
|
app.ServiceCache_OutputCacheHitsTotal+app.ServiceCache_OutputCacheMissesTotal,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.ServiceCache_OutputCacheHitsTotal,
|
|
prometheus.CounterValue,
|
|
app.ServiceCache_OutputCacheHitsTotal,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.ServiceCache_OutputCacheFlushedItemsTotal,
|
|
prometheus.CounterValue,
|
|
app.ServiceCache_OutputCacheFlushedItemsTotal,
|
|
)
|
|
ch <- prometheus.MustNewConstMetric(
|
|
c.ServiceCache_OutputCacheFlushesTotal,
|
|
prometheus.CounterValue,
|
|
app.ServiceCache_OutputCacheFlushesTotal,
|
|
)
|
|
}
|
|
|
|
return nil, nil
|
|
}
|