diff --git a/plugins/inputs/irqstat/README.md b/plugins/inputs/irqstat/README.md index 3bc83a04195c6..eab1c47edbf8f 100644 --- a/plugins/inputs/irqstat/README.md +++ b/plugins/inputs/irqstat/README.md @@ -1,6 +1,6 @@ # Irqstat Input Plugin -The irqstat plugin gathers metrics about the interrupt types and associated values from `/proc/interrupts` and `/proc/softirqs` for each CPU present on a system. +The irqstat plugin gathers metrics about IRQs from `/proc/interrupts` and `/proc/softirqs`. ### Configuration ``` @@ -12,10 +12,10 @@ The above configuration would result in an output similar to: ``` ./telegraf -config ~/irqstat_config.conf -test * Plugin: inputs.irqstat, Collection 1 -> interrupts,irq=30,type=PCI-MSI,device=65537-edge\ virtio1-input.0,host=hostname CPU0=1i,total=1i 1489346531000000000 +> interrupts,irq=0,type=IO-APIC,device=2-edge\ timer,host=hostname CPU0=23i,total=23i 1489346531000000000 > interrupts,irq=1,host=hostname,type=IO-APIC,device=1-edge\ i8042 CPU0=9i,total=9i 1489346531000000000 +> interrupts,irq=30,type=PCI-MSI,device=65537-edge\ virtio1-input.0,host=hostname CPU0=1i,total=1i 1489346531000000000 > soft_interrupts,irq=NET_RX,host=hostname CPU0=280879i,total=280879i 1489346531000000000 -> interrupts,irq=0,type=IO-APIC,device=2-edge\ timer,host=hostname CPU0=23i,total=23i 1489346531000000000 ``` # Measurements @@ -24,11 +24,10 @@ There are two measurements reported by this plugin. - `interrupts` reports metrics from the `/proc/interrupts` file - `soft_interrupts` reports metrics from the `/proc/softirqs` file -Depending on the content of each file there will multiple tags and fields for each measurement -- Fields: - - CPUx: the IRQ value based on CPU number - - Total: total IRQ value of all CPUs -- Tags: - - IRQ: the IRQ - - Type: the type associated with the IRQ - - Device: the device associated with the IRQ +#### Fields: +- CPUx: the amount of interrupts for the IRQ handled by that CPU +- Total: total amount of interrupts for all CPUs +#### Tags: +- IRQ: the interrupt ID +- Type: the type of interrupt +- Device: the name of the device that is located at that IRQ diff --git a/plugins/inputs/irqstat/irqstat.go b/plugins/inputs/irqstat/irqstat.go index 2b6015e41e77a..ea34b00c8eb5b 100644 --- a/plugins/inputs/irqstat/irqstat.go +++ b/plugins/inputs/irqstat/irqstat.go @@ -5,49 +5,47 @@ import ( "fmt" "github.com/influxdata/telegraf" "github.com/influxdata/telegraf/plugins/inputs" + "io/ioutil" "log" - "os" "strconv" "strings" ) type Irqstat struct { Include []string - Irqmap map[string]map[string]interface{} } -func NewIrqstat() *Irqstat { - return &Irqstat{ - Irqmap: make(map[string]map[string]interface{}), - } +type IRQ struct { + ID string + Fields map[string]interface{} + Tags map[string]string +} + +func NewIRQ(id string) *IRQ { + return &IRQ{ID: id, Fields: make(map[string]interface{}), Tags: make(map[string]string)} } const sampleConfig = ` ## A list of IRQs to include for metric ingestion, if not specified ## will default to collecting all IRQs. - # include = ["0", "1"] + # include = ["0", "1", "30", "NET_RX"] ` func (s *Irqstat) Description() string { - return "This plugin gathers IRQ types and associated values from /proc/interrupts and /proc/softirqs for each CPU." + return "This plugin gathers interrupts data from /proc/interrupts and /proc/softirqs." } func (s *Irqstat) SampleConfig() string { return sampleConfig } -func (s *Irqstat) ParseIrqFile(path string) { +func parseInterrupts(irqdata string, include []string) []IRQ { + var irqs []IRQ var cpucount int - file, err := os.Open(path) - if err != nil { - log.Fatal(err) - } - scanner := bufio.NewScanner(file) + scanner := bufio.NewScanner(strings.NewReader(irqdata)) for scanner.Scan() { - var irqval int64 - var irqtotal int64 - irqdesc := "none" - irqdevice := "none" + var irqval, irqtotal int64 + var irqtype, irqdevice string fields := strings.Fields(scanner.Text()) ff := fields[0] if ff == "CPU0" { @@ -56,24 +54,20 @@ func (s *Irqstat) ParseIrqFile(path string) { if ff[len(ff)-1:] == ":" { fields = fields[1:len(fields)] - irqtype := ff[:len(ff)-1] - if path == "/proc/softirqs" { - irqtype = irqtype + "_softirq" - } - _, err := strconv.ParseInt(irqtype, 10, 64) + irqid := ff[:len(ff)-1] + irq := NewIRQ(irqid) + _, err := strconv.ParseInt(irqid, 10, 64) if err == nil { - irqdesc = fields[cpucount] + irqtype = fields[cpucount] irqdevice = strings.Join(fields[cpucount+1:], " ") } else { if len(fields) > cpucount { - irqdesc = strings.Join(fields[cpucount:], " ") + irqtype = strings.Join(fields[cpucount:], " ") } } for i := 0; i < cpucount; i++ { - cpukey := fmt.Sprintf("CPU%d", i) - if s.Irqmap[irqtype] == nil { - s.Irqmap[irqtype] = make(map[string]interface{}) - } + cpu := fmt.Sprintf("CPU%d", i) + irq.Tags["irq"] = irqid irqval = 0 if i < len(fields) { irqval, err = strconv.ParseInt(fields[i], 10, 64) @@ -81,15 +75,22 @@ func (s *Irqstat) ParseIrqFile(path string) { log.Fatal(err) } } - s.Irqmap[irqtype][cpukey] = irqval + irq.Fields[cpu] = irqval irqtotal = irqval + irqtotal } - s.Irqmap[irqtype]["type"] = irqdesc - s.Irqmap[irqtype]["device"] = irqdevice - s.Irqmap[irqtype]["total"] = irqtotal + irq.Tags["type"] = irqtype + irq.Tags["device"] = irqdevice + irq.Fields["total"] = irqtotal + if len(include) == 0 { + irqs = append(irqs, *irq) + } else { + if stringInSlice(irq.ID, include) { + irqs = append(irqs, *irq) + } + } } } - file.Close() + return irqs } func stringInSlice(x string, list []string) bool { @@ -102,41 +103,19 @@ func stringInSlice(x string, list []string) bool { } func (s *Irqstat) Gather(acc telegraf.Accumulator) error { - irqtags := make(map[string]string) - irqfields := make(map[string]interface{}) files := []string{"/proc/interrupts", "/proc/softirqs"} for _, file := range files { - s.ParseIrqFile(file) - } - for irq, fields := range s.Irqmap { - irqtype := strings.Split(irq, "_softirq")[0] - irqtags["irq"] = irqtype - for k, v := range fields { - switch t := v.(type) { - case int64: - irqfields[k] = t - case string: - irqtags[k] = t - } + data, err := ioutil.ReadFile(file) + if err != nil { + log.Fatal(err) } - for k, _ := range irqtags { - if irqtags[k] == "none" { - delete(irqtags, k) - } - } - if len(s.Include) == 0 { - if strings.HasSuffix(irq, "_softirq") { - acc.AddFields("soft_interrupts", irqfields, irqtags) + irqdata := string(data) + irqs := parseInterrupts(irqdata, s.Include) + for _, irq := range irqs { + if file == "/proc/softirqs" { + acc.AddFields("soft_interrupts", irq.Fields, irq.Tags) } else { - acc.AddFields("interrupts", irqfields, irqtags) - } - } else { - if stringInSlice(irqtype, s.Include) { - if strings.HasSuffix(irq, "_softirq") { - acc.AddFields("soft_interrupts", irqfields, irqtags) - } else { - acc.AddFields("interrupts", irqfields, irqtags) - } + acc.AddFields("interrupts", irq.Fields, irq.Tags) } } } @@ -145,6 +124,6 @@ func (s *Irqstat) Gather(acc telegraf.Accumulator) error { func init() { inputs.Add("irqstat", func() telegraf.Input { - return NewIrqstat() + return &Irqstat{} }) } diff --git a/plugins/inputs/irqstat/irqstat_test.go b/plugins/inputs/irqstat/irqstat_test.go new file mode 100644 index 0000000000000..3672062e8312c --- /dev/null +++ b/plugins/inputs/irqstat/irqstat_test.go @@ -0,0 +1,38 @@ +package irqstat + +import "testing" + +func TestParseInterrupts(t *testing.T) { + include := []string{} + interruptStr := ` CPU0 CPU1 + 0: 134 0 IO-APIC-edge timer + 1: 7 3 IO-APIC-edge i8042 +NMI: 0 0 Non-maskable interrupts +LOC: 2338608687 2334309625 Local timer interrupts +MIS: 0` + + parsed := []IRQ{ + IRQ{ID: "0", Fields: map[string]interface{}{"CPU0": int64(134), "CPU1": int64(0)}, Tags: map[string]string{"type": "IO-APIC-edge", "device": "timer"}}, + IRQ{ID: "1", Fields: map[string]interface{}{"CPU0": int64(7), "CPU1": int64(3)}, Tags: map[string]string{"type": "IO-APIC-edge", "device": "i8042"}}, + IRQ{ID: "NMI", Fields: map[string]interface{}{"CPU0": int64(0), "CPU1": int64(0)}, Tags: map[string]string{"type": "Non-maskable interrupts"}}, + IRQ{ID: "LOC", Fields: map[string]interface{}{"CPU0": int64(2338608687), "CPU1": int64(2334309625)}, Tags: map[string]string{"type": "Local timer interrupts"}}, + IRQ{ID: "MIS", Fields: map[string]interface{}{"CPU0": int64(0), "CPU1": int64(0)}}, + } + + got := parseInterrupts(interruptStr, include) + if len(got) == 0 { + t.Fatalf("want %+v, got %+v", parsed, got) + } + for i := 0; i < len(parsed); i++ { + for k, _ := range parsed[i].Fields { + if parsed[i].Fields[k] != got[i].Fields[k] { + t.Fatalf("want %+v, got %+v", parsed[i].Fields[k], got[i].Fields[k]) + } + } + for k, _ := range parsed[i].Tags { + if parsed[i].Tags[k] != got[i].Tags[k] { + t.Fatalf("want %+v, got %+v", parsed[i].Tags[k], got[i].Tags[k]) + } + } + } +}