From ddf438dac00d32a77ae4a5e9a2b724e666bca3da Mon Sep 17 00:00:00 2001 From: JP Date: Wed, 12 Aug 2015 15:17:50 -0500 Subject: [PATCH 01/10] add missing import and Tag marshalling --- cmd/telegraf/telegraf.go | 6 ++++++ config.go | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/cmd/telegraf/telegraf.go b/cmd/telegraf/telegraf.go index d6fdd965b..f01ab947c 100644 --- a/cmd/telegraf/telegraf.go +++ b/cmd/telegraf/telegraf.go @@ -9,6 +9,7 @@ import ( "strings" "github.com/influxdb/telegraf" + _ "github.com/influxdb/telegraf/outputs/all" _ "github.com/influxdb/telegraf/plugins/all" ) @@ -63,6 +64,10 @@ func main() { if err != nil { log.Fatal(err) } + if len(outputs) == 0 { + log.Printf("Error: no ouputs found, did you provide a config file?") + os.Exit(1) + } plugins, err := ag.LoadPlugins(*fPLuginsFilter) if err != nil { @@ -111,6 +116,7 @@ func main() { log.Printf("Agent Config: Interval:%s, Debug:%#v, Hostname:%#v\n", ag.Interval, ag.Debug, ag.Hostname) } + log.Printf("Tags enabled: %v", config.ListTags) if *fPidfile != "" { f, err := os.Create(*fPidfile) diff --git a/config.go b/config.go index db49dfa2f..cba6a9489 100644 --- a/config.go +++ b/config.go @@ -273,6 +273,7 @@ func LoadConfig(path string) (*Config, error) { } c := &Config{ + Tags: make(map[string]string), plugins: make(map[string]*ast.Table), outputs: make(map[string]*ast.Table), } @@ -286,6 +287,10 @@ func LoadConfig(path string) (*Config, error) { switch name { case "agent": c.agent = subtbl + case "tags": + if err := toml.UnmarshalTable(subtbl, c.Tags); err != nil { + return nil, errInvalidConfig + } case "outputs": for outputName, outputVal := range subtbl.Fields { outputSubtbl, ok := outputVal.(*ast.Table) From 5cb3a096c1cc452f379ceaae20ec7dc1c632ffb7 Mon Sep 17 00:00:00 2001 From: Cameron Sparr Date: Wed, 12 Aug 2015 14:59:42 -0600 Subject: [PATCH 02/10] Fix influx.toml and ListTags string printing --- cmd/telegraf/telegraf.go | 2 +- testdata/influx.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/telegraf/telegraf.go b/cmd/telegraf/telegraf.go index f01ab947c..ed05ac80e 100644 --- a/cmd/telegraf/telegraf.go +++ b/cmd/telegraf/telegraf.go @@ -116,7 +116,7 @@ func main() { log.Printf("Agent Config: Interval:%s, Debug:%#v, Hostname:%#v\n", ag.Interval, ag.Debug, ag.Hostname) } - log.Printf("Tags enabled: %v", config.ListTags) + log.Printf("Tags enabled: %s", config.ListTags()) if *fPidfile != "" { f, err := os.Create(*fPidfile) diff --git a/testdata/influx.toml b/testdata/influx.toml index 492528cae..10684b159 100644 --- a/testdata/influx.toml +++ b/testdata/influx.toml @@ -11,7 +11,7 @@ password = "root" database = "telegraf" [tags] -dc = "us-phx-1" } +dc = "us-phx-1" [redis] address = ":6379" From 5d4b6c41a82123a648f70f0e6466425ce4afd5bc Mon Sep 17 00:00:00 2001 From: Cameron Sparr Date: Wed, 12 Aug 2015 15:22:55 -0600 Subject: [PATCH 03/10] circle.yml: verify that golint violations == 0 for some dirs --- circle.yml | 8 ++++---- config.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/circle.yml b/circle.yml index 5837a3dca..b3d07c0f9 100644 --- a/circle.yml +++ b/circle.yml @@ -13,11 +13,11 @@ test: # install binaries - go install ./... # Go fmt should pass all files - - "[ `git ls-files | grep '.go$' | xargs gofmt -l 2>&1 | wc -l` -eq 0 ]" + - "[ `git ls-files | grep '.go$' | xargs gofmt -l | tee /tmp/foo | wc -l` -eq 0 ] || (cat /tmp/foo; exit 1)" - go vet ./... - - golint . - - golint testutil/... - - golint cmd/... + - "[ `golint . | tee /tmp/foo | wc -l` == 0 ] || (cat /tmp/foo; exit 1)" + - "[ `golint testutil/... | tee /tmp/foo | wc -l` == 0 ] || (cat /tmp/foo; exit 1)" + - "[ `golint cmd/... | tee /tmp/foo | wc -l` == 0 ] || (cat /tmp/foo; exit 1)" override: - make test-short post: diff --git a/config.go b/config.go index cba6a9489..de756f917 100644 --- a/config.go +++ b/config.go @@ -51,7 +51,7 @@ func (c *Config) Outputs() map[string]*ast.Table { return c.outputs } -// The name of a tag, and the values on which to filter +// TagFilter is the name of a tag, and the values on which to filter type TagFilter struct { Name string Filter []string From 04963f12a399fcc407b27780d6570d20f3f15b82 Mon Sep 17 00:00:00 2001 From: Cameron Sparr Date: Thu, 13 Aug 2015 11:27:24 -0600 Subject: [PATCH 04/10] Allow a PerCPU configuration variable, issue #108 --- plugins/system/cpu.go | 15 +++++++++++-- plugins/system/mock_PS.go | 2 +- plugins/system/ps.go | 21 +++++++++++++++--- plugins/system/ps/cpu/cpu_darwin.go | 34 +++++++++-------------------- testdata/telegraf-agent.toml | 3 ++- 5 files changed, 44 insertions(+), 31 deletions(-) diff --git a/plugins/system/cpu.go b/plugins/system/cpu.go index e942ccb5e..7114e2b9a 100644 --- a/plugins/system/cpu.go +++ b/plugins/system/cpu.go @@ -8,16 +8,27 @@ import ( type CPUStats struct { ps PS + + PerCPU bool `toml:"percpu"` + TotalCPU bool `toml:"totalcpu"` } func (_ *CPUStats) Description() string { return "Read metrics about cpu usage" } -func (_ *CPUStats) SampleConfig() string { return "" } +var sampleConfig = ` +# Whether to report per-cpu stats or not +percpu = true +# Whether to report total system cpu stats or not +totalcpu = true` + +func (_ *CPUStats) SampleConfig() string { + return sampleConfig +} func (s *CPUStats) Gather(acc plugins.Accumulator) error { - times, err := s.ps.CPUTimes() + times, err := s.ps.CPUTimes(s.PerCPU, s.TotalCPU) if err != nil { return fmt.Errorf("error getting CPU info: %s", err) } diff --git a/plugins/system/mock_PS.go b/plugins/system/mock_PS.go index 17a4fc950..86a3932b2 100644 --- a/plugins/system/mock_PS.go +++ b/plugins/system/mock_PS.go @@ -21,7 +21,7 @@ func (m *MockPS) LoadAvg() (*load.LoadAvgStat, error) { return r0, r1 } -func (m *MockPS) CPUTimes() ([]cpu.CPUTimesStat, error) { +func (m *MockPS) CPUTimes(perCPU, totalCPU bool) ([]cpu.CPUTimesStat, error) { ret := m.Called() r0 := ret.Get(0).([]cpu.CPUTimesStat) diff --git a/plugins/system/ps.go b/plugins/system/ps.go index 760f799ca..687ccdf7e 100644 --- a/plugins/system/ps.go +++ b/plugins/system/ps.go @@ -25,7 +25,7 @@ type DockerContainerStat struct { type PS interface { LoadAvg() (*load.LoadAvgStat, error) - CPUTimes() ([]cpu.CPUTimesStat, error) + CPUTimes(perCPU, totalCPU bool) ([]cpu.CPUTimesStat, error) DiskUsage() ([]*disk.DiskUsageStat, error) NetIO() ([]net.NetIOCountersStat, error) DiskIO() (map[string]disk.DiskIOCountersStat, error) @@ -49,8 +49,23 @@ func (s *systemPS) LoadAvg() (*load.LoadAvgStat, error) { return load.LoadAvg() } -func (s *systemPS) CPUTimes() ([]cpu.CPUTimesStat, error) { - return cpu.CPUTimes(true) +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) { diff --git a/plugins/system/ps/cpu/cpu_darwin.go b/plugins/system/ps/cpu/cpu_darwin.go index c5c0ddc02..0c054d2ef 100644 --- a/plugins/system/ps/cpu/cpu_darwin.go +++ b/plugins/system/ps/cpu/cpu_darwin.go @@ -85,18 +85,11 @@ func perCPUTimes() ([]CPUTimesStat, error) { } c := CPUTimesStat{ - CPU: fmt.Sprintf("cpu%d", i), - User: float64(cpu_ticks[C.CPU_STATE_USER]) / ClocksPerSec, - System: float64(cpu_ticks[C.CPU_STATE_SYSTEM]) / ClocksPerSec, - Nice: float64(cpu_ticks[C.CPU_STATE_NICE]) / ClocksPerSec, - Idle: float64(cpu_ticks[C.CPU_STATE_IDLE]) / ClocksPerSec, - Iowait: -1, - Irq: -1, - Softirq: -1, - Steal: -1, - Guest: -1, - GuestNice: -1, - Stolen: -1, + CPU: fmt.Sprintf("cpu%d", i), + User: float64(cpu_ticks[C.CPU_STATE_USER]) / ClocksPerSec, + System: float64(cpu_ticks[C.CPU_STATE_SYSTEM]) / ClocksPerSec, + Nice: float64(cpu_ticks[C.CPU_STATE_NICE]) / ClocksPerSec, + Idle: float64(cpu_ticks[C.CPU_STATE_IDLE]) / ClocksPerSec, } ret = append(ret, c) @@ -119,18 +112,11 @@ func allCPUTimes() ([]CPUTimesStat, error) { } c := CPUTimesStat{ - CPU: "cpu-total", - User: float64(cpuload.cpu_ticks[C.CPU_STATE_USER]) / ClocksPerSec, - System: float64(cpuload.cpu_ticks[C.CPU_STATE_SYSTEM]) / ClocksPerSec, - Nice: float64(cpuload.cpu_ticks[C.CPU_STATE_NICE]) / ClocksPerSec, - Idle: float64(cpuload.cpu_ticks[C.CPU_STATE_IDLE]) / ClocksPerSec, - Iowait: -1, - Irq: -1, - Softirq: -1, - Steal: -1, - Guest: -1, - GuestNice: -1, - Stolen: -1, + CPU: "cpu-total", + User: float64(cpuload.cpu_ticks[C.CPU_STATE_USER]) / ClocksPerSec, + System: float64(cpuload.cpu_ticks[C.CPU_STATE_SYSTEM]) / ClocksPerSec, + Nice: float64(cpuload.cpu_ticks[C.CPU_STATE_NICE]) / ClocksPerSec, + Idle: float64(cpuload.cpu_ticks[C.CPU_STATE_IDLE]) / ClocksPerSec, } return []CPUTimesStat{c}, nil diff --git a/testdata/telegraf-agent.toml b/testdata/telegraf-agent.toml index fd0221a47..98037dc6b 100644 --- a/testdata/telegraf-agent.toml +++ b/testdata/telegraf-agent.toml @@ -57,7 +57,8 @@ database = "telegraf" # required. # Read metrics about cpu usage [cpu] - # no configuration +totalcpu = true +percpu = false # Read metrics about disk usage by mount point [disk] From 4ce61875a4ca8bba86299c4bf7ea2c41c513abee Mon Sep 17 00:00:00 2001 From: Cameron Sparr Date: Thu, 13 Aug 2015 13:44:41 -0600 Subject: [PATCH 05/10] README updates for readability and ease of use --- README.md | 119 +++++++++++++++++++++++++++--------------------------- 1 file changed, 59 insertions(+), 60 deletions(-) diff --git a/README.md b/README.md index 0ab29f284..2f282b892 100644 --- a/README.md +++ b/README.md @@ -35,22 +35,10 @@ brew install telegraf * Run `telegraf -sample-config > telegraf.toml` to create an initial configuration * Edit the configuration to match your needs * Run `telegraf -config telegraf.toml -test` to output one full measurement sample to STDOUT -* Run `telegraf -config telegraf.toml` to gather and send metrics to InfluxDB -* Run `telegraf -config telegraf.toml -filter system:swap` to enable only two plugins described into config file +* Run `telegraf -config telegraf.toml` to gather and send metrics to configured outputs. +* Run `telegraf -config telegraf.toml -filter system:swap` +to enable only the system & swap plugins defined in the config. -### Telegraf Usage - -```telegraf --help``` - -* -config="": configuration file to load -* -debug=false: show metrics as they're generated to stdout -* -filter="": filter the plugins to enable, separator is : -* -httptest.serve="": if non-empty, httptest.NewServer serves on this address and blocks -* -pidfile="": file to write our pid to -* -sample-config=false: print out full sample configuration -* -test=false: gather metrics, print them out, and exit -* -version=false: display the version - ## Telegraf Options Telegraf has a few options you can configure under the `agent` section of the @@ -66,6 +54,62 @@ unit parser, ie "10s" for 10 seconds or "5m" for 5 minutes. * **debug**: Set to true to gather and send metrics to STDOUT as well as InfluxDB. +## Plugin Options + +There are 5 configuration options that are configurable per plugin: + +* **pass**: An array of strings that is used to filter metrics generated by the +current plugin. Each string in the array is tested as a prefix against metric names +and if it matches, the metric is emitted. +* **drop**: The inverse of pass, if a metric name matches, it is not emitted. +* **tagpass**: tag names and arrays of strings that are used to filter metrics by +the current plugin. Each string in the array is tested as an exact match against +the tag name, and if it matches the metric is emitted. +* **tagdrop**: The inverse of tagpass. If a tag matches, the metric is not emitted. +This is tested on metrics that have passed the tagpass test. +* **interval**: How often to gather this metric. Normal plugins use a single +global interval, but if one particular plugin should be run less or more often, +you can configure that here. + +### Plugin Configuration Examples + +This is a full working config that will output CPU data to an InfluxDB instance +at 192.168.59.103:8086, tagging measurements with dc="Denver-1". It will output +measurements at a 10s interval and will collect totalcpu & percpu data. +``` +[outputs] +[outputs.influxdb] +url = "http://192.168.59.103:8086" # required. +database = "telegraf" # required. + +[tags] +dc = "denver-1" + +[agent] +interval = "10s" + +# PLUGINS +[cpu] +percpu = true +totalcpu = true +``` + +Below is how to configure `tagpass` parameters (added in 0.1.5) + +``` +# Don't collect CPU data for cpu6 & cpu7 +[cpu.tagdrop] +cpu = [ "cpu6", "cpu7" ] + +[disk] +[disk.tagpass] +# tagpass conditions are OR, not AND. +# If the (filesystem is ext4 or xfs) OR (the path is /opt or /home) +# then the metric passes +fstype = [ "ext4", "xfs" ] +path = [ "/opt", "/home" ] +``` + ## Supported Plugins Telegraf currently has support for collecting metrics from: @@ -87,51 +131,6 @@ Telegraf currently has support for collecting metrics from: We'll be adding support for many more over the coming months. Read on if you want to add support for another service or third-party API. -## Plugin Options - -There are 3 configuration options that are configurable per plugin: - -* **pass**: An array of strings that is used to filter metrics generated by the -current plugin. Each string in the array is tested as a prefix against metric names -and if it matches, the metric is emitted. -* **drop**: The inverse of pass, if a metric name matches, it is not emitted. -* **tagpass**: tag names and arrays of strings that are used to filter metrics by -the current plugin. Each string in the array is tested as an exact match against -the tag name, and if it matches the metric is emitted. -* **tagdrop**: The inverse of tagpass. If a tag matches, the metric is not emitted. -This is tested on metrics that have passed the tagpass test. -* **interval**: How often to gather this metric. Normal plugins use a single -global interval, but if one particular plugin should be run less or more often, -you can configure that here. - -### Plugin Configuration Examples - -``` -# Read metrics about disk usage by mount point -[disk] -interval = "1m" # Run at a 1 minute interval instead of the default - -[disk.tagpass] -# These tag conditions are OR, not AND. -# If the (filesystem is ext4 or xfs) or (the path is /opt or /home) then the metric passes -fstype = [ "ext4", "xfs" ] -path = [ "/opt", "/home" ] - -[postgresql] - -[postgresql.tagdrop] -# Don't report stats about the database name 'testdatabase' -db = [ "testdatabase" ] - -``` - -``` -[disk] -# Don't report stats about the following filesystem types -[disk.tagdrop] -fstype = [ "nfs", "tmpfs", "ecryptfs" ] -``` - ## Plugins This section is for developers that want to create new collection plugins. From ba1e4917d185cd6c3c96008e34b689f5ff954b4c Mon Sep 17 00:00:00 2001 From: Cameron Sparr Date: Thu, 13 Aug 2015 14:25:16 -0600 Subject: [PATCH 06/10] Removing DefaultConfig function because there's really no point --- cmd/telegraf/telegraf.go | 8 +++++--- config.go | 5 ----- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/cmd/telegraf/telegraf.go b/cmd/telegraf/telegraf.go index ed05ac80e..b758206ef 100644 --- a/cmd/telegraf/telegraf.go +++ b/cmd/telegraf/telegraf.go @@ -48,7 +48,9 @@ func main() { log.Fatal(err) } } else { - config = telegraf.DefaultConfig() + fmt.Println("Usage: Telegraf") + flag.PrintDefaults() + return } ag, err := telegraf.NewAgent(config) @@ -65,7 +67,7 @@ func main() { log.Fatal(err) } if len(outputs) == 0 { - log.Printf("Error: no ouputs found, did you provide a config file?") + log.Printf("Error: no outputs found, did you provide a valid config file?") os.Exit(1) } @@ -74,7 +76,7 @@ func main() { log.Fatal(err) } if len(plugins) == 0 { - log.Printf("Error: no plugins found, did you provide a config file?") + log.Printf("Error: no plugins found, did you provide a valid config file?") os.Exit(1) } diff --git a/config.go b/config.go index de756f917..3ba9d4a49 100644 --- a/config.go +++ b/config.go @@ -253,11 +253,6 @@ func declared(endpoints map[string]*ast.Table) []string { return names } -// DefaultConfig returns an empty default configuration -func DefaultConfig() *Config { - return &Config{} -} - var errInvalidConfig = errors.New("invalid configuration") // LoadConfig loads the given config file and returns a *Config pointer From 1e742aec04d5758e529a9be1112d4c6e15b36348 Mon Sep 17 00:00:00 2001 From: Josh Palay Date: Wed, 12 Aug 2015 11:09:20 -0700 Subject: [PATCH 07/10] Adds cpu busy time and percentages --- plugins/system/cpu.go | 65 +++++++++++++++-- plugins/system/system_test.go | 128 ++++++++++++++++++++++++++++++---- 2 files changed, 176 insertions(+), 17 deletions(-) diff --git a/plugins/system/cpu.go b/plugins/system/cpu.go index 7114e2b9a..1f8cd7ce5 100644 --- a/plugins/system/cpu.go +++ b/plugins/system/cpu.go @@ -4,13 +4,25 @@ import ( "fmt" "github.com/influxdb/telegraf/plugins" + "github.com/influxdb/telegraf/plugins/system/ps/cpu" ) type CPUStats struct { - ps PS + ps PS + lastStats []cpu.CPUTimesStat + + PerCPU bool `toml:"percpu"` + TotalCPU bool `toml:"totalcpu"` +} - PerCPU bool `toml:"percpu"` - TotalCPU bool `toml:"totalcpu"` +func NewCPUStats(ps PS) *CPUStats { + times, _ := ps.CPUTimes() + stats := CPUStats{ + ps: ps, + lastStats: times, + } + + return &stats } func (_ *CPUStats) Description() string { @@ -33,11 +45,14 @@ func (s *CPUStats) Gather(acc plugins.Accumulator) error { return fmt.Errorf("error getting CPU info: %s", err) } - for _, cts := range times { + for i, cts := range times { tags := map[string]string{ "cpu": cts.CPU, } + busy, total := busyAndTotalCpuTime(cts) + + // Add total cpu numbers add(acc, "user", cts.User, tags) add(acc, "system", cts.System, tags) add(acc, "idle", cts.Idle, tags) @@ -49,13 +64,53 @@ func (s *CPUStats) Gather(acc plugins.Accumulator) error { add(acc, "guest", cts.Guest, tags) add(acc, "guestNice", cts.GuestNice, tags) add(acc, "stolen", cts.Stolen, tags) + add(acc, "busy", busy, tags) + + // Add in percentage + lastCts := s.lastStats[i] + lastBusy, lastTotal := busyAndTotalCpuTime(lastCts) + busyDelta := busy - lastBusy + totalDelta := total - lastTotal + + if totalDelta < 0 { + return fmt.Errorf("Error: current total CPU time is less than previous total CPU time") + } + + if totalDelta == 0 { + return nil + } + + add(acc, "percentageUser", 100*(cts.User-lastCts.User)/totalDelta, tags) + add(acc, "percentageSystem", 100*(cts.System-lastCts.System)/totalDelta, tags) + add(acc, "percentageIdle", 100*(cts.Idle-lastCts.Idle)/totalDelta, tags) + add(acc, "percentageNice", 100*(cts.Nice-lastCts.Nice)/totalDelta, tags) + add(acc, "percentageIowait", 100*(cts.Iowait-lastCts.Iowait)/totalDelta, tags) + add(acc, "percentageIrq", 100*(cts.Irq-lastCts.Irq)/totalDelta, tags) + add(acc, "percentageSoftirq", 100*(cts.Softirq-lastCts.Softirq)/totalDelta, tags) + add(acc, "percentageSteal", 100*(cts.Steal-lastCts.Steal)/totalDelta, tags) + add(acc, "percentageGuest", 100*(cts.Guest-lastCts.Guest)/totalDelta, tags) + add(acc, "percentageGuestNice", 100*(cts.GuestNice-lastCts.GuestNice)/totalDelta, tags) + add(acc, "percentageStolen", 100*(cts.Stolen-lastCts.Stolen)/totalDelta, tags) + + add(acc, "percentageBusy", 100*busyDelta/totalDelta, tags) + } + s.lastStats = times + return nil } +func busyAndTotalCpuTime(t cpu.CPUTimesStat) (float64, float64) { + busy := t.User + t.System + t.Nice + t.Iowait + t.Irq + t.Softirq + t.Steal + + t.Guest + t.GuestNice + t.Stolen + + return busy, busy + t.Idle +} + func init() { plugins.Add("cpu", func() plugins.Plugin { - return &CPUStats{ps: &systemPS{}} + realPS := &systemPS{} + return NewCPUStats(realPS) }) } diff --git a/plugins/system/system_test.go b/plugins/system/system_test.go index d7b63c988..19e05e64d 100644 --- a/plugins/system/system_test.go +++ b/plugins/system/system_test.go @@ -1,6 +1,8 @@ package system import ( + "fmt" + "reflect" "testing" "github.com/influxdb/telegraf/plugins/system/ps/cpu" @@ -44,6 +46,21 @@ func TestSystemStats_GenerateStats(t *testing.T) { Stolen: 0.051, } + 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.0002, // increased by 0.0001 + Guest: 12.9, // increased by 4.8 + GuestNice: 2.524, // increased by 2.2 + Stolen: 0.281, // increased by 0.23 + } + mps.On("CPUTimes").Return([]cpu.CPUTimesStat{cts}, nil) du := &disk.DiskUsageStat{ @@ -171,26 +188,72 @@ func TestSystemStats_GenerateStats(t *testing.T) { assert.True(t, acc.CheckValue("load5", 1.5)) assert.True(t, acc.CheckValue("load15", 0.8)) - cs := &CPUStats{ps: &mps} + cs := NewCPUStats(&mps) cputags := map[string]string{ "cpu": "cpu0", } + preCPUPoints := len(acc.Points) + err = cs.Gather(&acc) + require.NoError(t, err) + numCPUPoints := len(acc.Points) - preCPUPoints + + expectedCPUPoints := 12 + assert.Equal(t, numCPUPoints, expectedCPUPoints) + + // Computed values are checked with delta > 0 becasue of floating point arithmatic + // imprecision + assertContainsTaggedFloat(t, acc, "user", 3.1, 0, cputags) + assertContainsTaggedFloat(t, acc, "system", 8.2, 0, cputags) + assertContainsTaggedFloat(t, acc, "idle", 80.1, 0, cputags) + assertContainsTaggedFloat(t, acc, "nice", 1.3, 0, cputags) + assertContainsTaggedFloat(t, acc, "iowait", 0.2, 0, cputags) + assertContainsTaggedFloat(t, acc, "irq", 0.1, 0, cputags) + assertContainsTaggedFloat(t, acc, "softirq", 0.11, 0, cputags) + assertContainsTaggedFloat(t, acc, "steal", 0.0001, 0, cputags) + assertContainsTaggedFloat(t, acc, "guest", 8.1, 0, cputags) + assertContainsTaggedFloat(t, acc, "guestNice", 0.324, 0, cputags) + assertContainsTaggedFloat(t, acc, "stolen", 0.051, 0, cputags) + assertContainsTaggedFloat(t, acc, "busy", 21.4851, 0.0005, 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) - assert.True(t, acc.CheckTaggedValue("user", 3.1, cputags)) - assert.True(t, acc.CheckTaggedValue("system", 8.2, cputags)) - assert.True(t, acc.CheckTaggedValue("idle", 80.1, cputags)) - assert.True(t, acc.CheckTaggedValue("nice", 1.3, cputags)) - assert.True(t, acc.CheckTaggedValue("iowait", 0.2, cputags)) - assert.True(t, acc.CheckTaggedValue("irq", 0.1, cputags)) - assert.True(t, acc.CheckTaggedValue("softirq", 0.11, cputags)) - assert.True(t, acc.CheckTaggedValue("steal", 0.0001, cputags)) - assert.True(t, acc.CheckTaggedValue("guest", 8.1, cputags)) - assert.True(t, acc.CheckTaggedValue("guestNice", 0.324, cputags)) - assert.True(t, acc.CheckTaggedValue("stolen", 0.051, cputags)) + numCPUPoints = len(acc.Points) - (preCPUPoints + numCPUPoints) + expectedCPUPoints = 24 + assert.Equal(t, numCPUPoints, expectedCPUPoints) + + assertContainsTaggedFloat(t, acc, "user", 11.4, 0, cputags) + assertContainsTaggedFloat(t, acc, "system", 10.9, 0, cputags) + assertContainsTaggedFloat(t, acc, "idle", 158.8699, 0, cputags) + assertContainsTaggedFloat(t, acc, "nice", 2.5, 0, cputags) + assertContainsTaggedFloat(t, acc, "iowait", 0.7, 0, cputags) + assertContainsTaggedFloat(t, acc, "irq", 1.2, 0, cputags) + assertContainsTaggedFloat(t, acc, "softirq", 0.31, 0, cputags) + assertContainsTaggedFloat(t, acc, "steal", 0.0002, 0, cputags) + assertContainsTaggedFloat(t, acc, "guest", 12.9, 0, cputags) + assertContainsTaggedFloat(t, acc, "guestNice", 2.524, 0, cputags) + assertContainsTaggedFloat(t, acc, "stolen", 0.281, 0, cputags) + assertContainsTaggedFloat(t, acc, "busy", 42.7152, 0.0005, cputags) + + assertContainsTaggedFloat(t, acc, "percentageUser", 8.3, 0.0005, cputags) + assertContainsTaggedFloat(t, acc, "percentageSystem", 2.7, 0.0005, cputags) + assertContainsTaggedFloat(t, acc, "percentageIdle", 78.7699, 0.0005, cputags) + assertContainsTaggedFloat(t, acc, "percentageNice", 1.2, 0.0005, cputags) + assertContainsTaggedFloat(t, acc, "percentageIowait", 0.5, 0.0005, cputags) + assertContainsTaggedFloat(t, acc, "percentageIrq", 1.1, 0.0005, cputags) + assertContainsTaggedFloat(t, acc, "percentageSoftirq", 0.2, 0.0005, cputags) + assertContainsTaggedFloat(t, acc, "percentageSteal", 0.0001, 0.0005, cputags) + assertContainsTaggedFloat(t, acc, "percentageGuest", 4.8, 0.0005, cputags) + assertContainsTaggedFloat(t, acc, "percentageGuestNice", 2.2, 0.0005, cputags) + assertContainsTaggedFloat(t, acc, "percentageStolen", 0.23, 0.0005, cputags) + assertContainsTaggedFloat(t, acc, "percentageBusy", 21.2301, 0.0005, cputags) err = (&DiskStats{&mps}).Gather(&acc) require.NoError(t, err) @@ -319,3 +382,44 @@ func TestSystemStats_GenerateStats(t *testing.T) { assert.True(t, acc.CheckTaggedValue("total_active_file", uint64(26), dockertags)) assert.True(t, acc.CheckTaggedValue("total_unevictable", uint64(27), dockertags)) } + +// 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, + expectedValue float64, + delta float64, + tags map[string]string, +) { + for _, pt := range acc.Points { + if pt.Measurement == measurement { + if (tags == nil) || reflect.DeepEqual(pt.Tags, tags) { + if value, ok := pt.Values["value"].(float64); ok { + 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)) + } + + } + } + } + assert.Fail(t, fmt.Sprintf("Could not find measurement \"%s\" with requested tags within %f of %f", measurement, delta, expectedValue)) +} From 0e65d8e64e0d4acfc7f29dc0c855dd1c26d99f62 Mon Sep 17 00:00:00 2001 From: Cameron Sparr Date: Thu, 13 Aug 2015 14:36:18 -0600 Subject: [PATCH 08/10] Rebase and fixups for PR #111, fixes issue #33 --- CHANGELOG.md | 2 ++ plugins/system/cpu.go | 23 +++++++++++------------ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e94713337..754d55d82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,8 @@ - [#103](https://github.com/influxdb/telegraf/pull/103): Filter by metric tags. Thanks @srfraser! - [#106](https://github.com/influxdb/telegraf/pull/106): Options to filter plugins on startup. Thanks @zepouet! - [#107](https://github.com/influxdb/telegraf/pull/107): Multiple outputs beyong influxdb. Thanks @jipperinbham! +- [#108](https://github.com/influxdb/telegraf/issues/108): Support setting per-CPU and total-CPU gathering. +- [#111](https://github.com/influxdb/telegraf/pull/111): Report CPU Usage in cpu plugin. Thanks @jpalay! ### Bugfixes - [#85](https://github.com/influxdb/telegraf/pull/85): Fix GetLocalHost testutil function for mac users diff --git a/plugins/system/cpu.go b/plugins/system/cpu.go index 1f8cd7ce5..1096b8517 100644 --- a/plugins/system/cpu.go +++ b/plugins/system/cpu.go @@ -10,19 +10,15 @@ import ( type CPUStats struct { ps PS lastStats []cpu.CPUTimesStat - - PerCPU bool `toml:"percpu"` - TotalCPU bool `toml:"totalcpu"` + + PerCPU bool `toml:"percpu"` + TotalCPU bool `toml:"totalcpu"` } func NewCPUStats(ps PS) *CPUStats { - times, _ := ps.CPUTimes() - stats := CPUStats{ - ps: ps, - lastStats: times, + return &CPUStats{ + ps: ps, } - - return &stats } func (_ *CPUStats) Description() string { @@ -67,6 +63,10 @@ func (s *CPUStats) Gather(acc plugins.Accumulator) error { add(acc, "busy", busy, tags) // Add in percentage + if len(s.lastStats) == 0 { + // If it's the 1st gather, can't get CPU stats yet + continue + } lastCts := s.lastStats[i] lastBusy, lastTotal := busyAndTotalCpuTime(lastCts) busyDelta := busy - lastBusy @@ -77,7 +77,7 @@ func (s *CPUStats) Gather(acc plugins.Accumulator) error { } if totalDelta == 0 { - return nil + continue } add(acc, "percentageUser", 100*(cts.User-lastCts.User)/totalDelta, tags) @@ -110,7 +110,6 @@ func busyAndTotalCpuTime(t cpu.CPUTimesStat) (float64, float64) { func init() { plugins.Add("cpu", func() plugins.Plugin { - realPS := &systemPS{} - return NewCPUStats(realPS) + return &CPUStats{ps: &systemPS{}} }) } From b199d7a9fee7173083ddcb294d71c01ffb38abde Mon Sep 17 00:00:00 2001 From: Cameron Sparr Date: Thu, 13 Aug 2015 15:38:05 -0600 Subject: [PATCH 09/10] Put quotes around potentially empty bash variables --- package.sh | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package.sh b/package.sh index cbae8ae86..3ec8fda7e 100755 --- a/package.sh +++ b/package.sh @@ -224,7 +224,7 @@ EOF if [ $# -ne 1 ]; then usage 1 -elif [ $1 == "-h" ]; then +elif [ "$1" == "-h" ]; then usage 0 else VERSION=$1 @@ -232,11 +232,11 @@ fi echo -e "\nStarting package process...\n" -if [ $CIRCLE_BRANCH == "" ]; then +if [ "$CIRCLE_BRANCH" == "" ]; then check_gvm fi check_gopath -if [ $CIRCLE_BRANCH == "" ]; then +if [ "$CIRCLE_BRANCH" == "" ]; then check_clean_tree update_tree fi @@ -282,7 +282,7 @@ generate_postinstall_script $VERSION ########################################################################### # Create the actual packages. -if [ $CIRCLE_BRANCH == "" ]; then +if [ "$CIRCLE_BRANCH" == "" ]; then echo -n "Commence creation of $ARCH packages, version $VERSION? [Y/n] " read response response=`echo $response | tr 'A-Z' 'a-z'` @@ -323,7 +323,7 @@ echo "Debian package created successfully." ########################################################################### # Offer to tag the repo. -if [ $CIRCLE_BRANCH == "" ]; then +if [ "$CIRCLE_BRANCH" == "" ]; then echo -n "Tag source tree with v$VERSION and push to repo? [y/N] " read response response=`echo $response | tr 'A-Z' 'a-z'` @@ -347,7 +347,7 @@ fi ########################################################################### # Offer to publish the packages. -if [ $CIRCLE_BRANCH == "" ]; then +if [ "$CIRCLE_BRANCH" == "" ]; then echo -n "Publish packages to S3? [y/N] " read response response=`echo $response | tr 'A-Z' 'a-z'` From c26fa33094964dd1f2406d0891a28f9c744448a8 Mon Sep 17 00:00:00 2001 From: Cameron Sparr Date: Thu, 13 Aug 2015 15:44:46 -0600 Subject: [PATCH 10/10] Release 0.1.5, updating CHANGELOG and README --- CHANGELOG.md | 8 +++++++- README.md | 9 +++++++-- cmd/telegraf/telegraf.go | 2 +- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 754d55d82..f6fdf638b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,10 @@ -## v0.1.5 [unreleased] +## v0.1.6 [unreleased] + +### Features + +### Bugfixes + +## v0.1.5 [2015-08-13] ### Features - [#54](https://github.com/influxdb/telegraf/pull/54): MongoDB plugin. Thanks @jipperinbham! diff --git a/README.md b/README.md index 2f282b892..8074e5940 100644 --- a/README.md +++ b/README.md @@ -18,9 +18,14 @@ writing new plugins. ### Linux packages for Debian/Ubuntu and RHEL/CentOS: +NOTE: version 0.1.5 has introduced some breaking changes! A 0.1.5 telegraf +agent is NOT backwards-compatible with a config file from 0.1.4 and below. +That being said, the difference is not huge, see below for an example on +how to setup the new config file. + ``` -http://get.influxdb.org/telegraf/telegraf_0.1.4_amd64.deb -http://get.influxdb.org/telegraf/telegraf-0.1.4-1.x86_64.rpm +http://get.influxdb.org/telegraf/telegraf_0.1.5_amd64.deb +http://get.influxdb.org/telegraf/telegraf-0.1.5-1.x86_64.rpm ``` ### OSX via Homebrew: diff --git a/cmd/telegraf/telegraf.go b/cmd/telegraf/telegraf.go index b758206ef..1ca049af8 100644 --- a/cmd/telegraf/telegraf.go +++ b/cmd/telegraf/telegraf.go @@ -22,7 +22,7 @@ var fPidfile = flag.String("pidfile", "", "file to write our pid to") var fPLuginsFilter = flag.String("filter", "", "filter the plugins to enable, separator is :") // Telegraf version -var Version = "0.1.5-dev" +var Version = "0.1.6-dev" func main() { flag.Parse()