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])
+ }
+ }
+ }
+}