renaming plugins -> inputs
This commit is contained in:
83
plugins/inputs/system/CPU_README.md
Normal file
83
plugins/inputs/system/CPU_README.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# Telegraf plugin: CPU
|
||||
|
||||
#### Plugin arguments:
|
||||
- **totalcpu** boolean: If true, include `cpu-total` data
|
||||
- **percpu** boolean: If true, include data on a per-cpu basis `cpu0, cpu1, etc.`
|
||||
|
||||
#### Description
|
||||
|
||||
The CPU plugin collects standard CPU metrics as defined in `man proc`. All
|
||||
architectures do not support all of these metrics.
|
||||
|
||||
```
|
||||
cpu 3357 0 4313 1362393
|
||||
The amount of time, measured in units of USER_HZ (1/100ths of a second on
|
||||
most architectures, use sysconf(_SC_CLK_TCK) to obtain the right value),
|
||||
that the system spent in various states:
|
||||
|
||||
user (1) Time spent in user mode.
|
||||
|
||||
nice (2) Time spent in user mode with low priority (nice).
|
||||
|
||||
system (3) Time spent in system mode.
|
||||
|
||||
idle (4) Time spent in the idle task. This value should be USER_HZ times
|
||||
the second entry in the /proc/uptime pseudo-file.
|
||||
|
||||
iowait (since Linux 2.5.41)
|
||||
(5) Time waiting for I/O to complete.
|
||||
|
||||
irq (since Linux 2.6.0-test4)
|
||||
(6) Time servicing interrupts.
|
||||
|
||||
softirq (since Linux 2.6.0-test4)
|
||||
(7) Time servicing softirqs.
|
||||
|
||||
steal (since Linux 2.6.11)
|
||||
(8) Stolen time, which is the time spent in other operating systems
|
||||
when running in a virtualized environment
|
||||
|
||||
guest (since Linux 2.6.24)
|
||||
(9) Time spent running a virtual CPU for guest operating systems
|
||||
under the control of the Linux kernel.
|
||||
|
||||
guest_nice (since Linux 2.6.33)
|
||||
(10) Time spent running a niced guest (virtual CPU for guest operating systems under the control of the Linux kernel).
|
||||
```
|
||||
|
||||
# Measurements:
|
||||
### CPU Time measurements:
|
||||
|
||||
Meta:
|
||||
- units: CPU Time
|
||||
- tags: `cpu=<cpuN> or <cpu-total>`
|
||||
|
||||
Measurement names:
|
||||
- cpu_time_user
|
||||
- cpu_time_system
|
||||
- cpu_time_idle
|
||||
- cpu_time_nice
|
||||
- cpu_time_iowait
|
||||
- cpu_time_irq
|
||||
- cpu_time_softirq
|
||||
- cpu_time_steal
|
||||
- cpu_time_guest
|
||||
- cpu_time_guest_nice
|
||||
|
||||
### CPU Usage Percent Measurements:
|
||||
|
||||
Meta:
|
||||
- units: percent (out of 100)
|
||||
- tags: `cpu=<cpuN> or <cpu-total>`
|
||||
|
||||
Measurement names:
|
||||
- cpu_usage_user
|
||||
- cpu_usage_system
|
||||
- cpu_usage_idle
|
||||
- cpu_usage_nice
|
||||
- cpu_usage_iowait
|
||||
- cpu_usage_irq
|
||||
- cpu_usage_softirq
|
||||
- cpu_usage_steal
|
||||
- cpu_usage_guest
|
||||
- cpu_usage_guest_nice
|
||||
44
plugins/inputs/system/MEM_README.md
Normal file
44
plugins/inputs/system/MEM_README.md
Normal file
@@ -0,0 +1,44 @@
|
||||
## Telegraf Plugin: MEM
|
||||
|
||||
#### Description
|
||||
|
||||
The mem plugin collects memory metrics, defined as follows. For a more complete
|
||||
explanation of the difference between `used` and `actual_used` RAM, see
|
||||
[Linux ate my ram](http://www.linuxatemyram.com/).
|
||||
|
||||
- **total**: total physical memory available
|
||||
- **available**: the actual amount of available memory that can be given instantly
|
||||
to processes that request more memory in bytes; In linux kernel 3.14+, this
|
||||
is available natively in /proc/meminfo. In other platforms, this is calculated by
|
||||
summing different memory values depending on the platform
|
||||
(e.g. free + buffers + cached on Linux).
|
||||
It is supposed to be used to monitor actual memory usage in a cross platform fashion.
|
||||
- **available_percent**: Percent of memory available, `available / total * 100`
|
||||
- **used**: memory used, calculated differently depending on the platform and
|
||||
designed for informational purposes only.
|
||||
- **free**: memory not being used at all (zeroed) that is readily available; note
|
||||
that this doesn't reflect the actual memory available (use 'available' instead).
|
||||
- **used_percent**: the percentage usage calculated as `(total - used) / total * 100`
|
||||
|
||||
## Measurements:
|
||||
#### Raw Memory measurements:
|
||||
|
||||
Meta:
|
||||
- units: bytes
|
||||
- tags: `nil`
|
||||
|
||||
Measurement names:
|
||||
- mem_total
|
||||
- mem_available
|
||||
- mem_used
|
||||
- mem_free
|
||||
|
||||
#### Derived usage percentages:
|
||||
|
||||
Meta:
|
||||
- units: percent (out of 100)
|
||||
- tags: `nil`
|
||||
|
||||
Measurement names:
|
||||
- mem_used_percent
|
||||
- mem_available_percent
|
||||
52
plugins/inputs/system/NETSTAT_README.md
Normal file
52
plugins/inputs/system/NETSTAT_README.md
Normal file
@@ -0,0 +1,52 @@
|
||||
Telegraf plugin: NETSTAT
|
||||
|
||||
#### Description
|
||||
|
||||
The NETSTAT plugin collects TCP connections state and UDP socket counts by using `lsof`.
|
||||
|
||||
Supported TCP Connection states are follows.
|
||||
|
||||
- established
|
||||
- syn_sent
|
||||
- syn_recv
|
||||
- fin_wait1
|
||||
- fin_wait2
|
||||
- time_wait
|
||||
- close
|
||||
- close_wait
|
||||
- last_ack
|
||||
- listen
|
||||
- closing
|
||||
- none
|
||||
|
||||
|
||||
# Measurements:
|
||||
### TCP Connection State measurements:
|
||||
|
||||
Meta:
|
||||
- units: counts
|
||||
|
||||
Measurement names:
|
||||
- tcp_established
|
||||
- tcp_syn_sent
|
||||
- tcp_syn_recv
|
||||
- tcp_fin_wait1
|
||||
- tcp_fin_wait2
|
||||
- tcp_time_wait
|
||||
- tcp_close
|
||||
- tcp_close_wait
|
||||
- tcp_last_ack
|
||||
- tcp_listen
|
||||
- tcp_closing
|
||||
- tcp_none
|
||||
|
||||
If there are no connection on the state, the metric is not counted.
|
||||
|
||||
### UDP socket counts measurements:
|
||||
|
||||
Meta:
|
||||
- units: counts
|
||||
|
||||
Measurement names:
|
||||
- udp_socket
|
||||
|
||||
117
plugins/inputs/system/cpu.go
Normal file
117
plugins/inputs/system/cpu.go
Normal file
@@ -0,0 +1,117 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/influxdb/telegraf/plugins/inputs"
|
||||
"github.com/shirou/gopsutil/cpu"
|
||||
)
|
||||
|
||||
type CPUStats struct {
|
||||
ps PS
|
||||
lastStats []cpu.CPUTimesStat
|
||||
|
||||
PerCPU bool `toml:"percpu"`
|
||||
TotalCPU bool `toml:"totalcpu"`
|
||||
}
|
||||
|
||||
func NewCPUStats(ps PS) *CPUStats {
|
||||
return &CPUStats{
|
||||
ps: ps,
|
||||
}
|
||||
}
|
||||
|
||||
func (_ *CPUStats) Description() string {
|
||||
return "Read metrics about cpu usage"
|
||||
}
|
||||
|
||||
var sampleConfig = `
|
||||
# Whether to report per-cpu stats or not
|
||||
percpu = true
|
||||
# Whether to report total system cpu stats or not
|
||||
totalcpu = true
|
||||
# Comment this line if you want the raw CPU time metrics
|
||||
drop = ["time_*"]
|
||||
`
|
||||
|
||||
func (_ *CPUStats) SampleConfig() string {
|
||||
return sampleConfig
|
||||
}
|
||||
|
||||
func (s *CPUStats) Gather(acc inputs.Accumulator) error {
|
||||
times, err := s.ps.CPUTimes(s.PerCPU, s.TotalCPU)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting CPU info: %s", err)
|
||||
}
|
||||
now := time.Now()
|
||||
|
||||
for i, cts := range times {
|
||||
tags := map[string]string{
|
||||
"cpu": cts.CPU,
|
||||
}
|
||||
|
||||
total := totalCpuTime(cts)
|
||||
|
||||
// Add cpu time metrics
|
||||
fields := map[string]interface{}{
|
||||
"time_user": cts.User,
|
||||
"time_system": cts.System,
|
||||
"time_idle": cts.Idle,
|
||||
"time_nice": cts.Nice,
|
||||
"time_iowait": cts.Iowait,
|
||||
"time_irq": cts.Irq,
|
||||
"time_softirq": cts.Softirq,
|
||||
"time_steal": cts.Steal,
|
||||
"time_guest": cts.Guest,
|
||||
"time_guest_nice": cts.GuestNice,
|
||||
}
|
||||
|
||||
// Add in percentage
|
||||
if len(s.lastStats) == 0 {
|
||||
acc.AddFields("cpu", fields, tags, now)
|
||||
// If it's the 1st gather, can't get CPU Usage stats yet
|
||||
continue
|
||||
}
|
||||
lastCts := s.lastStats[i]
|
||||
lastTotal := totalCpuTime(lastCts)
|
||||
totalDelta := total - lastTotal
|
||||
|
||||
if totalDelta < 0 {
|
||||
s.lastStats = times
|
||||
return fmt.Errorf("Error: current total CPU time is less than previous total CPU time")
|
||||
}
|
||||
|
||||
if totalDelta == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
fields["usage_user"] = 100 * (cts.User - lastCts.User) / totalDelta
|
||||
fields["usage_system"] = 100 * (cts.System - lastCts.System) / totalDelta
|
||||
fields["usage_idle"] = 100 * (cts.Idle - lastCts.Idle) / totalDelta
|
||||
fields["usage_nice"] = 100 * (cts.Nice - lastCts.Nice) / totalDelta
|
||||
fields["usage_iowait"] = 100 * (cts.Iowait - lastCts.Iowait) / totalDelta
|
||||
fields["usage_irq"] = 100 * (cts.Irq - lastCts.Irq) / totalDelta
|
||||
fields["usage_softirq"] = 100 * (cts.Softirq - lastCts.Softirq) / totalDelta
|
||||
fields["usage_steal"] = 100 * (cts.Steal - lastCts.Steal) / totalDelta
|
||||
fields["usage_guest"] = 100 * (cts.Guest - lastCts.Guest) / totalDelta
|
||||
fields["usage_guest_nice"] = 100 * (cts.GuestNice - lastCts.GuestNice) / totalDelta
|
||||
acc.AddFields("cpu", fields, tags, now)
|
||||
}
|
||||
|
||||
s.lastStats = times
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func totalCpuTime(t cpu.CPUTimesStat) float64 {
|
||||
total := t.User + t.System + t.Nice + t.Iowait + t.Irq + t.Softirq + t.Steal +
|
||||
t.Guest + t.GuestNice + t.Idle
|
||||
return total
|
||||
}
|
||||
|
||||
func init() {
|
||||
inputs.Add("cpu", func() inputs.Input {
|
||||
return &CPUStats{ps: &systemPS{}}
|
||||
})
|
||||
}
|
||||
148
plugins/inputs/system/cpu_test.go
Normal file
148
plugins/inputs/system/cpu_test.go
Normal file
@@ -0,0 +1,148 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/influxdb/telegraf/testutil"
|
||||
"github.com/shirou/gopsutil/cpu"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestCPUStats(t *testing.T) {
|
||||
var mps MockPS
|
||||
defer mps.AssertExpectations(t)
|
||||
var acc testutil.Accumulator
|
||||
|
||||
cts := cpu.CPUTimesStat{
|
||||
CPU: "cpu0",
|
||||
User: 3.1,
|
||||
System: 8.2,
|
||||
Idle: 80.1,
|
||||
Nice: 1.3,
|
||||
Iowait: 0.2,
|
||||
Irq: 0.1,
|
||||
Softirq: 0.11,
|
||||
Steal: 0.0511,
|
||||
Guest: 8.1,
|
||||
GuestNice: 0.324,
|
||||
}
|
||||
|
||||
cts2 := cpu.CPUTimesStat{
|
||||
CPU: "cpu0",
|
||||
User: 11.4, // increased by 8.3
|
||||
System: 10.9, // increased by 2.7
|
||||
Idle: 158.8699, // increased by 78.7699 (for total increase of 100)
|
||||
Nice: 2.5, // increased by 1.2
|
||||
Iowait: 0.7, // increased by 0.5
|
||||
Irq: 1.2, // increased by 1.1
|
||||
Softirq: 0.31, // increased by 0.2
|
||||
Steal: 0.2812, // increased by 0.0001
|
||||
Guest: 12.9, // increased by 4.8
|
||||
GuestNice: 2.524, // increased by 2.2
|
||||
}
|
||||
|
||||
mps.On("CPUTimes").Return([]cpu.CPUTimesStat{cts}, nil)
|
||||
|
||||
cs := NewCPUStats(&mps)
|
||||
|
||||
cputags := map[string]string{
|
||||
"cpu": "cpu0",
|
||||
}
|
||||
|
||||
err := cs.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Computed values are checked with delta > 0 becasue of floating point arithmatic
|
||||
// imprecision
|
||||
assertContainsTaggedFloat(t, &acc, "cpu", "time_user", 3.1, 0, cputags)
|
||||
assertContainsTaggedFloat(t, &acc, "cpu", "time_system", 8.2, 0, cputags)
|
||||
assertContainsTaggedFloat(t, &acc, "cpu", "time_idle", 80.1, 0, cputags)
|
||||
assertContainsTaggedFloat(t, &acc, "cpu", "time_nice", 1.3, 0, cputags)
|
||||
assertContainsTaggedFloat(t, &acc, "cpu", "time_iowait", 0.2, 0, cputags)
|
||||
assertContainsTaggedFloat(t, &acc, "cpu", "time_irq", 0.1, 0, cputags)
|
||||
assertContainsTaggedFloat(t, &acc, "cpu", "time_softirq", 0.11, 0, cputags)
|
||||
assertContainsTaggedFloat(t, &acc, "cpu", "time_steal", 0.0511, 0, cputags)
|
||||
assertContainsTaggedFloat(t, &acc, "cpu", "time_guest", 8.1, 0, cputags)
|
||||
assertContainsTaggedFloat(t, &acc, "cpu", "time_guest_nice", 0.324, 0, cputags)
|
||||
|
||||
mps2 := MockPS{}
|
||||
mps2.On("CPUTimes").Return([]cpu.CPUTimesStat{cts2}, nil)
|
||||
cs.ps = &mps2
|
||||
|
||||
// Should have added cpu percentages too
|
||||
err = cs.Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
|
||||
assertContainsTaggedFloat(t, &acc, "cpu", "time_user", 11.4, 0, cputags)
|
||||
assertContainsTaggedFloat(t, &acc, "cpu", "time_system", 10.9, 0, cputags)
|
||||
assertContainsTaggedFloat(t, &acc, "cpu", "time_idle", 158.8699, 0, cputags)
|
||||
assertContainsTaggedFloat(t, &acc, "cpu", "time_nice", 2.5, 0, cputags)
|
||||
assertContainsTaggedFloat(t, &acc, "cpu", "time_iowait", 0.7, 0, cputags)
|
||||
assertContainsTaggedFloat(t, &acc, "cpu", "time_irq", 1.2, 0, cputags)
|
||||
assertContainsTaggedFloat(t, &acc, "cpu", "time_softirq", 0.31, 0, cputags)
|
||||
assertContainsTaggedFloat(t, &acc, "cpu", "time_steal", 0.2812, 0, cputags)
|
||||
assertContainsTaggedFloat(t, &acc, "cpu", "time_guest", 12.9, 0, cputags)
|
||||
assertContainsTaggedFloat(t, &acc, "cpu", "time_guest_nice", 2.524, 0, cputags)
|
||||
|
||||
assertContainsTaggedFloat(t, &acc, "cpu", "usage_user", 8.3, 0.0005, cputags)
|
||||
assertContainsTaggedFloat(t, &acc, "cpu", "usage_system", 2.7, 0.0005, cputags)
|
||||
assertContainsTaggedFloat(t, &acc, "cpu", "usage_idle", 78.7699, 0.0005, cputags)
|
||||
assertContainsTaggedFloat(t, &acc, "cpu", "usage_nice", 1.2, 0.0005, cputags)
|
||||
assertContainsTaggedFloat(t, &acc, "cpu", "usage_iowait", 0.5, 0.0005, cputags)
|
||||
assertContainsTaggedFloat(t, &acc, "cpu", "usage_irq", 1.1, 0.0005, cputags)
|
||||
assertContainsTaggedFloat(t, &acc, "cpu", "usage_softirq", 0.2, 0.0005, cputags)
|
||||
assertContainsTaggedFloat(t, &acc, "cpu", "usage_steal", 0.2301, 0.0005, cputags)
|
||||
assertContainsTaggedFloat(t, &acc, "cpu", "usage_guest", 4.8, 0.0005, cputags)
|
||||
assertContainsTaggedFloat(t, &acc, "cpu", "usage_guest_nice", 2.2, 0.0005, cputags)
|
||||
}
|
||||
|
||||
// Asserts that a given accumulator contains a measurment of type float64 with
|
||||
// specific tags within a certain distance of a given expected value. Asserts a failure
|
||||
// if the measurement is of the wrong type, or if no matching measurements are found
|
||||
//
|
||||
// Paramaters:
|
||||
// t *testing.T : Testing object to use
|
||||
// acc testutil.Accumulator: Accumulator to examine
|
||||
// measurement string : Name of the measurement to examine
|
||||
// expectedValue float64 : Value to search for within the measurement
|
||||
// delta float64 : Maximum acceptable distance of an accumulated value
|
||||
// from the expectedValue parameter. Useful when
|
||||
// floating-point arithmatic imprecision makes looking
|
||||
// for an exact match impractical
|
||||
// tags map[string]string : Tag set the found measurement must have. Set to nil to
|
||||
// ignore the tag set.
|
||||
func assertContainsTaggedFloat(
|
||||
t *testing.T,
|
||||
acc *testutil.Accumulator,
|
||||
measurement string,
|
||||
field string,
|
||||
expectedValue float64,
|
||||
delta float64,
|
||||
tags map[string]string,
|
||||
) {
|
||||
var actualValue float64
|
||||
for _, pt := range acc.Points {
|
||||
if pt.Measurement == measurement {
|
||||
for fieldname, value := range pt.Fields {
|
||||
if fieldname == field {
|
||||
if value, ok := value.(float64); ok {
|
||||
actualValue = value
|
||||
if (value >= expectedValue-delta) && (value <= expectedValue+delta) {
|
||||
// Found the point, return without failing
|
||||
return
|
||||
}
|
||||
} else {
|
||||
assert.Fail(t, fmt.Sprintf("Measurement \"%s\" does not have type float64",
|
||||
measurement))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
msg := fmt.Sprintf(
|
||||
"Could not find measurement \"%s\" with requested tags within %f of %f, Actual: %f",
|
||||
measurement, delta, expectedValue, actualValue)
|
||||
assert.Fail(t, msg)
|
||||
}
|
||||
144
plugins/inputs/system/disk.go
Normal file
144
plugins/inputs/system/disk.go
Normal file
@@ -0,0 +1,144 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/influxdb/telegraf/plugins/inputs"
|
||||
)
|
||||
|
||||
type DiskStats struct {
|
||||
ps PS
|
||||
|
||||
Mountpoints []string
|
||||
}
|
||||
|
||||
func (_ *DiskStats) Description() string {
|
||||
return "Read metrics about disk usage by mount point"
|
||||
}
|
||||
|
||||
var diskSampleConfig = `
|
||||
# By default, telegraf gather stats for all mountpoints.
|
||||
# Setting mountpoints will restrict the stats to the specified mountpoints.
|
||||
# Mountpoints=["/"]
|
||||
`
|
||||
|
||||
func (_ *DiskStats) SampleConfig() string {
|
||||
return diskSampleConfig
|
||||
}
|
||||
|
||||
func (s *DiskStats) Gather(acc inputs.Accumulator) error {
|
||||
disks, err := s.ps.DiskUsage()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting disk usage info: %s", err)
|
||||
}
|
||||
|
||||
var restrictMpoints bool
|
||||
mPoints := make(map[string]bool)
|
||||
if len(s.Mountpoints) != 0 {
|
||||
restrictMpoints = true
|
||||
for _, mp := range s.Mountpoints {
|
||||
mPoints[mp] = true
|
||||
}
|
||||
}
|
||||
|
||||
for _, du := range disks {
|
||||
_, member := mPoints[du.Path]
|
||||
if restrictMpoints && !member {
|
||||
continue
|
||||
}
|
||||
tags := map[string]string{
|
||||
"path": du.Path,
|
||||
"fstype": du.Fstype,
|
||||
}
|
||||
fields := map[string]interface{}{
|
||||
"total": du.Total,
|
||||
"free": du.Free,
|
||||
"used": du.Total - du.Free,
|
||||
"inodes_total": du.InodesTotal,
|
||||
"inodes_free": du.InodesFree,
|
||||
"inodes_used": du.InodesTotal - du.InodesFree,
|
||||
}
|
||||
acc.AddFields("disk", fields, tags)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type DiskIOStats struct {
|
||||
ps PS
|
||||
|
||||
Devices []string
|
||||
SkipSerialNumber bool
|
||||
}
|
||||
|
||||
func (_ *DiskIOStats) Description() string {
|
||||
return "Read metrics about disk IO by device"
|
||||
}
|
||||
|
||||
var diskIoSampleConfig = `
|
||||
# By default, telegraf will gather stats for all devices including
|
||||
# disk partitions.
|
||||
# Setting devices will restrict the stats to the specified devcies.
|
||||
# devices = ["sda","sdb"]
|
||||
# Uncomment the following line if you do not need disk serial numbers.
|
||||
# skip_serial_number = true
|
||||
`
|
||||
|
||||
func (_ *DiskIOStats) SampleConfig() string {
|
||||
return diskIoSampleConfig
|
||||
}
|
||||
|
||||
func (s *DiskIOStats) Gather(acc inputs.Accumulator) error {
|
||||
diskio, err := s.ps.DiskIO()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting disk io info: %s", err)
|
||||
}
|
||||
|
||||
var restrictDevices bool
|
||||
devices := make(map[string]bool)
|
||||
if len(s.Devices) != 0 {
|
||||
restrictDevices = true
|
||||
for _, dev := range s.Devices {
|
||||
devices[dev] = true
|
||||
}
|
||||
}
|
||||
|
||||
for _, io := range diskio {
|
||||
_, member := devices[io.Name]
|
||||
if restrictDevices && !member {
|
||||
continue
|
||||
}
|
||||
tags := map[string]string{}
|
||||
tags["name"] = io.Name
|
||||
if !s.SkipSerialNumber {
|
||||
if len(io.SerialNumber) != 0 {
|
||||
tags["serial"] = io.SerialNumber
|
||||
} else {
|
||||
tags["serial"] = "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
fields := map[string]interface{}{
|
||||
"reads": io.ReadCount,
|
||||
"writes": io.WriteCount,
|
||||
"read_bytes": io.ReadBytes,
|
||||
"write_bytes": io.WriteBytes,
|
||||
"read_time": io.ReadTime,
|
||||
"write_time": io.WriteTime,
|
||||
"io_time": io.IoTime,
|
||||
}
|
||||
acc.AddFields("diskio", fields, tags)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
inputs.Add("disk", func() inputs.Input {
|
||||
return &DiskStats{ps: &systemPS{}}
|
||||
})
|
||||
|
||||
inputs.Add("diskio", func() inputs.Input {
|
||||
return &DiskIOStats{ps: &systemPS{}}
|
||||
})
|
||||
}
|
||||
165
plugins/inputs/system/disk_test.go
Normal file
165
plugins/inputs/system/disk_test.go
Normal file
@@ -0,0 +1,165 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/influxdb/telegraf/testutil"
|
||||
"github.com/shirou/gopsutil/disk"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDiskStats(t *testing.T) {
|
||||
var mps MockPS
|
||||
defer mps.AssertExpectations(t)
|
||||
var acc testutil.Accumulator
|
||||
var err error
|
||||
|
||||
du := []*disk.DiskUsageStat{
|
||||
{
|
||||
Path: "/",
|
||||
Fstype: "ext4",
|
||||
Total: 128,
|
||||
Free: 23,
|
||||
InodesTotal: 1234,
|
||||
InodesFree: 234,
|
||||
},
|
||||
{
|
||||
Path: "/home",
|
||||
Fstype: "ext4",
|
||||
Total: 256,
|
||||
Free: 46,
|
||||
InodesTotal: 2468,
|
||||
InodesFree: 468,
|
||||
},
|
||||
}
|
||||
|
||||
mps.On("DiskUsage").Return(du, nil)
|
||||
|
||||
err = (&DiskStats{ps: &mps}).Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
|
||||
numDiskPoints := acc.NFields()
|
||||
expectedAllDiskPoints := 12
|
||||
assert.Equal(t, expectedAllDiskPoints, numDiskPoints)
|
||||
|
||||
tags1 := map[string]string{
|
||||
"path": "/",
|
||||
"fstype": "ext4",
|
||||
}
|
||||
tags2 := map[string]string{
|
||||
"path": "/home",
|
||||
"fstype": "ext4",
|
||||
}
|
||||
|
||||
fields1 := map[string]interface{}{
|
||||
"total": uint64(128), //tags1)
|
||||
"used": uint64(105), //tags1)
|
||||
"free": uint64(23), //tags1)
|
||||
"inodes_total": uint64(1234), //tags1)
|
||||
"inodes_free": uint64(234), //tags1)
|
||||
"inodes_used": uint64(1000), //tags1)
|
||||
}
|
||||
fields2 := map[string]interface{}{
|
||||
"total": uint64(256), //tags2)
|
||||
"used": uint64(210), //tags2)
|
||||
"free": uint64(46), //tags2)
|
||||
"inodes_total": uint64(2468), //tags2)
|
||||
"inodes_free": uint64(468), //tags2)
|
||||
"inodes_used": uint64(2000), //tags2)
|
||||
}
|
||||
acc.AssertContainsTaggedFields(t, "disk", fields1, tags1)
|
||||
acc.AssertContainsTaggedFields(t, "disk", fields2, tags2)
|
||||
|
||||
// We expect 6 more DiskPoints to show up with an explicit match on "/"
|
||||
// and /home not matching the /dev in Mountpoints
|
||||
err = (&DiskStats{ps: &mps, Mountpoints: []string{"/", "/dev"}}).Gather(&acc)
|
||||
assert.Equal(t, expectedAllDiskPoints+6, acc.NFields())
|
||||
|
||||
// We should see all the diskpoints as Mountpoints includes both
|
||||
// / and /home
|
||||
err = (&DiskStats{ps: &mps, Mountpoints: []string{"/", "/home"}}).Gather(&acc)
|
||||
assert.Equal(t, 2*expectedAllDiskPoints+6, acc.NFields())
|
||||
}
|
||||
|
||||
// func TestDiskIOStats(t *testing.T) {
|
||||
// var mps MockPS
|
||||
// defer mps.AssertExpectations(t)
|
||||
// var acc testutil.Accumulator
|
||||
// var err error
|
||||
|
||||
// diskio1 := disk.DiskIOCountersStat{
|
||||
// ReadCount: 888,
|
||||
// WriteCount: 5341,
|
||||
// ReadBytes: 100000,
|
||||
// WriteBytes: 200000,
|
||||
// ReadTime: 7123,
|
||||
// WriteTime: 9087,
|
||||
// Name: "sda1",
|
||||
// IoTime: 123552,
|
||||
// SerialNumber: "ab-123-ad",
|
||||
// }
|
||||
// diskio2 := disk.DiskIOCountersStat{
|
||||
// ReadCount: 444,
|
||||
// WriteCount: 2341,
|
||||
// ReadBytes: 200000,
|
||||
// WriteBytes: 400000,
|
||||
// ReadTime: 3123,
|
||||
// WriteTime: 6087,
|
||||
// Name: "sdb1",
|
||||
// IoTime: 246552,
|
||||
// SerialNumber: "bb-123-ad",
|
||||
// }
|
||||
|
||||
// mps.On("DiskIO").Return(
|
||||
// map[string]disk.DiskIOCountersStat{"sda1": diskio1, "sdb1": diskio2},
|
||||
// nil)
|
||||
|
||||
// err = (&DiskIOStats{ps: &mps}).Gather(&acc)
|
||||
// require.NoError(t, err)
|
||||
|
||||
// numDiskIOPoints := acc.NFields()
|
||||
// expectedAllDiskIOPoints := 14
|
||||
// assert.Equal(t, expectedAllDiskIOPoints, numDiskIOPoints)
|
||||
|
||||
// dtags1 := map[string]string{
|
||||
// "name": "sda1",
|
||||
// "serial": "ab-123-ad",
|
||||
// }
|
||||
// dtags2 := map[string]string{
|
||||
// "name": "sdb1",
|
||||
// "serial": "bb-123-ad",
|
||||
// }
|
||||
|
||||
// assert.True(t, acc.CheckTaggedValue("reads", uint64(888), dtags1))
|
||||
// assert.True(t, acc.CheckTaggedValue("writes", uint64(5341), dtags1))
|
||||
// assert.True(t, acc.CheckTaggedValue("read_bytes", uint64(100000), dtags1))
|
||||
// assert.True(t, acc.CheckTaggedValue("write_bytes", uint64(200000), dtags1))
|
||||
// assert.True(t, acc.CheckTaggedValue("read_time", uint64(7123), dtags1))
|
||||
// assert.True(t, acc.CheckTaggedValue("write_time", uint64(9087), dtags1))
|
||||
// assert.True(t, acc.CheckTaggedValue("io_time", uint64(123552), dtags1))
|
||||
// assert.True(t, acc.CheckTaggedValue("reads", uint64(444), dtags2))
|
||||
// assert.True(t, acc.CheckTaggedValue("writes", uint64(2341), dtags2))
|
||||
// assert.True(t, acc.CheckTaggedValue("read_bytes", uint64(200000), dtags2))
|
||||
// assert.True(t, acc.CheckTaggedValue("write_bytes", uint64(400000), dtags2))
|
||||
// assert.True(t, acc.CheckTaggedValue("read_time", uint64(3123), dtags2))
|
||||
// assert.True(t, acc.CheckTaggedValue("write_time", uint64(6087), dtags2))
|
||||
// assert.True(t, acc.CheckTaggedValue("io_time", uint64(246552), dtags2))
|
||||
|
||||
// // We expect 7 more DiskIOPoints to show up with an explicit match on "sdb1"
|
||||
// // and serial should be missing from the tags with SkipSerialNumber set
|
||||
// err = (&DiskIOStats{ps: &mps, Devices: []string{"sdb1"}, SkipSerialNumber: true}).Gather(&acc)
|
||||
// assert.Equal(t, expectedAllDiskIOPoints+7, acc.NFields())
|
||||
|
||||
// dtags3 := map[string]string{
|
||||
// "name": "sdb1",
|
||||
// }
|
||||
|
||||
// assert.True(t, acc.CheckTaggedValue("reads", uint64(444), dtags3))
|
||||
// assert.True(t, acc.CheckTaggedValue("writes", uint64(2341), dtags3))
|
||||
// assert.True(t, acc.CheckTaggedValue("read_bytes", uint64(200000), dtags3))
|
||||
// assert.True(t, acc.CheckTaggedValue("write_bytes", uint64(400000), dtags3))
|
||||
// assert.True(t, acc.CheckTaggedValue("read_time", uint64(3123), dtags3))
|
||||
// assert.True(t, acc.CheckTaggedValue("write_time", uint64(6087), dtags3))
|
||||
// assert.True(t, acc.CheckTaggedValue("io_time", uint64(246552), dtags3))
|
||||
// }
|
||||
89
plugins/inputs/system/docker.go
Normal file
89
plugins/inputs/system/docker.go
Normal file
@@ -0,0 +1,89 @@
|
||||
// +build linux
|
||||
|
||||
package system
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/influxdb/telegraf/plugins/inputs"
|
||||
)
|
||||
|
||||
type DockerStats struct {
|
||||
ps PS
|
||||
}
|
||||
|
||||
func (_ *DockerStats) Description() string {
|
||||
return "Read metrics about docker containers"
|
||||
}
|
||||
|
||||
func (_ *DockerStats) SampleConfig() string { return "" }
|
||||
|
||||
func (s *DockerStats) Gather(acc inputs.Accumulator) error {
|
||||
containers, err := s.ps.DockerStat()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting docker info: %s", err)
|
||||
}
|
||||
|
||||
for _, cont := range containers {
|
||||
tags := map[string]string{
|
||||
"id": cont.Id,
|
||||
"name": cont.Name,
|
||||
"command": cont.Command,
|
||||
}
|
||||
for k, v := range cont.Labels {
|
||||
tags[k] = v
|
||||
}
|
||||
|
||||
cts := cont.CPU
|
||||
|
||||
fields := map[string]interface{}{
|
||||
"user": cts.User,
|
||||
"system": cts.System,
|
||||
"idle": cts.Idle,
|
||||
"nice": cts.Nice,
|
||||
"iowait": cts.Iowait,
|
||||
"irq": cts.Irq,
|
||||
"softirq": cts.Softirq,
|
||||
"steal": cts.Steal,
|
||||
"guest": cts.Guest,
|
||||
"guest_nice": cts.GuestNice,
|
||||
|
||||
"cache": cont.Mem.Cache,
|
||||
"rss": cont.Mem.RSS,
|
||||
"rss_huge": cont.Mem.RSSHuge,
|
||||
"mapped_file": cont.Mem.MappedFile,
|
||||
"swap_in": cont.Mem.Pgpgin,
|
||||
"swap_out": cont.Mem.Pgpgout,
|
||||
"page_fault": cont.Mem.Pgfault,
|
||||
"page_major_fault": cont.Mem.Pgmajfault,
|
||||
"inactive_anon": cont.Mem.InactiveAnon,
|
||||
"active_anon": cont.Mem.ActiveAnon,
|
||||
"inactive_file": cont.Mem.InactiveFile,
|
||||
"active_file": cont.Mem.ActiveFile,
|
||||
"unevictable": cont.Mem.Unevictable,
|
||||
"memory_limit": cont.Mem.HierarchicalMemoryLimit,
|
||||
"total_cache": cont.Mem.TotalCache,
|
||||
"total_rss": cont.Mem.TotalRSS,
|
||||
"total_rss_huge": cont.Mem.TotalRSSHuge,
|
||||
"total_mapped_file": cont.Mem.TotalMappedFile,
|
||||
"total_swap_in": cont.Mem.TotalPgpgIn,
|
||||
"total_swap_out": cont.Mem.TotalPgpgOut,
|
||||
"total_page_fault": cont.Mem.TotalPgFault,
|
||||
"total_page_major_fault": cont.Mem.TotalPgMajFault,
|
||||
"total_inactive_anon": cont.Mem.TotalInactiveAnon,
|
||||
"total_active_anon": cont.Mem.TotalActiveAnon,
|
||||
"total_inactive_file": cont.Mem.TotalInactiveFile,
|
||||
"total_active_file": cont.Mem.TotalActiveFile,
|
||||
"total_unevictable": cont.Mem.TotalUnevictable,
|
||||
}
|
||||
acc.AddFields("docker", fields, tags)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
inputs.Add("docker", func() inputs.Input {
|
||||
return &DockerStats{ps: &systemPS{}}
|
||||
})
|
||||
}
|
||||
119
plugins/inputs/system/docker_test.go
Normal file
119
plugins/inputs/system/docker_test.go
Normal file
@@ -0,0 +1,119 @@
|
||||
// +build linux
|
||||
|
||||
package system
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/influxdb/telegraf/testutil"
|
||||
"github.com/shirou/gopsutil/cpu"
|
||||
"github.com/shirou/gopsutil/docker"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestDockerStats_GenerateStats(t *testing.T) {
|
||||
var mps MockPS
|
||||
var acc testutil.Accumulator
|
||||
|
||||
ds := &DockerContainerStat{
|
||||
Name: "blah",
|
||||
CPU: &cpu.CPUTimesStat{
|
||||
CPU: "all",
|
||||
User: 3.1,
|
||||
System: 8.2,
|
||||
Idle: 80.1,
|
||||
Nice: 1.3,
|
||||
Iowait: 0.2,
|
||||
Irq: 0.1,
|
||||
Softirq: 0.11,
|
||||
Steal: 0.0001,
|
||||
Guest: 8.1,
|
||||
GuestNice: 0.324,
|
||||
},
|
||||
Mem: &docker.CgroupMemStat{
|
||||
ContainerID: "blah",
|
||||
Cache: 1,
|
||||
RSS: 2,
|
||||
RSSHuge: 3,
|
||||
MappedFile: 4,
|
||||
Pgpgin: 5,
|
||||
Pgpgout: 6,
|
||||
Pgfault: 7,
|
||||
Pgmajfault: 8,
|
||||
InactiveAnon: 9,
|
||||
ActiveAnon: 10,
|
||||
InactiveFile: 11,
|
||||
ActiveFile: 12,
|
||||
Unevictable: 13,
|
||||
HierarchicalMemoryLimit: 14,
|
||||
TotalCache: 15,
|
||||
TotalRSS: 16,
|
||||
TotalRSSHuge: 17,
|
||||
TotalMappedFile: 18,
|
||||
TotalPgpgIn: 19,
|
||||
TotalPgpgOut: 20,
|
||||
TotalPgFault: 21,
|
||||
TotalPgMajFault: 22,
|
||||
TotalInactiveAnon: 23,
|
||||
TotalActiveAnon: 24,
|
||||
TotalInactiveFile: 25,
|
||||
TotalActiveFile: 26,
|
||||
TotalUnevictable: 27,
|
||||
},
|
||||
}
|
||||
|
||||
mps.On("DockerStat").Return([]*DockerContainerStat{ds}, nil)
|
||||
|
||||
err := (&DockerStats{&mps}).Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
|
||||
dockertags := map[string]string{
|
||||
"name": "blah",
|
||||
"id": "",
|
||||
"command": "",
|
||||
}
|
||||
|
||||
fields := map[string]interface{}{
|
||||
"user": 3.1,
|
||||
"system": 8.2,
|
||||
"idle": 80.1,
|
||||
"nice": 1.3,
|
||||
"iowait": 0.2,
|
||||
"irq": 0.1,
|
||||
"softirq": 0.11,
|
||||
"steal": 0.0001,
|
||||
"guest": 8.1,
|
||||
"guest_nice": 0.324,
|
||||
|
||||
"cache": uint64(1),
|
||||
"rss": uint64(2),
|
||||
"rss_huge": uint64(3),
|
||||
"mapped_file": uint64(4),
|
||||
"swap_in": uint64(5),
|
||||
"swap_out": uint64(6),
|
||||
"page_fault": uint64(7),
|
||||
"page_major_fault": uint64(8),
|
||||
"inactive_anon": uint64(9),
|
||||
"active_anon": uint64(10),
|
||||
"inactive_file": uint64(11),
|
||||
"active_file": uint64(12),
|
||||
"unevictable": uint64(13),
|
||||
"memory_limit": uint64(14),
|
||||
"total_cache": uint64(15),
|
||||
"total_rss": uint64(16),
|
||||
"total_rss_huge": uint64(17),
|
||||
"total_mapped_file": uint64(18),
|
||||
"total_swap_in": uint64(19),
|
||||
"total_swap_out": uint64(20),
|
||||
"total_page_fault": uint64(21),
|
||||
"total_page_major_fault": uint64(22),
|
||||
"total_inactive_anon": uint64(23),
|
||||
"total_active_anon": uint64(24),
|
||||
"total_inactive_file": uint64(25),
|
||||
"total_active_file": uint64(26),
|
||||
"total_unevictable": uint64(27),
|
||||
}
|
||||
|
||||
acc.AssertContainsTaggedFields(t, "docker", fields, dockertags)
|
||||
}
|
||||
77
plugins/inputs/system/memory.go
Normal file
77
plugins/inputs/system/memory.go
Normal file
@@ -0,0 +1,77 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/influxdb/telegraf/plugins/inputs"
|
||||
)
|
||||
|
||||
type MemStats struct {
|
||||
ps PS
|
||||
}
|
||||
|
||||
func (_ *MemStats) Description() string {
|
||||
return "Read metrics about memory usage"
|
||||
}
|
||||
|
||||
func (_ *MemStats) SampleConfig() string { return "" }
|
||||
|
||||
func (s *MemStats) Gather(acc inputs.Accumulator) error {
|
||||
vm, err := s.ps.VMStat()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting virtual memory info: %s", err)
|
||||
}
|
||||
|
||||
fields := map[string]interface{}{
|
||||
"total": vm.Total,
|
||||
"available": vm.Available,
|
||||
"used": vm.Used,
|
||||
"free": vm.Free,
|
||||
"cached": vm.Cached,
|
||||
"buffered": vm.Buffers,
|
||||
"used_percent": 100 * float64(vm.Used) / float64(vm.Total),
|
||||
"available_percent": 100 * float64(vm.Available) / float64(vm.Total),
|
||||
}
|
||||
acc.AddFields("mem", fields, nil)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type SwapStats struct {
|
||||
ps PS
|
||||
}
|
||||
|
||||
func (_ *SwapStats) Description() string {
|
||||
return "Read metrics about swap memory usage"
|
||||
}
|
||||
|
||||
func (_ *SwapStats) SampleConfig() string { return "" }
|
||||
|
||||
func (s *SwapStats) Gather(acc inputs.Accumulator) error {
|
||||
swap, err := s.ps.SwapStat()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting swap memory info: %s", err)
|
||||
}
|
||||
|
||||
fields := map[string]interface{}{
|
||||
"total": swap.Total,
|
||||
"used": swap.Used,
|
||||
"free": swap.Free,
|
||||
"used_percent": swap.UsedPercent,
|
||||
"in": swap.Sin,
|
||||
"out": swap.Sout,
|
||||
}
|
||||
acc.AddFields("swap", fields, nil)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
inputs.Add("mem", func() inputs.Input {
|
||||
return &MemStats{ps: &systemPS{}}
|
||||
})
|
||||
|
||||
inputs.Add("swap", func() inputs.Input {
|
||||
return &SwapStats{ps: &systemPS{}}
|
||||
})
|
||||
}
|
||||
72
plugins/inputs/system/memory_test.go
Normal file
72
plugins/inputs/system/memory_test.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/influxdb/telegraf/testutil"
|
||||
"github.com/shirou/gopsutil/mem"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestMemStats(t *testing.T) {
|
||||
var mps MockPS
|
||||
var err error
|
||||
defer mps.AssertExpectations(t)
|
||||
var acc testutil.Accumulator
|
||||
|
||||
vms := &mem.VirtualMemoryStat{
|
||||
Total: 12400,
|
||||
Available: 7600,
|
||||
Used: 5000,
|
||||
Free: 1235,
|
||||
// Active: 8134,
|
||||
// Inactive: 1124,
|
||||
// Buffers: 771,
|
||||
// Cached: 4312,
|
||||
// Wired: 134,
|
||||
// Shared: 2142,
|
||||
}
|
||||
|
||||
mps.On("VMStat").Return(vms, nil)
|
||||
|
||||
sms := &mem.SwapMemoryStat{
|
||||
Total: 8123,
|
||||
Used: 1232,
|
||||
Free: 6412,
|
||||
UsedPercent: 12.2,
|
||||
Sin: 7,
|
||||
Sout: 830,
|
||||
}
|
||||
|
||||
mps.On("SwapStat").Return(sms, nil)
|
||||
|
||||
err = (&MemStats{&mps}).Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
|
||||
memfields := map[string]interface{}{
|
||||
"total": uint64(12400),
|
||||
"available": uint64(7600),
|
||||
"used": uint64(5000),
|
||||
"available_percent": float64(7600) / float64(12400) * 100,
|
||||
"used_percent": float64(5000) / float64(12400) * 100,
|
||||
"free": uint64(1235),
|
||||
"cached": uint64(0),
|
||||
"buffered": uint64(0),
|
||||
}
|
||||
acc.AssertContainsTaggedFields(t, "mem", memfields, make(map[string]string))
|
||||
|
||||
acc.Points = nil
|
||||
|
||||
err = (&SwapStats{&mps}).Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
|
||||
swapfields := map[string]interface{}{
|
||||
"total": uint64(8123),
|
||||
"used": uint64(1232),
|
||||
"used_percent": float64(12.2),
|
||||
"free": uint64(6412),
|
||||
"in": uint64(7),
|
||||
"out": uint64(830),
|
||||
}
|
||||
acc.AssertContainsTaggedFields(t, "swap", swapfields, make(map[string]string))
|
||||
}
|
||||
106
plugins/inputs/system/mock_PS.go
Normal file
106
plugins/inputs/system/mock_PS.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/mock"
|
||||
|
||||
"github.com/shirou/gopsutil/cpu"
|
||||
"github.com/shirou/gopsutil/disk"
|
||||
|
||||
"github.com/shirou/gopsutil/load"
|
||||
"github.com/shirou/gopsutil/mem"
|
||||
"github.com/shirou/gopsutil/net"
|
||||
)
|
||||
|
||||
type MockPS struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (m *MockPS) LoadAvg() (*load.LoadAvgStat, error) {
|
||||
ret := m.Called()
|
||||
|
||||
r0 := ret.Get(0).(*load.LoadAvgStat)
|
||||
r1 := ret.Error(1)
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m *MockPS) CPUTimes(perCPU, totalCPU bool) ([]cpu.CPUTimesStat, error) {
|
||||
ret := m.Called()
|
||||
|
||||
r0 := ret.Get(0).([]cpu.CPUTimesStat)
|
||||
r1 := ret.Error(1)
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m *MockPS) DiskUsage() ([]*disk.DiskUsageStat, error) {
|
||||
ret := m.Called()
|
||||
|
||||
r0 := ret.Get(0).([]*disk.DiskUsageStat)
|
||||
r1 := ret.Error(1)
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m *MockPS) NetIO() ([]net.NetIOCountersStat, error) {
|
||||
ret := m.Called()
|
||||
|
||||
r0 := ret.Get(0).([]net.NetIOCountersStat)
|
||||
r1 := ret.Error(1)
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m *MockPS) NetProto() ([]net.NetProtoCountersStat, error) {
|
||||
ret := m.Called()
|
||||
|
||||
r0 := ret.Get(0).([]net.NetProtoCountersStat)
|
||||
r1 := ret.Error(1)
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m *MockPS) DiskIO() (map[string]disk.DiskIOCountersStat, error) {
|
||||
ret := m.Called()
|
||||
|
||||
r0 := ret.Get(0).(map[string]disk.DiskIOCountersStat)
|
||||
r1 := ret.Error(1)
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m *MockPS) VMStat() (*mem.VirtualMemoryStat, error) {
|
||||
ret := m.Called()
|
||||
|
||||
r0 := ret.Get(0).(*mem.VirtualMemoryStat)
|
||||
r1 := ret.Error(1)
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m *MockPS) SwapStat() (*mem.SwapMemoryStat, error) {
|
||||
ret := m.Called()
|
||||
|
||||
r0 := ret.Get(0).(*mem.SwapMemoryStat)
|
||||
r1 := ret.Error(1)
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m *MockPS) DockerStat() ([]*DockerContainerStat, error) {
|
||||
ret := m.Called()
|
||||
|
||||
r0 := ret.Get(0).([]*DockerContainerStat)
|
||||
r1 := ret.Error(1)
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m *MockPS) NetConnections() ([]net.NetConnectionStat, error) {
|
||||
ret := m.Called()
|
||||
|
||||
r0 := ret.Get(0).([]net.NetConnectionStat)
|
||||
r1 := ret.Error(1)
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
106
plugins/inputs/system/net.go
Normal file
106
plugins/inputs/system/net.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/influxdb/telegraf/plugins/inputs"
|
||||
)
|
||||
|
||||
type NetIOStats struct {
|
||||
ps PS
|
||||
|
||||
skipChecks bool
|
||||
Interfaces []string
|
||||
}
|
||||
|
||||
func (_ *NetIOStats) Description() string {
|
||||
return "Read metrics about network interface usage"
|
||||
}
|
||||
|
||||
var netSampleConfig = `
|
||||
# By default, telegraf gathers stats from any up interface (excluding loopback)
|
||||
# Setting interfaces will tell it to gather these explicit interfaces,
|
||||
# regardless of status.
|
||||
#
|
||||
# interfaces = ["eth0", ... ]
|
||||
`
|
||||
|
||||
func (_ *NetIOStats) SampleConfig() string {
|
||||
return netSampleConfig
|
||||
}
|
||||
|
||||
func (s *NetIOStats) Gather(acc inputs.Accumulator) error {
|
||||
netio, err := s.ps.NetIO()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting net io info: %s", err)
|
||||
}
|
||||
|
||||
for _, io := range netio {
|
||||
if len(s.Interfaces) != 0 {
|
||||
var found bool
|
||||
|
||||
for _, name := range s.Interfaces {
|
||||
if name == io.Name {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
continue
|
||||
}
|
||||
} else if !s.skipChecks {
|
||||
iface, err := net.InterfaceByName(io.Name)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if iface.Flags&net.FlagLoopback == net.FlagLoopback {
|
||||
continue
|
||||
}
|
||||
|
||||
if iface.Flags&net.FlagUp == 0 {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
tags := map[string]string{
|
||||
"interface": io.Name,
|
||||
}
|
||||
|
||||
fields := map[string]interface{}{
|
||||
"bytes_sent": io.BytesSent,
|
||||
"bytes_recv": io.BytesRecv,
|
||||
"packets_sent": io.PacketsSent,
|
||||
"packets_recv": io.PacketsRecv,
|
||||
"err_in": io.Errin,
|
||||
"err_out": io.Errout,
|
||||
"drop_in": io.Dropin,
|
||||
"drop_out": io.Dropout,
|
||||
}
|
||||
acc.AddFields("net", fields, tags)
|
||||
}
|
||||
|
||||
// Get system wide stats for different network protocols
|
||||
// (ignore these stats if the call fails)
|
||||
netprotos, _ := s.ps.NetProto()
|
||||
fields := make(map[string]interface{})
|
||||
for _, proto := range netprotos {
|
||||
for stat, value := range proto.Stats {
|
||||
name := fmt.Sprintf("%s_%s", strings.ToLower(proto.Protocol),
|
||||
strings.ToLower(stat))
|
||||
fields[name] = value
|
||||
}
|
||||
}
|
||||
acc.AddFields("net", fields, nil)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
inputs.Add("net", func() inputs.Input {
|
||||
return &NetIOStats{ps: &systemPS{}}
|
||||
})
|
||||
}
|
||||
106
plugins/inputs/system/net_test.go
Normal file
106
plugins/inputs/system/net_test.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"github.com/influxdb/telegraf/testutil"
|
||||
"github.com/shirou/gopsutil/net"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNetStats(t *testing.T) {
|
||||
var mps MockPS
|
||||
var err error
|
||||
defer mps.AssertExpectations(t)
|
||||
var acc testutil.Accumulator
|
||||
|
||||
netio := net.NetIOCountersStat{
|
||||
Name: "eth0",
|
||||
BytesSent: 1123,
|
||||
BytesRecv: 8734422,
|
||||
PacketsSent: 781,
|
||||
PacketsRecv: 23456,
|
||||
Errin: 832,
|
||||
Errout: 8,
|
||||
Dropin: 7,
|
||||
Dropout: 1,
|
||||
}
|
||||
|
||||
mps.On("NetIO").Return([]net.NetIOCountersStat{netio}, nil)
|
||||
|
||||
netprotos := []net.NetProtoCountersStat{
|
||||
net.NetProtoCountersStat{
|
||||
Protocol: "Udp",
|
||||
Stats: map[string]int64{
|
||||
"InDatagrams": 4655,
|
||||
"NoPorts": 892592,
|
||||
},
|
||||
},
|
||||
}
|
||||
mps.On("NetProto").Return(netprotos, nil)
|
||||
|
||||
netstats := []net.NetConnectionStat{
|
||||
net.NetConnectionStat{
|
||||
Type: syscall.SOCK_DGRAM,
|
||||
},
|
||||
net.NetConnectionStat{
|
||||
Status: "ESTABLISHED",
|
||||
},
|
||||
net.NetConnectionStat{
|
||||
Status: "ESTABLISHED",
|
||||
},
|
||||
net.NetConnectionStat{
|
||||
Status: "CLOSE",
|
||||
},
|
||||
}
|
||||
|
||||
mps.On("NetConnections").Return(netstats, nil)
|
||||
|
||||
err = (&NetIOStats{ps: &mps, skipChecks: true}).Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
|
||||
ntags := map[string]string{
|
||||
"interface": "eth0",
|
||||
}
|
||||
|
||||
fields1 := map[string]interface{}{
|
||||
"bytes_sent": uint64(1123),
|
||||
"bytes_recv": uint64(8734422),
|
||||
"packets_sent": uint64(781),
|
||||
"packets_recv": uint64(23456),
|
||||
"err_in": uint64(832),
|
||||
"err_out": uint64(8),
|
||||
"drop_in": uint64(7),
|
||||
"drop_out": uint64(1),
|
||||
}
|
||||
acc.AssertContainsTaggedFields(t, "net", fields1, ntags)
|
||||
|
||||
fields2 := map[string]interface{}{
|
||||
"udp_noports": int64(892592),
|
||||
"udp_indatagrams": int64(4655),
|
||||
}
|
||||
acc.AssertContainsTaggedFields(t, "net", fields2, make(map[string]string))
|
||||
|
||||
acc.Points = nil
|
||||
|
||||
err = (&NetStats{&mps}).Gather(&acc)
|
||||
require.NoError(t, err)
|
||||
|
||||
fields3 := map[string]interface{}{
|
||||
"tcp_established": 2,
|
||||
"tcp_syn_sent": 0,
|
||||
"tcp_syn_recv": 0,
|
||||
"tcp_fin_wait1": 0,
|
||||
"tcp_fin_wait2": 0,
|
||||
"tcp_time_wait": 0,
|
||||
"tcp_close": 1,
|
||||
"tcp_close_wait": 0,
|
||||
"tcp_last_ack": 0,
|
||||
"tcp_listen": 0,
|
||||
"tcp_closing": 0,
|
||||
"tcp_none": 0,
|
||||
"udp_socket": 1,
|
||||
}
|
||||
acc.AssertContainsTaggedFields(t, "netstat", fields3, make(map[string]string))
|
||||
}
|
||||
70
plugins/inputs/system/netstat.go
Normal file
70
plugins/inputs/system/netstat.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
|
||||
"github.com/influxdb/telegraf/plugins/inputs"
|
||||
)
|
||||
|
||||
type NetStats struct {
|
||||
ps PS
|
||||
}
|
||||
|
||||
func (_ *NetStats) Description() string {
|
||||
return "Read metrics about TCP status such as established, time wait etc and UDP sockets counts."
|
||||
}
|
||||
|
||||
var tcpstatSampleConfig = ""
|
||||
|
||||
func (_ *NetStats) SampleConfig() string {
|
||||
return tcpstatSampleConfig
|
||||
}
|
||||
|
||||
func (s *NetStats) Gather(acc inputs.Accumulator) error {
|
||||
netconns, err := s.ps.NetConnections()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting net connections info: %s", err)
|
||||
}
|
||||
counts := make(map[string]int)
|
||||
counts["UDP"] = 0
|
||||
|
||||
// TODO: add family to tags or else
|
||||
tags := map[string]string{}
|
||||
for _, netcon := range netconns {
|
||||
if netcon.Type == syscall.SOCK_DGRAM {
|
||||
counts["UDP"] += 1
|
||||
continue // UDP has no status
|
||||
}
|
||||
c, ok := counts[netcon.Status]
|
||||
if !ok {
|
||||
counts[netcon.Status] = 0
|
||||
}
|
||||
counts[netcon.Status] = c + 1
|
||||
}
|
||||
|
||||
fields := map[string]interface{}{
|
||||
"tcp_established": counts["ESTABLISHED"],
|
||||
"tcp_syn_sent": counts["SYN_SENT"],
|
||||
"tcp_syn_recv": counts["SYN_RECV"],
|
||||
"tcp_fin_wait1": counts["FIN_WAIT1"],
|
||||
"tcp_fin_wait2": counts["FIN_WAIT2"],
|
||||
"tcp_time_wait": counts["TIME_WAIT"],
|
||||
"tcp_close": counts["CLOSE"],
|
||||
"tcp_close_wait": counts["CLOSE_WAIT"],
|
||||
"tcp_last_ack": counts["LAST_ACK"],
|
||||
"tcp_listen": counts["LISTEN"],
|
||||
"tcp_closing": counts["CLOSING"],
|
||||
"tcp_none": counts["NONE"],
|
||||
"udp_socket": counts["UDP"],
|
||||
}
|
||||
acc.AddFields("netstat", fields, tags)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
inputs.Add("netstat", func() inputs.Input {
|
||||
return &NetStats{ps: &systemPS{}}
|
||||
})
|
||||
}
|
||||
168
plugins/inputs/system/ps.go
Normal file
168
plugins/inputs/system/ps.go
Normal file
@@ -0,0 +1,168 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
gonet "net"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/influxdb/telegraf/internal"
|
||||
"github.com/influxdb/telegraf/plugins/inputs"
|
||||
|
||||
dc "github.com/fsouza/go-dockerclient"
|
||||
"github.com/shirou/gopsutil/cpu"
|
||||
"github.com/shirou/gopsutil/disk"
|
||||
"github.com/shirou/gopsutil/docker"
|
||||
"github.com/shirou/gopsutil/mem"
|
||||
"github.com/shirou/gopsutil/net"
|
||||
)
|
||||
|
||||
type DockerContainerStat struct {
|
||||
Id string
|
||||
Name string
|
||||
Command string
|
||||
Labels map[string]string
|
||||
CPU *cpu.CPUTimesStat
|
||||
Mem *docker.CgroupMemStat
|
||||
}
|
||||
|
||||
type PS interface {
|
||||
CPUTimes(perCPU, totalCPU bool) ([]cpu.CPUTimesStat, error)
|
||||
DiskUsage() ([]*disk.DiskUsageStat, error)
|
||||
NetIO() ([]net.NetIOCountersStat, error)
|
||||
NetProto() ([]net.NetProtoCountersStat, error)
|
||||
DiskIO() (map[string]disk.DiskIOCountersStat, error)
|
||||
VMStat() (*mem.VirtualMemoryStat, error)
|
||||
SwapStat() (*mem.SwapMemoryStat, error)
|
||||
DockerStat() ([]*DockerContainerStat, error)
|
||||
NetConnections() ([]net.NetConnectionStat, error)
|
||||
}
|
||||
|
||||
func add(acc inputs.Accumulator,
|
||||
name string, val float64, tags map[string]string) {
|
||||
if val >= 0 {
|
||||
acc.Add(name, val, tags)
|
||||
}
|
||||
}
|
||||
|
||||
type systemPS struct {
|
||||
dockerClient *dc.Client
|
||||
}
|
||||
|
||||
func (s *systemPS) CPUTimes(perCPU, totalCPU bool) ([]cpu.CPUTimesStat, error) {
|
||||
var cpuTimes []cpu.CPUTimesStat
|
||||
if perCPU {
|
||||
if perCPUTimes, err := cpu.CPUTimes(true); err == nil {
|
||||
cpuTimes = append(cpuTimes, perCPUTimes...)
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if totalCPU {
|
||||
if totalCPUTimes, err := cpu.CPUTimes(false); err == nil {
|
||||
cpuTimes = append(cpuTimes, totalCPUTimes...)
|
||||
} else {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return cpuTimes, nil
|
||||
}
|
||||
|
||||
func (s *systemPS) DiskUsage() ([]*disk.DiskUsageStat, error) {
|
||||
parts, err := disk.DiskPartitions(true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var usage []*disk.DiskUsageStat
|
||||
|
||||
for _, p := range parts {
|
||||
if _, err := os.Stat(p.Mountpoint); err == nil {
|
||||
du, err := disk.DiskUsage(p.Mountpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
du.Fstype = p.Fstype
|
||||
usage = append(usage, du)
|
||||
}
|
||||
}
|
||||
|
||||
return usage, nil
|
||||
}
|
||||
|
||||
func (s *systemPS) NetProto() ([]net.NetProtoCountersStat, error) {
|
||||
return net.NetProtoCounters(nil)
|
||||
}
|
||||
|
||||
func (s *systemPS) NetIO() ([]net.NetIOCountersStat, error) {
|
||||
return net.NetIOCounters(true)
|
||||
}
|
||||
|
||||
func (s *systemPS) NetConnections() ([]net.NetConnectionStat, error) {
|
||||
return net.NetConnections("all")
|
||||
}
|
||||
|
||||
func (s *systemPS) DiskIO() (map[string]disk.DiskIOCountersStat, error) {
|
||||
m, err := disk.DiskIOCounters()
|
||||
if err == internal.NotImplementedError {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return m, err
|
||||
}
|
||||
|
||||
func (s *systemPS) VMStat() (*mem.VirtualMemoryStat, error) {
|
||||
return mem.VirtualMemory()
|
||||
}
|
||||
|
||||
func (s *systemPS) SwapStat() (*mem.SwapMemoryStat, error) {
|
||||
return mem.SwapMemory()
|
||||
}
|
||||
|
||||
func (s *systemPS) DockerStat() ([]*DockerContainerStat, error) {
|
||||
if s.dockerClient == nil {
|
||||
c, err := dc.NewClient("unix:///var/run/docker.sock")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.dockerClient = c
|
||||
}
|
||||
|
||||
opts := dc.ListContainersOptions{}
|
||||
|
||||
containers, err := s.dockerClient.ListContainers(opts)
|
||||
if err != nil {
|
||||
if _, ok := err.(*gonet.OpError); ok {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var stats []*DockerContainerStat
|
||||
|
||||
for _, container := range containers {
|
||||
ctu, err := docker.CgroupCPUDocker(container.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mem, err := docker.CgroupMemDocker(container.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
name := strings.Join(container.Names, " ")
|
||||
|
||||
stats = append(stats, &DockerContainerStat{
|
||||
Id: container.ID,
|
||||
Name: name,
|
||||
Command: container.Command,
|
||||
Labels: container.Labels,
|
||||
CPU: ctu,
|
||||
Mem: mem,
|
||||
})
|
||||
}
|
||||
|
||||
return stats, nil
|
||||
}
|
||||
74
plugins/inputs/system/system.go
Normal file
74
plugins/inputs/system/system.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package system
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/shirou/gopsutil/host"
|
||||
"github.com/shirou/gopsutil/load"
|
||||
|
||||
"github.com/influxdb/telegraf/plugins/inputs"
|
||||
)
|
||||
|
||||
type SystemStats struct{}
|
||||
|
||||
func (_ *SystemStats) Description() string {
|
||||
return "Read metrics about system load & uptime"
|
||||
}
|
||||
|
||||
func (_ *SystemStats) SampleConfig() string { return "" }
|
||||
|
||||
func (_ *SystemStats) Gather(acc inputs.Accumulator) error {
|
||||
loadavg, err := load.LoadAvg()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hostinfo, err := host.HostInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fields := map[string]interface{}{
|
||||
"load1": loadavg.Load1,
|
||||
"load5": loadavg.Load5,
|
||||
"load15": loadavg.Load15,
|
||||
"uptime": hostinfo.Uptime,
|
||||
"uptime_format": format_uptime(hostinfo.Uptime),
|
||||
}
|
||||
acc.AddFields("system", fields, nil)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func format_uptime(uptime uint64) string {
|
||||
buf := new(bytes.Buffer)
|
||||
w := bufio.NewWriter(buf)
|
||||
|
||||
days := uptime / (60 * 60 * 24)
|
||||
|
||||
if days != 0 {
|
||||
s := ""
|
||||
if days > 1 {
|
||||
s = "s"
|
||||
}
|
||||
fmt.Fprintf(w, "%d day%s, ", days, s)
|
||||
}
|
||||
|
||||
minutes := uptime / 60
|
||||
hours := minutes / 60
|
||||
hours %= 24
|
||||
minutes %= 60
|
||||
|
||||
fmt.Fprintf(w, "%2d:%02d", hours, minutes)
|
||||
|
||||
w.Flush()
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func init() {
|
||||
inputs.Add("system", func() inputs.Input {
|
||||
return &SystemStats{}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user