2015-11-24 21:22:11 +00:00
|
|
|
package config
|
2015-04-01 16:34:32 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
2015-11-13 23:14:07 +00:00
|
|
|
"log"
|
2015-10-19 07:09:36 +00:00
|
|
|
"path/filepath"
|
2015-04-01 16:34:32 +00:00
|
|
|
"sort"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
2016-01-27 21:21:36 +00:00
|
|
|
"github.com/influxdata/telegraf"
|
2016-01-20 18:57:35 +00:00
|
|
|
"github.com/influxdata/telegraf/internal"
|
2016-01-22 18:54:12 +00:00
|
|
|
"github.com/influxdata/telegraf/internal/models"
|
2016-01-20 18:57:35 +00:00
|
|
|
"github.com/influxdata/telegraf/plugins/inputs"
|
|
|
|
"github.com/influxdata/telegraf/plugins/outputs"
|
2016-02-06 00:36:35 +00:00
|
|
|
"github.com/influxdata/telegraf/plugins/parsers"
|
2016-02-10 22:50:07 +00:00
|
|
|
"github.com/influxdata/telegraf/plugins/serializers"
|
2015-11-24 21:22:11 +00:00
|
|
|
|
2015-12-18 15:36:30 +00:00
|
|
|
"github.com/influxdata/config"
|
2015-04-01 16:34:32 +00:00
|
|
|
"github.com/naoina/toml/ast"
|
|
|
|
)
|
|
|
|
|
2015-08-11 16:34:00 +00:00
|
|
|
// Config specifies the URL/user/password for the database that telegraf
|
2015-08-04 14:58:32 +00:00
|
|
|
// will be logging to, as well as all the plugins that the user has
|
|
|
|
// specified
|
2015-04-01 16:34:32 +00:00
|
|
|
type Config struct {
|
2015-11-24 21:22:11 +00:00
|
|
|
Tags map[string]string
|
2016-01-07 20:39:43 +00:00
|
|
|
InputFilters []string
|
2015-11-24 21:22:11 +00:00
|
|
|
OutputFilters []string
|
2015-08-11 20:02:04 +00:00
|
|
|
|
2015-11-26 01:42:07 +00:00
|
|
|
Agent *AgentConfig
|
2016-01-27 21:21:36 +00:00
|
|
|
Inputs []*internal_models.RunningInput
|
|
|
|
Outputs []*internal_models.RunningOutput
|
2015-04-01 16:34:32 +00:00
|
|
|
}
|
|
|
|
|
2015-11-23 23:28:11 +00:00
|
|
|
func NewConfig() *Config {
|
|
|
|
c := &Config{
|
2015-11-26 01:42:07 +00:00
|
|
|
// Agent defaults:
|
|
|
|
Agent: &AgentConfig{
|
2015-12-04 18:44:56 +00:00
|
|
|
Interval: internal.Duration{Duration: 10 * time.Second},
|
2015-11-26 01:42:07 +00:00
|
|
|
RoundInterval: true,
|
2015-12-04 18:44:56 +00:00
|
|
|
FlushInterval: internal.Duration{Duration: 10 * time.Second},
|
|
|
|
FlushJitter: internal.Duration{Duration: 5 * time.Second},
|
2015-11-26 01:42:07 +00:00
|
|
|
},
|
|
|
|
|
2015-11-24 21:22:11 +00:00
|
|
|
Tags: make(map[string]string),
|
2016-01-27 21:21:36 +00:00
|
|
|
Inputs: make([]*internal_models.RunningInput, 0),
|
|
|
|
Outputs: make([]*internal_models.RunningOutput, 0),
|
2016-01-07 20:39:43 +00:00
|
|
|
InputFilters: make([]string, 0),
|
2015-11-24 21:22:11 +00:00
|
|
|
OutputFilters: make([]string, 0),
|
2015-11-23 23:28:11 +00:00
|
|
|
}
|
|
|
|
return c
|
|
|
|
}
|
|
|
|
|
2015-11-26 01:42:07 +00:00
|
|
|
type AgentConfig struct {
|
|
|
|
// Interval at which to gather information
|
|
|
|
Interval internal.Duration
|
|
|
|
|
|
|
|
// RoundInterval rounds collection interval to 'interval'.
|
|
|
|
// ie, if Interval=10s then always collect on :00, :10, :20, etc.
|
|
|
|
RoundInterval bool
|
|
|
|
|
2016-01-19 20:00:36 +00:00
|
|
|
// CollectionJitter is used to jitter the collection by a random amount.
|
|
|
|
// Each plugin will sleep for a random time within jitter before collecting.
|
|
|
|
// This can be used to avoid many plugins querying things like sysfs at the
|
|
|
|
// same time, which can have a measurable effect on the system.
|
|
|
|
CollectionJitter internal.Duration
|
|
|
|
|
2016-02-16 00:21:38 +00:00
|
|
|
// FlushInterval is the Interval at which to flush data
|
2015-11-26 01:42:07 +00:00
|
|
|
FlushInterval internal.Duration
|
|
|
|
|
2016-01-19 20:00:36 +00:00
|
|
|
// FlushJitter Jitters the flush interval by a random amount.
|
|
|
|
// This is primarily to avoid large write spikes for users running a large
|
|
|
|
// number of telegraf instances.
|
|
|
|
// ie, a jitter of 5s and interval 10s means flushes will happen every 10-15s
|
2015-11-26 01:42:07 +00:00
|
|
|
FlushJitter internal.Duration
|
|
|
|
|
2016-01-22 18:54:12 +00:00
|
|
|
// MetricBufferLimit is the max number of metrics that each output plugin
|
|
|
|
// will cache. The buffer is cleared when a successful write occurs. When
|
|
|
|
// full, the oldest metrics will be overwritten.
|
|
|
|
MetricBufferLimit int
|
|
|
|
|
2016-02-16 00:21:38 +00:00
|
|
|
// FlushBufferWhenFull tells Telegraf to flush the metric buffer whenever
|
|
|
|
// it fills up, regardless of FlushInterval. Setting this option to true
|
|
|
|
// does _not_ deactivate FlushInterval.
|
|
|
|
FlushBufferWhenFull bool
|
|
|
|
|
2015-11-26 01:42:07 +00:00
|
|
|
// TODO(cam): Remove UTC and Precision parameters, they are no longer
|
|
|
|
// valid for the agent config. Leaving them here for now for backwards-
|
|
|
|
// compatability
|
|
|
|
UTC bool `toml:"utc"`
|
|
|
|
Precision string
|
|
|
|
|
2016-01-15 19:25:56 +00:00
|
|
|
// Debug is the option for running in debug mode
|
|
|
|
Debug bool
|
|
|
|
|
|
|
|
// Quiet is the option for running in quiet mode
|
|
|
|
Quiet bool
|
2015-11-26 01:42:07 +00:00
|
|
|
Hostname string
|
|
|
|
}
|
|
|
|
|
2016-01-07 20:39:43 +00:00
|
|
|
// Inputs returns a list of strings of the configured inputs.
|
|
|
|
func (c *Config) InputNames() []string {
|
2015-11-24 21:22:11 +00:00
|
|
|
var name []string
|
2016-01-07 20:39:43 +00:00
|
|
|
for _, input := range c.Inputs {
|
|
|
|
name = append(name, input.Name)
|
2015-08-11 20:02:04 +00:00
|
|
|
}
|
2015-11-24 21:22:11 +00:00
|
|
|
return name
|
2015-08-07 20:31:25 +00:00
|
|
|
}
|
|
|
|
|
2016-01-07 20:39:43 +00:00
|
|
|
// Outputs returns a list of strings of the configured inputs.
|
2015-11-24 21:22:11 +00:00
|
|
|
func (c *Config) OutputNames() []string {
|
|
|
|
var name []string
|
|
|
|
for _, output := range c.Outputs {
|
|
|
|
name = append(name, output.Name)
|
2015-11-24 01:00:54 +00:00
|
|
|
}
|
2015-11-24 21:22:11 +00:00
|
|
|
return name
|
2015-11-24 01:00:54 +00:00
|
|
|
}
|
|
|
|
|
2015-08-04 14:58:32 +00:00
|
|
|
// ListTags returns a string of tags specified in the config,
|
|
|
|
// line-protocol style
|
2015-04-01 16:34:32 +00:00
|
|
|
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, " ")
|
|
|
|
}
|
2015-05-18 22:10:11 +00:00
|
|
|
|
2016-02-18 04:57:33 +00:00
|
|
|
var header = `# Telegraf Configuration
|
2015-05-18 22:10:11 +00:00
|
|
|
|
2015-05-22 23:45:14 +00:00
|
|
|
# Telegraf is entirely plugin driven. All metrics are gathered from the
|
2016-01-22 18:54:12 +00:00
|
|
|
# declared inputs, and sent to the declared outputs.
|
2015-05-18 22:10:11 +00:00
|
|
|
|
2016-01-22 18:54:12 +00:00
|
|
|
# Plugins must be declared in here to be active.
|
|
|
|
# To deactivate a plugin, comment out the name and any variables.
|
2015-05-18 22:10:11 +00:00
|
|
|
|
2016-01-22 18:54:12 +00:00
|
|
|
# Use 'telegraf -config telegraf.conf -test' to see what metrics a config
|
2015-05-18 22:51:11 +00:00
|
|
|
# file would generate.
|
|
|
|
|
2016-01-27 18:09:14 +00:00
|
|
|
# Global tags can be specified here in key="value" format.
|
2016-02-08 22:56:43 +00:00
|
|
|
[global_tags]
|
2016-01-27 18:09:14 +00:00
|
|
|
# dc = "us-east-1" # will tag all metrics with dc=us-east-1
|
|
|
|
# rack = "1a"
|
2015-05-22 23:26:32 +00:00
|
|
|
|
2015-09-02 16:30:44 +00:00
|
|
|
# Configuration for telegraf agent
|
2015-08-25 23:59:12 +00:00
|
|
|
[agent]
|
2016-02-18 21:26:51 +00:00
|
|
|
## Default data collection interval for all inputs
|
2015-10-15 21:53:29 +00:00
|
|
|
interval = "10s"
|
2016-02-18 21:26:51 +00:00
|
|
|
## Rounds collection interval to 'interval'
|
|
|
|
## ie, if interval="10s" then always collect on :00, :10, :20, etc.
|
2015-10-21 20:05:27 +00:00
|
|
|
round_interval = true
|
2016-01-22 18:54:12 +00:00
|
|
|
|
2016-02-18 21:26:51 +00:00
|
|
|
## Telegraf will cache metric_buffer_limit metrics for each output, and will
|
|
|
|
## flush this buffer on a successful write.
|
2016-01-22 18:54:12 +00:00
|
|
|
metric_buffer_limit = 10000
|
2016-02-18 21:26:51 +00:00
|
|
|
## Flush the buffer whenever full, regardless of flush_interval.
|
2016-02-16 00:21:38 +00:00
|
|
|
flush_buffer_when_full = true
|
2016-01-22 18:54:12 +00:00
|
|
|
|
2016-02-18 21:26:51 +00:00
|
|
|
## Collection jitter is used to jitter the collection by a random amount.
|
|
|
|
## Each plugin will sleep for a random time within jitter before collecting.
|
|
|
|
## This can be used to avoid many plugins querying things like sysfs at the
|
|
|
|
## same time, which can have a measurable effect on the system.
|
2016-01-19 20:00:36 +00:00
|
|
|
collection_jitter = "0s"
|
2015-10-21 20:05:27 +00:00
|
|
|
|
2016-02-18 21:26:51 +00:00
|
|
|
## Default flushing interval for all outputs. You shouldn't set this below
|
|
|
|
## interval. Maximum flush_interval will be flush_interval + flush_jitter
|
2015-10-16 22:13:32 +00:00
|
|
|
flush_interval = "10s"
|
2016-02-18 21:26:51 +00:00
|
|
|
## Jitter the flush interval by a random amount. This is primarily to avoid
|
|
|
|
## large write spikes for users running a large number of telegraf instances.
|
|
|
|
## ie, a jitter of 5s and interval 10s means flushes will happen every 10-15s
|
2015-10-23 17:23:08 +00:00
|
|
|
flush_jitter = "0s"
|
2015-10-21 20:05:27 +00:00
|
|
|
|
2016-02-18 21:26:51 +00:00
|
|
|
## Run telegraf in debug mode
|
2015-10-15 21:53:29 +00:00
|
|
|
debug = false
|
2016-02-18 21:26:51 +00:00
|
|
|
## Run telegraf in quiet mode
|
2016-01-15 19:25:56 +00:00
|
|
|
quiet = false
|
2016-02-18 21:26:51 +00:00
|
|
|
## Override default hostname, if empty use os.Hostname()
|
2015-10-15 21:53:29 +00:00
|
|
|
hostname = ""
|
2015-05-18 22:10:11 +00:00
|
|
|
|
2015-08-26 17:02:10 +00:00
|
|
|
|
2015-08-25 23:59:12 +00:00
|
|
|
###############################################################################
|
|
|
|
# OUTPUTS #
|
|
|
|
###############################################################################
|
2015-05-18 22:10:11 +00:00
|
|
|
|
2015-08-25 23:59:12 +00:00
|
|
|
`
|
2015-05-18 22:10:11 +00:00
|
|
|
|
2015-09-24 18:06:11 +00:00
|
|
|
var pluginHeader = `
|
2015-05-20 05:19:32 +00:00
|
|
|
|
2015-08-25 23:59:12 +00:00
|
|
|
###############################################################################
|
2016-01-07 20:39:43 +00:00
|
|
|
# INPUTS #
|
2015-08-25 23:59:12 +00:00
|
|
|
###############################################################################
|
2015-11-20 02:08:02 +00:00
|
|
|
|
2015-05-18 22:10:11 +00:00
|
|
|
`
|
|
|
|
|
2016-01-07 20:39:43 +00:00
|
|
|
var serviceInputHeader = `
|
2015-09-24 18:06:11 +00:00
|
|
|
|
|
|
|
###############################################################################
|
2016-01-07 20:39:43 +00:00
|
|
|
# SERVICE INPUTS #
|
2015-09-24 18:06:11 +00:00
|
|
|
###############################################################################
|
|
|
|
`
|
|
|
|
|
2015-09-22 01:38:57 +00:00
|
|
|
// PrintSampleConfig prints the sample config
|
|
|
|
func PrintSampleConfig(pluginFilters []string, outputFilters []string) {
|
2015-05-18 22:10:11 +00:00
|
|
|
fmt.Printf(header)
|
|
|
|
|
2015-09-24 18:06:11 +00:00
|
|
|
// Filter outputs
|
2015-08-25 23:59:12 +00:00
|
|
|
var onames []string
|
|
|
|
for oname := range outputs.Outputs {
|
2015-09-22 01:38:57 +00:00
|
|
|
if len(outputFilters) == 0 || sliceContains(oname, outputFilters) {
|
|
|
|
onames = append(onames, oname)
|
|
|
|
}
|
2015-05-18 22:10:11 +00:00
|
|
|
}
|
2015-08-25 23:59:12 +00:00
|
|
|
sort.Strings(onames)
|
2015-05-18 22:10:11 +00:00
|
|
|
|
2015-09-24 18:06:11 +00:00
|
|
|
// Print Outputs
|
2015-08-25 23:59:12 +00:00
|
|
|
for _, oname := range onames {
|
|
|
|
creator := outputs.Outputs[oname]
|
|
|
|
output := creator()
|
2015-11-24 21:22:11 +00:00
|
|
|
printConfig(oname, output, "outputs")
|
2015-08-25 23:59:12 +00:00
|
|
|
}
|
2015-05-18 22:10:11 +00:00
|
|
|
|
2016-01-07 20:39:43 +00:00
|
|
|
// Filter inputs
|
2015-08-25 23:59:12 +00:00
|
|
|
var pnames []string
|
2016-01-07 20:39:43 +00:00
|
|
|
for pname := range inputs.Inputs {
|
2015-09-22 01:38:57 +00:00
|
|
|
if len(pluginFilters) == 0 || sliceContains(pname, pluginFilters) {
|
|
|
|
pnames = append(pnames, pname)
|
|
|
|
}
|
2015-08-25 23:59:12 +00:00
|
|
|
}
|
|
|
|
sort.Strings(pnames)
|
|
|
|
|
2016-01-07 20:39:43 +00:00
|
|
|
// Print Inputs
|
2015-09-24 18:06:11 +00:00
|
|
|
fmt.Printf(pluginHeader)
|
2016-01-27 21:21:36 +00:00
|
|
|
servInputs := make(map[string]telegraf.ServiceInput)
|
2015-08-25 23:59:12 +00:00
|
|
|
for _, pname := range pnames {
|
2016-01-07 20:39:43 +00:00
|
|
|
creator := inputs.Inputs[pname]
|
|
|
|
input := creator()
|
2015-05-18 22:10:11 +00:00
|
|
|
|
2016-01-07 20:39:43 +00:00
|
|
|
switch p := input.(type) {
|
2016-01-27 21:21:36 +00:00
|
|
|
case telegraf.ServiceInput:
|
2016-01-07 20:39:43 +00:00
|
|
|
servInputs[pname] = p
|
2015-09-24 18:06:11 +00:00
|
|
|
continue
|
2015-05-18 22:10:11 +00:00
|
|
|
}
|
2015-09-24 18:06:11 +00:00
|
|
|
|
2016-01-07 20:39:43 +00:00
|
|
|
printConfig(pname, input, "inputs")
|
2015-09-24 18:06:11 +00:00
|
|
|
}
|
|
|
|
|
2016-01-07 20:39:43 +00:00
|
|
|
// Print Service Inputs
|
|
|
|
fmt.Printf(serviceInputHeader)
|
|
|
|
for name, input := range servInputs {
|
|
|
|
printConfig(name, input, "inputs")
|
2015-09-24 18:06:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-22 20:24:51 +00:00
|
|
|
type printer interface {
|
|
|
|
Description() string
|
|
|
|
SampleConfig() string
|
|
|
|
}
|
|
|
|
|
2015-11-24 21:22:11 +00:00
|
|
|
func printConfig(name string, p printer, op string) {
|
|
|
|
fmt.Printf("\n# %s\n[[%s.%s]]", p.Description(), op, name)
|
2015-10-22 20:24:51 +00:00
|
|
|
config := p.SampleConfig()
|
2015-09-24 18:06:11 +00:00
|
|
|
if config == "" {
|
2015-10-15 21:53:29 +00:00
|
|
|
fmt.Printf("\n # no configuration\n")
|
2015-09-24 18:06:11 +00:00
|
|
|
} else {
|
|
|
|
fmt.Printf(config)
|
2015-05-18 22:10:11 +00:00
|
|
|
}
|
|
|
|
}
|
2015-08-24 20:52:46 +00:00
|
|
|
|
2015-09-22 01:38:57 +00:00
|
|
|
func sliceContains(name string, list []string) bool {
|
|
|
|
for _, b := range list {
|
|
|
|
if b == name {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2016-01-07 20:39:43 +00:00
|
|
|
// PrintInputConfig prints the config usage of a single input.
|
|
|
|
func PrintInputConfig(name string) error {
|
|
|
|
if creator, ok := inputs.Inputs[name]; ok {
|
|
|
|
printConfig(name, creator(), "inputs")
|
2015-08-24 20:52:46 +00:00
|
|
|
} else {
|
2016-01-07 20:39:43 +00:00
|
|
|
return errors.New(fmt.Sprintf("Input %s not found", name))
|
2015-08-24 20:52:46 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2015-10-17 04:47:13 +00:00
|
|
|
|
2015-10-22 20:24:51 +00:00
|
|
|
// PrintOutputConfig prints the config usage of a single output.
|
|
|
|
func PrintOutputConfig(name string) error {
|
|
|
|
if creator, ok := outputs.Outputs[name]; ok {
|
2015-11-24 21:22:11 +00:00
|
|
|
printConfig(name, creator(), "outputs")
|
2015-10-22 20:24:51 +00:00
|
|
|
} else {
|
|
|
|
return errors.New(fmt.Sprintf("Output %s not found", name))
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-10-19 07:09:36 +00:00
|
|
|
func (c *Config) LoadDirectory(path string) error {
|
|
|
|
directoryEntries, err := ioutil.ReadDir(path)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
for _, entry := range directoryEntries {
|
|
|
|
if entry.IsDir() {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
name := entry.Name()
|
2015-11-26 01:42:07 +00:00
|
|
|
if len(name) < 6 || name[len(name)-5:] != ".conf" {
|
2015-10-19 07:09:36 +00:00
|
|
|
continue
|
|
|
|
}
|
2015-11-23 23:28:11 +00:00
|
|
|
err := c.LoadConfig(filepath.Join(path, name))
|
2015-10-19 07:09:36 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-11-24 21:22:11 +00:00
|
|
|
// LoadConfig loads the given config file and applies it to c
|
2015-11-23 23:28:11 +00:00
|
|
|
func (c *Config) LoadConfig(path string) error {
|
2015-12-18 15:36:30 +00:00
|
|
|
tbl, err := config.ParseFile(path)
|
2015-10-17 04:47:13 +00:00
|
|
|
if err != nil {
|
2015-11-23 23:28:11 +00:00
|
|
|
return err
|
2015-10-17 04:47:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for name, val := range tbl.Fields {
|
2015-11-13 23:14:07 +00:00
|
|
|
subTable, ok := val.(*ast.Table)
|
2015-10-17 04:47:13 +00:00
|
|
|
if !ok {
|
2015-11-23 23:28:11 +00:00
|
|
|
return errors.New("invalid configuration")
|
2015-10-17 04:47:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
switch name {
|
|
|
|
case "agent":
|
2015-12-18 15:36:30 +00:00
|
|
|
if err = config.UnmarshalTable(subTable, c.Agent); err != nil {
|
2015-11-26 01:42:07 +00:00
|
|
|
log.Printf("Could not parse [agent] config\n")
|
|
|
|
return err
|
|
|
|
}
|
2016-02-08 22:56:43 +00:00
|
|
|
case "global_tags", "tags":
|
2015-12-18 15:36:30 +00:00
|
|
|
if err = config.UnmarshalTable(subTable, c.Tags); err != nil {
|
2016-02-08 22:56:43 +00:00
|
|
|
log.Printf("Could not parse [global_tags] config\n")
|
2015-11-23 23:28:11 +00:00
|
|
|
return err
|
2015-10-17 04:47:13 +00:00
|
|
|
}
|
|
|
|
case "outputs":
|
2016-01-07 20:39:43 +00:00
|
|
|
for pluginName, pluginVal := range subTable.Fields {
|
|
|
|
switch pluginSubTable := pluginVal.(type) {
|
2015-11-13 23:14:07 +00:00
|
|
|
case *ast.Table:
|
2016-01-07 20:39:43 +00:00
|
|
|
if err = c.addOutput(pluginName, pluginSubTable); err != nil {
|
2015-11-24 21:22:11 +00:00
|
|
|
return err
|
|
|
|
}
|
2015-11-13 23:14:07 +00:00
|
|
|
case []*ast.Table:
|
2016-01-07 20:39:43 +00:00
|
|
|
for _, t := range pluginSubTable {
|
|
|
|
if err = c.addOutput(pluginName, t); err != nil {
|
2015-11-24 21:22:11 +00:00
|
|
|
return err
|
|
|
|
}
|
2015-11-13 23:14:07 +00:00
|
|
|
}
|
|
|
|
default:
|
2015-11-23 23:28:11 +00:00
|
|
|
return fmt.Errorf("Unsupported config format: %s",
|
2016-01-07 20:39:43 +00:00
|
|
|
pluginName)
|
2015-10-17 04:47:13 +00:00
|
|
|
}
|
|
|
|
}
|
2016-01-08 19:49:50 +00:00
|
|
|
case "inputs", "plugins":
|
2015-11-20 02:08:02 +00:00
|
|
|
for pluginName, pluginVal := range subTable.Fields {
|
|
|
|
switch pluginSubTable := pluginVal.(type) {
|
|
|
|
case *ast.Table:
|
2016-01-07 20:39:43 +00:00
|
|
|
if err = c.addInput(pluginName, pluginSubTable); err != nil {
|
2015-11-24 21:22:11 +00:00
|
|
|
return err
|
|
|
|
}
|
2015-11-20 02:08:02 +00:00
|
|
|
case []*ast.Table:
|
2015-11-24 21:22:11 +00:00
|
|
|
for _, t := range pluginSubTable {
|
2016-01-07 20:39:43 +00:00
|
|
|
if err = c.addInput(pluginName, t); err != nil {
|
2015-11-24 21:22:11 +00:00
|
|
|
return err
|
|
|
|
}
|
2015-11-20 02:08:02 +00:00
|
|
|
}
|
|
|
|
default:
|
2015-11-23 23:28:11 +00:00
|
|
|
return fmt.Errorf("Unsupported config format: %s",
|
2015-11-20 02:08:02 +00:00
|
|
|
pluginName)
|
|
|
|
}
|
|
|
|
}
|
2016-01-07 20:39:43 +00:00
|
|
|
// Assume it's an input input for legacy config file support if no other
|
2015-11-20 02:08:02 +00:00
|
|
|
// identifiers are present
|
2015-10-17 04:47:13 +00:00
|
|
|
default:
|
2016-01-07 20:39:43 +00:00
|
|
|
if err = c.addInput(name, subTable); err != nil {
|
2015-11-24 21:22:11 +00:00
|
|
|
return err
|
|
|
|
}
|
2015-10-17 04:47:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2015-11-24 21:22:11 +00:00
|
|
|
|
|
|
|
func (c *Config) addOutput(name string, table *ast.Table) error {
|
|
|
|
if len(c.OutputFilters) > 0 && !sliceContains(name, c.OutputFilters) {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
creator, ok := outputs.Outputs[name]
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("Undefined but requested output: %s", name)
|
|
|
|
}
|
2015-12-01 14:15:28 +00:00
|
|
|
output := creator()
|
2015-11-24 21:22:11 +00:00
|
|
|
|
2016-02-10 22:50:07 +00:00
|
|
|
// If the output has a SetSerializer function, then this means it can write
|
|
|
|
// arbitrary types of output, so build the serializer and set it.
|
|
|
|
switch t := output.(type) {
|
|
|
|
case serializers.SerializerOutput:
|
|
|
|
serializer, err := buildSerializer(name, table)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
t.SetSerializer(serializer)
|
|
|
|
}
|
|
|
|
|
2015-12-01 14:15:28 +00:00
|
|
|
outputConfig, err := buildOutput(name, table)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2015-12-18 15:36:30 +00:00
|
|
|
if err := config.UnmarshalTable(table, output); err != nil {
|
2015-11-24 21:22:11 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-01-27 21:21:36 +00:00
|
|
|
ro := internal_models.NewRunningOutput(name, output, outputConfig)
|
2016-01-22 18:54:12 +00:00
|
|
|
if c.Agent.MetricBufferLimit > 0 {
|
2016-02-16 00:21:38 +00:00
|
|
|
ro.MetricBufferLimit = c.Agent.MetricBufferLimit
|
2015-11-24 21:22:11 +00:00
|
|
|
}
|
2016-02-16 00:21:38 +00:00
|
|
|
ro.FlushBufferWhenFull = c.Agent.FlushBufferWhenFull
|
2015-11-24 21:22:11 +00:00
|
|
|
c.Outputs = append(c.Outputs, ro)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-01-07 20:39:43 +00:00
|
|
|
func (c *Config) addInput(name string, table *ast.Table) error {
|
|
|
|
if len(c.InputFilters) > 0 && !sliceContains(name, c.InputFilters) {
|
2015-11-24 21:22:11 +00:00
|
|
|
return nil
|
|
|
|
}
|
2016-01-07 20:39:43 +00:00
|
|
|
// Legacy support renaming io input to diskio
|
2015-12-15 20:21:02 +00:00
|
|
|
if name == "io" {
|
|
|
|
name = "diskio"
|
|
|
|
}
|
|
|
|
|
2016-01-07 20:39:43 +00:00
|
|
|
creator, ok := inputs.Inputs[name]
|
2015-11-24 21:22:11 +00:00
|
|
|
if !ok {
|
2016-01-07 20:39:43 +00:00
|
|
|
return fmt.Errorf("Undefined but requested input: %s", name)
|
2015-11-24 21:22:11 +00:00
|
|
|
}
|
2016-01-07 20:39:43 +00:00
|
|
|
input := creator()
|
2015-11-24 21:22:11 +00:00
|
|
|
|
2016-02-06 00:36:35 +00:00
|
|
|
// If the input has a SetParser function, then this means it can accept
|
|
|
|
// arbitrary types of input, so build the parser and set it.
|
|
|
|
switch t := input.(type) {
|
|
|
|
case parsers.ParserInput:
|
|
|
|
parser, err := buildParser(name, table)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
t.SetParser(parser)
|
|
|
|
}
|
|
|
|
|
2016-01-07 20:39:43 +00:00
|
|
|
pluginConfig, err := buildInput(name, table)
|
2015-11-24 21:22:11 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-12-01 14:15:28 +00:00
|
|
|
|
2015-12-18 15:36:30 +00:00
|
|
|
if err := config.UnmarshalTable(table, input); err != nil {
|
2015-12-01 14:15:28 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-01-27 21:21:36 +00:00
|
|
|
rp := &internal_models.RunningInput{
|
2015-11-24 21:22:11 +00:00
|
|
|
Name: name,
|
2016-01-07 20:39:43 +00:00
|
|
|
Input: input,
|
2015-11-24 21:22:11 +00:00
|
|
|
Config: pluginConfig,
|
|
|
|
}
|
2016-01-07 20:39:43 +00:00
|
|
|
c.Inputs = append(c.Inputs, rp)
|
2015-11-24 21:22:11 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-02-22 20:35:06 +00:00
|
|
|
// buildFilter builds a Filter
|
|
|
|
// (tagpass/tagdrop/namepass/namedrop/fieldpass/fielddrop) to
|
2016-01-27 21:21:36 +00:00
|
|
|
// be inserted into the internal_models.OutputConfig/internal_models.InputConfig to be used for prefix
|
2015-12-01 14:15:28 +00:00
|
|
|
// filtering on tags and measurements
|
2016-01-27 21:21:36 +00:00
|
|
|
func buildFilter(tbl *ast.Table) internal_models.Filter {
|
|
|
|
f := internal_models.Filter{}
|
2015-11-24 21:22:11 +00:00
|
|
|
|
2016-02-20 05:35:12 +00:00
|
|
|
if node, ok := tbl.Fields["namepass"]; ok {
|
2015-11-24 21:22:11 +00:00
|
|
|
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 {
|
2016-02-20 05:35:12 +00:00
|
|
|
f.NamePass = append(f.NamePass, str.Value)
|
2015-12-01 14:15:28 +00:00
|
|
|
f.IsActive = true
|
2015-11-24 21:22:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-20 05:35:12 +00:00
|
|
|
if node, ok := tbl.Fields["namedrop"]; ok {
|
2015-11-24 21:22:11 +00:00
|
|
|
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 {
|
2016-02-20 05:35:12 +00:00
|
|
|
f.NameDrop = append(f.NameDrop, str.Value)
|
2015-12-01 14:15:28 +00:00
|
|
|
f.IsActive = true
|
2015-11-24 21:22:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-20 05:35:12 +00:00
|
|
|
fields := []string{"pass", "fieldpass"}
|
|
|
|
for _, field := range fields {
|
|
|
|
if node, ok := tbl.Fields[field]; 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 {
|
|
|
|
f.FieldPass = append(f.FieldPass, str.Value)
|
|
|
|
f.IsActive = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fields = []string{"drop", "fielddrop"}
|
|
|
|
for _, field := range fields {
|
|
|
|
if node, ok := tbl.Fields[field]; 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 {
|
|
|
|
f.FieldDrop = append(f.FieldDrop, str.Value)
|
|
|
|
f.IsActive = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-24 21:22:11 +00:00
|
|
|
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 {
|
2016-01-27 21:21:36 +00:00
|
|
|
tagfilter := &internal_models.TagFilter{Name: name}
|
2015-11-24 21:22:11 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-12-01 14:15:28 +00:00
|
|
|
f.TagPass = append(f.TagPass, *tagfilter)
|
|
|
|
f.IsActive = true
|
2015-11-24 21:22:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2016-01-27 21:21:36 +00:00
|
|
|
tagfilter := &internal_models.TagFilter{Name: name}
|
2015-11-24 21:22:11 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-12-01 14:15:28 +00:00
|
|
|
f.TagDrop = append(f.TagDrop, *tagfilter)
|
|
|
|
f.IsActive = true
|
2015-11-24 21:22:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-20 05:35:12 +00:00
|
|
|
delete(tbl.Fields, "namedrop")
|
|
|
|
delete(tbl.Fields, "namepass")
|
|
|
|
delete(tbl.Fields, "fielddrop")
|
|
|
|
delete(tbl.Fields, "fieldpass")
|
2015-11-24 21:22:11 +00:00
|
|
|
delete(tbl.Fields, "drop")
|
|
|
|
delete(tbl.Fields, "pass")
|
|
|
|
delete(tbl.Fields, "tagdrop")
|
|
|
|
delete(tbl.Fields, "tagpass")
|
2015-12-01 14:15:28 +00:00
|
|
|
return f
|
|
|
|
}
|
|
|
|
|
2016-01-07 20:39:43 +00:00
|
|
|
// buildInput parses input specific items from the ast.Table,
|
2015-12-11 20:07:32 +00:00
|
|
|
// builds the filter and returns a
|
2016-01-27 21:21:36 +00:00
|
|
|
// internal_models.InputConfig to be inserted into internal_models.RunningInput
|
|
|
|
func buildInput(name string, tbl *ast.Table) (*internal_models.InputConfig, error) {
|
|
|
|
cp := &internal_models.InputConfig{Name: name}
|
2015-12-01 14:15:28 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-12-11 20:07:32 +00:00
|
|
|
|
|
|
|
if node, ok := tbl.Fields["name_prefix"]; ok {
|
|
|
|
if kv, ok := node.(*ast.KeyValue); ok {
|
|
|
|
if str, ok := kv.Value.(*ast.String); ok {
|
|
|
|
cp.MeasurementPrefix = str.Value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if node, ok := tbl.Fields["name_suffix"]; ok {
|
|
|
|
if kv, ok := node.(*ast.KeyValue); ok {
|
|
|
|
if str, ok := kv.Value.(*ast.String); ok {
|
|
|
|
cp.MeasurementSuffix = str.Value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if node, ok := tbl.Fields["name_override"]; ok {
|
|
|
|
if kv, ok := node.(*ast.KeyValue); ok {
|
|
|
|
if str, ok := kv.Value.(*ast.String); ok {
|
|
|
|
cp.NameOverride = str.Value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cp.Tags = make(map[string]string)
|
|
|
|
if node, ok := tbl.Fields["tags"]; ok {
|
|
|
|
if subtbl, ok := node.(*ast.Table); ok {
|
2015-12-18 15:36:30 +00:00
|
|
|
if err := config.UnmarshalTable(subtbl, cp.Tags); err != nil {
|
2016-01-07 20:39:43 +00:00
|
|
|
log.Printf("Could not parse tags for input %s\n", name)
|
2015-12-11 20:07:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
delete(tbl.Fields, "name_prefix")
|
|
|
|
delete(tbl.Fields, "name_suffix")
|
|
|
|
delete(tbl.Fields, "name_override")
|
2015-12-01 14:15:28 +00:00
|
|
|
delete(tbl.Fields, "interval")
|
2015-12-11 20:07:32 +00:00
|
|
|
delete(tbl.Fields, "tags")
|
2015-12-01 14:15:28 +00:00
|
|
|
cp.Filter = buildFilter(tbl)
|
|
|
|
return cp, nil
|
|
|
|
}
|
|
|
|
|
2016-02-06 00:36:35 +00:00
|
|
|
// buildParser grabs the necessary entries from the ast.Table for creating
|
|
|
|
// a parsers.Parser object, and creates it, which can then be added onto
|
|
|
|
// an Input object.
|
|
|
|
func buildParser(name string, tbl *ast.Table) (parsers.Parser, error) {
|
|
|
|
c := &parsers.Config{}
|
|
|
|
|
|
|
|
if node, ok := tbl.Fields["data_format"]; ok {
|
|
|
|
if kv, ok := node.(*ast.KeyValue); ok {
|
|
|
|
if str, ok := kv.Value.(*ast.String); ok {
|
|
|
|
c.DataFormat = str.Value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-10 18:50:05 +00:00
|
|
|
// Legacy support, exec plugin originally parsed JSON by default.
|
|
|
|
if name == "exec" && c.DataFormat == "" {
|
|
|
|
c.DataFormat = "json"
|
|
|
|
} else if c.DataFormat == "" {
|
2016-02-06 00:36:35 +00:00
|
|
|
c.DataFormat = "influx"
|
|
|
|
}
|
|
|
|
|
|
|
|
if node, ok := tbl.Fields["separator"]; ok {
|
|
|
|
if kv, ok := node.(*ast.KeyValue); ok {
|
|
|
|
if str, ok := kv.Value.(*ast.String); ok {
|
|
|
|
c.Separator = str.Value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if node, ok := tbl.Fields["templates"]; 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 {
|
|
|
|
c.Templates = append(c.Templates, str.Value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if node, ok := tbl.Fields["tag_keys"]; 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 {
|
|
|
|
c.TagKeys = append(c.TagKeys, str.Value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
c.MetricName = name
|
|
|
|
|
|
|
|
delete(tbl.Fields, "data_format")
|
|
|
|
delete(tbl.Fields, "separator")
|
|
|
|
delete(tbl.Fields, "templates")
|
|
|
|
delete(tbl.Fields, "tag_keys")
|
|
|
|
|
|
|
|
return parsers.NewParser(c)
|
|
|
|
}
|
|
|
|
|
2016-02-10 22:50:07 +00:00
|
|
|
// buildSerializer grabs the necessary entries from the ast.Table for creating
|
|
|
|
// a serializers.Serializer object, and creates it, which can then be added onto
|
|
|
|
// an Output object.
|
|
|
|
func buildSerializer(name string, tbl *ast.Table) (serializers.Serializer, error) {
|
|
|
|
c := &serializers.Config{}
|
|
|
|
|
|
|
|
if node, ok := tbl.Fields["data_format"]; ok {
|
|
|
|
if kv, ok := node.(*ast.KeyValue); ok {
|
|
|
|
if str, ok := kv.Value.(*ast.String); ok {
|
|
|
|
c.DataFormat = str.Value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if c.DataFormat == "" {
|
|
|
|
c.DataFormat = "influx"
|
|
|
|
}
|
|
|
|
|
|
|
|
if node, ok := tbl.Fields["prefix"]; ok {
|
|
|
|
if kv, ok := node.(*ast.KeyValue); ok {
|
|
|
|
if str, ok := kv.Value.(*ast.String); ok {
|
|
|
|
c.Prefix = str.Value
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
delete(tbl.Fields, "data_format")
|
|
|
|
delete(tbl.Fields, "prefix")
|
|
|
|
return serializers.NewSerializer(c)
|
|
|
|
}
|
|
|
|
|
2015-12-01 14:15:28 +00:00
|
|
|
// buildOutput parses output specific items from the ast.Table, builds the filter and returns an
|
2016-01-27 21:21:36 +00:00
|
|
|
// internal_models.OutputConfig to be inserted into internal_models.RunningInput
|
2015-12-01 14:15:28 +00:00
|
|
|
// Note: error exists in the return for future calls that might require error
|
2016-01-27 21:21:36 +00:00
|
|
|
func buildOutput(name string, tbl *ast.Table) (*internal_models.OutputConfig, error) {
|
|
|
|
oc := &internal_models.OutputConfig{
|
2015-12-01 14:15:28 +00:00
|
|
|
Name: name,
|
|
|
|
Filter: buildFilter(tbl),
|
|
|
|
}
|
2016-02-22 20:35:06 +00:00
|
|
|
// Outputs don't support FieldDrop/FieldPass, so set to NameDrop/NamePass
|
|
|
|
if len(oc.Filter.FieldDrop) > 0 {
|
|
|
|
oc.Filter.NameDrop = oc.Filter.FieldDrop
|
|
|
|
}
|
|
|
|
if len(oc.Filter.FieldPass) > 0 {
|
|
|
|
oc.Filter.NamePass = oc.Filter.FieldPass
|
|
|
|
}
|
2015-12-01 14:15:28 +00:00
|
|
|
return oc, nil
|
2015-11-24 21:22:11 +00:00
|
|
|
}
|