initial commit

This commit is contained in:
Martin Lindhe 2016-08-26 08:59:27 +02:00
commit dac70ad805
6 changed files with 250 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/wmi_exporter.exe

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 Martin Lindhe
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

20
README.md Normal file
View File

@ -0,0 +1,20 @@
# WMI exporter
Prometheus exporter for Windows machines, using the WMI (Windows Management Instrumentation).
## Status
EXPERIMENTAL, use at your own risk!
## Collectors
Name | Description
---------|-------------
os | Exposes Win32_OperatingSystem statistics (memory, processes, users)
## License
Under [MIT](LICENSE)

128
collectors/os.go Normal file
View File

@ -0,0 +1,128 @@
// returns data points from Win32_OperatingSystem
package collectors
import (
"log"
"github.com/StackExchange/wmi"
"github.com/prometheus/client_golang/prometheus"
)
// A OSCollector is a Prometheus collector for WMI OperatingSystem metrics
type OSCollector struct {
FreePhysicalMemory *prometheus.Desc
FreeSpaceInPagingFiles *prometheus.Desc
FreeVirtualMemory *prometheus.Desc
NumberOfProcesses *prometheus.Desc
NumberOfUsers *prometheus.Desc
}
// NewOSCollector ...
func NewOSCollector() *OSCollector {
return &OSCollector{
FreePhysicalMemory: prometheus.NewDesc(
prometheus.BuildFQName(wmiNamespace, "os", "free_physical_memory"),
"Free physical memory.",
nil,
nil,
),
FreeSpaceInPagingFiles: prometheus.NewDesc(
prometheus.BuildFQName(wmiNamespace, "os", "free_space_in_paging_files"),
"Free space in paging files.",
nil,
nil,
),
FreeVirtualMemory: prometheus.NewDesc(
prometheus.BuildFQName(wmiNamespace, "os", "free_virtual_memory"),
"Free virtual memory.",
nil,
nil,
),
NumberOfProcesses: prometheus.NewDesc(
prometheus.BuildFQName(wmiNamespace, "os", "number_of_processes"),
"No. of processes running on the system.",
nil,
nil,
),
NumberOfUsers: prometheus.NewDesc(
prometheus.BuildFQName(wmiNamespace, "os", "number_of_users"),
"No. of users logged in.",
nil,
nil,
),
}
}
// Collect sends the metric values for each metric
// to the provided prometheus Metric channel.
func (c *OSCollector) Collect(ch chan<- prometheus.Metric) {
if desc, err := c.collect(ch); err != nil {
log.Println("[ERROR] failed collecting process metrics:", desc, err)
return
}
}
// 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.FreePhysicalMemory
ch <- c.FreeSpaceInPagingFiles
ch <- c.FreeVirtualMemory
ch <- c.NumberOfProcesses
ch <- c.NumberOfUsers
}
type Win32_OperatingSystem struct {
FreePhysicalMemory uint64
FreeSpaceInPagingFiles uint64
FreeVirtualMemory uint64
NumberOfProcesses uint32
NumberOfUsers uint32
}
func (c *OSCollector) collect(ch chan<- prometheus.Metric) (*prometheus.Desc, error) {
var dst []Win32_OperatingSystem
q := wmi.CreateQuery(&dst, "")
if err := wmi.Query(q, &dst); err != nil {
return nil, err
}
ch <- prometheus.MustNewConstMetric(
c.FreePhysicalMemory,
prometheus.GaugeValue,
float64(dst[0].FreePhysicalMemory),
)
ch <- prometheus.MustNewConstMetric(
c.FreeSpaceInPagingFiles,
prometheus.GaugeValue,
float64(dst[0].FreeSpaceInPagingFiles),
)
ch <- prometheus.MustNewConstMetric(
c.FreeVirtualMemory,
prometheus.GaugeValue,
float64(dst[0].FreeVirtualMemory),
)
ch <- prometheus.MustNewConstMetric(
c.NumberOfProcesses,
prometheus.GaugeValue,
float64(dst[0].NumberOfProcesses),
)
ch <- prometheus.MustNewConstMetric(
c.NumberOfUsers,
prometheus.GaugeValue,
float64(dst[0].NumberOfUsers),
)
return nil, nil
}

5
collectors/wmi.go Normal file
View File

@ -0,0 +1,5 @@
package collectors
const (
wmiNamespace = "wmi"
)

75
exporter.go Normal file
View File

@ -0,0 +1,75 @@
package main
import (
"flag"
"log"
"net/http"
"sync"
"github.com/martinlindhe/wmi_exporter/collectors"
"github.com/prometheus/client_golang/prometheus"
)
// 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
}
// Verify that the WmiExporter implements the prometheus.Collector interface.
var _ prometheus.Collector = &WmiExporter{}
// 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{
collectors.NewOSCollector(),
},
}
}
// 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)
}
}
// 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 main() {
var (
addr = flag.String("telemetry.addr", ":9129", "host:port for WMI exporter")
metricsPath = flag.String("telemetry.path", "/metrics", "URL path for surfacing collected metrics")
)
flag.Parse()
prometheus.MustRegister(NewWmiExporter())
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.Fatalf("cannot start WMI exporter: %s", err)
}
}