diff --git a/CHANGELOG.md b/CHANGELOG.md index cf7c31c4b..fa4b820c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -62,6 +62,7 @@ be deprecated eventually. - [#1948](https://github.com/influxdata/telegraf/pull/1948): Support adding SNMP table indexes as tags. - [#2332](https://github.com/influxdata/telegraf/pull/2332): Add Elasticsearch 5.x output - [#2587](https://github.com/influxdata/telegraf/pull/2587): Add json timestamp units configurability +- [#2597](https://github.com/influxdata/telegraf/issues/2597): Add support for Linux sysctl-fs metrics. ### Bugfixes diff --git a/README.md b/README.md index 906862714..55154e36a 100644 --- a/README.md +++ b/README.md @@ -174,6 +174,7 @@ configuration options. * processes * kernel (/proc/stat) * kernel (/proc/vmstat) + * linux_sysctl_fs (/proc/sys/fs) Telegraf can also collect metrics via the following service plugins: diff --git a/plugins/inputs/system/LINUX_SYSCTL_FS_README.md b/plugins/inputs/system/LINUX_SYSCTL_FS_README.md new file mode 100644 index 000000000..e9341c322 --- /dev/null +++ b/plugins/inputs/system/LINUX_SYSCTL_FS_README.md @@ -0,0 +1,9 @@ +# Linux Sysctl FS Input + +The linux_sysctl_fs input provides Linux system level file metrics. The documentation on these fields can be found at https://www.kernel.org/doc/Documentation/sysctl/fs.txt. + +Example output: + +``` +> linux_sysctl_fs,host=foo dentry-want-pages=0i,file-max=44222i,aio-max-nr=65536i,inode-preshrink-nr=0i,dentry-nr=64340i,dentry-unused-nr=55274i,file-nr=1568i,aio-nr=0i,inode-nr=35952i,inode-free-nr=12957i,dentry-age-limit=45i 1490982022000000000 +``` diff --git a/plugins/inputs/system/linux_sysctl_fs.go b/plugins/inputs/system/linux_sysctl_fs.go new file mode 100644 index 000000000..93e426e75 --- /dev/null +++ b/plugins/inputs/system/linux_sysctl_fs.go @@ -0,0 +1,88 @@ +package system + +import ( + "bytes" + "io/ioutil" + "strconv" + + "github.com/influxdata/telegraf" + "github.com/influxdata/telegraf/plugins/inputs" +) + +// https://www.kernel.org/doc/Documentation/sysctl/fs.txt +type SysctlFS struct { + path string +} + +var sysctlFSDescription = `Provides Linux sysctl fs metrics` +var sysctlFSSampleConfig = `` + +func (_ SysctlFS) Description() string { + return sysctlFSDescription +} +func (_ SysctlFS) SampleConfig() string { + return sysctlFSSampleConfig +} + +func (sfs *SysctlFS) gatherList(file string, fields map[string]interface{}, fieldNames ...string) error { + bs, err := ioutil.ReadFile(sfs.path + "/" + file) + if err != nil { + return err + } + + bsplit := bytes.Split(bytes.TrimRight(bs, "\n"), []byte{'\t'}) + for i, name := range fieldNames { + if i >= len(bsplit) { + break + } + if name == "" { + continue + } + + v, err := strconv.ParseUint(string(bsplit[i]), 10, 64) + if err != nil { + return err + } + fields[name] = v + } + + return nil +} + +func (sfs *SysctlFS) gatherOne(name string, fields map[string]interface{}) error { + bs, err := ioutil.ReadFile(sfs.path + "/" + name) + if err != nil { + return err + } + + v, err := strconv.ParseUint(string(bytes.TrimRight(bs, "\n")), 10, 64) + if err != nil { + return err + } + + fields[name] = v + return nil +} + +func (sfs *SysctlFS) Gather(acc telegraf.Accumulator) error { + fields := map[string]interface{}{} + + for _, n := range []string{"aio-nr", "aio-max-nr", "dquot-nr", "dquot-max", "super-nr", "super-max"} { + sfs.gatherOne(n, fields) + } + + sfs.gatherList("inode-state", fields, "inode-nr", "inode-free-nr", "inode-preshrink-nr") + sfs.gatherList("dentry-state", fields, "dentry-nr", "dentry-unused-nr", "dentry-age-limit", "dentry-want-pages") + sfs.gatherList("file-nr", fields, "file-nr", "", "file-max") + + acc.AddFields("linux_sysctl_fs", fields, nil) + return nil +} + +func init() { + inputs.Add("linux_sysctl_fs", func() telegraf.Input { + return &SysctlFS{ + path: "/proc/sys/fs", + } + }) +} diff --git a/plugins/inputs/system/linux_sysctl_fs_test.go b/plugins/inputs/system/linux_sysctl_fs_test.go new file mode 100644 index 000000000..6561465cb --- /dev/null +++ b/plugins/inputs/system/linux_sysctl_fs_test.go @@ -0,0 +1,41 @@ +package system + +import ( + "io/ioutil" + "os" + "testing" + + "github.com/influxdata/telegraf/testutil" + "github.com/stretchr/testify/require" +) + +func TestSysctlFSGather(t *testing.T) { + td, err := ioutil.TempDir("", "") + require.NoError(t, err) + defer os.RemoveAll(td) + + require.NoError(t, ioutil.WriteFile(td+"/aio-nr", []byte("100\n"), 0644)) + require.NoError(t, ioutil.WriteFile(td+"/aio-max-nr", []byte("101\n"), 0644)) + require.NoError(t, ioutil.WriteFile(td+"/super-nr", []byte("102\n"), 0644)) + require.NoError(t, ioutil.WriteFile(td+"/super-max", []byte("103\n"), 0644)) + require.NoError(t, ioutil.WriteFile(td+"/file-nr", []byte("104\t0\t106\n"), 0644)) + require.NoError(t, ioutil.WriteFile(td+"/inode-state", []byte("107\t108\t109\t0\t0\t0\t0\n"), 0644)) + + sfs := &SysctlFS{ + path: td, + } + var acc testutil.Accumulator + require.NoError(t, sfs.Gather(&acc)) + + acc.AssertContainsFields(t, "linux_sysctl_fs", map[string]interface{}{ + "aio-nr": uint64(100), + "aio-max-nr": uint64(101), + "super-nr": uint64(102), + "super-max": uint64(103), + "file-nr": uint64(104), + "file-max": uint64(106), + "inode-nr": uint64(107), + "inode-free-nr": uint64(108), + "inode-preshrink-nr": uint64(109), + }) +}