436 lines
9.9 KiB
Go
436 lines
9.9 KiB
Go
package telegraf
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"sort"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/influxdb/telegraf/outputs"
|
|
"github.com/influxdb/telegraf/plugins"
|
|
"github.com/naoina/toml"
|
|
"github.com/naoina/toml/ast"
|
|
)
|
|
|
|
// Duration just wraps time.Duration
|
|
type Duration struct {
|
|
time.Duration
|
|
}
|
|
|
|
// UnmarshalTOML parses the duration from the TOML config file
|
|
func (d *Duration) UnmarshalTOML(b []byte) error {
|
|
dur, err := time.ParseDuration(string(b[1 : len(b)-1]))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
d.Duration = dur
|
|
|
|
return nil
|
|
}
|
|
|
|
// Config specifies the URL/user/password for the database that telegraf
|
|
// will be logging to, as well as all the plugins that the user has
|
|
// specified
|
|
type Config struct {
|
|
Tags map[string]string
|
|
|
|
agent *ast.Table
|
|
plugins map[string]*ast.Table
|
|
outputs map[string]*ast.Table
|
|
}
|
|
|
|
// Plugins returns the configured plugins as a map of name -> plugin toml
|
|
func (c *Config) Plugins() map[string]*ast.Table {
|
|
return c.plugins
|
|
}
|
|
|
|
// Outputs returns the configured outputs as a map of name -> output toml
|
|
func (c *Config) Outputs() map[string]*ast.Table {
|
|
return c.outputs
|
|
}
|
|
|
|
// TagFilter is the name of a tag, and the values on which to filter
|
|
type TagFilter struct {
|
|
Name string
|
|
Filter []string
|
|
}
|
|
|
|
// ConfiguredPlugin containing a name, interval, and drop/pass prefix lists
|
|
// Also lists the tags to filter
|
|
type ConfiguredPlugin struct {
|
|
Name string
|
|
|
|
Drop []string
|
|
Pass []string
|
|
|
|
TagDrop []TagFilter
|
|
TagPass []TagFilter
|
|
|
|
Interval time.Duration
|
|
}
|
|
|
|
// ShouldPass returns true if the metric should pass, false if should drop
|
|
func (cp *ConfiguredPlugin) ShouldPass(measurement string, tags map[string]string) bool {
|
|
if cp.Pass != nil {
|
|
for _, pat := range cp.Pass {
|
|
if strings.HasPrefix(measurement, pat) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
if cp.Drop != nil {
|
|
for _, pat := range cp.Drop {
|
|
if strings.HasPrefix(measurement, pat) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
if cp.TagPass != nil {
|
|
for _, pat := range cp.TagPass {
|
|
if tagval, ok := tags[pat.Name]; ok {
|
|
for _, filter := range pat.Filter {
|
|
if filter == tagval {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
if cp.TagDrop != nil {
|
|
for _, pat := range cp.TagDrop {
|
|
if tagval, ok := tags[pat.Name]; ok {
|
|
for _, filter := range pat.Filter {
|
|
if filter == tagval {
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
// ApplyOutput loads the toml config into the given interface
|
|
func (c *Config) ApplyOutput(name string, v interface{}) error {
|
|
if c.outputs[name] != nil {
|
|
return toml.UnmarshalTable(c.outputs[name], v)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ApplyAgent loads the toml config into the given interface
|
|
func (c *Config) ApplyAgent(v interface{}) error {
|
|
if c.agent != nil {
|
|
return toml.UnmarshalTable(c.agent, v)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ApplyPlugin takes defined plugin names and applies them to the given
|
|
// interface, returning a ConfiguredPlugin object in the end that can
|
|
// be inserted into a runningPlugin by the agent.
|
|
func (c *Config) ApplyPlugin(name string, v interface{}) (*ConfiguredPlugin, error) {
|
|
cp := &ConfiguredPlugin{Name: name}
|
|
|
|
if tbl, ok := c.plugins[name]; ok {
|
|
|
|
if node, ok := tbl.Fields["pass"]; ok {
|
|
if kv, ok := node.(*ast.KeyValue); ok {
|
|
if ary, ok := kv.Value.(*ast.Array); ok {
|
|
for _, elem := range ary.Value {
|
|
if str, ok := elem.(*ast.String); ok {
|
|
cp.Pass = append(cp.Pass, str.Value)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if node, ok := tbl.Fields["drop"]; ok {
|
|
if kv, ok := node.(*ast.KeyValue); ok {
|
|
if ary, ok := kv.Value.(*ast.Array); ok {
|
|
for _, elem := range ary.Value {
|
|
if str, ok := elem.(*ast.String); ok {
|
|
cp.Drop = append(cp.Drop, str.Value)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if node, ok := tbl.Fields["interval"]; ok {
|
|
if kv, ok := node.(*ast.KeyValue); ok {
|
|
if str, ok := kv.Value.(*ast.String); ok {
|
|
dur, err := time.ParseDuration(str.Value)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
cp.Interval = dur
|
|
}
|
|
}
|
|
}
|
|
|
|
if node, ok := tbl.Fields["tagpass"]; ok {
|
|
if subtbl, ok := node.(*ast.Table); ok {
|
|
for name, val := range subtbl.Fields {
|
|
if kv, ok := val.(*ast.KeyValue); ok {
|
|
tagfilter := &TagFilter{Name: name}
|
|
if ary, ok := kv.Value.(*ast.Array); ok {
|
|
for _, elem := range ary.Value {
|
|
if str, ok := elem.(*ast.String); ok {
|
|
tagfilter.Filter = append(tagfilter.Filter, str.Value)
|
|
}
|
|
}
|
|
}
|
|
cp.TagPass = append(cp.TagPass, *tagfilter)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if node, ok := tbl.Fields["tagdrop"]; ok {
|
|
if subtbl, ok := node.(*ast.Table); ok {
|
|
for name, val := range subtbl.Fields {
|
|
if kv, ok := val.(*ast.KeyValue); ok {
|
|
tagfilter := &TagFilter{Name: name}
|
|
if ary, ok := kv.Value.(*ast.Array); ok {
|
|
for _, elem := range ary.Value {
|
|
if str, ok := elem.(*ast.String); ok {
|
|
tagfilter.Filter = append(tagfilter.Filter, str.Value)
|
|
}
|
|
}
|
|
}
|
|
cp.TagDrop = append(cp.TagDrop, *tagfilter)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
delete(tbl.Fields, "drop")
|
|
delete(tbl.Fields, "pass")
|
|
delete(tbl.Fields, "interval")
|
|
delete(tbl.Fields, "tagdrop")
|
|
delete(tbl.Fields, "tagpass")
|
|
return cp, toml.UnmarshalTable(tbl, v)
|
|
}
|
|
|
|
return cp, nil
|
|
}
|
|
|
|
// PluginsDeclared returns the name of all plugins declared in the config.
|
|
func (c *Config) PluginsDeclared() []string {
|
|
return declared(c.plugins)
|
|
}
|
|
|
|
// OutputsDeclared returns the name of all outputs declared in the config.
|
|
func (c *Config) OutputsDeclared() []string {
|
|
return declared(c.outputs)
|
|
}
|
|
|
|
func declared(endpoints map[string]*ast.Table) []string {
|
|
var names []string
|
|
|
|
for name := range endpoints {
|
|
names = append(names, name)
|
|
}
|
|
|
|
sort.Strings(names)
|
|
|
|
return names
|
|
}
|
|
|
|
var errInvalidConfig = errors.New("invalid configuration")
|
|
|
|
// LoadConfig loads the given config file and returns a *Config pointer
|
|
func LoadConfig(path string) (*Config, error) {
|
|
data, err := ioutil.ReadFile(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
tbl, err := toml.Parse(data)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
c := &Config{
|
|
Tags: make(map[string]string),
|
|
plugins: make(map[string]*ast.Table),
|
|
outputs: make(map[string]*ast.Table),
|
|
}
|
|
|
|
for name, val := range tbl.Fields {
|
|
subtbl, ok := val.(*ast.Table)
|
|
if !ok {
|
|
return nil, errInvalidConfig
|
|
}
|
|
|
|
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)
|
|
if !ok {
|
|
return nil, errInvalidConfig
|
|
}
|
|
c.outputs[outputName] = outputSubtbl
|
|
}
|
|
default:
|
|
c.plugins[name] = subtbl
|
|
}
|
|
}
|
|
|
|
return c, nil
|
|
}
|
|
|
|
// ListTags returns a string of tags specified in the config,
|
|
// line-protocol style
|
|
func (c *Config) ListTags() string {
|
|
var tags []string
|
|
|
|
for k, v := range c.Tags {
|
|
tags = append(tags, fmt.Sprintf("%s=%s", k, v))
|
|
}
|
|
|
|
sort.Strings(tags)
|
|
|
|
return strings.Join(tags, " ")
|
|
}
|
|
|
|
type hasConfig interface {
|
|
BasicConfig() string
|
|
}
|
|
|
|
type hasDescr interface {
|
|
Description() string
|
|
}
|
|
|
|
var header = `# Telegraf configuration
|
|
|
|
# Telegraf is entirely plugin driven. All metrics are gathered from the
|
|
# declared plugins.
|
|
|
|
# Even if a plugin has no configuration, it must be declared in here
|
|
# to be active. Declaring a plugin means just specifying the name
|
|
# as a section with no variables. To deactivate a plugin, comment
|
|
# out the name and any variables.
|
|
|
|
# Use 'telegraf -config telegraf.toml -test' to see what metrics a config
|
|
# file would generate.
|
|
|
|
# One rule that plugins conform to is wherever a connection string
|
|
# can be passed, the values '' and 'localhost' are treated specially.
|
|
# They indicate to the plugin to use their own builtin configuration to
|
|
# connect to the local system.
|
|
|
|
# NOTE: The configuration has a few required parameters. They are marked
|
|
# with 'required'. Be sure to edit those to make this configuration work.
|
|
|
|
# Tags can also be specified via a normal map, but only one form at a time:
|
|
[tags]
|
|
# dc = "us-east-1"
|
|
|
|
# Configuration for telegraf itself
|
|
[agent]
|
|
# interval = "10s"
|
|
# debug = false
|
|
# hostname = "prod3241"
|
|
|
|
###############################################################################
|
|
# OUTPUTS #
|
|
###############################################################################
|
|
|
|
[outputs]
|
|
`
|
|
|
|
var header2 = `
|
|
|
|
###############################################################################
|
|
# PLUGINS #
|
|
###############################################################################
|
|
|
|
`
|
|
|
|
// PrintSampleConfig prints the sample config!
|
|
func PrintSampleConfig() {
|
|
fmt.Printf(header)
|
|
|
|
// Print Outputs
|
|
var onames []string
|
|
|
|
for oname := range outputs.Outputs {
|
|
onames = append(onames, oname)
|
|
}
|
|
sort.Strings(onames)
|
|
|
|
for _, oname := range onames {
|
|
creator := outputs.Outputs[oname]
|
|
output := creator()
|
|
|
|
fmt.Printf("\n# %s\n[outputs.%s]", output.Description(), oname)
|
|
|
|
config := output.SampleConfig()
|
|
if config == "" {
|
|
fmt.Printf("\n # no configuration\n")
|
|
} else {
|
|
fmt.Printf(config)
|
|
}
|
|
}
|
|
|
|
fmt.Printf(header2)
|
|
|
|
// Print Plugins
|
|
var pnames []string
|
|
|
|
for pname := range plugins.Plugins {
|
|
pnames = append(pnames, pname)
|
|
}
|
|
sort.Strings(pnames)
|
|
|
|
for _, pname := range pnames {
|
|
creator := plugins.Plugins[pname]
|
|
plugin := creator()
|
|
|
|
fmt.Printf("\n# %s\n[%s]", plugin.Description(), pname)
|
|
|
|
config := plugin.SampleConfig()
|
|
if config == "" {
|
|
fmt.Printf("\n # no configuration\n")
|
|
} else {
|
|
fmt.Printf(config)
|
|
}
|
|
}
|
|
}
|
|
|
|
// PrintPluginConfig prints the config usage of a single plugin.
|
|
func PrintPluginConfig(name string) error {
|
|
if creator, ok := plugins.Plugins[name]; ok {
|
|
plugin := creator()
|
|
fmt.Printf("# %s\n[%s]\n", plugin.Description(), name)
|
|
fmt.Printf(strings.TrimSpace(plugin.SampleConfig()))
|
|
} else {
|
|
return errors.New(fmt.Sprintf("Plugin %s not found", name))
|
|
}
|
|
return nil
|
|
}
|