forked from influxdata/telegraf
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Irqstat input plugin (influxdata#2494)
closes influxdata#2469
- Loading branch information
1 parent
dff216c
commit cadd845
Showing
6 changed files
with
237 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
# Interrupts Input Plugin | ||
|
||
The interrupts plugin gathers metrics about IRQs from `/proc/interrupts` and `/proc/softirqs`. | ||
|
||
### Configuration | ||
``` | ||
[[inputs.interrupts]] | ||
## A list of IRQs to include for metric ingestion, if not specified | ||
## will default to collecting all IRQs. | ||
include = ["0", "1", "30", "NET_RX"] | ||
``` | ||
|
||
### Measurements | ||
There are two measurements reported by this plugin. | ||
- `interrupts` gathers metrics from the `/proc/interrupts` file | ||
- `soft_interrupts` gathers metrics from the `/proc/softirqs` file | ||
|
||
### Fields | ||
- CPUx: the amount of interrupts for the IRQ handled by that CPU | ||
- total: total amount of interrupts for all CPUs | ||
|
||
### Tags | ||
- irq: the IRQ | ||
- type: the type of interrupt | ||
- device: the name of the device that is located at that IRQ | ||
|
||
### Example Output | ||
``` | ||
./telegraf -config ~/interrupts_config.conf -test | ||
* Plugin: inputs.interrupts, Collection 1 | ||
> 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 | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
package interrupts | ||
|
||
import ( | ||
"bufio" | ||
"fmt" | ||
"github.com/influxdata/telegraf" | ||
"github.com/influxdata/telegraf/plugins/inputs" | ||
"io/ioutil" | ||
"strconv" | ||
"strings" | ||
) | ||
|
||
type Interrupts struct{} | ||
|
||
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 = ` | ||
## 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(irqdata string) ([]IRQ, error) { | ||
var irqs []IRQ | ||
var cpucount int | ||
scanner := bufio.NewScanner(strings.NewReader(irqdata)) | ||
ok := scanner.Scan() | ||
if ok { | ||
cpus := strings.Fields(scanner.Text()) | ||
if cpus[0] == "CPU0" { | ||
cpucount = len(cpus) | ||
} | ||
} else if scanner.Err() != nil { | ||
return irqs, fmt.Errorf("Reading %s: %s", scanner.Text(), scanner.Err()) | ||
} | ||
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:len(fields)] | ||
for i := 0; i < cpucount; i++ { | ||
if i < len(irqvals) { | ||
irqval, err := strconv.ParseInt(irqvals[i], 10, 64) | ||
if err != nil { | ||
return irqs, fmt.Errorf("Unable to parse %q from %q: %s", irqvals[i], scanner.Text(), err) | ||
} | ||
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) | ||
} | ||
return irqs, nil | ||
} | ||
|
||
func fileToString(path string) (string, error) { | ||
data, err := ioutil.ReadFile(path) | ||
if err != nil { | ||
return "", err | ||
} | ||
content := string(data) | ||
return content, 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 { | ||
irqdata, err := fileToString("/proc/interrupts") | ||
if err != nil { | ||
acc.AddError(fmt.Errorf("Reading %s: %s", "/proc/interrupts", err)) | ||
} | ||
irqs, err := parseInterrupts(irqdata) | ||
if err != nil { | ||
acc.AddError(fmt.Errorf("Parsing %s: %s", "/proc/interrupts", err)) | ||
} else { | ||
for _, irq := range irqs { | ||
tags, fields := gatherTagsFields(irq) | ||
acc.AddFields("interrupts", fields, tags) | ||
} | ||
} | ||
|
||
irqdata, err = fileToString("/proc/softirqs") | ||
if err != nil { | ||
acc.AddError(fmt.Errorf("Reading %s: %s", "/proc/softirqs", err)) | ||
} | ||
irqs, err = parseInterrupts(irqdata) | ||
if err != nil { | ||
acc.AddError(fmt.Errorf("Parsing %s: %s", "/proc/softirqs", err)) | ||
} else { | ||
for _, irq := range irqs { | ||
tags, fields := gatherTagsFields(irq) | ||
acc.AddFields("softirqs", fields, tags) | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func init() { | ||
inputs.Add("interrupts", func() telegraf.Input { | ||
return &Interrupts{} | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
package interrupts | ||
|
||
import ( | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
"testing" | ||
) | ||
|
||
func TestParseInterrupts(t *testing.T) { | ||
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 | ||
NET_RX: 867028 225 | ||
TASKLET: 205 0` | ||
|
||
parsed := []IRQ{ | ||
IRQ{ | ||
ID: "0", Type: "IO-APIC-edge", Device: "timer", | ||
Cpus: []int64{int64(134), int64(0)}, Total: int64(134), | ||
}, | ||
IRQ{ | ||
ID: "1", Type: "IO-APIC-edge", Device: "i8042", | ||
Cpus: []int64{int64(7), int64(3)}, Total: int64(10), | ||
}, | ||
IRQ{ | ||
ID: "NMI", Type: "Non-maskable interrupts", | ||
Cpus: []int64{int64(0), int64(0)}, Total: int64(0), | ||
}, | ||
IRQ{ | ||
ID: "LOC", Type: "Local timer interrupts", | ||
Cpus: []int64{int64(2338608687), int64(2334309625)}, | ||
Total: int64(4672918312), | ||
}, | ||
IRQ{ | ||
ID: "MIS", Cpus: []int64{int64(0)}, Total: int64(0), | ||
}, | ||
IRQ{ | ||
ID: "NET_RX", Cpus: []int64{int64(867028), int64(225)}, | ||
Total: int64(867253), | ||
}, | ||
IRQ{ | ||
ID: "TASKLET", Cpus: []int64{int64(205), int64(0)}, | ||
Total: int64(205), | ||
}, | ||
} | ||
got, err := parseInterrupts(interruptStr) | ||
require.Equal(t, nil, err) | ||
require.NotEqual(t, 0, len(got)) | ||
require.Equal(t, len(got), len(parsed)) | ||
for i := 0; i < len(parsed); i++ { | ||
assert.Equal(t, parsed[i], got[i]) | ||
for k := 0; k < len(parsed[i].Cpus); k++ { | ||
assert.Equal(t, parsed[i].Cpus[k], got[i].Cpus[k]) | ||
} | ||
} | ||
} |