diff --git a/cmd/telegraf/telegraf.go b/cmd/telegraf/telegraf.go index 8990769954a25..b36d0d28c3ff8 100644 --- a/cmd/telegraf/telegraf.go +++ b/cmd/telegraf/telegraf.go @@ -23,6 +23,7 @@ import ( "github.com/influxdata/telegraf" "github.com/influxdata/telegraf/agent" "github.com/influxdata/telegraf/config" + "github.com/influxdata/telegraf/config/printer" "github.com/influxdata/telegraf/internal" "github.com/influxdata/telegraf/internal/goplugin" "github.com/influxdata/telegraf/logger" @@ -476,7 +477,7 @@ func main() { processorFilters = subProcessorFilters } - config.PrintSampleConfig( + printer.PrintSampleConfig( sectionFilters, inputFilters, outputFilters, @@ -536,7 +537,7 @@ func main() { fmt.Println(formatFullVersion()) return case *fSampleConfig: - config.PrintSampleConfig( + printer.PrintSampleConfig( sectionFilters, inputFilters, outputFilters, @@ -545,8 +546,8 @@ func main() { ) return case *fUsage != "": - err := config.PrintInputConfig(*fUsage) - err2 := config.PrintOutputConfig(*fUsage) + err := printer.PrintInputConfig(*fUsage) + err2 := printer.PrintOutputConfig(*fUsage) if err != nil && err2 != nil { log.Fatalf("E! %s and %s", err, err2) } diff --git a/config/config.go b/config/config.go index 61b407e631d0a..c04aa0b65ab3b 100644 --- a/config/config.go +++ b/config/config.go @@ -39,17 +39,6 @@ import ( ) var ( - // Default sections - sectionDefaults = []string{"global_tags", "agent", "outputs", - "processors", "aggregators", "inputs"} - - // Default input plugins - inputDefaults = []string{"cpu", "mem", "swap", "system", "kernel", - "processes", "disk", "diskio"} - - // Default output plugins - outputDefaults = []string{"influxdb"} - // envVarRe is a regex to find environment variables in the config file envVarRe = regexp.MustCompile(`\$\{(\w+)\}|\$(\w+)`) @@ -315,309 +304,6 @@ func (c *Config) ListTags() string { return strings.Join(tags, " ") } -var header = `# Telegraf Configuration -# -# Telegraf is entirely plugin driven. All metrics are gathered from the -# declared inputs, and sent to the declared outputs. -# -# Plugins must be declared in here to be active. -# To deactivate a plugin, comment out the name and any variables. -# -# Use 'telegraf -config telegraf.conf -test' to see what metrics a config -# file would generate. -# -# Environment variables can be used anywhere in this config file, simply surround -# them with ${}. For strings the variable must be within quotes (ie, "${STR_VAR}"), -# for numbers and booleans they should be plain (ie, ${INT_VAR}, ${BOOL_VAR}) - -` -var globalTagsConfig = ` -# Global tags can be specified here in key="value" format. -[global_tags] - # dc = "us-east-1" # will tag all metrics with dc=us-east-1 - # rack = "1a" - ## Environment variables can be used as tags, and throughout the config file - # user = "$USER" - -` - -// DO NOT REMOVE THE NEXT TWO LINES! This is required to embed the agentConfig data. -//go:embed agent.conf -var agentConfig string - -var outputHeader = ` -############################################################################### -# OUTPUT PLUGINS # -############################################################################### - -` - -var processorHeader = ` -############################################################################### -# PROCESSOR PLUGINS # -############################################################################### - -` - -var aggregatorHeader = ` -############################################################################### -# AGGREGATOR PLUGINS # -############################################################################### - -` - -var inputHeader = ` -############################################################################### -# INPUT PLUGINS # -############################################################################### - -` - -var serviceInputHeader = ` -############################################################################### -# SERVICE INPUT PLUGINS # -############################################################################### - -` - -// PrintSampleConfig prints the sample config -func PrintSampleConfig( - sectionFilters []string, - inputFilters []string, - outputFilters []string, - aggregatorFilters []string, - processorFilters []string, -) { - // print headers - fmt.Print(header) - - if len(sectionFilters) == 0 { - sectionFilters = sectionDefaults - } - printFilteredGlobalSections(sectionFilters) - - // print output plugins - if sliceContains("outputs", sectionFilters) { - if len(outputFilters) != 0 { - if len(outputFilters) >= 3 && outputFilters[1] != "none" { - fmt.Print(outputHeader) - } - printFilteredOutputs(outputFilters, false) - } else { - fmt.Print(outputHeader) - printFilteredOutputs(outputDefaults, false) - // Print non-default outputs, commented - var pnames []string - for pname := range outputs.Outputs { - if !sliceContains(pname, outputDefaults) { - pnames = append(pnames, pname) - } - } - sort.Strings(pnames) - printFilteredOutputs(pnames, true) - } - } - - // print processor plugins - if sliceContains("processors", sectionFilters) { - if len(processorFilters) != 0 { - if len(processorFilters) >= 3 && processorFilters[1] != "none" { - fmt.Print(processorHeader) - } - printFilteredProcessors(processorFilters, false) - } else { - fmt.Print(processorHeader) - pnames := []string{} - for pname := range processors.Processors { - pnames = append(pnames, pname) - } - sort.Strings(pnames) - printFilteredProcessors(pnames, true) - } - } - - // print aggregator plugins - if sliceContains("aggregators", sectionFilters) { - if len(aggregatorFilters) != 0 { - if len(aggregatorFilters) >= 3 && aggregatorFilters[1] != "none" { - fmt.Print(aggregatorHeader) - } - printFilteredAggregators(aggregatorFilters, false) - } else { - fmt.Print(aggregatorHeader) - pnames := []string{} - for pname := range aggregators.Aggregators { - pnames = append(pnames, pname) - } - sort.Strings(pnames) - printFilteredAggregators(pnames, true) - } - } - - // print input plugins - if sliceContains("inputs", sectionFilters) { - if len(inputFilters) != 0 { - if len(inputFilters) >= 3 && inputFilters[1] != "none" { - fmt.Print(inputHeader) - } - printFilteredInputs(inputFilters, false) - } else { - fmt.Print(inputHeader) - printFilteredInputs(inputDefaults, false) - // Print non-default inputs, commented - var pnames []string - for pname := range inputs.Inputs { - if !sliceContains(pname, inputDefaults) { - pnames = append(pnames, pname) - } - } - sort.Strings(pnames) - printFilteredInputs(pnames, true) - } - } -} - -func printFilteredProcessors(processorFilters []string, commented bool) { - // Filter processors - var pnames []string - for pname := range processors.Processors { - if sliceContains(pname, processorFilters) { - pnames = append(pnames, pname) - } - } - sort.Strings(pnames) - - // Print Outputs - for _, pname := range pnames { - creator := processors.Processors[pname] - output := creator() - printConfig(pname, output, "processors", commented, processors.Deprecations[pname]) - } -} - -func printFilteredAggregators(aggregatorFilters []string, commented bool) { - // Filter outputs - var anames []string - for aname := range aggregators.Aggregators { - if sliceContains(aname, aggregatorFilters) { - anames = append(anames, aname) - } - } - sort.Strings(anames) - - // Print Outputs - for _, aname := range anames { - creator := aggregators.Aggregators[aname] - output := creator() - printConfig(aname, output, "aggregators", commented, aggregators.Deprecations[aname]) - } -} - -func printFilteredInputs(inputFilters []string, commented bool) { - // Filter inputs - var pnames []string - for pname := range inputs.Inputs { - if sliceContains(pname, inputFilters) { - pnames = append(pnames, pname) - } - } - sort.Strings(pnames) - - // cache service inputs to print them at the end - servInputs := make(map[string]telegraf.ServiceInput) - // for alphabetical looping: - servInputNames := []string{} - - // Print Inputs - for _, pname := range pnames { - // Skip inputs that are registered twice for backward compatibility - switch pname { - case "cisco_telemetry_gnmi", "io", "KNXListener": - continue - } - creator := inputs.Inputs[pname] - input := creator() - - if p, ok := input.(telegraf.ServiceInput); ok { - servInputs[pname] = p - servInputNames = append(servInputNames, pname) - continue - } - - printConfig(pname, input, "inputs", commented, inputs.Deprecations[pname]) - } - - // Print Service Inputs - if len(servInputs) == 0 { - return - } - sort.Strings(servInputNames) - - fmt.Print(serviceInputHeader) - for _, name := range servInputNames { - printConfig(name, servInputs[name], "inputs", commented, inputs.Deprecations[name]) - } -} - -func printFilteredOutputs(outputFilters []string, commented bool) { - // Filter outputs - var onames []string - for oname := range outputs.Outputs { - if sliceContains(oname, outputFilters) { - onames = append(onames, oname) - } - } - sort.Strings(onames) - - // Print Outputs - for _, oname := range onames { - creator := outputs.Outputs[oname] - output := creator() - printConfig(oname, output, "outputs", commented, outputs.Deprecations[oname]) - } -} - -func printFilteredGlobalSections(sectionFilters []string) { - if sliceContains("global_tags", sectionFilters) { - fmt.Print(globalTagsConfig) - } - - if sliceContains("agent", sectionFilters) { - fmt.Print(agentConfig) - } -} - -func printConfig(name string, p telegraf.PluginDescriber, op string, commented bool, di telegraf.DeprecationInfo) { - comment := "" - if commented { - comment = "# " - } - - if di.Since != "" { - removalNote := "" - if di.RemovalIn != "" { - removalNote = " and will be removed in " + di.RemovalIn - } - fmt.Printf("\n%s ## DEPRECATED: The '%s' plugin is deprecated in version %s%s, %s.", comment, name, di.Since, removalNote, di.Notice) - } - - config := p.SampleConfig() - if config == "" { - fmt.Printf("\n#[[%s.%s]]", op, name) - fmt.Printf("\n%s # no configuration\n\n", comment) - } else { - lines := strings.Split(config, "\n") - fmt.Print("\n") - for i, line := range lines { - if i == len(lines)-1 { - fmt.Print("\n") - continue - } - fmt.Print(strings.TrimRight(comment+line, " ") + "\n") - } - } -} - func sliceContains(name string, list []string) bool { for _, b := range list { if b == name { @@ -627,28 +313,6 @@ func sliceContains(name string, list []string) bool { return false } -// PrintInputConfig prints the config usage of a single input. -func PrintInputConfig(name string) error { - creator, ok := inputs.Inputs[name] - if !ok { - return fmt.Errorf("input %s not found", name) - } - - printConfig(name, creator(), "inputs", false, inputs.Deprecations[name]) - return nil -} - -// PrintOutputConfig prints the config usage of a single output. -func PrintOutputConfig(name string) error { - creator, ok := outputs.Outputs[name] - if !ok { - return fmt.Errorf("output %s not found", name) - } - - printConfig(name, creator(), "outputs", false, outputs.Deprecations[name]) - return nil -} - // LoadDirectory loads all toml config files found in the specified path, recursively. func (c *Config) LoadDirectory(path string) error { walkfn := func(thispath string, info os.FileInfo, _ error) error { diff --git a/config/agent.conf b/config/printer/agent.conf similarity index 100% rename from config/agent.conf rename to config/printer/agent.conf diff --git a/config/printer/printer.go b/config/printer/printer.go new file mode 100644 index 0000000000000..f1521d1351e3a --- /dev/null +++ b/config/printer/printer.go @@ -0,0 +1,381 @@ +package printer + +import ( + _ "embed" + "fmt" + "sort" + "strings" + + "github.com/influxdata/telegraf" + "github.com/influxdata/telegraf/plugins/aggregators" + "github.com/influxdata/telegraf/plugins/inputs" + "github.com/influxdata/telegraf/plugins/outputs" + "github.com/influxdata/telegraf/plugins/processors" +) + +var ( + // Default sections + sectionDefaults = []string{"global_tags", "agent", "outputs", + "processors", "aggregators", "inputs"} + + // Default input plugins + inputDefaults = []string{"cpu", "mem", "swap", "system", "kernel", + "processes", "disk", "diskio"} + + // Default output plugins + outputDefaults = []string{"influxdb"} +) + +var header = `# Telegraf Configuration +# +# Telegraf is entirely plugin driven. All metrics are gathered from the +# declared inputs, and sent to the declared outputs. +# +# Plugins must be declared in here to be active. +# To deactivate a plugin, comment out the name and any variables. +# +# Use 'telegraf -config telegraf.conf -test' to see what metrics a config +# file would generate. +# +# Environment variables can be used anywhere in this config file, simply surround +# them with ${}. For strings the variable must be within quotes (ie, "${STR_VAR}"), +# for numbers and booleans they should be plain (ie, ${INT_VAR}, ${BOOL_VAR}) + +` +var globalTagsConfig = ` +# Global tags can be specified here in key="value" format. +[global_tags] + # dc = "us-east-1" # will tag all metrics with dc=us-east-1 + # rack = "1a" + ## Environment variables can be used as tags, and throughout the config file + # user = "$USER" + +` + +// DO NOT REMOVE THE NEXT TWO LINES! This is required to embed the agentConfig data. +//go:embed agent.conf +var agentConfig string + +var outputHeader = ` +############################################################################### +# OUTPUT PLUGINS # +############################################################################### + +` + +var processorHeader = ` +############################################################################### +# PROCESSOR PLUGINS # +############################################################################### + +` + +var aggregatorHeader = ` +############################################################################### +# AGGREGATOR PLUGINS # +############################################################################### + +` + +var inputHeader = ` +############################################################################### +# INPUT PLUGINS # +############################################################################### + +` + +var serviceInputHeader = ` +############################################################################### +# SERVICE INPUT PLUGINS # +############################################################################### + +` + +func sliceContains(name string, list []string) bool { + for _, b := range list { + if b == name { + return true + } + } + return false +} + +// PrintSampleConfig prints the sample config +func PrintSampleConfig( + sectionFilters []string, + inputFilters []string, + outputFilters []string, + aggregatorFilters []string, + processorFilters []string, +) { + // print headers + fmt.Print(header) + + if len(sectionFilters) == 0 { + sectionFilters = sectionDefaults + } + printFilteredGlobalSections(sectionFilters) + + // print output plugins + if sliceContains("outputs", sectionFilters) { + if len(outputFilters) != 0 { + if len(outputFilters) >= 3 && outputFilters[1] != "none" { + fmt.Print(outputHeader) + } + printFilteredOutputs(outputFilters, false) + } else { + fmt.Print(outputHeader) + printFilteredOutputs(outputDefaults, false) + // Print non-default outputs, commented + var pnames []string + for pname := range outputs.Outputs { + if !sliceContains(pname, outputDefaults) { + pnames = append(pnames, pname) + } + } + sort.Strings(pnames) + printFilteredOutputs(pnames, true) + } + } + + // print processor plugins + if sliceContains("processors", sectionFilters) { + if len(processorFilters) != 0 { + if len(processorFilters) >= 3 && processorFilters[1] != "none" { + fmt.Print(processorHeader) + } + printFilteredProcessors(processorFilters, false) + } else { + fmt.Print(processorHeader) + pnames := []string{} + for pname := range processors.Processors { + pnames = append(pnames, pname) + } + sort.Strings(pnames) + printFilteredProcessors(pnames, true) + } + } + + // print aggregator plugins + if sliceContains("aggregators", sectionFilters) { + if len(aggregatorFilters) != 0 { + if len(aggregatorFilters) >= 3 && aggregatorFilters[1] != "none" { + fmt.Print(aggregatorHeader) + } + printFilteredAggregators(aggregatorFilters, false) + } else { + fmt.Print(aggregatorHeader) + pnames := []string{} + for pname := range aggregators.Aggregators { + pnames = append(pnames, pname) + } + sort.Strings(pnames) + printFilteredAggregators(pnames, true) + } + } + + // print input plugins + if sliceContains("inputs", sectionFilters) { + if len(inputFilters) != 0 { + if len(inputFilters) >= 3 && inputFilters[1] != "none" { + fmt.Print(inputHeader) + } + printFilteredInputs(inputFilters, false) + } else { + fmt.Print(inputHeader) + printFilteredInputs(inputDefaults, false) + // Print non-default inputs, commented + var pnames []string + for pname := range inputs.Inputs { + if !sliceContains(pname, inputDefaults) { + pnames = append(pnames, pname) + } + } + sort.Strings(pnames) + printFilteredInputs(pnames, true) + } + } +} + +// PluginNameCounts returns a list of sorted plugin names and their count +func PluginNameCounts(plugins []string) []string { + names := make(map[string]int) + for _, plugin := range plugins { + names[plugin]++ + } + + var namecount []string + for name, count := range names { + if count == 1 { + namecount = append(namecount, name) + } else { + namecount = append(namecount, fmt.Sprintf("%s (%dx)", name, count)) + } + } + + sort.Strings(namecount) + return namecount +} + +func printFilteredProcessors(processorFilters []string, commented bool) { + // Filter processors + var pnames []string + for pname := range processors.Processors { + if sliceContains(pname, processorFilters) { + pnames = append(pnames, pname) + } + } + sort.Strings(pnames) + + // Print Outputs + for _, pname := range pnames { + creator := processors.Processors[pname] + output := creator() + printConfig(pname, output, "processors", commented, processors.Deprecations[pname]) + } +} + +func printFilteredAggregators(aggregatorFilters []string, commented bool) { + // Filter outputs + var anames []string + for aname := range aggregators.Aggregators { + if sliceContains(aname, aggregatorFilters) { + anames = append(anames, aname) + } + } + sort.Strings(anames) + + // Print Outputs + for _, aname := range anames { + creator := aggregators.Aggregators[aname] + output := creator() + printConfig(aname, output, "aggregators", commented, aggregators.Deprecations[aname]) + } +} + +func printFilteredInputs(inputFilters []string, commented bool) { + // Filter inputs + var pnames []string + for pname := range inputs.Inputs { + if sliceContains(pname, inputFilters) { + pnames = append(pnames, pname) + } + } + sort.Strings(pnames) + + // cache service inputs to print them at the end + servInputs := make(map[string]telegraf.ServiceInput) + // for alphabetical looping: + servInputNames := []string{} + + // Print Inputs + for _, pname := range pnames { + // Skip inputs that are registered twice for backward compatibility + switch pname { + case "cisco_telemetry_gnmi", "io", "KNXListener": + continue + } + creator := inputs.Inputs[pname] + input := creator() + + if p, ok := input.(telegraf.ServiceInput); ok { + servInputs[pname] = p + servInputNames = append(servInputNames, pname) + continue + } + + printConfig(pname, input, "inputs", commented, inputs.Deprecations[pname]) + } + + // Print Service Inputs + if len(servInputs) == 0 { + return + } + sort.Strings(servInputNames) + + fmt.Print(serviceInputHeader) + for _, name := range servInputNames { + printConfig(name, servInputs[name], "inputs", commented, inputs.Deprecations[name]) + } +} + +func printFilteredOutputs(outputFilters []string, commented bool) { + // Filter outputs + var onames []string + for oname := range outputs.Outputs { + if sliceContains(oname, outputFilters) { + onames = append(onames, oname) + } + } + sort.Strings(onames) + + // Print Outputs + for _, oname := range onames { + creator := outputs.Outputs[oname] + output := creator() + printConfig(oname, output, "outputs", commented, outputs.Deprecations[oname]) + } +} + +func printFilteredGlobalSections(sectionFilters []string) { + if sliceContains("global_tags", sectionFilters) { + fmt.Print(globalTagsConfig) + } + + if sliceContains("agent", sectionFilters) { + fmt.Print(agentConfig) + } +} + +func printConfig(name string, p telegraf.PluginDescriber, op string, commented bool, di telegraf.DeprecationInfo) { + comment := "" + if commented { + comment = "# " + } + + if di.Since != "" { + removalNote := "" + if di.RemovalIn != "" { + removalNote = " and will be removed in " + di.RemovalIn + } + fmt.Printf("\n%s ## DEPRECATED: The '%s' plugin is deprecated in version %s%s, %s.", comment, name, di.Since, removalNote, di.Notice) + } + + config := p.SampleConfig() + if config == "" { + fmt.Printf("\n#[[%s.%s]]", op, name) + fmt.Printf("\n%s # no configuration\n\n", comment) + } else { + lines := strings.Split(config, "\n") + fmt.Print("\n") + for i, line := range lines { + if i == len(lines)-1 { + fmt.Print("\n") + continue + } + fmt.Print(strings.TrimRight(comment+line, " ") + "\n") + } + } +} + +// PrintInputConfig prints the config usage of a single input. +func PrintInputConfig(name string) error { + creator, ok := inputs.Inputs[name] + if !ok { + return fmt.Errorf("input %s not found", name) + } + + printConfig(name, creator(), "inputs", false, inputs.Deprecations[name]) + return nil +} + +// PrintOutputConfig prints the config usage of a single output. +func PrintOutputConfig(name string) error { + creator, ok := outputs.Outputs[name] + if !ok { + return fmt.Errorf("output %s not found", name) + } + + printConfig(name, creator(), "outputs", false, outputs.Deprecations[name]) + return nil +}