Skip to content

Commit

Permalink
Update irqstat plugin
Browse files Browse the repository at this point in the history
The plugin reports IRQ metrics to telegraf based on IRQ instead
of CPU. It now parses `/proc/softirqs` as a separate measurement.
It also collects additional metrics including IRQ type, device
and IRQ value total.
  • Loading branch information
calerogers committed Apr 5, 2017
1 parent 079c2f4 commit 6c92430
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 49 deletions.
24 changes: 18 additions & 6 deletions plugins/inputs/irqstat/README.md
Original file line number Diff line number Diff line change
@@ -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`:
- <strong>Fields:</strong> IRQs
- <strong>Tags:</strong> 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
- <strong>Fields:</strong>
- CPUx: the IRQ value based on CPU number
- Total: total IRQ value of all CPUs
- <strong>Tags:</strong>
- IRQ: the IRQ
- Type: the type associated with the IRQ
- Device: the device associated with the IRQ
106 changes: 63 additions & 43 deletions plugins/inputs/irqstat/irqstat.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (

type Irqstat struct {
Include []string
Path string
Irqmap map[string]map[string]interface{}
}

Expand All @@ -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 {
Expand All @@ -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()
Expand All @@ -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
}
Expand Down

0 comments on commit 6c92430

Please sign in to comment.