Add hsperfdata Input Plugin

For gather data from the shared memory exposed by running processes.
This commit is contained in:
Nick White 2016-11-17 17:26:28 +00:00
parent 536dbfb724
commit 68f132001f
8 changed files with 311 additions and 0 deletions

1
Godeps
View File

@ -51,6 +51,7 @@ github.com/shirou/gopsutil 1516eb9ddc5e61ba58874047a98f8b44b5e585e8
github.com/soniah/gosnmp 3fe3beb30fa9700988893c56a63b1df8e1b68c26
github.com/streadway/amqp b4f3ceab0337f013208d31348b578d83c0064744
github.com/stretchr/testify 1f4a1643a57e798696635ea4c126e9127adb7d3c
github.com/tokuhirom/go-hsperfdata 63efb7d3b4adbb23528abb6edcb7361a0c7ac22b
github.com/vjeantet/grok 83bfdfdfd1a8146795b28e547a8e3c8b28a466c2
github.com/wvanbergen/kafka 46f9a1cf3f670edec492029fadded9c2d9e18866
github.com/wvanbergen/kazoo-go 0f768712ae6f76454f987c3356177e138df258f8

View File

@ -157,6 +157,7 @@ configuration options.
* [filestat](./plugins/inputs/filestat)
* [haproxy](./plugins/inputs/haproxy)
* [hddtemp](./plugins/inputs/hddtemp)
* [hsperfdata](./plugins/inputs/hsperfdata) (Hostpot JVMs)
* [http_response](./plugins/inputs/http_response)
* [httpjson](./plugins/inputs/httpjson) (generic JSON-emitting http service plugin)
* [influxdb](./plugins/inputs/influxdb)

View File

@ -32,6 +32,7 @@ import (
_ "github.com/influxdata/telegraf/plugins/inputs/jolokia"
_ "github.com/influxdata/telegraf/plugins/inputs/kafka_consumer"
_ "github.com/influxdata/telegraf/plugins/inputs/kubernetes"
_ "github.com/influxdata/telegraf/plugins/inputs/hsperfdata"
_ "github.com/influxdata/telegraf/plugins/inputs/leofs"
_ "github.com/influxdata/telegraf/plugins/inputs/logparser"
_ "github.com/influxdata/telegraf/plugins/inputs/lustre2"

View File

@ -0,0 +1,39 @@
# hsperfdata Plugin
The plugin gathers data from Hotspot JVMs via the hsperfdata files they expose. This plugin won't work if you've disabled their creation using `-XX:-UsePerfData` or `-XX:+PerfDisableSharedMem`!
### Configuration:
```toml
[[inputs.hsperfdata]]
# Optional: gather data from processes belonging to a different user. By
# default, the username in the USER environment variable is used to generate
# the hsperfdata directory name (usually "/tmp/hsperfdata_username")
user: "root"
# use the named keys in the hsperfdata file as tags, not fields. By default,
# every key is exposed as a field. This example shows how to tag by JVM major
# version:
tags: ["java.property.java.vm.specification.version"]
```
### Measurements & Fields:
All metrics are gathered as the "java" measurement.
All keys in the hsperfdata file are exposed as fields; there's no comprehensive list as they vary by Hotspot version.
### Tags:
- All measurements have the following tags:
- pid (the process id of the monitored process)
- procname (the class name containing the `main` function being run)
### Example Output:
Most fields abbreviated; there's usually 200-300 of them:
```
$ ./telegraf -config telegraf.conf -input-filter example -test
java,host=nwhite91-mac,pid=17427,procname=com.sun.javaws.Main java.ci.totalTime="49874911809",...,sun.zip.zipFiles="28" 1479466710000000000
```

View File

@ -0,0 +1,89 @@
package hsperfdata
import (
"fmt"
"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/plugins/inputs"
"github.com/tokuhirom/go-hsperfdata/hsperfdata"
)
type Hsperfdata struct {
User string
Tags []string
}
var sampleConfig = `
## Use the hsperfdata directory belonging to a different user.
# user = "root"
#
## Use the value for these keys in the hsperfdata as tags, not fields. By
## default everything is a field.
# tags = ["sun.rt.jvmVersion"]
`
func (n *Hsperfdata) SampleConfig() string {
return sampleConfig
}
func (n *Hsperfdata) Repo() (*hsperfdata.Repository, error) {
if n.User == "" {
return hsperfdata.New()
} else {
return hsperfdata.NewUser(n.User)
}
}
func (n *Hsperfdata) Gather(acc telegraf.Accumulator) error {
repo, err := n.Repo()
if err != nil {
return err
}
files, err := repo.GetFiles()
if err != nil {
// the directory doesn't exist - so there aren't any Java processes running
return nil
}
for _, file := range files {
result, err := file.Read()
if err != nil {
return err
}
tags := map[string]string{"pid": file.GetPid()}
fields := result.GetMap()
procname := result.GetProcName()
if procname != "" {
tags["procname"] = procname
}
for _, tag := range n.Tags {
// don't tag metrics with "nil", just skip the tag if it's not there
if value, ok := fields[tag]; ok {
if valuestr, ok := value.(string); ok {
tags[tag] = valuestr
} else {
tags[tag] = fmt.Sprintf("%#v", fields[tag])
}
delete(fields, tag)
}
}
acc.AddFields("java", fields, tags)
}
return nil
}
func (n *Hsperfdata) Description() string {
return "Read performance data from running hotspot JVMs from shared memory"
}
func init() {
inputs.Add("hsperfdata", func() telegraf.Input {
return &Hsperfdata{}
})
}

View File

@ -0,0 +1,180 @@
package hsperfdata
import (
"fmt"
"os"
"path/filepath"
"reflect"
"runtime"
"strings"
"testing"
"github.com/influxdata/telegraf/testutil"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
const datadir = "hsperfdata_tokuhirom"
func TestGatherNoTags(t *testing.T) {
setup()
defer teardown()
hs := &Hsperfdata{User: "tokuhirom"}
acc := testutil.Accumulator{}
require.NoError(t, hs.Gather(&acc))
assert.New(t).Equal(acc.NMetrics(), uint64(2))
acc.Lock()
defer acc.Unlock()
for _, p := range acc.Metrics {
if reflect.DeepEqual(
map[string]string{
"pid": "13223",
},
p.Tags) {
assert.Equal(
t,
212,
len(p.Fields))
// verify some of the fields (there's quite a lot!)
assert.Equal(
t,
"367001600",
p.Fields["sun.gc.generation.2.space.0.capacity"])
assert.Equal(
t,
"/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Libraries",
p.Fields["sun.property.sun.boot.library.path"])
} else if reflect.DeepEqual(map[string]string{
"pid": "21916",
"procname": "org.jetbrains.jps.cmdline.Launcher",
}, p.Tags) {
assert.Equal(
t,
253,
len(p.Fields),
fmt.Sprintf("wrong number of fields in %v", p))
assert.Equal(
t,
"3313990237",
p.Fields["java.ci.totalTime"])
assert.Equal(
t,
"/Library/Java/JavaVirtualMachines/jdk1.8.0_31.jdk/Contents/Home/jre/lib",
p.Fields["sun.property.sun.boot.library.path"])
} else {
msg := fmt.Sprintf("unknown with tags %v", p.Tags)
assert.Fail(t, msg)
}
}
}
func TestGatherWithTags(t *testing.T) {
setup()
defer teardown()
hs := &Hsperfdata{
User: "tokuhirom",
Tags: []string{"java.property.java.vm.specification.vendor", "sun.gc.policy.minorCollectionSlope"}}
acc := testutil.Accumulator{}
require.NoError(t, hs.Gather(&acc))
assert.New(t).Equal(acc.NMetrics(), uint64(2))
acc.Lock()
defer acc.Unlock()
for _, p := range acc.Metrics {
if reflect.DeepEqual(
map[string]string{
"pid": "13223",
"java.property.java.vm.specification.vendor": "Sun Microsystems Inc.",
},
p.Tags) {
assert.Equal(
t,
211,
len(p.Fields))
assert.NotContains(
t,
p.Fields,
"java.property.java.vm.specification.vendor",
"value promoted to tag")
// verify some of the fields (there's quite a lot!)
assert.Equal(
t,
"367001600",
p.Fields["sun.gc.generation.2.space.0.capacity"])
assert.Equal(
t,
"/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Libraries",
p.Fields["sun.property.sun.boot.library.path"])
} else if reflect.DeepEqual(map[string]string{
"pid": "21916",
"procname": "org.jetbrains.jps.cmdline.Launcher",
"java.property.java.vm.specification.vendor": "Oracle Corporation",
"sun.gc.policy.minorCollectionSlope": "0",
}, p.Tags) {
assert.Equal(
t,
251,
len(p.Fields))
assert.NotContains(
t,
p.Fields,
"java.property.java.vm.specification.vendor",
"value promoted to tag")
assert.Equal(
t,
"3313990237",
p.Fields["java.ci.totalTime"])
assert.Equal(
t,
"/Library/Java/JavaVirtualMachines/jdk1.8.0_31.jdk/Contents/Home/jre/lib",
p.Fields["sun.property.sun.boot.library.path"])
} else {
msg := fmt.Sprintf("unknown with tags %v", p.Tags)
assert.Fail(t, msg)
}
}
}
func TestNoDirectoryNoMeasurements(t *testing.T) {
hs := &Hsperfdata{User: "tokuhirom"}
acc := testutil.Accumulator{}
require.NoError(t, hs.Gather(&acc))
assert.New(t).Equal(acc.NMetrics(), uint64(0))
}
func setup() {
_, filename, _, _ := runtime.Caller(1)
src := filepath.Join(
strings.Replace(filename, "hsperfdata_test.go", "testdata", 1),
datadir)
dest := filepath.Join(
os.TempDir(),
datadir)
os.Symlink(src, dest)
}
func teardown() {
os.Remove(filepath.Join(os.TempDir(), datadir))
}

Binary file not shown.

Binary file not shown.