diff --git a/README.md b/README.md index 4c721ae2..30e24519 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ Name | Description | OS arp | Exposes ARP statistics from `/proc/net/arp`. | Linux conntrack | Shows conntrack statistics (does nothing if no `/proc/sys/net/netfilter/` present). | Linux cpu | Exposes CPU statistics | Darwin, Dragonfly, FreeBSD, Linux -diskstats | Exposes disk I/O statistics from `/proc/diskstats`. | Linux +diskstats | Exposes disk I/O statistics. | Darwin, Linux edac | Exposes error detection and correction statistics. | Linux entropy | Exposes available entropy. | Linux exec | Exposes execution statistics. | Dragonfly, FreeBSD diff --git a/collector/diskstats_darwin.go b/collector/diskstats_darwin.go new file mode 100644 index 00000000..753bfc62 --- /dev/null +++ b/collector/diskstats_darwin.go @@ -0,0 +1,177 @@ +// Copyright 2017 The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// +build !nodiskstats + +package collector + +import ( + "fmt" + + "github.com/lufia/iostat" + "github.com/prometheus/client_golang/prometheus" +) + +const ( + diskSubsystem = "disk" +) + +type typedDescFunc struct { + typedDesc + value func(stat *iostat.DriveStats) float64 +} + +type diskstatsCollector struct { + descs []typedDescFunc +} + +func init() { + Factories["diskstats"] = NewDiskstatsCollector +} + +// NewDiskstatsCollector returns a new Collector exposing disk device stats. +func NewDiskstatsCollector() (Collector, error) { + var diskLabelNames = []string{"device"} + + return &diskstatsCollector{ + descs: []typedDescFunc{ + { + typedDesc: typedDesc{ + desc: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, diskSubsystem, "reads_completed_total"), + "The total number of reads completed successfully.", + diskLabelNames, + nil, + ), + valueType: prometheus.CounterValue, + }, + value: func(stat *iostat.DriveStats) float64 { + return float64(stat.NumRead) + }, + }, + { + typedDesc: typedDesc{ + desc: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, diskSubsystem, "read_sectors_total"), + "The total number of sectors read successfully.", + diskLabelNames, + nil, + ), + valueType: prometheus.CounterValue, + }, + value: func(stat *iostat.DriveStats) float64 { + return float64(stat.NumRead) / float64(stat.BlockSize) + }, + }, + { + typedDesc: typedDesc{ + desc: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, diskSubsystem, "read_seconds_total"), + "The total number of seconds spent by all reads.", + diskLabelNames, + nil, + ), + valueType: prometheus.CounterValue, + }, + value: func(stat *iostat.DriveStats) float64 { + return stat.TotalReadTime.Seconds() + }, + }, + { + typedDesc: typedDesc{ + desc: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, diskSubsystem, "writes_completed_total"), + "The total number of writes completed successfully.", + diskLabelNames, + nil, + ), + valueType: prometheus.CounterValue, + }, + value: func(stat *iostat.DriveStats) float64 { + return float64(stat.NumWrite) + }, + }, + { + typedDesc: typedDesc{ + desc: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, diskSubsystem, "written_sectors_total"), + "The total number of sectors written successfully.", + diskLabelNames, + nil, + ), + valueType: prometheus.CounterValue, + }, + value: func(stat *iostat.DriveStats) float64 { + return float64(stat.NumWrite) / float64(stat.BlockSize) + }, + }, + { + typedDesc: typedDesc{ + desc: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, diskSubsystem, "write_seconds_total"), + "This is the total number of seconds spent by all writes.", + diskLabelNames, + nil, + ), + valueType: prometheus.CounterValue, + }, + value: func(stat *iostat.DriveStats) float64 { + return stat.TotalWriteTime.Seconds() + }, + }, + { + typedDesc: typedDesc{ + desc: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, diskSubsystem, "read_bytes_total"), + "The total number of bytes read successfully.", + diskLabelNames, + nil, + ), + valueType: prometheus.CounterValue, + }, + value: func(stat *iostat.DriveStats) float64 { + return float64(stat.BytesRead) + }, + }, + { + typedDesc: typedDesc{ + desc: prometheus.NewDesc( + prometheus.BuildFQName(Namespace, diskSubsystem, "written_bytes_total"), + "The total number of bytes written successfully.", + diskLabelNames, + nil, + ), + valueType: prometheus.CounterValue, + }, + value: func(stat *iostat.DriveStats) float64 { + return float64(stat.BytesWritten) + }, + }, + }, + }, nil +} + +func (c *diskstatsCollector) Update(ch chan<- prometheus.Metric) error { + diskStats, err := iostat.ReadDriveStats() + if err != nil { + return fmt.Errorf("couldn't get diskstats: %s", err) + } + + for _, stats := range diskStats { + for _, desc := range c.descs { + v := desc.value(stats) + ch <- desc.mustNewConstMetric(v, stats.Name) + } + } + return nil +} diff --git a/vendor/github.com/lufia/iostat/LICENSE b/vendor/github.com/lufia/iostat/LICENSE new file mode 100644 index 00000000..1172cfff --- /dev/null +++ b/vendor/github.com/lufia/iostat/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2017, kadota kyohei +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/lufia/iostat/README.md b/vendor/github.com/lufia/iostat/README.md new file mode 100644 index 00000000..d6cd477c --- /dev/null +++ b/vendor/github.com/lufia/iostat/README.md @@ -0,0 +1,7 @@ +# iostat - reports I/O statistics + +[![GoDoc](https://godoc.org/github.com/lufia/iostat?status.svg)](https://godoc.org/github.com/lufia/iostat) + +*iostat* reports I/O statistics. currently supported OSes are: + +* macOS diff --git a/vendor/github.com/lufia/iostat/iostat.go b/vendor/github.com/lufia/iostat/iostat.go new file mode 100644 index 00000000..dfc50d3e --- /dev/null +++ b/vendor/github.com/lufia/iostat/iostat.go @@ -0,0 +1,20 @@ +// Package iostat presents I/O statistics. +package iostat + +import "time" + +// DriveStats represents I/O statistics of a drive. +type DriveStats struct { + Name string // drive name + Size int64 // total drive size in bytes + BlockSize int64 // block size in bytes + + BytesRead int64 + BytesWritten int64 + NumRead int64 + NumWrite int64 + TotalReadTime time.Duration + TotalWriteTime time.Duration + ReadLatency time.Duration + WriteLatency time.Duration +} diff --git a/vendor/github.com/lufia/iostat/iostat_darwin.c b/vendor/github.com/lufia/iostat/iostat_darwin.c new file mode 100644 index 00000000..4371ea49 --- /dev/null +++ b/vendor/github.com/lufia/iostat/iostat_darwin.c @@ -0,0 +1,128 @@ +#include +#include "iostat_darwin.h" + +#define IOKIT 1 /* to get io_name_t in device_types.h */ + +#include +#include +#include +#include +#include + +static int getdrivestat(io_registry_entry_t d, DriveStats *stat); +static int fillstat(io_registry_entry_t d, DriveStats *stat); + +int +readdrivestat(DriveStats a[], int n) +{ + mach_port_t port; + CFMutableDictionaryRef match; + io_iterator_t drives; + io_registry_entry_t d; + kern_return_t status; + int na, rv; + + IOMasterPort(bootstrap_port, &port); + match = IOServiceMatching("IOMedia"); + CFDictionaryAddValue(match, CFSTR(kIOMediaWholeKey), kCFBooleanTrue); + status = IOServiceGetMatchingServices(port, match, &drives); + if(status != KERN_SUCCESS) + return -1; + + na = 0; + while(na < n && (d=IOIteratorNext(drives)) > 0){ + rv = getdrivestat(d, &a[na]); + if(rv < 0) + return -1; + if(rv > 0) + na++; + IOObjectRelease(d); + } + IOObjectRelease(drives); + return na; +} + +static int +getdrivestat(io_registry_entry_t d, DriveStats *stat) +{ + io_registry_entry_t parent; + kern_return_t status; + CFDictionaryRef props; + CFStringRef name; + CFNumberRef num; + int rv; + + memset(stat, 0, sizeof *stat); + status = IORegistryEntryGetParentEntry(d, kIOServicePlane, &parent); + if(status != KERN_SUCCESS) + return -1; + if(!IOObjectConformsTo(parent, "IOBlockStorageDriver")){ + IOObjectRelease(parent); + return 0; + } + + status = IORegistryEntryCreateCFProperties(d, (CFMutableDictionaryRef *)&props, kCFAllocatorDefault, kNilOptions); + if(status != KERN_SUCCESS){ + IOObjectRelease(parent); + return -1; + } + name = (CFStringRef)CFDictionaryGetValue(props, CFSTR(kIOBSDNameKey)); + CFStringGetCString(name, stat->name, NAMELEN, CFStringGetSystemEncoding()); + num = (CFNumberRef)CFDictionaryGetValue(props, CFSTR(kIOMediaSizeKey)); + CFNumberGetValue(num, kCFNumberSInt64Type, &stat->size); + num = (CFNumberRef)CFDictionaryGetValue(props, CFSTR(kIOMediaPreferredBlockSizeKey)); + CFNumberGetValue(num, kCFNumberSInt64Type, &stat->blocksize); + CFRelease(props); + + rv = fillstat(parent, stat); + IOObjectRelease(parent); + if(rv < 0) + return -1; + return 1; +} + +static struct { + char *key; + size_t off; +} statstab[] = { + {kIOBlockStorageDriverStatisticsBytesReadKey, offsetof(DriveStats, read)}, + {kIOBlockStorageDriverStatisticsBytesWrittenKey, offsetof(DriveStats, written)}, + {kIOBlockStorageDriverStatisticsReadsKey, offsetof(DriveStats, nread)}, + {kIOBlockStorageDriverStatisticsWritesKey, offsetof(DriveStats, nwrite)}, + {kIOBlockStorageDriverStatisticsTotalReadTimeKey, offsetof(DriveStats, readtime)}, + {kIOBlockStorageDriverStatisticsTotalWriteTimeKey, offsetof(DriveStats, writetime)}, + {kIOBlockStorageDriverStatisticsLatentReadTimeKey, offsetof(DriveStats, readlat)}, + {kIOBlockStorageDriverStatisticsLatentWriteTimeKey, offsetof(DriveStats, writelat)}, +}; + +static int +fillstat(io_registry_entry_t d, DriveStats *stat) +{ + CFDictionaryRef props, v; + CFNumberRef num; + kern_return_t status; + typeof(statstab[0]) *bp, *ep; + + status = IORegistryEntryCreateCFProperties(d, (CFMutableDictionaryRef *)&props, kCFAllocatorDefault, kNilOptions); + if(status != KERN_SUCCESS) + return -1; + v = (CFDictionaryRef)CFDictionaryGetValue(props, CFSTR(kIOBlockStorageDriverStatisticsKey)); + if(v == NULL){ + CFRelease(props); + return -1; + } + + ep = &statstab[sizeof(statstab)/sizeof(statstab[0])]; + for(bp = &statstab[0]; bp < ep; bp++){ + CFStringRef s; + + s = CFStringCreateWithCString(kCFAllocatorDefault, bp->key, CFStringGetSystemEncoding()); + num = (CFNumberRef)CFDictionaryGetValue(v, s); + if(num) + CFNumberGetValue(num, kCFNumberSInt64Type, ((char*)stat)+bp->off); + CFRelease(s); + } + + CFRelease(props); + return 0; +} diff --git a/vendor/github.com/lufia/iostat/iostat_darwin.go b/vendor/github.com/lufia/iostat/iostat_darwin.go new file mode 100644 index 00000000..81295469 --- /dev/null +++ b/vendor/github.com/lufia/iostat/iostat_darwin.go @@ -0,0 +1,37 @@ +// +build darwin + +package iostat + +// #cgo LDFLAGS: -framework CoreFoundation -framework IOKit +// #include +// #include "iostat_darwin.h" +import "C" +import ( + "time" +) + +// ReadDriveStats returns statictics of each of the drives. +func ReadDriveStats() ([]*DriveStats, error) { + var buf [C.NDRIVE]C.DriveStats + n, err := C.readdrivestat(&buf[0], C.int(len(buf))) + if err != nil { + return nil, err + } + stats := make([]*DriveStats, n) + for i := 0; i < int(n); i++ { + stats[i] = &DriveStats{ + Name: C.GoString(&buf[i].name[0]), + Size: int64(buf[i].size), + BlockSize: int64(buf[i].blocksize), + BytesRead: int64(buf[i].read), + BytesWritten: int64(buf[i].written), + NumRead: int64(buf[i].nread), + NumWrite: int64(buf[i].nwrite), + TotalReadTime: time.Duration(buf[i].readtime), + TotalWriteTime: time.Duration(buf[i].writetime), + ReadLatency: time.Duration(buf[i].readlat), + WriteLatency: time.Duration(buf[i].writelat), + } + } + return stats, nil +} diff --git a/vendor/github.com/lufia/iostat/iostat_darwin.h b/vendor/github.com/lufia/iostat/iostat_darwin.h new file mode 100644 index 00000000..7d1c8f6a --- /dev/null +++ b/vendor/github.com/lufia/iostat/iostat_darwin.h @@ -0,0 +1,23 @@ +typedef struct DriveStats DriveStats; + +enum { + NDRIVE = 16, + NAMELEN = 31 +}; + +struct DriveStats { + char name[NAMELEN+1]; + int64_t size; + int64_t blocksize; + + int64_t read; + int64_t written; + int64_t nread; + int64_t nwrite; + int64_t readtime; + int64_t writetime; + int64_t readlat; + int64_t writelat; +}; + +extern int readdrivestat(DriveStats a[], int n); diff --git a/vendor/github.com/lufia/iostat/iostat_linux.go b/vendor/github.com/lufia/iostat/iostat_linux.go new file mode 100644 index 00000000..cc212234 --- /dev/null +++ b/vendor/github.com/lufia/iostat/iostat_linux.go @@ -0,0 +1,12 @@ +// +build !darwin + +package iostat + +import ( + "errors" +) + +// ReadDriveStats returns statictics of each of the drives. +func ReadDriveStats() ([]*DriveStats, error) { + return nil, errors.New("not implement") +} diff --git a/vendor/vendor.json b/vendor/vendor.json index f347023e..35e4218a 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -50,6 +50,12 @@ "revision": "0826b98aaa29c0766956cb40d45cf7482a597671", "revisionTime": "2015-04-13T19:18:30Z" }, + { + "checksumSHA1": "82wQShWC1Udgn1UbTK42s0l/IpY=", + "path": "github.com/lufia/iostat", + "revision": "8c7e013c17ce06e6651d2affabe369045b98d3b7", + "revisionTime": "2017-06-03T08:40:47Z" + }, { "checksumSHA1": "bKMZjd2wPw13VwoE7mBeSv5djFA=", "path": "github.com/matttproud/golang_protobuf_extensions/pbutil",