From d8f62e07c2a767b010e7558a838446edf752abde Mon Sep 17 00:00:00 2001 From: Martin Lindhe Date: Thu, 1 Sep 2016 16:04:43 +0200 Subject: [PATCH] make enabled collectors configurable (based on code from node_exporter) --- collector/iis.go | 61 +++++++-------- collector/logical_disk.go | 52 +++++-------- collector/os.go | 51 +++++------- collector/wmi.go | 14 +++- exporter.go | 159 +++++++++++++++++++++++++++++--------- 5 files changed, 206 insertions(+), 131 deletions(-) diff --git a/collector/iis.go b/collector/iis.go index f5554f4c..54c6efcd 100644 --- a/collector/iis.go +++ b/collector/iis.go @@ -13,6 +13,10 @@ import ( "github.com/prometheus/client_golang/prometheus" ) +func init() { + Factories["iis"] = NewIISCollector +} + var ( siteWhitelist = flag.String("collector.iis.site-whitelist", ".+", "Regexp of sites to whitelist. Site name must both match whitelist and not match blacklist to be included.") 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.") @@ -48,43 +52,43 @@ type IISCollector struct { } // NewIISCollector ... -func NewIISCollector() *IISCollector { +func NewIISCollector() (Collector, error) { const subsystem = "iis" return &IISCollector{ // Gauges CurrentAnonymousUsers: prometheus.NewDesc( - prometheus.BuildFQName(wmiNamespace, subsystem, "current_anonymous_users"), + 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(wmiNamespace, subsystem, "current_blocked_async_io_requests"), + 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(wmiNamespace, subsystem, "current_cgi_requests"), + 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(wmiNamespace, subsystem, "current_connections"), + 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(wmiNamespace, subsystem, "current_isapi_extension_requests"), + 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(wmiNamespace, subsystem, "current_non_anonymous_users"), + 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, @@ -92,91 +96,91 @@ func NewIISCollector() *IISCollector { // Counters TotalBytesReceived: prometheus.NewDesc( - prometheus.BuildFQName(wmiNamespace, subsystem, "received_bytes_total"), + 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(wmiNamespace, subsystem, "sent_bytes_total"), + 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(wmiNamespace, subsystem, "anonymous_users_total"), + 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(wmiNamespace, subsystem, "blocked_async_io_requests_total"), + 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(wmiNamespace, subsystem, "cgi_requests_total"), + 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(wmiNamespace, subsystem, "connection_attempts_all_instances_total"), + 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(wmiNamespace, subsystem, "requests_total"), + prometheus.BuildFQName(Namespace, subsystem, "requests_total"), "Number of HTTP requests (WebService.TotalRequests)", []string{"site", "method"}, nil, ), TotalFilesReceived: prometheus.NewDesc( - prometheus.BuildFQName(wmiNamespace, subsystem, "files_received_total"), + 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(wmiNamespace, subsystem, "files_sent_total"), + 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(wmiNamespace, subsystem, "ipapi_extension_requests_total"), + prometheus.BuildFQName(Namespace, subsystem, "ipapi_extension_requests_total"), "ISAPI Extension Requests received (WebService.TotalISAPIExtensionRequests)", []string{"site"}, nil, ), TotalLockedErrors: prometheus.NewDesc( - prometheus.BuildFQName(wmiNamespace, subsystem, "locked_errors_total"), + 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(wmiNamespace, subsystem, "logon_attempts_total"), + 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(wmiNamespace, subsystem, "non_anonymous_users_total"), + 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(wmiNamespace, subsystem, "not_found_errors_total"), + 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(wmiNamespace, subsystem, "rejected_async_io_requests_total"), + prometheus.BuildFQName(Namespace, subsystem, "rejected_async_io_requests_total"), "Requests rejected due to bandwidth throttling settings (WebService.TotalRejectedAsyncIORequests)", []string{"site"}, nil, @@ -184,22 +188,17 @@ func NewIISCollector() *IISCollector { siteWhitelistPattern: regexp.MustCompile(fmt.Sprintf("^(?:%s)$", *siteWhitelist)), siteBlacklistPattern: regexp.MustCompile(fmt.Sprintf("^(?:%s)$", *siteBlacklist)), - } + }, nil } // Collect sends the metric values for each metric // to the provided prometheus Metric channel. -func (c *IISCollector) Collect(ch chan<- prometheus.Metric) { +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) - return + return err } -} - -// Describe sends the descriptors of each metric over to the provided channel. -// The corresponding metric values are sent separately. -func (c *IISCollector) Describe(ch chan<- *prometheus.Desc) { - + return nil } type Win32_PerfRawData_W3SVC_WebService struct { diff --git a/collector/logical_disk.go b/collector/logical_disk.go index 6d33519d..3ed6df80 100644 --- a/collector/logical_disk.go +++ b/collector/logical_disk.go @@ -14,6 +14,10 @@ import ( "github.com/prometheus/client_golang/prometheus" ) +func init() { + Factories["logical_disk"] = NewLogicalDiskCollector +} + const ( ticksToSecondsScaleFactor = 1 / 1e7 ) @@ -42,82 +46,82 @@ type LogicalDiskCollector struct { } // NewLogicalDiskCollector ... -func NewLogicalDiskCollector() *LogicalDiskCollector { +func NewLogicalDiskCollector() (Collector, error) { const subsystem = "logical_disk" return &LogicalDiskCollector{ RequestsQueued: prometheus.NewDesc( - prometheus.BuildFQName(wmiNamespace, subsystem, "requests_queued"), + prometheus.BuildFQName(Namespace, subsystem, "requests_queued"), "The number of requests queued to the disk (LogicalDisk.CurrentDiskQueueLength)", []string{"volume"}, nil, ), ReadBytesTotal: prometheus.NewDesc( - prometheus.BuildFQName(wmiNamespace, subsystem, "read_bytes_total"), + prometheus.BuildFQName(Namespace, subsystem, "read_bytes_total"), "The number of bytes transferred from the disk during read operations (LogicalDisk.DiskReadBytesPerSec)", []string{"volume"}, nil, ), ReadsTotal: prometheus.NewDesc( - prometheus.BuildFQName(wmiNamespace, subsystem, "reads_total"), + prometheus.BuildFQName(Namespace, subsystem, "reads_total"), "The number of read operations on the disk (LogicalDisk.DiskReadsPerSec)", []string{"volume"}, nil, ), WriteBytesTotal: prometheus.NewDesc( - prometheus.BuildFQName(wmiNamespace, subsystem, "write_bytes_total"), + prometheus.BuildFQName(Namespace, subsystem, "write_bytes_total"), "The number of bytes transferred to the disk during write operations (LogicalDisk.DiskWriteBytesPerSec)", []string{"volume"}, nil, ), WritesTotal: prometheus.NewDesc( - prometheus.BuildFQName(wmiNamespace, subsystem, "writes_total"), + prometheus.BuildFQName(Namespace, subsystem, "writes_total"), "The number of write operations on the disk (LogicalDisk.DiskWritesPerSec)", []string{"volume"}, nil, ), ReadTime: prometheus.NewDesc( - prometheus.BuildFQName(wmiNamespace, subsystem, "read_seconds_total"), + prometheus.BuildFQName(Namespace, subsystem, "read_seconds_total"), "Seconds that the disk was busy servicing read requests (LogicalDisk.PercentDiskReadTime)", []string{"volume"}, nil, ), WriteTime: prometheus.NewDesc( - prometheus.BuildFQName(wmiNamespace, subsystem, "write_seconds_total"), + prometheus.BuildFQName(Namespace, subsystem, "write_seconds_total"), "Seconds that the disk was busy servicing write requests (LogicalDisk.PercentDiskWriteTime)", []string{"volume"}, nil, ), FreeSpace: prometheus.NewDesc( - prometheus.BuildFQName(wmiNamespace, subsystem, "free_bytes"), + prometheus.BuildFQName(Namespace, subsystem, "free_bytes"), "Free space in bytes (LogicalDisk.PercentFreeSpace)", []string{"volume"}, nil, ), TotalSpace: prometheus.NewDesc( - prometheus.BuildFQName(wmiNamespace, subsystem, "size_bytes"), + prometheus.BuildFQName(Namespace, subsystem, "size_bytes"), "Total space in bytes (LogicalDisk.PercentFreeSpace_Base)", []string{"volume"}, nil, ), IdleTime: prometheus.NewDesc( - prometheus.BuildFQName(wmiNamespace, subsystem, "idle_seconds_total"), + prometheus.BuildFQName(Namespace, subsystem, "idle_seconds_total"), "Seconds that the disk was idle (LogicalDisk.PercentIdleTime)", []string{"volume"}, nil, ), SplitIOs: prometheus.NewDesc( - prometheus.BuildFQName(wmiNamespace, subsystem, "split_ios_total"), + prometheus.BuildFQName(Namespace, subsystem, "split_ios_total"), "The number of I/Os to the disk were split into multiple I/Os (LogicalDisk.SplitIOPerSec)", []string{"volume"}, nil, @@ -125,33 +129,17 @@ func NewLogicalDiskCollector() *LogicalDiskCollector { volumeWhitelistPattern: regexp.MustCompile(fmt.Sprintf("^(?:%s)$", *volumeWhitelist)), volumeBlacklistPattern: regexp.MustCompile(fmt.Sprintf("^(?:%s)$", *volumeBlacklist)), - } + }, nil } // Collect sends the metric values for each metric // to the provided prometheus Metric channel. -func (c *LogicalDiskCollector) Collect(ch chan<- prometheus.Metric) { +func (c *LogicalDiskCollector) Collect(ch chan<- prometheus.Metric) error { if desc, err := c.collect(ch); err != nil { log.Println("[ERROR] failed collecting logical_disk metrics:", desc, err) - return + return err } -} - -// Describe sends the descriptors of each metric over to the provided channel. -// The corresponding metric values are sent separately. -func (c *LogicalDiskCollector) Describe(ch chan<- *prometheus.Desc) { - - ch <- c.RequestsQueued - ch <- c.ReadBytesTotal - ch <- c.ReadsTotal - ch <- c.WriteBytesTotal - ch <- c.WritesTotal - ch <- c.ReadTime - ch <- c.WriteTime - ch <- c.FreeSpace - ch <- c.TotalSpace - ch <- c.IdleTime - ch <- c.SplitIOs + return nil } type Win32_PerfRawData_PerfDisk_LogicalDisk struct { diff --git a/collector/os.go b/collector/os.go index ccb213e8..d46f58ea 100644 --- a/collector/os.go +++ b/collector/os.go @@ -10,6 +10,10 @@ import ( "github.com/prometheus/client_golang/prometheus" ) +func init() { + Factories["os"] = NewOSCollector +} + // A OSCollector is a Prometheus collector for WMI Win32_OperatingSystem metrics type OSCollector struct { PhysicalMemoryFreeBytes *prometheus.Desc @@ -25,105 +29,92 @@ type OSCollector struct { } // NewOSCollector ... -func NewOSCollector() *OSCollector { +func NewOSCollector() (Collector, error) { + + const subsystem = "os" return &OSCollector{ PagingMaxBytes: prometheus.NewDesc( - prometheus.BuildFQName(wmiNamespace, "os", "paging_max_bytes"), + prometheus.BuildFQName(Namespace, subsystem, "paging_max_bytes"), "SizeStoredInPagingFiles", nil, nil, ), PagingFreeBytes: prometheus.NewDesc( - prometheus.BuildFQName(wmiNamespace, "os", "paging_free_bytes"), + prometheus.BuildFQName(Namespace, subsystem, "paging_free_bytes"), "FreeSpaceInPagingFiles", nil, nil, ), PhysicalMemoryFreeBytes: prometheus.NewDesc( - prometheus.BuildFQName(wmiNamespace, "os", "physical_memory_free_bytes"), + prometheus.BuildFQName(Namespace, subsystem, "physical_memory_free_bytes"), "FreePhysicalMemory", nil, nil, ), Processes: prometheus.NewDesc( - prometheus.BuildFQName(wmiNamespace, "os", "processes"), + prometheus.BuildFQName(Namespace, subsystem, "processes"), "NumberOfProcesses", nil, nil, ), ProcessesMax: prometheus.NewDesc( - prometheus.BuildFQName(wmiNamespace, "os", "processes_max"), + prometheus.BuildFQName(Namespace, subsystem, "processes_max"), "MaxNumberOfProcesses", nil, nil, ), ProcessMemoryMaxBytes: prometheus.NewDesc( - prometheus.BuildFQName(wmiNamespace, "os", "process_memory_max_bytes"), + prometheus.BuildFQName(Namespace, subsystem, "process_memory_max_bytes"), "MaxProcessMemorySize", nil, nil, ), Users: prometheus.NewDesc( - prometheus.BuildFQName(wmiNamespace, "os", "users"), + prometheus.BuildFQName(Namespace, subsystem, "users"), "NumberOfUsers", nil, nil, ), VirtualMemoryBytes: prometheus.NewDesc( - prometheus.BuildFQName(wmiNamespace, "os", "virtual_memory_bytes"), + prometheus.BuildFQName(Namespace, subsystem, "virtual_memory_bytes"), "TotalVirtualMemorySize", nil, nil, ), VisibleMemoryBytes: prometheus.NewDesc( - prometheus.BuildFQName(wmiNamespace, "os", "visible_memory_bytes"), + prometheus.BuildFQName(Namespace, subsystem, "visible_memory_bytes"), "TotalVisibleMemorySize", nil, nil, ), VirtualMemoryFreeBytes: prometheus.NewDesc( - prometheus.BuildFQName(wmiNamespace, "os", "virtual_memory_free_bytes"), + prometheus.BuildFQName(Namespace, subsystem, "virtual_memory_free_bytes"), "FreeVirtualMemory", nil, nil, ), - } + }, nil } // Collect sends the metric values for each metric // to the provided prometheus Metric channel. -func (c *OSCollector) Collect(ch chan<- prometheus.Metric) { +func (c *OSCollector) Collect(ch chan<- prometheus.Metric) error { if desc, err := c.collect(ch); err != nil { log.Println("[ERROR] failed collecting os metrics:", desc, err) - return + return err } -} - -// Describe sends the descriptors of each metric over to the provided channel. -// The corresponding metric values are sent separately. -func (c *OSCollector) Describe(ch chan<- *prometheus.Desc) { - - ch <- c.PhysicalMemoryFreeBytes - ch <- c.PagingFreeBytes - ch <- c.VirtualMemoryFreeBytes - ch <- c.ProcessesMax - ch <- c.ProcessMemoryMaxBytes - ch <- c.Processes - ch <- c.Users - ch <- c.PagingMaxBytes - ch <- c.VirtualMemoryBytes - ch <- c.VisibleMemoryBytes + return nil } type Win32_OperatingSystem struct { diff --git a/collector/wmi.go b/collector/wmi.go index b955faac..3d4f3e71 100644 --- a/collector/wmi.go +++ b/collector/wmi.go @@ -1,5 +1,17 @@ package collector +import "github.com/prometheus/client_golang/prometheus" + +// ... const ( - wmiNamespace = "wmi" + Namespace = "wmi" ) + +// Factories ... +var Factories = make(map[string]func() (Collector, error)) + +// Collector is the interface a collector has to implement. +type Collector interface { + // Get new metrics and expose them via prometheus registry. + Collect(ch chan<- prometheus.Metric) (err error) +} diff --git a/exporter.go b/exporter.go index 43da87fa..0e788d38 100644 --- a/exporter.go +++ b/exporter.go @@ -2,76 +2,161 @@ package main import ( "flag" - "log" + "fmt" "net/http" + "os" + "sort" + "strings" "sync" + "time" "github.com/martinlindhe/wmi_exporter/collector" "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/common/log" + "github.com/prometheus/common/version" ) -// WmiExporter wraps all the WMI collectors and provides a single global -// exporter to extracts metrics out of. It also ensures that the collection -// is done in a thread-safe manner, the necessary requirement stated by -// prometheus. It also implements a prometheus.Collector interface in order -// to register it correctly. -type WmiExporter struct { - mu sync.Mutex - collectors []prometheus.Collector +// WmiCollector implements the prometheus.Collector interface. +type WmiCollector struct { + collectors map[string]collector.Collector } -// Verify that the WmiExporter implements the prometheus.Collector interface. -var _ prometheus.Collector = &WmiExporter{} +const ( + defaultCollectors = "logical_disk,os" +) -// NewWmiExporter creates an instance to WmiExporter and returns a reference -// to it. We can choose to enable a collector to extract stats out of by adding -// it to the list of collectors. -func NewWmiExporter() *WmiExporter { - return &WmiExporter{ - collectors: []prometheus.Collector{ - collector.NewOSCollector(), - collector.NewLogicalDiskCollector(), - collector.NewIISCollector(), +var ( + scrapeDurations = prometheus.NewSummaryVec( + prometheus.SummaryOpts{ + Namespace: collector.Namespace, + Subsystem: "exporter", + Name: "scrape_duration_seconds", + Help: "wmi_exporter: Duration of a scrape job.", }, - } -} + []string{"collector", "result"}, + ) +) // Describe sends all the descriptors of the collectors included to // the provided channel. -func (c *WmiExporter) Describe(ch chan<- *prometheus.Desc) { - for _, cc := range c.collectors { - cc.Describe(ch) - } +func (coll WmiCollector) Describe(ch chan<- *prometheus.Desc) { + scrapeDurations.Describe(ch) } // Collect sends the collected metrics from each of the collectors to // prometheus. Collect could be called several times concurrently // and thus its run is protected by a single mutex. -func (c *WmiExporter) Collect(ch chan<- prometheus.Metric) { - c.mu.Lock() - defer c.mu.Unlock() - - for _, cc := range c.collectors { - cc.Collect(ch) +func (coll WmiCollector) Collect(ch chan<- prometheus.Metric) { + wg := sync.WaitGroup{} + wg.Add(len(coll.collectors)) + for name, c := range coll.collectors { + go func(name string, c collector.Collector) { + execute(name, c, ch) + wg.Done() + }(name, c) } + wg.Wait() + scrapeDurations.Collect(ch) +} + +func filterAvailableCollectors(collectors string) string { + var availableCollectors []string + for _, c := range strings.Split(collectors, ",") { + _, ok := collector.Factories[c] + if ok { + availableCollectors = append(availableCollectors, c) + } + } + return strings.Join(availableCollectors, ",") +} + +func execute(name string, c collector.Collector, ch chan<- prometheus.Metric) { + begin := time.Now() + err := c.Collect(ch) + duration := time.Since(begin) + var result string + + if err != nil { + log.Errorf("ERROR: %s collector failed after %fs: %s", name, duration.Seconds(), err) + result = "error" + } else { + log.Debugf("OK: %s collector succeeded after %fs.", name, duration.Seconds()) + result = "success" + } + scrapeDurations.WithLabelValues(name, result).Observe(duration.Seconds()) +} + +func loadCollectors(list string) (map[string]collector.Collector, error) { + collectors := map[string]collector.Collector{} + for _, name := range strings.Split(list, ",") { + fn, ok := collector.Factories[name] + if !ok { + return nil, fmt.Errorf("collector '%s' not available", name) + } + c, err := fn() + if err != nil { + return nil, err + } + collectors[name] = c + } + return collectors, nil +} + +func init() { + prometheus.MustRegister(version.NewCollector("wmi_exporter")) } func main() { var ( - addr = flag.String("telemetry.addr", ":9182", "host:port for WMI exporter") - metricsPath = flag.String("telemetry.path", "/metrics", "URL path for surfacing collected metrics") + showVersion = flag.Bool("version", false, "Print version information.") + listenAddress = flag.String("telemetry.addr", ":9182", "host:port for WMI exporter.") + metricsPath = flag.String("telemetry.path", "/metrics", "URL path for surfacing collected metrics.") + enabledCollectors = flag.String("collectors.enabled", filterAvailableCollectors(defaultCollectors), "Comma-separated list of collectors to use.") + printCollectors = flag.Bool("collectors.print", false, "If true, print available collectors and exit.") ) flag.Parse() - prometheus.MustRegister(NewWmiExporter()) + if *showVersion { + fmt.Fprintln(os.Stdout, version.Print("wmi_exporter")) + os.Exit(0) + } + + if *printCollectors { + collectorNames := make(sort.StringSlice, 0, len(collector.Factories)) + for n := range collector.Factories { + collectorNames = append(collectorNames, n) + } + collectorNames.Sort() + fmt.Printf("Available collectors:\n") + for _, n := range collectorNames { + fmt.Printf(" - %s\n", n) + } + return + } + + collectors, err := loadCollectors(*enabledCollectors) + if err != nil { + log.Fatalf("Couldn't load collectors: %s", err) + } + + log.Infof("Enabled collectors:") + for n := range collectors { + log.Infof(" - %s", n) + } + + nodeCollector := WmiCollector{collectors: collectors} + prometheus.MustRegister(nodeCollector) http.Handle(*metricsPath, prometheus.Handler()) http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, *metricsPath, http.StatusMovedPermanently) }) - log.Printf("Starting WMI exporter on %q", *addr) - if err := http.ListenAndServe(*addr, nil); err != nil { + log.Infoln("Starting WMI exporter", version.Info()) + log.Infoln("Build context", version.BuildContext()) + + log.Infoln("Listening on", *listenAddress) + if err := http.ListenAndServe(*listenAddress, nil); err != nil { log.Fatalf("cannot start WMI exporter: %s", err) } }