diff --git a/plugins/inputs/irqstat/README.md b/plugins/inputs/irqstat/README.md index b3da55b92589a..3bc83a04195c6 100644 --- a/plugins/inputs/irqstat/README.md +++ b/plugins/inputs/irqstat/README.md @@ -1,22 +1,34 @@ # Irqstat Input Plugin -The irqstat plugin gathers metrics about the interrupt types and associated values for each CPU present on a system. +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. ### Configuration ``` [[inputs.irqstat]] - include = ["0", "1"] + include = ["0", "1", "30", "NET_RX"] ``` The above configuration would result in an output similar to: ``` ./telegraf -config ~/irqstat_config.conf -test * Plugin: inputs.irqstat, Collection 1 -> irqstat,host=hostname,cpu=CPU0 1=9i,0=22i 1488751337000000000 +> interrupts,irq=30,type=PCI-MSI,device=65537-edge\ virtio1-input.0,host=hostname CPU0=1i,total=1i 1489346531000000000 +> interrupts,irq=1,host=hostname,type=IO-APIC,device=1-edge\ i8042 CPU0=9i,total=9i 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 -There is only one measurement reported by this plugin, `irqstat`: -- Fields: IRQs -- Tags: CPUs +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 diff --git a/plugins/inputs/irqstat/irqstat.go b/plugins/inputs/irqstat/irqstat.go index 130577414d6f2..2b6015e41e77a 100644 --- a/plugins/inputs/irqstat/irqstat.go +++ b/plugins/inputs/irqstat/irqstat.go @@ -13,7 +13,6 @@ import ( type Irqstat struct { Include []string - Path string Irqmap map[string]map[string]interface{} } @@ -27,13 +26,10 @@ const sampleConfig = ` ## A list of IRQs to include for metric ingestion, if not specified ## will default to collecting all IRQs. # include = ["0", "1"] - # - ## The location of the interrupts file, defaults to /proc/interrupts. - # path = "/some/path/interrupts" ` func (s *Irqstat) Description() string { - return "This plugin gathers IRQ types and associated values from /proc/interrupts for each CPU." + return "This plugin gathers IRQ types and associated values from /proc/interrupts and /proc/softirqs for each CPU." } func (s *Irqstat) SampleConfig() string { @@ -46,35 +42,51 @@ func (s *Irqstat) ParseIrqFile(path string) { if err != nil { log.Fatal(err) } - scanner := bufio.NewScanner(file) for scanner.Scan() { + var irqval int64 + var irqtotal int64 + irqdesc := "none" + irqdevice := "none" fields := strings.Fields(scanner.Text()) ff := fields[0] - if ff == "CPU0" { cpucount = len(fields) } if ff[len(ff)-1:] == ":" { - irqtype := 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) + if err == nil { + irqdesc = fields[cpucount] + irqdevice = strings.Join(fields[cpucount+1:], " ") + } else { + if len(fields) > cpucount { + irqdesc = strings.Join(fields[cpucount:], " ") + } + } for i := 0; i < cpucount; i++ { cpukey := fmt.Sprintf("CPU%d", i) - - if s.Irqmap[cpukey] == nil { - s.Irqmap[cpukey] = make(map[string]interface{}) + if s.Irqmap[irqtype] == nil { + s.Irqmap[irqtype] = make(map[string]interface{}) } - - irqval := 0 // Default an IRQ's value to 0 + irqval = 0 if i < len(fields) { - irqval, err = strconv.Atoi(fields[i]) + irqval, err = strconv.ParseInt(fields[i], 10, 64) if err != nil { log.Fatal(err) } } - s.Irqmap[cpukey][irqtype] = irqval + s.Irqmap[irqtype][cpukey] = irqval + irqtotal = irqval + irqtotal } + s.Irqmap[irqtype]["type"] = irqdesc + s.Irqmap[irqtype]["device"] = irqdevice + s.Irqmap[irqtype]["total"] = irqtotal } } file.Close() @@ -89,36 +101,44 @@ func stringInSlice(x string, list []string) bool { return false } -func (s *Irqstat) ParseIrqMap(include []string) { - for cpukey, irqfields := range s.Irqmap { - s.Irqmap[cpukey] = make(map[string]interface{}) - for irqtype, irqval := range irqfields { - if stringInSlice(irqtype, include) { - s.Irqmap[cpukey][irqtype] = irqval - } - } - } -} - func (s *Irqstat) Gather(acc telegraf.Accumulator) error { - cputags := make(map[string]string) - path := s.Path - include := s.Include - - if len(path) == 0 { - path = "/proc/interrupts" - } - - if len(include) == 0 { - s.ParseIrqFile(path) - } else { - s.ParseIrqFile(path) - s.ParseIrqMap(include) + irqtags := make(map[string]string) + irqfields := make(map[string]interface{}) + files := []string{"/proc/interrupts", "/proc/softirqs"} + for _, file := range files { + s.ParseIrqFile(file) } - - for cpukey, irqfields := range s.Irqmap { - cputags["cpu"] = cpukey - acc.AddFields("irqstat", irqfields, cputags) + 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 + } + } + 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) + } 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) + } + } + } } return nil }