151 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			151 lines
		
	
	
		
			3.7 KiB
		
	
	
	
		
			Go
		
	
	
	
| package interrupts
 | |
| 
 | |
| import (
 | |
| 	"bufio"
 | |
| 	"fmt"
 | |
| 	"io"
 | |
| 	"os"
 | |
| 	"strconv"
 | |
| 	"strings"
 | |
| 
 | |
| 	"github.com/influxdata/telegraf"
 | |
| 	"github.com/influxdata/telegraf/plugins/inputs"
 | |
| )
 | |
| 
 | |
| type Interrupts struct {
 | |
| 	CpuAsTag bool `toml:"cpu_as_tag"`
 | |
| }
 | |
| 
 | |
| type IRQ struct {
 | |
| 	ID     string
 | |
| 	Type   string
 | |
| 	Device string
 | |
| 	Total  int64
 | |
| 	Cpus   []int64
 | |
| }
 | |
| 
 | |
| func NewIRQ(id string) *IRQ {
 | |
| 	return &IRQ{ID: id, Cpus: []int64{}}
 | |
| }
 | |
| 
 | |
| const sampleConfig = `
 | |
|   ## When set to true, cpu metrics are tagged with the cpu.  Otherwise cpu is
 | |
|   ## stored as a field.
 | |
|   ##
 | |
|   ## The default is false for backwards compatibility, and will be changed to
 | |
|   ## true in a future version.  It is recommended to set to true on new
 | |
|   ## deployments.
 | |
|   # cpu_as_tag = false
 | |
| 
 | |
|   ## To filter which IRQs to collect, make use of tagpass / tagdrop, i.e.
 | |
|   # [inputs.interrupts.tagdrop]
 | |
|   #   irq = [ "NET_RX", "TASKLET" ]
 | |
| `
 | |
| 
 | |
| func (s *Interrupts) Description() string {
 | |
| 	return "This plugin gathers interrupts data from /proc/interrupts and /proc/softirqs."
 | |
| }
 | |
| 
 | |
| func (s *Interrupts) SampleConfig() string {
 | |
| 	return sampleConfig
 | |
| }
 | |
| 
 | |
| func parseInterrupts(r io.Reader) ([]IRQ, error) {
 | |
| 	var irqs []IRQ
 | |
| 	var cpucount int
 | |
| 	scanner := bufio.NewScanner(r)
 | |
| 	if scanner.Scan() {
 | |
| 		cpus := strings.Fields(scanner.Text())
 | |
| 		if cpus[0] != "CPU0" {
 | |
| 			return nil, fmt.Errorf("Expected first line to start with CPU0, but was %s", scanner.Text())
 | |
| 		}
 | |
| 		cpucount = len(cpus)
 | |
| 	}
 | |
| 
 | |
| scan:
 | |
| 	for scanner.Scan() {
 | |
| 		fields := strings.Fields(scanner.Text())
 | |
| 		if !strings.HasSuffix(fields[0], ":") {
 | |
| 			continue
 | |
| 		}
 | |
| 		irqid := strings.TrimRight(fields[0], ":")
 | |
| 		irq := NewIRQ(irqid)
 | |
| 		irqvals := fields[1:]
 | |
| 		for i := 0; i < cpucount; i++ {
 | |
| 			if i < len(irqvals) {
 | |
| 				irqval, err := strconv.ParseInt(irqvals[i], 10, 64)
 | |
| 				if err != nil {
 | |
| 					continue scan
 | |
| 				}
 | |
| 				irq.Cpus = append(irq.Cpus, irqval)
 | |
| 			}
 | |
| 		}
 | |
| 		for _, irqval := range irq.Cpus {
 | |
| 			irq.Total += irqval
 | |
| 		}
 | |
| 		_, err := strconv.ParseInt(irqid, 10, 64)
 | |
| 		if err == nil && len(fields) >= cpucount+2 {
 | |
| 			irq.Type = fields[cpucount+1]
 | |
| 			irq.Device = strings.Join(fields[cpucount+2:], " ")
 | |
| 		} else if len(fields) > cpucount {
 | |
| 			irq.Type = strings.Join(fields[cpucount+1:], " ")
 | |
| 		}
 | |
| 		irqs = append(irqs, *irq)
 | |
| 	}
 | |
| 	if scanner.Err() != nil {
 | |
| 		return nil, fmt.Errorf("Error scanning file: %s", scanner.Err())
 | |
| 	}
 | |
| 	return irqs, nil
 | |
| }
 | |
| 
 | |
| func gatherTagsFields(irq IRQ) (map[string]string, map[string]interface{}) {
 | |
| 	tags := map[string]string{"irq": irq.ID, "type": irq.Type, "device": irq.Device}
 | |
| 	fields := map[string]interface{}{"total": irq.Total}
 | |
| 	for i := 0; i < len(irq.Cpus); i++ {
 | |
| 		cpu := fmt.Sprintf("CPU%d", i)
 | |
| 		fields[cpu] = irq.Cpus[i]
 | |
| 	}
 | |
| 	return tags, fields
 | |
| }
 | |
| 
 | |
| func (s *Interrupts) Gather(acc telegraf.Accumulator) error {
 | |
| 	for measurement, file := range map[string]string{"interrupts": "/proc/interrupts", "soft_interrupts": "/proc/softirqs"} {
 | |
| 		f, err := os.Open(file)
 | |
| 		if err != nil {
 | |
| 			acc.AddError(fmt.Errorf("Could not open file: %s", file))
 | |
| 			continue
 | |
| 		}
 | |
| 		defer f.Close()
 | |
| 		irqs, err := parseInterrupts(f)
 | |
| 		if err != nil {
 | |
| 			acc.AddError(fmt.Errorf("Parsing %s: %s", file, err))
 | |
| 			continue
 | |
| 		}
 | |
| 		reportMetrics(measurement, irqs, acc, s.CpuAsTag)
 | |
| 	}
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func reportMetrics(measurement string, irqs []IRQ, acc telegraf.Accumulator, cpusAsTags bool) {
 | |
| 	for _, irq := range irqs {
 | |
| 		tags, fields := gatherTagsFields(irq)
 | |
| 		if cpusAsTags {
 | |
| 			for cpu, count := range irq.Cpus {
 | |
| 				cpuTags := map[string]string{"cpu": fmt.Sprintf("cpu%d", cpu)}
 | |
| 				for k, v := range tags {
 | |
| 					cpuTags[k] = v
 | |
| 				}
 | |
| 				acc.AddFields(measurement, map[string]interface{}{"count": count}, cpuTags)
 | |
| 			}
 | |
| 		} else {
 | |
| 			acc.AddFields(measurement, fields, tags)
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func init() {
 | |
| 	inputs.Add("interrupts", func() telegraf.Input {
 | |
| 		return &Interrupts{}
 | |
| 	})
 | |
| }
 |