Allow users to specify outputs as lists
This will provide the ability to specify multiple outputs for a single type of output. In essence, allowing this: [outputs] [[outputs.influxdb]] urls = ["udp://localhost:8089"] database = "udp-telegraf" [[outputs.influxdb]] urls = ["http://myhost:8086"] database = "telegraf" [[outputs.kafka]] brokers = ["192.168.99.100:9092"] topic = "telegraf" closes #335
This commit is contained in:
parent
bf8e0f4cae
commit
b10b186cc8
22
agent.go
22
agent.go
|
@ -7,6 +7,7 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -147,17 +148,13 @@ func (a *Agent) Close() error {
|
||||||
func (a *Agent) LoadOutputs(filters []string, config *Config) ([]string, error) {
|
func (a *Agent) LoadOutputs(filters []string, config *Config) ([]string, error) {
|
||||||
var names []string
|
var names []string
|
||||||
|
|
||||||
for _, name := range config.OutputsDeclared() {
|
for name, output := range config.OutputsDeclared() {
|
||||||
creator, ok := outputs.Outputs[name]
|
// Trim the ID off the output name for filtering
|
||||||
if !ok {
|
filtername := strings.TrimRight(name, "-0123456789")
|
||||||
return nil, fmt.Errorf("Undefined but requested output: %s", name)
|
if sliceContains(filtername, filters) || len(filters) == 0 {
|
||||||
}
|
|
||||||
|
|
||||||
if sliceContains(name, filters) || len(filters) == 0 {
|
|
||||||
if a.Debug {
|
if a.Debug {
|
||||||
log.Println("Output Enabled: ", name)
|
log.Println("Output Enabled: ", name)
|
||||||
}
|
}
|
||||||
output := creator()
|
|
||||||
|
|
||||||
err := config.ApplyOutput(name, output)
|
err := config.ApplyOutput(name, output)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -178,15 +175,8 @@ func (a *Agent) LoadOutputs(filters []string, config *Config) ([]string, error)
|
||||||
func (a *Agent) LoadPlugins(filters []string, config *Config) ([]string, error) {
|
func (a *Agent) LoadPlugins(filters []string, config *Config) ([]string, error) {
|
||||||
var names []string
|
var names []string
|
||||||
|
|
||||||
for _, name := range config.PluginsDeclared() {
|
for name, plugin := range config.PluginsDeclared() {
|
||||||
creator, ok := plugins.Plugins[name]
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("Undefined but requested plugin: %s", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
if sliceContains(name, filters) || len(filters) == 0 {
|
if sliceContains(name, filters) || len(filters) == 0 {
|
||||||
plugin := creator()
|
|
||||||
|
|
||||||
config, err := config.ApplyPlugin(name, plugin)
|
config, err := config.ApplyPlugin(name, plugin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
80
config.go
80
config.go
|
@ -4,6 +4,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
|
@ -20,8 +21,8 @@ import (
|
||||||
// will be logging to, as well as all the plugins that the user has
|
// will be logging to, as well as all the plugins that the user has
|
||||||
// specified
|
// specified
|
||||||
type Config struct {
|
type Config struct {
|
||||||
// This lives outside the agent because mergeStruct doesn't need to handle maps normally.
|
// This lives outside the agent because mergeStruct doesn't need to handle
|
||||||
// We just copy the elements manually in ApplyAgent.
|
// maps normally. We just copy the elements manually in ApplyAgent.
|
||||||
Tags map[string]string
|
Tags map[string]string
|
||||||
|
|
||||||
agent *Agent
|
agent *Agent
|
||||||
|
@ -156,23 +157,13 @@ func (c *Config) ApplyPlugin(name string, v interface{}) (*ConfiguredPlugin, err
|
||||||
// Couldn't figure out how to get this to work with the declared function.
|
// Couldn't figure out how to get this to work with the declared function.
|
||||||
|
|
||||||
// PluginsDeclared returns the name of all plugins declared in the config.
|
// PluginsDeclared returns the name of all plugins declared in the config.
|
||||||
func (c *Config) PluginsDeclared() []string {
|
func (c *Config) PluginsDeclared() map[string]plugins.Plugin {
|
||||||
var names []string
|
return c.plugins
|
||||||
for name := range c.plugins {
|
|
||||||
names = append(names, name)
|
|
||||||
}
|
|
||||||
sort.Strings(names)
|
|
||||||
return names
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// OutputsDeclared returns the name of all outputs declared in the config.
|
// OutputsDeclared returns the name of all outputs declared in the config.
|
||||||
func (c *Config) OutputsDeclared() []string {
|
func (c *Config) OutputsDeclared() map[string]outputs.Output {
|
||||||
var names []string
|
return c.outputs
|
||||||
for name := range c.outputs {
|
|
||||||
names = append(names, name)
|
|
||||||
}
|
|
||||||
sort.Strings(names)
|
|
||||||
return names
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ListTags returns a string of tags specified in the config,
|
// ListTags returns a string of tags specified in the config,
|
||||||
|
@ -283,7 +274,7 @@ func PrintSampleConfig(pluginFilters []string, outputFilters []string) {
|
||||||
creator := outputs.Outputs[oname]
|
creator := outputs.Outputs[oname]
|
||||||
output := creator()
|
output := creator()
|
||||||
|
|
||||||
fmt.Printf("\n# %s\n[outputs.%s]", output.Description(), oname)
|
fmt.Printf("\n# %s\n[[outputs.%s]]", output.Description(), oname)
|
||||||
|
|
||||||
config := output.SampleConfig()
|
config := output.SampleConfig()
|
||||||
if config == "" {
|
if config == "" {
|
||||||
|
@ -394,20 +385,24 @@ func findField(fieldName string, value reflect.Value) reflect.Value {
|
||||||
return reflect.Value{}
|
return reflect.Value{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A very limited merge. Merges the fields named in the fields parameter, replacing most values, but appending to arrays.
|
// A very limited merge. Merges the fields named in the fields parameter,
|
||||||
|
// replacing most values, but appending to arrays.
|
||||||
func mergeStruct(base, overlay interface{}, fields []string) error {
|
func mergeStruct(base, overlay interface{}, fields []string) error {
|
||||||
baseValue := reflect.ValueOf(base).Elem()
|
baseValue := reflect.ValueOf(base).Elem()
|
||||||
overlayValue := reflect.ValueOf(overlay).Elem()
|
overlayValue := reflect.ValueOf(overlay).Elem()
|
||||||
if baseValue.Kind() != reflect.Struct {
|
if baseValue.Kind() != reflect.Struct {
|
||||||
return fmt.Errorf("Tried to merge something that wasn't a struct: type %v was %v", baseValue.Type(), baseValue.Kind())
|
return fmt.Errorf("Tried to merge something that wasn't a struct: type %v was %v",
|
||||||
|
baseValue.Type(), baseValue.Kind())
|
||||||
}
|
}
|
||||||
if baseValue.Type() != overlayValue.Type() {
|
if baseValue.Type() != overlayValue.Type() {
|
||||||
return fmt.Errorf("Tried to merge two different types: %v and %v", baseValue.Type(), overlayValue.Type())
|
return fmt.Errorf("Tried to merge two different types: %v and %v",
|
||||||
|
baseValue.Type(), overlayValue.Type())
|
||||||
}
|
}
|
||||||
for _, field := range fields {
|
for _, field := range fields {
|
||||||
overlayFieldValue := findField(field, overlayValue)
|
overlayFieldValue := findField(field, overlayValue)
|
||||||
if !overlayFieldValue.IsValid() {
|
if !overlayFieldValue.IsValid() {
|
||||||
return fmt.Errorf("could not find field in %v matching %v", overlayValue.Type(), field)
|
return fmt.Errorf("could not find field in %v matching %v",
|
||||||
|
overlayValue.Type(), field)
|
||||||
}
|
}
|
||||||
baseFieldValue := findField(field, baseValue)
|
baseFieldValue := findField(field, baseValue)
|
||||||
if overlayFieldValue.Kind() == reflect.Slice {
|
if overlayFieldValue.Kind() == reflect.Slice {
|
||||||
|
@ -519,34 +514,49 @@ func LoadConfig(path string) (*Config, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, val := range tbl.Fields {
|
for name, val := range tbl.Fields {
|
||||||
subtbl, ok := val.(*ast.Table)
|
subTable, ok := val.(*ast.Table)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("invalid configuration")
|
return nil, errors.New("invalid configuration")
|
||||||
}
|
}
|
||||||
|
|
||||||
switch name {
|
switch name {
|
||||||
case "agent":
|
case "agent":
|
||||||
err := c.parseAgent(subtbl)
|
err := c.parseAgent(subTable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("Could not parse [agent] config\n")
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
case "tags":
|
case "tags":
|
||||||
if err = toml.UnmarshalTable(subtbl, c.Tags); err != nil {
|
if err = toml.UnmarshalTable(subTable, c.Tags); err != nil {
|
||||||
|
log.Printf("Could not parse [tags] config\n")
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
case "outputs":
|
case "outputs":
|
||||||
for outputName, outputVal := range subtbl.Fields {
|
for outputName, outputVal := range subTable.Fields {
|
||||||
outputSubtbl, ok := outputVal.(*ast.Table)
|
switch outputSubTable := outputVal.(type) {
|
||||||
if !ok {
|
case *ast.Table:
|
||||||
return nil, err
|
err = c.parseOutput(outputName, outputSubTable, 0)
|
||||||
}
|
if err != nil {
|
||||||
err = c.parseOutput(outputName, outputSubtbl)
|
log.Printf("Could not parse config for output: %s\n",
|
||||||
if err != nil {
|
outputName)
|
||||||
return nil, err
|
return nil, err
|
||||||
|
}
|
||||||
|
case []*ast.Table:
|
||||||
|
for id, t := range outputSubTable {
|
||||||
|
err = c.parseOutput(outputName, t, id)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Could not parse config for output: %s\n",
|
||||||
|
outputName)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("Unsupported config format: %s",
|
||||||
|
outputName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
err = c.parsePlugin(name, subtbl)
|
err = c.parsePlugin(name, subTable)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -579,7 +589,7 @@ func (c *Config) parseAgent(agentAst *ast.Table) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse an output config out of the given *ast.Table.
|
// Parse an output config out of the given *ast.Table.
|
||||||
func (c *Config) parseOutput(name string, outputAst *ast.Table) error {
|
func (c *Config) parseOutput(name string, outputAst *ast.Table, id int) error {
|
||||||
c.outputFieldsSet[name] = extractFieldNames(outputAst)
|
c.outputFieldsSet[name] = extractFieldNames(outputAst)
|
||||||
creator, ok := outputs.Outputs[name]
|
creator, ok := outputs.Outputs[name]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -590,7 +600,7 @@ func (c *Config) parseOutput(name string, outputAst *ast.Table) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.outputs[name] = output
|
c.outputs[fmt.Sprintf("%s-%d", name, id)] = output
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue