222 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			222 lines
		
	
	
		
			5.1 KiB
		
	
	
	
		
			Go
		
	
	
	
| package diskio
 | |
| 
 | |
| import (
 | |
| 	"fmt"
 | |
| 	"log"
 | |
| 	"regexp"
 | |
| 	"strings"
 | |
| 
 | |
| 	"github.com/influxdata/telegraf"
 | |
| 	"github.com/influxdata/telegraf/filter"
 | |
| 	"github.com/influxdata/telegraf/plugins/inputs"
 | |
| 	"github.com/influxdata/telegraf/plugins/inputs/system"
 | |
| )
 | |
| 
 | |
| var (
 | |
| 	varRegex = regexp.MustCompile(`\$(?:\w+|\{\w+\})`)
 | |
| )
 | |
| 
 | |
| type DiskIO struct {
 | |
| 	ps system.PS
 | |
| 
 | |
| 	Devices          []string
 | |
| 	DeviceTags       []string
 | |
| 	NameTemplates    []string
 | |
| 	SkipSerialNumber bool
 | |
| 
 | |
| 	infoCache    map[string]diskInfoCache
 | |
| 	deviceFilter filter.Filter
 | |
| 	initialized  bool
 | |
| }
 | |
| 
 | |
| func (_ *DiskIO) Description() string {
 | |
| 	return "Read metrics about disk IO by device"
 | |
| }
 | |
| 
 | |
| var diskIOsampleConfig = `
 | |
|   ## By default, telegraf will gather stats for all devices including
 | |
|   ## disk partitions.
 | |
|   ## Setting devices will restrict the stats to the specified devices.
 | |
|   # devices = ["sda", "sdb", "vd*"]
 | |
|   ## Uncomment the following line if you need disk serial numbers.
 | |
|   # skip_serial_number = false
 | |
|   #
 | |
|   ## On systems which support it, device metadata can be added in the form of
 | |
|   ## tags.
 | |
|   ## Currently only Linux is supported via udev properties. You can view
 | |
|   ## available properties for a device by running:
 | |
|   ## 'udevadm info -q property -n /dev/sda'
 | |
|   ## Note: Most, but not all, udev properties can be accessed this way. Properties
 | |
|   ## that are currently inaccessible include DEVTYPE, DEVNAME, and DEVPATH.
 | |
|   # device_tags = ["ID_FS_TYPE", "ID_FS_USAGE"]
 | |
|   #
 | |
|   ## Using the same metadata source as device_tags, you can also customize the
 | |
|   ## name of the device via templates.
 | |
|   ## The 'name_templates' parameter is a list of templates to try and apply to
 | |
|   ## the device. The template may contain variables in the form of '$PROPERTY' or
 | |
|   ## '${PROPERTY}'. The first template which does not contain any variables not
 | |
|   ## present for the device is used as the device name tag.
 | |
|   ## The typical use case is for LVM volumes, to get the VG/LV name instead of
 | |
|   ## the near-meaningless DM-0 name.
 | |
|   # name_templates = ["$ID_FS_LABEL","$DM_VG_NAME/$DM_LV_NAME"]
 | |
| `
 | |
| 
 | |
| func (_ *DiskIO) SampleConfig() string {
 | |
| 	return diskIOsampleConfig
 | |
| }
 | |
| 
 | |
| // hasMeta reports whether s contains any special glob characters.
 | |
| func hasMeta(s string) bool {
 | |
| 	return strings.IndexAny(s, "*?[") >= 0
 | |
| }
 | |
| 
 | |
| func (s *DiskIO) init() error {
 | |
| 	for _, device := range s.Devices {
 | |
| 		if hasMeta(device) {
 | |
| 			filter, err := filter.Compile(s.Devices)
 | |
| 			if err != nil {
 | |
| 				return fmt.Errorf("error compiling device pattern: %v", err)
 | |
| 			}
 | |
| 			s.deviceFilter = filter
 | |
| 		}
 | |
| 	}
 | |
| 	s.initialized = true
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (s *DiskIO) Gather(acc telegraf.Accumulator) error {
 | |
| 	if !s.initialized {
 | |
| 		err := s.init()
 | |
| 		if err != nil {
 | |
| 			return err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	devices := []string{}
 | |
| 	if s.deviceFilter == nil {
 | |
| 		devices = s.Devices
 | |
| 	}
 | |
| 
 | |
| 	diskio, err := s.ps.DiskIO(devices)
 | |
| 	if err != nil {
 | |
| 		return fmt.Errorf("error getting disk io info: %s", err)
 | |
| 	}
 | |
| 
 | |
| 	for _, io := range diskio {
 | |
| 
 | |
| 		match := false
 | |
| 		if s.deviceFilter != nil && s.deviceFilter.Match(io.Name) {
 | |
| 			match = true
 | |
| 		}
 | |
| 
 | |
| 		tags := map[string]string{}
 | |
| 		var devLinks []string
 | |
| 		tags["name"], devLinks = s.diskName(io.Name)
 | |
| 
 | |
| 		if s.deviceFilter != nil && !match {
 | |
| 			for _, devLink := range devLinks {
 | |
| 				if s.deviceFilter.Match(devLink) {
 | |
| 					match = true
 | |
| 					break
 | |
| 				}
 | |
| 			}
 | |
| 			if !match {
 | |
| 				continue
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		for t, v := range s.diskTags(io.Name) {
 | |
| 			tags[t] = v
 | |
| 		}
 | |
| 
 | |
| 		if !s.SkipSerialNumber {
 | |
| 			if len(io.SerialNumber) != 0 {
 | |
| 				tags["serial"] = io.SerialNumber
 | |
| 			} else {
 | |
| 				tags["serial"] = "unknown"
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		fields := map[string]interface{}{
 | |
| 			"reads":            io.ReadCount,
 | |
| 			"writes":           io.WriteCount,
 | |
| 			"read_bytes":       io.ReadBytes,
 | |
| 			"write_bytes":      io.WriteBytes,
 | |
| 			"read_time":        io.ReadTime,
 | |
| 			"write_time":       io.WriteTime,
 | |
| 			"io_time":          io.IoTime,
 | |
| 			"weighted_io_time": io.WeightedIO,
 | |
| 			"iops_in_progress": io.IopsInProgress,
 | |
| 		}
 | |
| 		acc.AddCounter("diskio", fields, tags)
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (s *DiskIO) diskName(devName string) (string, []string) {
 | |
| 	di, err := s.diskInfo(devName)
 | |
| 	devLinks := strings.Split(di["DEVLINKS"], " ")
 | |
| 	for i, devLink := range devLinks {
 | |
| 		devLinks[i] = strings.TrimPrefix(devLink, "/dev/")
 | |
| 	}
 | |
| 
 | |
| 	if len(s.NameTemplates) == 0 {
 | |
| 		return devName, devLinks
 | |
| 	}
 | |
| 
 | |
| 	if err != nil {
 | |
| 		log.Printf("W! Error gathering disk info: %s", err)
 | |
| 		return devName, devLinks
 | |
| 	}
 | |
| 
 | |
| 	for _, nt := range s.NameTemplates {
 | |
| 		miss := false
 | |
| 		name := varRegex.ReplaceAllStringFunc(nt, func(sub string) string {
 | |
| 			sub = sub[1:] // strip leading '$'
 | |
| 			if sub[0] == '{' {
 | |
| 				sub = sub[1 : len(sub)-1] // strip leading & trailing '{' '}'
 | |
| 			}
 | |
| 			if v, ok := di[sub]; ok {
 | |
| 				return v
 | |
| 			}
 | |
| 			miss = true
 | |
| 			return ""
 | |
| 		})
 | |
| 
 | |
| 		if !miss {
 | |
| 			return name, devLinks
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return devName, devLinks
 | |
| }
 | |
| 
 | |
| func (s *DiskIO) diskTags(devName string) map[string]string {
 | |
| 	if len(s.DeviceTags) == 0 {
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	di, err := s.diskInfo(devName)
 | |
| 	if err != nil {
 | |
| 		log.Printf("W! Error gathering disk info: %s", err)
 | |
| 		return nil
 | |
| 	}
 | |
| 
 | |
| 	tags := map[string]string{}
 | |
| 	for _, dt := range s.DeviceTags {
 | |
| 		if v, ok := di[dt]; ok {
 | |
| 			tags[dt] = v
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return tags
 | |
| }
 | |
| 
 | |
| func init() {
 | |
| 	ps := system.NewSystemPS()
 | |
| 	inputs.Add("diskio", func() telegraf.Input {
 | |
| 		return &DiskIO{ps: ps, SkipSerialNumber: true}
 | |
| 	})
 | |
| }
 |