diff --git a/.golangci.yml b/.golangci.yml index e0639cbc4001f..02b4533fa25eb 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -4,7 +4,6 @@ linters: # - telegraflinter - bodyclose - dogsled - - errcheck - goprintffuncname - gosimple - govet @@ -71,7 +70,7 @@ linters-settings: - name: unconditional-recursion - name: unexported-naming - name: unhandled-error - arguments: ["fmt.Printf", "fmt.Println", "fmt.Print"] + arguments: ["outputBuffer.Write", "fmt.Printf", "fmt.Println", "fmt.Print"] - name: unnecessary-stmt - name: unreachable-code # - name: unused-parameter diff --git a/Makefile b/Makefile index 5053353be4bdf..37b7ad7f11a1d 100644 --- a/Makefile +++ b/Makefile @@ -46,11 +46,11 @@ GOOS ?= $(shell go env GOOS) GOARCH ?= $(shell go env GOARCH) HOSTGO := env -u GOOS -u GOARCH -u GOARM -- go INTERNAL_PKG=github.com/influxdata/telegraf/internal -LDFLAGS := $(LDFLAGS) -X $(INTERNAL_PKG).commit=$(commit) -X $(INTERNAL_PKG).branch=$(branch) +LDFLAGS := $(LDFLAGS) -X $(INTERNAL_PKG).Commit=$(commit) -X $(INTERNAL_PKG).Branch=$(branch) ifneq ($(tag),) - LDFLAGS += -X $(INTERNAL_PKG).version=$(version) + LDFLAGS += -X $(INTERNAL_PKG).Version=$(version) else - LDFLAGS += -X $(INTERNAL_PKG).version=$(version)-$(commit) + LDFLAGS += -X $(INTERNAL_PKG).Version=$(version)-$(commit) endif # Go built-in race detector works only for 64 bits architectures. diff --git a/config/printer/agent.conf b/cmd/telegraf/agent.conf similarity index 100% rename from config/printer/agent.conf rename to cmd/telegraf/agent.conf diff --git a/cmd/telegraf/main.go b/cmd/telegraf/main.go new file mode 100644 index 0000000000000..28a631c74ef49 --- /dev/null +++ b/cmd/telegraf/main.go @@ -0,0 +1,355 @@ +package main + +import ( + "fmt" + "io" + "log" //nolint:revive + "os" + "sort" + "strings" + + "github.com/influxdata/telegraf/config" + "github.com/influxdata/telegraf/internal" + "github.com/influxdata/telegraf/internal/goplugin" + "github.com/influxdata/telegraf/logger" + _ "github.com/influxdata/telegraf/plugins/aggregators/all" + "github.com/influxdata/telegraf/plugins/inputs" + _ "github.com/influxdata/telegraf/plugins/inputs/all" + "github.com/influxdata/telegraf/plugins/outputs" + _ "github.com/influxdata/telegraf/plugins/outputs/all" + _ "github.com/influxdata/telegraf/plugins/parsers/all" + _ "github.com/influxdata/telegraf/plugins/processors/all" + "github.com/urfave/cli/v2" +) + +type TelegrafConfig interface { + CollectDeprecationInfos([]string, []string, []string, []string) map[string][]config.PluginDeprecationInfo + PrintDeprecationList([]config.PluginDeprecationInfo) +} + +type Filters struct { + section []string + input []string + output []string + aggregator []string + processor []string +} + +func processFilterFlags(section, input, output, aggregator, processor string) Filters { + sectionFilters := deleteEmpty(strings.Split(section, ":")) + inputFilters := deleteEmpty(strings.Split(input, ":")) + outputFilters := deleteEmpty(strings.Split(output, ":")) + aggregatorFilters := deleteEmpty(strings.Split(aggregator, ":")) + processorFilters := deleteEmpty(strings.Split(processor, ":")) + return Filters{sectionFilters, inputFilters, outputFilters, aggregatorFilters, processorFilters} +} + +func deleteEmpty(s []string) []string { + var r []string + for _, str := range s { + if str != "" { + r = append(r, str) + } + } + return r +} + +// runApp defines all the subcommands and flags for Telegraf +// this abstraction is used for testing, so outputBuffer and args can be changed +func runApp(args []string, outputBuffer io.Writer, pprof Server, c TelegrafConfig, m App) error { + pluginFilterFlags := []cli.Flag{ + &cli.StringFlag{ + Name: "section-filter", + Usage: "filter the sections to print, separator is ':'. Valid values are 'agent', 'global_tags', 'outputs', 'processors', 'aggregators' and 'inputs'", + }, + &cli.StringFlag{ + Name: "input-filter", + Usage: "filter the inputs to enable, separator is ':'", + }, + &cli.StringFlag{ + Name: "output-filter", + Usage: "filter the outputs to enable, separator is ':'", + }, + &cli.StringFlag{ + Name: "aggregator-filter", + Usage: "filter the aggregators to enable, separator is ':'", + }, + &cli.StringFlag{ + Name: "processor-filter", + Usage: "filter the processors to enable, separator is ':'", + }, + } + + extraFlags := append(pluginFilterFlags, cliFlags()...) + + // This function is used when Telegraf is run with only flags + action := func(cCtx *cli.Context) error { + logger.SetupLogging(logger.LogConfig{}) + + // Deprecated: Use execd instead + // Load external plugins, if requested. + if cCtx.String("plugin-directory") != "" { + log.Printf("I! Loading external plugins from: %s", cCtx.String("plugin-directory")) + if err := goplugin.LoadExternalPlugins(cCtx.String("plugin-directory")); err != nil { + return fmt.Errorf("E! %w", err) + } + } + + // switch for flags which just do something and exit immediately + switch { + // print available input plugins + case cCtx.Bool("deprecation-list"): + filters := processFilterFlags( + cCtx.String("section-filter"), + cCtx.String("input-filter"), + cCtx.String("output-filter"), + cCtx.String("aggregator-filter"), + cCtx.String("processor-filter"), + ) + infos := c.CollectDeprecationInfos( + filters.input, filters.output, filters.aggregator, filters.processor, + ) + outputBuffer.Write([]byte("Deprecated Input Plugins:\n")) + c.PrintDeprecationList(infos["inputs"]) + outputBuffer.Write([]byte("Deprecated Output Plugins:\n")) + c.PrintDeprecationList(infos["outputs"]) + outputBuffer.Write([]byte("Deprecated Processor Plugins:\n")) + c.PrintDeprecationList(infos["processors"]) + outputBuffer.Write([]byte("Deprecated Aggregator Plugins:\n")) + c.PrintDeprecationList(infos["aggregators"]) + return nil + // print available output plugins + case cCtx.Bool("output-list"): + outputBuffer.Write([]byte("Available Output Plugins:\n")) + names := make([]string, 0, len(outputs.Outputs)) + for k := range outputs.Outputs { + names = append(names, k) + } + sort.Strings(names) + for _, k := range names { + outputBuffer.Write([]byte(fmt.Sprintf(" %s\n", k))) + } + return nil + // print available input plugins + case cCtx.Bool("input-list"): + outputBuffer.Write([]byte("Available Input Plugins:\n")) + names := make([]string, 0, len(inputs.Inputs)) + for k := range inputs.Inputs { + names = append(names, k) + } + sort.Strings(names) + for _, k := range names { + outputBuffer.Write([]byte(fmt.Sprintf(" %s\n", k))) + } + return nil + // print usage for a plugin, ie, 'telegraf --usage mysql' + case cCtx.String("usage") != "": + err := PrintInputConfig(cCtx.String("usage"), outputBuffer) + err2 := PrintOutputConfig(cCtx.String("usage"), outputBuffer) + if err != nil && err2 != nil { + return fmt.Errorf("E! %s and %s", err, err2) + } + return nil + // DEPRECATED + case cCtx.Bool("version"): + outputBuffer.Write([]byte(fmt.Sprintf("%s\n", internal.FormatFullVersion()))) + return nil + // DEPRECATED + case cCtx.Bool("sample-config"): + filters := processFilterFlags( + cCtx.String("section-filter"), + cCtx.String("input-filter"), + cCtx.String("output-filter"), + cCtx.String("aggregator-filter"), + cCtx.String("processor-filter"), + ) + + printSampleConfig( + outputBuffer, + filters.section, + filters.input, + filters.output, + filters.aggregator, + filters.processor, + ) + return nil + } + + if cCtx.String("pprof-addr") != "" { + pprof.Start(cCtx.String("pprof-addr")) + } + + filters := processFilterFlags( + cCtx.String("section-filter"), + cCtx.String("input-filter"), + cCtx.String("output-filter"), + cCtx.String("aggregator-filter"), + cCtx.String("processor-filter"), + ) + + g := GlobalFlags{ + config: cCtx.StringSlice("config"), + configDir: cCtx.StringSlice("config-directory"), + testWait: cCtx.Int("test-wait"), + watchConfig: cCtx.String("watch-config"), + pidFile: cCtx.String("pidfile"), + plugindDir: cCtx.String("plugin-directory"), + test: cCtx.Bool("test"), + debug: cCtx.Bool("debug"), + once: cCtx.Bool("once"), + quiet: cCtx.Bool("quiet"), + } + + w := WindowFlags{ + service: cCtx.String("service"), + serviceName: cCtx.String("service-name"), + serviceDisplayName: cCtx.String("service-display-name"), + serviceRestartDelay: cCtx.String("service-restart-delay"), + serviceAutoRestart: cCtx.Bool("service-auto-restart"), + console: cCtx.Bool("console"), + } + + m.Init(pprof.ErrChan(), filters, g, w) + return m.Run() + } + + app := &cli.App{ + Name: "Telegraf", + Usage: "The plugin-driven server agent for collecting & reporting metrics.", + Writer: outputBuffer, + Flags: append( + []cli.Flag{ + // String slice flags + &cli.StringSliceFlag{ + Name: "config", + Usage: "configuration file to load", + }, + &cli.StringSliceFlag{ + Name: "config-directory", + Usage: "directory containing additional *.conf files", + }, + // Int flags + &cli.IntFlag{ + Name: "test-wait", + Usage: "wait up to this many seconds for service inputs to complete in test mode", + }, + // + // String flags + &cli.StringFlag{ + Name: "usage", + Usage: "print usage for a plugin, ie, 'telegraf --usage mysql'", + }, + &cli.StringFlag{ + Name: "pprof-addr", + Usage: "pprof host/IP and port to listen on (e.g. 'localhost:6060')", + }, + &cli.StringFlag{ + Name: "watch-config", + Usage: "monitoring config changes [notify, poll]", + }, + &cli.StringFlag{ + Name: "pidfile", + Usage: "file to write our pid to", + }, + // + // Bool flags + &cli.BoolFlag{ + Name: "once", + Usage: "run one gather and exit", + }, + &cli.BoolFlag{ + Name: "debug", + Usage: "turn on debug logging", + }, + &cli.BoolFlag{ + Name: "quiet", + Usage: "run in quiet mode", + }, + &cli.BoolFlag{ + Name: "test", + Usage: "enable test mode: gather metrics, print them out, and exit. Note: Test mode only runs inputs, not processors, aggregators, or outputs", + }, + // TODO: Change "deprecation-list, input-list, output-list" flags to become a subcommand "list" that takes + // "input,output,aggregator,processor, deprecated" as parameters + &cli.BoolFlag{ + Name: "deprecation-list", + Usage: "print all deprecated plugins or plugin options", + }, + &cli.BoolFlag{ + Name: "input-list", + Usage: "print available input plugins", + }, + &cli.BoolFlag{ + Name: "output-list", + Usage: "print available output plugins", + }, + // + // !!! The following flags are DEPRECATED !!! + // Already covered with the subcommand `./telegraf version` + &cli.BoolFlag{ + Name: "version", + Usage: "DEPRECATED: display the version and exit", + }, + // Already covered with the subcommand `./telegraf config` + &cli.BoolFlag{ + Name: "sample-config", + Usage: "DEPRECATED: print out full sample configuration", + }, + // Using execd plugin to add external plugins is preffered (less size impact, easier for end user) + &cli.StringFlag{ + Name: "plugin-directory", + Usage: "DEPRECATED: path to directory containing external plugins", + }, + // !!! + }, extraFlags...), + Action: action, + Commands: []*cli.Command{ + { + Name: "config", + Usage: "print out full sample configuration to stdout", + Flags: pluginFilterFlags, + Action: func(cCtx *cli.Context) error { + // The sub_Filters are populated when the filter flags are set after the subcommand config + // e.g. telegraf config --section-filter inputs + filters := processFilterFlags( + cCtx.String("section-filter"), + cCtx.String("input-filter"), + cCtx.String("output-filter"), + cCtx.String("aggregator-filter"), + cCtx.String("processor-filter"), + ) + + printSampleConfig( + outputBuffer, + filters.section, + filters.input, + filters.output, + filters.aggregator, + filters.processor, + ) + return nil + }, + }, + { + Name: "version", + Usage: "print current version to stdout", + Action: func(cCtx *cli.Context) error { + outputBuffer.Write([]byte(fmt.Sprintf("%s\n", internal.FormatFullVersion()))) + return nil + }, + }, + }, + } + + return app.Run(args) +} + +func main() { + agent := Telegraf{} + pprof := NewPprofServer() + c := config.NewConfig() + err := runApp(os.Args, os.Stdout, pprof, c, &agent) + if err != nil { + log.Fatalf("E! %s", err) + } +} diff --git a/cmd/telegraf/main_test.go b/cmd/telegraf/main_test.go new file mode 100644 index 0000000000000..ec8df27b2e262 --- /dev/null +++ b/cmd/telegraf/main_test.go @@ -0,0 +1,463 @@ +package main + +import ( + "bytes" + "fmt" + "io" + "os" + "strconv" + "strings" + "testing" + + "github.com/influxdata/telegraf" + "github.com/influxdata/telegraf/config" + "github.com/influxdata/telegraf/internal" + "github.com/influxdata/telegraf/plugins/inputs" + "github.com/influxdata/telegraf/plugins/outputs" + "github.com/stretchr/testify/require" +) + +type MockTelegraf struct { + GlobalFlags + WindowFlags +} + +func NewMockTelegraf() *MockTelegraf { + return &MockTelegraf{} +} + +func (m *MockTelegraf) Init(serverErr <-chan error, f Filters, g GlobalFlags, w WindowFlags) { + m.GlobalFlags = g + m.WindowFlags = w +} + +func (m *MockTelegraf) Run() error { + return nil +} + +type MockConfig struct { + Buffer io.Writer + ExpectedDeprecatedPlugins map[string][]config.PluginDeprecationInfo +} + +func NewMockConfig(buffer io.Writer) *MockConfig { + return &MockConfig{ + Buffer: buffer, + } +} + +func (m *MockConfig) CollectDeprecationInfos(inFilter, outFilter, aggFilter, procFilter []string) map[string][]config.PluginDeprecationInfo { + return m.ExpectedDeprecatedPlugins +} + +func (m *MockConfig) PrintDeprecationList(plugins []config.PluginDeprecationInfo) { + for _, p := range plugins { + _, _ = m.Buffer.Write([]byte(fmt.Sprintf("plugin name: %s\n", p.Name))) + } +} + +type MockServer struct { + Address string +} + +func NewMockServer() *MockServer { + return &MockServer{} +} + +func (m *MockServer) Start(address string) { + m.Address = "localhost:6060" +} + +func (m *MockServer) ErrChan() <-chan error { + return nil +} + +func TestUsageFlag(t *testing.T) { + tests := []struct { + PluginName string + ExpectedError string + ExpectedOutput string + }{ + { + PluginName: "example", + ExpectedError: "E! input example not found and output example not found", + }, + { + PluginName: "temp", + ExpectedOutput: ` +# Read metrics about temperature +[[inputs.temp]] + # no configuration + +`, + }, + } + + for _, test := range tests { + buf := new(bytes.Buffer) + args := os.Args[0:1] + args = append(args, "--usage", test.PluginName) + err := runApp(args, buf, NewMockServer(), NewMockConfig(buf), NewMockTelegraf()) + if test.ExpectedError != "" { + require.ErrorContains(t, err, test.ExpectedError) + continue + } + require.NoError(t, err) + // To run this test on windows and linux, remove windows carriage return + o := strings.Replace(buf.String(), "\r", "", -1) + require.Equal(t, test.ExpectedOutput, o) + } +} + +func TestInputListFlag(t *testing.T) { + buf := new(bytes.Buffer) + args := os.Args[0:1] + args = append(args, "--input-list") + temp := inputs.Inputs + inputs.Inputs = map[string]inputs.Creator{ + "test": func() telegraf.Input { return nil }, + } + err := runApp(args, buf, NewMockServer(), NewMockConfig(buf), NewMockTelegraf()) + require.NoError(t, err) + expectedOutput := `Available Input Plugins: + test +` + require.Equal(t, expectedOutput, buf.String()) + inputs.Inputs = temp +} + +func TestOutputListFlag(t *testing.T) { + buf := new(bytes.Buffer) + args := os.Args[0:1] + args = append(args, "--output-list") + temp := outputs.Outputs + outputs.Outputs = map[string]outputs.Creator{ + "test": func() telegraf.Output { return nil }, + } + err := runApp(args, buf, NewMockServer(), NewMockConfig(buf), NewMockTelegraf()) + require.NoError(t, err) + expectedOutput := `Available Output Plugins: + test +` + require.Equal(t, expectedOutput, buf.String()) + outputs.Outputs = temp +} + +func TestDeprecationListFlag(t *testing.T) { + buf := new(bytes.Buffer) + args := os.Args[0:1] + args = append(args, "--deprecation-list") + mS := NewMockServer() + mC := NewMockConfig(buf) + mC.ExpectedDeprecatedPlugins = make(map[string][]config.PluginDeprecationInfo) + mC.ExpectedDeprecatedPlugins["inputs"] = []config.PluginDeprecationInfo{ + { + DeprecationInfo: config.DeprecationInfo{ + Name: "test", + }, + }, + } + err := runApp(args, buf, mS, mC, NewMockTelegraf()) + require.NoError(t, err) + expectedOutput := `Deprecated Input Plugins: +plugin name: test +Deprecated Output Plugins: +Deprecated Processor Plugins: +Deprecated Aggregator Plugins: +` + + require.Equal(t, expectedOutput, buf.String()) +} + +func TestPprofAddressFlag(t *testing.T) { + buf := new(bytes.Buffer) + args := os.Args[0:1] + address := "localhost:6060" + args = append(args, "--pprof-addr", address) + m := NewMockServer() + err := runApp(args, buf, m, NewMockConfig(buf), NewMockTelegraf()) + require.NoError(t, err) + require.Equal(t, address, m.Address) +} + +// !!! DEPRECATED !!! +// TestPluginDirectoryFlag tests `--plugin-directory` +func TestPluginDirectoryFlag(t *testing.T) { + buf := new(bytes.Buffer) + args := os.Args[0:1] + args = append(args, "--plugin-directory", ".") + err := runApp(args, buf, NewMockServer(), NewMockConfig(buf), NewMockTelegraf()) + require.ErrorContains(t, err, "E! go plugin support is not enabled") +} + +func TestCommandConfig(t *testing.T) { + tests := []struct { + name string + commands []string + expectedHeaders []string + removedHeaders []string + expectedPlugins []string + removedPlugins []string + }{ + // Deprecated flag replaced with command "config" + { + name: "no filters", + commands: []string{"--sample-config"}, + expectedHeaders: []string{ + outputHeader, + inputHeader, + aggregatorHeader, + processorHeader, + serviceInputHeader, + }, + }, + { + name: "no filters", + commands: []string{"config"}, + expectedHeaders: []string{ + outputHeader, + inputHeader, + aggregatorHeader, + processorHeader, + serviceInputHeader, + }, + }, + { + name: "filter sections for inputs", + commands: []string{"config", "--section-filter", "inputs"}, + expectedHeaders: []string{ + inputHeader, + }, + removedHeaders: []string{ + outputHeader, + aggregatorHeader, + processorHeader, + }, + }, + { + name: "filter sections for inputs,outputs", + commands: []string{"config", "--section-filter", "inputs:outputs"}, + expectedHeaders: []string{ + inputHeader, + outputHeader, + }, + removedHeaders: []string{ + aggregatorHeader, + processorHeader, + }, + }, + { + name: "filter input plugins", + commands: []string{"config", "--input-filter", "cpu:file"}, + expectedPlugins: []string{ + "[[inputs.cpu]]", + "[[inputs.file]]", + }, + removedPlugins: []string{ + "[[inputs.disk]]", + }, + }, + { + name: "filter output plugins", + commands: []string{"config", "--output-filter", "influxdb:http"}, + expectedPlugins: []string{ + "[[outputs.influxdb]]", + "[[outputs.http]]", + }, + removedPlugins: []string{ + "[[outputs.file]]", + }, + }, + { + name: "filter processor plugins", + commands: []string{"config", "--processor-filter", "date:enum"}, + expectedPlugins: []string{ + "[[processors.date]]", + "[[processors.enum]]", + }, + removedPlugins: []string{ + "[[processors.parser]]", + }, + }, + { + name: "filter aggregator plugins", + commands: []string{"config", "--aggregator-filter", "basicstats:starlark"}, + expectedPlugins: []string{ + "[[aggregators.basicstats]]", + "[[aggregators.starlark]]", + }, + removedPlugins: []string{ + "[[aggregators.minmax]]", + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + buf := new(bytes.Buffer) + args := os.Args[0:1] + args = append(args, test.commands...) + err := runApp(args, buf, NewMockServer(), NewMockConfig(buf), NewMockTelegraf()) + require.NoError(t, err) + output := buf.String() + for _, e := range test.expectedHeaders { + require.Contains(t, output, e, "expected header not found") + } + for _, r := range test.removedHeaders { + require.NotContains(t, output, r, "removed header found") + } + for _, e := range test.expectedPlugins { + require.Contains(t, output, e, "expected plugin not found") + } + for _, r := range test.removedPlugins { + require.NotContains(t, output, r, "removed plugin found") + } + }) + } +} + +func TestCommandVersion(t *testing.T) { + tests := []struct { + Version string + Branch string + Commit string + ExpectedOutput string + }{ + { + Version: "v2.0.0", + ExpectedOutput: "Telegraf v2.0.0\n", + }, + { + ExpectedOutput: "Telegraf unknown\n", + }, + { + Version: "v2.0.0", + Branch: "master", + ExpectedOutput: "Telegraf v2.0.0 (git: master@unknown)\n", + }, + { + Version: "v2.0.0", + Branch: "master", + Commit: "123", + ExpectedOutput: "Telegraf v2.0.0 (git: master@123)\n", + }, + { + Version: "v2.0.0", + Commit: "123", + ExpectedOutput: "Telegraf v2.0.0 (git: unknown@123)\n", + }, + } + + for _, test := range tests { + buf := new(bytes.Buffer) + args := os.Args[0:1] + args = append(args, "version") + internal.Version = test.Version + internal.Branch = test.Branch + internal.Commit = test.Commit + err := runApp(args, buf, NewMockServer(), NewMockConfig(buf), NewMockTelegraf()) + require.NoError(t, err) + require.Equal(t, test.ExpectedOutput, buf.String()) + } +} + +// Deprecated in favor of command version +func TestFlagVersion(t *testing.T) { + tests := []struct { + Version string + Branch string + Commit string + ExpectedOutput string + }{ + { + Version: "v2.0.0", + ExpectedOutput: "Telegraf v2.0.0\n", + }, + { + ExpectedOutput: "Telegraf unknown\n", + }, + { + Version: "v2.0.0", + Branch: "master", + ExpectedOutput: "Telegraf v2.0.0 (git: master@unknown)\n", + }, + { + Version: "v2.0.0", + Branch: "master", + Commit: "123", + ExpectedOutput: "Telegraf v2.0.0 (git: master@123)\n", + }, + { + Version: "v2.0.0", + Commit: "123", + ExpectedOutput: "Telegraf v2.0.0 (git: unknown@123)\n", + }, + } + + for _, test := range tests { + buf := new(bytes.Buffer) + args := os.Args[0:1] + args = append(args, "--version") + internal.Version = test.Version + internal.Branch = test.Branch + internal.Commit = test.Commit + err := runApp(args, buf, NewMockServer(), NewMockConfig(buf), NewMockTelegraf()) + require.NoError(t, err) + require.Equal(t, test.ExpectedOutput, buf.String()) + } +} + +func TestGlobablBoolFlags(t *testing.T) { + commands := []string{ + "--debug", + "--test", + "--quiet", + "--once", + } + + buf := new(bytes.Buffer) + args := os.Args[0:1] + args = append(args, commands...) + m := NewMockTelegraf() + err := runApp(args, buf, NewMockServer(), NewMockConfig(buf), m) + require.NoError(t, err) + + require.Equal(t, true, m.debug) + require.Equal(t, true, m.test) + require.Equal(t, true, m.once) + require.Equal(t, true, m.quiet) +} + +func TestFlagsAreSet(t *testing.T) { + expectedInt := 1 + expectedString := "test" + + commands := []string{ + "--config", expectedString, + "--config-directory", expectedString, + "--debug", + "--test", + "--quiet", + "--once", + "--test-wait", strconv.Itoa(expectedInt), + "--watch-config", expectedString, + "--pidfile", expectedString, + } + + buf := new(bytes.Buffer) + args := os.Args[0:1] + args = append(args, commands...) + m := NewMockTelegraf() + err := runApp(args, buf, NewMockServer(), NewMockConfig(buf), m) + require.NoError(t, err) + + require.Equal(t, []string{expectedString}, m.config) + require.Equal(t, []string{expectedString}, m.configDir) + require.Equal(t, true, m.debug) + require.Equal(t, true, m.test) + require.Equal(t, true, m.once) + require.Equal(t, true, m.quiet) + require.Equal(t, expectedInt, m.testWait) + require.Equal(t, expectedString, m.watchConfig) + require.Equal(t, expectedString, m.pidFile) +} diff --git a/cmd/telegraf/main_win_test.go b/cmd/telegraf/main_win_test.go new file mode 100644 index 0000000000000..61c43ffd454f9 --- /dev/null +++ b/cmd/telegraf/main_win_test.go @@ -0,0 +1,39 @@ +//go:build windows +// +build windows + +package main + +import ( + "bytes" + "os" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestWindowsFlagsAreSet(t *testing.T) { + expectedString := "test" + + commands := []string{ + "--service", expectedString, + "--service-name", expectedString, + "--service-display-name", expectedString, + "--service-restart-delay", expectedString, + "--service-auto-restart", + "--console", + } + + buf := new(bytes.Buffer) + args := os.Args[0:1] + args = append(args, commands...) + m := NewMockTelegraf() + err := runApp(args, buf, NewMockServer(), NewMockConfig(buf), m) + require.NoError(t, err) + + require.Equal(t, expectedString, m.service) + require.Equal(t, expectedString, m.serviceName) + require.Equal(t, expectedString, m.serviceDisplayName) + require.Equal(t, expectedString, m.serviceRestartDelay) + require.Equal(t, true, m.serviceAutoRestart) + require.Equal(t, true, m.console) +} diff --git a/cmd/telegraf/pprof.go b/cmd/telegraf/pprof.go new file mode 100644 index 0000000000000..9c997c7e0f79c --- /dev/null +++ b/cmd/telegraf/pprof.go @@ -0,0 +1,45 @@ +package main + +import ( + "fmt" + "log" //nolint: revive + "net/http" + "strings" +) + +type Server interface { + Start(string) + ErrChan() <-chan error +} + +type PprofServer struct { + err chan error +} + +func NewPprofServer() *PprofServer { + return &PprofServer{ + err: make(chan error), + } +} + +func (p *PprofServer) Start(address string) { + go func() { + pprofHostPort := address + parts := strings.Split(pprofHostPort, ":") + if len(parts) == 2 && parts[0] == "" { + pprofHostPort = fmt.Sprintf("localhost:%s", parts[1]) + } + pprofHostPort = "http://" + pprofHostPort + "/debug/pprof" + + log.Printf("I! Starting pprof HTTP server at: %s", pprofHostPort) + + if err := http.ListenAndServe(address, nil); err != nil { + p.err <- fmt.Errorf("E! %w", err) + } + close(p.err) + }() +} + +func (p *PprofServer) ErrChan() <-chan error { + return p.err +} diff --git a/config/printer/printer.go b/cmd/telegraf/printer.go similarity index 77% rename from config/printer/printer.go rename to cmd/telegraf/printer.go index f1521d1351e3a..4bf03734b3f89 100644 --- a/config/printer/printer.go +++ b/cmd/telegraf/printer.go @@ -1,8 +1,9 @@ -package printer +package main import ( _ "embed" "fmt" + "io" "sort" "strings" @@ -100,8 +101,9 @@ func sliceContains(name string, list []string) bool { return false } -// PrintSampleConfig prints the sample config -func PrintSampleConfig( +// printSampleConfig prints the sample config +func printSampleConfig( + outputBuffer io.Writer, sectionFilters []string, inputFilters []string, outputFilters []string, @@ -109,23 +111,23 @@ func PrintSampleConfig( processorFilters []string, ) { // print headers - fmt.Print(header) + outputBuffer.Write([]byte(header)) if len(sectionFilters) == 0 { sectionFilters = sectionDefaults } - printFilteredGlobalSections(sectionFilters) + printFilteredGlobalSections(sectionFilters, outputBuffer) // print output plugins if sliceContains("outputs", sectionFilters) { if len(outputFilters) != 0 { if len(outputFilters) >= 3 && outputFilters[1] != "none" { - fmt.Print(outputHeader) + outputBuffer.Write([]byte(outputHeader)) } - printFilteredOutputs(outputFilters, false) + printFilteredOutputs(outputFilters, false, outputBuffer) } else { - fmt.Print(outputHeader) - printFilteredOutputs(outputDefaults, false) + outputBuffer.Write([]byte(outputHeader)) + printFilteredOutputs(outputDefaults, false, outputBuffer) // Print non-default outputs, commented var pnames []string for pname := range outputs.Outputs { @@ -134,7 +136,7 @@ func PrintSampleConfig( } } sort.Strings(pnames) - printFilteredOutputs(pnames, true) + printFilteredOutputs(pnames, true, outputBuffer) } } @@ -142,17 +144,17 @@ func PrintSampleConfig( if sliceContains("processors", sectionFilters) { if len(processorFilters) != 0 { if len(processorFilters) >= 3 && processorFilters[1] != "none" { - fmt.Print(processorHeader) + outputBuffer.Write([]byte(processorHeader)) } - printFilteredProcessors(processorFilters, false) + printFilteredProcessors(processorFilters, false, outputBuffer) } else { - fmt.Print(processorHeader) + outputBuffer.Write([]byte(processorHeader)) pnames := []string{} for pname := range processors.Processors { pnames = append(pnames, pname) } sort.Strings(pnames) - printFilteredProcessors(pnames, true) + printFilteredProcessors(pnames, true, outputBuffer) } } @@ -160,17 +162,17 @@ func PrintSampleConfig( if sliceContains("aggregators", sectionFilters) { if len(aggregatorFilters) != 0 { if len(aggregatorFilters) >= 3 && aggregatorFilters[1] != "none" { - fmt.Print(aggregatorHeader) + outputBuffer.Write([]byte(aggregatorHeader)) } - printFilteredAggregators(aggregatorFilters, false) + printFilteredAggregators(aggregatorFilters, false, outputBuffer) } else { - fmt.Print(aggregatorHeader) + outputBuffer.Write([]byte(aggregatorHeader)) pnames := []string{} for pname := range aggregators.Aggregators { pnames = append(pnames, pname) } sort.Strings(pnames) - printFilteredAggregators(pnames, true) + printFilteredAggregators(pnames, true, outputBuffer) } } @@ -178,12 +180,12 @@ func PrintSampleConfig( if sliceContains("inputs", sectionFilters) { if len(inputFilters) != 0 { if len(inputFilters) >= 3 && inputFilters[1] != "none" { - fmt.Print(inputHeader) + outputBuffer.Write([]byte(inputHeader)) } - printFilteredInputs(inputFilters, false) + printFilteredInputs(inputFilters, false, outputBuffer) } else { - fmt.Print(inputHeader) - printFilteredInputs(inputDefaults, false) + outputBuffer.Write([]byte(inputHeader)) + printFilteredInputs(inputDefaults, false, outputBuffer) // Print non-default inputs, commented var pnames []string for pname := range inputs.Inputs { @@ -192,7 +194,7 @@ func PrintSampleConfig( } } sort.Strings(pnames) - printFilteredInputs(pnames, true) + printFilteredInputs(pnames, true, outputBuffer) } } } @@ -217,7 +219,7 @@ func PluginNameCounts(plugins []string) []string { return namecount } -func printFilteredProcessors(processorFilters []string, commented bool) { +func printFilteredProcessors(processorFilters []string, commented bool, outputBuffer io.Writer) { // Filter processors var pnames []string for pname := range processors.Processors { @@ -231,11 +233,11 @@ func printFilteredProcessors(processorFilters []string, commented bool) { for _, pname := range pnames { creator := processors.Processors[pname] output := creator() - printConfig(pname, output, "processors", commented, processors.Deprecations[pname]) + printConfig(pname, output, "processors", commented, processors.Deprecations[pname], outputBuffer) } } -func printFilteredAggregators(aggregatorFilters []string, commented bool) { +func printFilteredAggregators(aggregatorFilters []string, commented bool, outputBuffer io.Writer) { // Filter outputs var anames []string for aname := range aggregators.Aggregators { @@ -249,11 +251,11 @@ func printFilteredAggregators(aggregatorFilters []string, commented bool) { for _, aname := range anames { creator := aggregators.Aggregators[aname] output := creator() - printConfig(aname, output, "aggregators", commented, aggregators.Deprecations[aname]) + printConfig(aname, output, "aggregators", commented, aggregators.Deprecations[aname], outputBuffer) } } -func printFilteredInputs(inputFilters []string, commented bool) { +func printFilteredInputs(inputFilters []string, commented bool, outputBuffer io.Writer) { // Filter inputs var pnames []string for pname := range inputs.Inputs { @@ -284,7 +286,7 @@ func printFilteredInputs(inputFilters []string, commented bool) { continue } - printConfig(pname, input, "inputs", commented, inputs.Deprecations[pname]) + printConfig(pname, input, "inputs", commented, inputs.Deprecations[pname], outputBuffer) } // Print Service Inputs @@ -293,13 +295,13 @@ func printFilteredInputs(inputFilters []string, commented bool) { } sort.Strings(servInputNames) - fmt.Print(serviceInputHeader) + outputBuffer.Write([]byte(serviceInputHeader)) for _, name := range servInputNames { - printConfig(name, servInputs[name], "inputs", commented, inputs.Deprecations[name]) + printConfig(name, servInputs[name], "inputs", commented, inputs.Deprecations[name], outputBuffer) } } -func printFilteredOutputs(outputFilters []string, commented bool) { +func printFilteredOutputs(outputFilters []string, commented bool, outputBuffer io.Writer) { // Filter outputs var onames []string for oname := range outputs.Outputs { @@ -313,21 +315,21 @@ func printFilteredOutputs(outputFilters []string, commented bool) { for _, oname := range onames { creator := outputs.Outputs[oname] output := creator() - printConfig(oname, output, "outputs", commented, outputs.Deprecations[oname]) + printConfig(oname, output, "outputs", commented, outputs.Deprecations[oname], outputBuffer) } } -func printFilteredGlobalSections(sectionFilters []string) { +func printFilteredGlobalSections(sectionFilters []string, outputBuffer io.Writer) { if sliceContains("global_tags", sectionFilters) { - fmt.Print(globalTagsConfig) + outputBuffer.Write([]byte(globalTagsConfig)) } if sliceContains("agent", sectionFilters) { - fmt.Print(agentConfig) + outputBuffer.Write([]byte(agentConfig)) } } -func printConfig(name string, p telegraf.PluginDescriber, op string, commented bool, di telegraf.DeprecationInfo) { +func printConfig(name string, p telegraf.PluginDescriber, op string, commented bool, di telegraf.DeprecationInfo, outputBuffer io.Writer) { comment := "" if commented { comment = "# " @@ -338,44 +340,44 @@ func printConfig(name string, p telegraf.PluginDescriber, op string, commented b 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) + outputBuffer.Write([]byte(fmt.Sprintf("\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) + sample := p.SampleConfig() + if sample == "" { + outputBuffer.Write([]byte(fmt.Sprintf("\n#[[%s.%s]]", op, name))) + outputBuffer.Write([]byte(fmt.Sprintf("\n%s # no configuration\n\n", comment))) } else { - lines := strings.Split(config, "\n") - fmt.Print("\n") + lines := strings.Split(sample, "\n") + outputBuffer.Write([]byte("\n")) for i, line := range lines { if i == len(lines)-1 { - fmt.Print("\n") + outputBuffer.Write([]byte("\n")) continue } - fmt.Print(strings.TrimRight(comment+line, " ") + "\n") + outputBuffer.Write([]byte(strings.TrimRight(comment+line, " ") + "\n")) } } } // PrintInputConfig prints the config usage of a single input. -func PrintInputConfig(name string) error { +func PrintInputConfig(name string, outputBuffer io.Writer) error { creator, ok := inputs.Inputs[name] if !ok { return fmt.Errorf("input %s not found", name) } - printConfig(name, creator(), "inputs", false, inputs.Deprecations[name]) + printConfig(name, creator(), "inputs", false, inputs.Deprecations[name], outputBuffer) return nil } // PrintOutputConfig prints the config usage of a single output. -func PrintOutputConfig(name string) error { +func PrintOutputConfig(name string, outputBuffer io.Writer) error { creator, ok := outputs.Outputs[name] if !ok { return fmt.Errorf("output %s not found", name) } - printConfig(name, creator(), "outputs", false, outputs.Deprecations[name]) + printConfig(name, creator(), "outputs", false, outputs.Deprecations[name], outputBuffer) return nil } diff --git a/cmd/telegraf/telegraf.go b/cmd/telegraf/telegraf.go index cc02f6aa9dd21..9bb71df6ac0c7 100644 --- a/cmd/telegraf/telegraf.go +++ b/cmd/telegraf/telegraf.go @@ -3,138 +3,78 @@ package main import ( "context" "errors" - "flag" "fmt" - "log" - "net/http" - _ "net/http/pprof" // Comment this line to disable pprof endpoint. + "log" //nolint:revive "os" "os/signal" - "sort" "strings" "syscall" "time" "github.com/coreos/go-systemd/daemon" - "github.com/fatih/color" - "github.com/influxdata/tail/watch" "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" "github.com/influxdata/telegraf/plugins/aggregators" - _ "github.com/influxdata/telegraf/plugins/aggregators/all" "github.com/influxdata/telegraf/plugins/inputs" - _ "github.com/influxdata/telegraf/plugins/inputs/all" "github.com/influxdata/telegraf/plugins/outputs" - _ "github.com/influxdata/telegraf/plugins/outputs/all" "github.com/influxdata/telegraf/plugins/parsers" - _ "github.com/influxdata/telegraf/plugins/parsers/all" "github.com/influxdata/telegraf/plugins/processors" - _ "github.com/influxdata/telegraf/plugins/processors/all" "gopkg.in/tomb.v1" ) -type sliceFlags []string +var stop chan struct{} -func (i *sliceFlags) String() string { - s := strings.Join(*i, " ") - return "[" + s + "]" +type GlobalFlags struct { + config []string + configDir []string + testWait int + watchConfig string + pidFile string + plugindDir string + test bool + debug bool + once bool + quiet bool } -func (i *sliceFlags) Set(value string) error { - *i = append(*i, value) - return nil +type WindowFlags struct { + service string + serviceName string + serviceDisplayName string + serviceRestartDelay string + serviceAutoRestart bool + console bool } -// If you update these, update usage.go and usage_windows.go -var fDebug = flag.Bool("debug", false, - "turn on debug logging") -var pprofAddr = flag.String("pprof-addr", "", - "pprof address to listen on, not activate pprof if empty") -var fQuiet = flag.Bool("quiet", false, - "run in quiet mode") -var fTest = flag.Bool("test", false, "enable test mode: gather metrics, print them out, and exit. Note: Test mode only runs inputs, not processors, aggregators, or outputs") -var fTestWait = flag.Int("test-wait", 0, "wait up to this many seconds for service inputs to complete in test mode") - -var fConfigs sliceFlags -var fConfigDirs sliceFlags -var fWatchConfig = flag.String("watch-config", "", "Monitoring config changes [notify, poll]") -var fVersion = flag.Bool("version", false, "display the version and exit") -var fSampleConfig = flag.Bool("sample-config", false, - "print out full sample configuration") -var fPidfile = flag.String("pidfile", "", "file to write our pid to") -var fDeprecationList = flag.Bool("deprecation-list", false, - "print all deprecated plugins or plugin options.") -var fSectionFilters = flag.String("section-filter", "", - "filter the sections to print, separator is ':'. Valid values are 'agent', 'global_tags', 'outputs', 'processors', 'aggregators' and 'inputs'") -var fInputFilters = flag.String("input-filter", "", - "filter the inputs to enable, separator is :") -var fInputList = flag.Bool("input-list", false, - "print available input plugins.") -var fOutputFilters = flag.String("output-filter", "", - "filter the outputs to enable, separator is :") -var fOutputList = flag.Bool("output-list", false, - "print available output plugins.") -var fAggregatorFilters = flag.String("aggregator-filter", "", - "filter the aggregators to enable, separator is :") -var fProcessorFilters = flag.String("processor-filter", "", - "filter the processors to enable, separator is :") -var fUsage = flag.String("usage", "", - "print usage for a plugin, ie, 'telegraf --usage mysql'") - -// Initialize the subcommand `telegraf config` -// This duplicates the above filters which are used for `telegraf --sample-config` and `telegraf --deprecation-list` -var configCmd = flag.NewFlagSet("config", flag.ExitOnError) -var fSubSectionFilters = configCmd.String("section-filter", "", - "filter the sections to print, separator is ':'. Valid values are 'agent', 'global_tags', 'outputs', 'processors', 'aggregators' and 'inputs'") -var fSubInputFilters = configCmd.String("input-filter", "", - "filter the inputs to enable, separator is :") -var fSubOutputFilters = configCmd.String("output-filter", "", - "filter the outputs to enable, separator is :") -var fsubAggregatorFilters = configCmd.String("aggregator-filter", "", - "filter the aggregators to enable, separator is :") -var fSubProcessorFilters = configCmd.String("processor-filter", "", - "filter the processors to enable, separator is :") - -//nolint:varcheck,unused // False positive - this var is used for non-default build tag: windows -var fService = flag.String("service", "", - "operate on the service (windows only)") - -//nolint:varcheck,unused // False positive - this var is used for non-default build tag: windows -var fServiceName = flag.String("service-name", "telegraf", - "service name (windows only)") - -//nolint:varcheck,unused // False positive - this var is used for non-default build tag: windows -var fServiceDisplayName = flag.String("service-display-name", "Telegraf Data Collector Service", - "service display name (windows only)") - -//nolint:varcheck,unused // False positive - this var is used for non-default build tag: windows -var fServiceAutoRestart = flag.Bool("service-auto-restart", false, - "auto restart service on failure (windows only)") - -//nolint:varcheck,unused // False positive - this var is used for non-default build tag: windows -var fServiceRestartDelay = flag.String("service-restart-delay", "5m", - "delay before service auto restart, default is 5m (windows only)") - -//nolint:varcheck,unused // False positive - this var is used for non-default build tag: windows -var fRunAsConsole = flag.Bool("console", false, - "run as console application (windows only)") -var fPlugins = flag.String("plugin-directory", "", - "path to directory containing external plugins") -var fRunOnce = flag.Bool("once", false, "run one gather and exit") +type App interface { + Init(<-chan error, Filters, GlobalFlags, WindowFlags) + Run() error +} -var stop chan struct{} +type Telegraf struct { + pprofErr <-chan error + + inputFilters []string + outputFilters []string + + GlobalFlags + WindowFlags +} -func reloadLoop( - inputFilters []string, - outputFilters []string, -) { +func (a *Telegraf) Init(pprofErr <-chan error, f Filters, g GlobalFlags, w WindowFlags) { + a.pprofErr = pprofErr + a.inputFilters = f.input + a.outputFilters = f.output + a.GlobalFlags = g + a.WindowFlags = w +} + +func (a *Telegraf) reloadLoop() error { reload := make(chan bool, 1) reload <- true for <-reload { @@ -144,10 +84,10 @@ func reloadLoop( signals := make(chan os.Signal, 1) signal.Notify(signals, os.Interrupt, syscall.SIGHUP, syscall.SIGTERM, syscall.SIGINT) - if *fWatchConfig != "" { - for _, fConfig := range fConfigs { + if a.watchConfig != "" { + for _, fConfig := range a.config { if _, err := os.Stat(fConfig); err == nil { - go watchLocalConfig(signals, fConfig) + go a.watchLocalConfig(signals, fConfig) } else { log.Printf("W! Cannot watch config %s: %s", fConfig, err) } @@ -162,22 +102,27 @@ func reloadLoop( reload <- true } cancel() + case err := <-a.pprofErr: + log.Printf("E! pprof server failed: %v", err) + cancel() case <-stop: cancel() } }() - err := runAgent(ctx, inputFilters, outputFilters) + err := a.runAgent(ctx) if err != nil && err != context.Canceled { - log.Fatalf("E! [telegraf] Error running agent: %v", err) + return fmt.Errorf("[telegraf] Error running agent: %v", err) } } + + return nil } -func watchLocalConfig(signals chan os.Signal, fConfig string) { +func (a *Telegraf) watchLocalConfig(signals chan os.Signal, fConfig string) { var mytomb tomb.Tomb var watcher watch.FileWatcher - if *fWatchConfig == "poll" { + if a.watchConfig == "poll" { watcher = watch.NewPollingFileWatcher(fConfig) } else { watcher = watch.NewInotifyFileWatcher(fConfig) @@ -214,40 +159,37 @@ func watchLocalConfig(signals chan os.Signal, fConfig string) { signals <- syscall.SIGHUP } -func runAgent(ctx context.Context, - inputFilters []string, - outputFilters []string, -) error { +func (a *Telegraf) runAgent(ctx context.Context) error { // If no other options are specified, load the config file and run. c := config.NewConfig() - c.OutputFilters = outputFilters - c.InputFilters = inputFilters + c.OutputFilters = a.outputFilters + c.InputFilters = a.inputFilters var err error // providing no "config" flag should load default config - if len(fConfigs) == 0 { + if len(a.config) == 0 { err = c.LoadConfig("") if err != nil { return err } } - for _, fConfig := range fConfigs { + for _, fConfig := range a.config { err = c.LoadConfig(fConfig) if err != nil { return err } } - for _, fConfigDirectory := range fConfigDirs { + for _, fConfigDirectory := range a.configDir { err = c.LoadDirectory(fConfigDirectory) if err != nil { return err } } - if !(*fTest || *fTestWait != 0) && len(c.Outputs) == 0 { + if !(a.test || a.testWait != 0) && len(c.Outputs) == 0 { return errors.New("Error: no outputs found, did you provide a valid config file?") } - if *fPlugins == "" && len(c.Inputs) == 0 { + if a.plugindDir == "" && len(c.Inputs) == 0 { return errors.New("Error: no inputs found, did you provide a valid config file?") } @@ -260,10 +202,10 @@ func runAgent(ctx context.Context, } // Setup logging as configured. - telegraf.Debug = c.Agent.Debug || *fDebug + telegraf.Debug = c.Agent.Debug || a.debug logConfig := logger.LogConfig{ Debug: telegraf.Debug, - Quiet: c.Agent.Quiet || *fQuiet, + Quiet: c.Agent.Quiet || a.quiet, LogTarget: c.Agent.LogTarget, Logfile: c.Agent.Logfile, RotationInterval: c.Agent.LogfileRotationInterval, @@ -274,7 +216,7 @@ func runAgent(ctx context.Context, logger.SetupLogging(logConfig) - log.Printf("I! Starting Telegraf %s%s", internal.Version(), internal.Customized) + log.Printf("I! Starting Telegraf %s%s", internal.Version, internal.Customized) log.Printf("I! Available plugins: %d inputs, %d aggregators, %d processors, %d parsers, %d outputs", len(inputs.Inputs), len(aggregators.Aggregators), @@ -285,7 +227,7 @@ func runAgent(ctx context.Context, log.Printf("I! Loaded inputs: %s", strings.Join(c.InputNames(), " ")) log.Printf("I! Loaded aggregators: %s", strings.Join(c.AggregatorNames(), " ")) log.Printf("I! Loaded processors: %s", strings.Join(c.ProcessorNames(), " ")) - if !*fRunOnce && (*fTest || *fTestWait != 0) { + if !a.once && (a.test || a.testWait != 0) { log.Print("W! " + color.RedString("Outputs are not used in testing mode!")) } else { log.Printf("I! Loaded outputs: %s", strings.Join(c.OutputNames(), " ")) @@ -316,27 +258,30 @@ func runAgent(ctx context.Context, // For platforms that use systemd, telegraf doesn't log if the notification failed. _, _ = daemon.SdNotify(false, daemon.SdNotifyReady) - if *fRunOnce { - wait := time.Duration(*fTestWait) * time.Second + if a.once { + wait := time.Duration(a.testWait) * time.Second return ag.Once(ctx, wait) } - if *fTest || *fTestWait != 0 { - wait := time.Duration(*fTestWait) * time.Second + if a.test || a.testWait != 0 { + wait := time.Duration(a.testWait) * time.Second return ag.Test(ctx, wait) } - if *fPidfile != "" { - f, err := os.OpenFile(*fPidfile, os.O_CREATE|os.O_WRONLY, 0644) + if a.pidFile != "" { + f, err := os.OpenFile(a.pidFile, os.O_CREATE|os.O_WRONLY, 0644) if err != nil { log.Printf("E! Unable to create pidfile: %s", err) } else { - fmt.Fprintf(f, "%d\n", os.Getpid()) + _, _ = fmt.Fprintf(f, "%d\n", os.Getpid()) - f.Close() + err = f.Close() + if err != nil { + return err + } defer func() { - err := os.Remove(*fPidfile) + err := os.Remove(a.pidFile) if err != nil { log.Printf("E! Unable to remove pidfile: %s", err) } @@ -346,192 +291,3 @@ func runAgent(ctx context.Context, return ag.Run(ctx) } - -func usageExit(rc int) { - fmt.Println(internal.Usage) - os.Exit(rc) -} - -func deleteEmpty(s []string) []string { - var r []string - for _, str := range s { - if str != "" { - r = append(r, str) - } - } - return r -} - -func main() { - flag.Var(&fConfigs, "config", "configuration file to load") - flag.Var(&fConfigDirs, "config-directory", "directory containing additional *.conf files") - - flag.Usage = func() { usageExit(0) } - flag.Parse() - args := flag.Args() - - sectionFilters, inputFilters, outputFilters := []string{}, []string{}, []string{} - if *fSectionFilters != "" { - sectionFilters = strings.Split(":"+strings.TrimSpace(*fSectionFilters)+":", ":") - } - if *fInputFilters != "" { - inputFilters = strings.Split(":"+strings.TrimSpace(*fInputFilters)+":", ":") - } - if *fOutputFilters != "" { - outputFilters = strings.Split(":"+strings.TrimSpace(*fOutputFilters)+":", ":") - } - - aggregatorFilters, processorFilters := []string{}, []string{} - if *fAggregatorFilters != "" { - aggregatorFilters = strings.Split(":"+strings.TrimSpace(*fAggregatorFilters)+":", ":") - } - if *fProcessorFilters != "" { - processorFilters = strings.Split(":"+strings.TrimSpace(*fProcessorFilters)+":", ":") - } - - logger.SetupLogging(logger.LogConfig{}) - - // Load external plugins, if requested. - if *fPlugins != "" { - log.Printf("I! Loading external plugins from: %s", *fPlugins) - if err := goplugin.LoadExternalPlugins(*fPlugins); err != nil { - log.Fatal("E! " + err.Error()) - } - } - - if *pprofAddr != "" { - go func() { - pprofHostPort := *pprofAddr - parts := strings.Split(pprofHostPort, ":") - if len(parts) == 2 && parts[0] == "" { - pprofHostPort = fmt.Sprintf("localhost:%s", parts[1]) - } - pprofHostPort = "http://" + pprofHostPort + "/debug/pprof" - - log.Printf("I! Starting pprof HTTP server at: %s", pprofHostPort) - - if err := http.ListenAndServe(*pprofAddr, nil); err != nil { - log.Fatal("E! " + err.Error()) - } - }() - } - - if len(args) > 0 { - switch args[0] { - case "version": - fmt.Println(internal.FormatFullVersion()) - return - case "config": - err := configCmd.Parse(args[1:]) - if err != nil { - log.Fatal("E! " + err.Error()) - } - - // The sub_Filters are populated when the filter flags are set after the subcommand config - // e.g. telegraf config --section-filter inputs - subSectionFilters := deleteEmpty(strings.Split(":"+strings.TrimSpace(*fSubSectionFilters)+":", ":")) - subInputFilters := deleteEmpty(strings.Split(":"+strings.TrimSpace(*fSubInputFilters)+":", ":")) - subOutputFilters := deleteEmpty(strings.Split(":"+strings.TrimSpace(*fSubOutputFilters)+":", ":")) - subAggregatorFilters := deleteEmpty(strings.Split(":"+strings.TrimSpace(*fsubAggregatorFilters)+":", ":")) - subProcessorFilters := deleteEmpty(strings.Split(":"+strings.TrimSpace(*fSubProcessorFilters)+":", ":")) - - // Overwrite the global filters if the subfilters are defined, this allows for backwards compatibility - // Now you can still filter the sample config like so: telegraf --section-filter inputs config - if len(subSectionFilters) > 0 { - sectionFilters = subSectionFilters - } - if len(subInputFilters) > 0 { - inputFilters = subInputFilters - } - if len(subOutputFilters) > 0 { - outputFilters = subOutputFilters - } - if len(subAggregatorFilters) > 0 { - aggregatorFilters = subAggregatorFilters - } - if len(subProcessorFilters) > 0 { - processorFilters = subProcessorFilters - } - - printer.PrintSampleConfig( - sectionFilters, - inputFilters, - outputFilters, - aggregatorFilters, - processorFilters, - ) - return - } - } - - // switch for flags which just do something and exit immediately - switch { - case *fDeprecationList: - c := config.NewConfig() - infos := c.CollectDeprecationInfos( - inputFilters, - outputFilters, - aggregatorFilters, - processorFilters, - ) - //nolint:revive // We will notice if Println fails - fmt.Println("Deprecated Input Plugins: ") - c.PrintDeprecationList(infos["inputs"]) - //nolint:revive // We will notice if Println fails - fmt.Println("Deprecated Output Plugins: ") - c.PrintDeprecationList(infos["outputs"]) - //nolint:revive // We will notice if Println fails - fmt.Println("Deprecated Processor Plugins: ") - c.PrintDeprecationList(infos["processors"]) - //nolint:revive // We will notice if Println fails - fmt.Println("Deprecated Aggregator Plugins: ") - c.PrintDeprecationList(infos["aggregators"]) - return - case *fOutputList: - fmt.Println("Available Output Plugins: ") - names := make([]string, 0, len(outputs.Outputs)) - for k := range outputs.Outputs { - names = append(names, k) - } - sort.Strings(names) - for _, k := range names { - fmt.Printf(" %s\n", k) - } - return - case *fInputList: - fmt.Println("Available Input Plugins:") - names := make([]string, 0, len(inputs.Inputs)) - for k := range inputs.Inputs { - names = append(names, k) - } - sort.Strings(names) - for _, k := range names { - fmt.Printf(" %s\n", k) - } - return - case *fVersion: - fmt.Println(internal.FormatFullVersion()) - return - case *fSampleConfig: - printer.PrintSampleConfig( - sectionFilters, - inputFilters, - outputFilters, - aggregatorFilters, - processorFilters, - ) - return - case *fUsage != "": - err := printer.PrintInputConfig(*fUsage) - err2 := printer.PrintOutputConfig(*fUsage) - if err != nil && err2 != nil { - log.Fatalf("E! %s and %s", err, err2) - } - return - } - - run( - inputFilters, - outputFilters, - ) -} diff --git a/cmd/telegraf/telegraf_posix.go b/cmd/telegraf/telegraf_posix.go index 21ad935b7147e..1919902c797bc 100644 --- a/cmd/telegraf/telegraf_posix.go +++ b/cmd/telegraf/telegraf_posix.go @@ -3,10 +3,13 @@ package main -func run(inputFilters, outputFilters []string) { +import "github.com/urfave/cli/v2" + +func (t *Telegraf) Run() error { stop = make(chan struct{}) - reloadLoop( - inputFilters, - outputFilters, - ) + return t.reloadLoop() +} + +func cliFlags() []cli.Flag { + return []cli.Flag{} } diff --git a/cmd/telegraf/telegraf_windows.go b/cmd/telegraf/telegraf_windows.go index ab80fc2be0564..4c565e6fc090a 100644 --- a/cmd/telegraf/telegraf_windows.go +++ b/cmd/telegraf/telegraf_windows.go @@ -6,49 +6,83 @@ package main import ( - "log" + "fmt" "os" - "runtime" "github.com/influxdata/telegraf/logger" "github.com/kardianos/service" + "github.com/urfave/cli/v2" ) -func run(inputFilters, outputFilters []string) { +func cliFlags() []cli.Flag { + return []cli.Flag{ + &cli.StringFlag{ + Name: "service", + Usage: "operate on the service (windows only)", + }, + &cli.StringFlag{ + Name: "service-name", + Value: "telegraf", + Usage: "service name (windows only)", + }, + &cli.StringFlag{ + Name: "service-display-name", + Value: "Telegraf Data Collector Service", + Usage: "service display name (windows only)", + }, + &cli.StringFlag{ + Name: "service-restart-delay", + Value: "5m", + }, + &cli.BoolFlag{ + Name: "service-auto-restart", + Usage: "auto restart service on failure (windows only)", + }, + &cli.BoolFlag{ + Name: "console", + Usage: "run as console application (windows only)", + }, + } +} + +func (t *Telegraf) Run() error { // Register the eventlog logging target for windows. - logger.RegisterEventLogger(*fServiceName) + err := logger.RegisterEventLogger(t.serviceName) + if err != nil { + return err + } - if runtime.GOOS == "windows" && windowsRunAsService() { - runAsWindowsService( - inputFilters, - outputFilters, - ) - } else { + if !t.windowsRunAsService() { stop = make(chan struct{}) - reloadLoop( - inputFilters, - outputFilters, - ) + return t.reloadLoop() } + + return t.runAsWindowsService() } type program struct { - inputFilters []string - outputFilters []string + *Telegraf } func (p *program) Start(s service.Service) error { - go p.run() + go func() { + stop = make(chan struct{}) + err := p.reloadLoop() + if err != nil { + fmt.Printf("E! %v\n", err) + } + close(stop) + }() return nil } -func (p *program) run() { + +func (p *program) run(errChan chan error) { stop = make(chan struct{}) - reloadLoop( - p.inputFilters, - p.outputFilters, - ) + err := p.reloadLoop() + errChan <- err close(stop) } + func (p *program) Stop(s service.Service) error { var empty struct{} stop <- empty // signal reloadLoop to finish (context cancel) @@ -56,70 +90,68 @@ func (p *program) Stop(s service.Service) error { return nil } -func runAsWindowsService(inputFilters, outputFilters []string) { +func (t *Telegraf) runAsWindowsService() error { programFiles := os.Getenv("ProgramFiles") if programFiles == "" { // Should never happen programFiles = "C:\\Program Files" } svcConfig := &service.Config{ - Name: *fServiceName, - DisplayName: *fServiceDisplayName, + Name: t.serviceName, + DisplayName: t.serviceDisplayName, Description: "Collects data using a series of plugins and publishes it to " + "another series of plugins.", Arguments: []string{"--config", programFiles + "\\Telegraf\\telegraf.conf"}, } prg := &program{ - inputFilters: inputFilters, - outputFilters: outputFilters, + Telegraf: t, } s, err := service.New(prg, svcConfig) if err != nil { - log.Fatal("E! " + err.Error()) + return fmt.Errorf("E! " + err.Error()) } // Handle the --service flag here to prevent any issues with tooling that // may not have an interactive session, e.g. installing from Ansible. - if *fService != "" { - if len(fConfigs) > 0 { + if t.service != "" { + if len(t.config) > 0 { svcConfig.Arguments = []string{} } - for _, fConfig := range fConfigs { + for _, fConfig := range t.config { svcConfig.Arguments = append(svcConfig.Arguments, "--config", fConfig) } - for _, fConfigDirectory := range fConfigDirs { + for _, fConfigDirectory := range t.configDir { svcConfig.Arguments = append(svcConfig.Arguments, "--config-directory", fConfigDirectory) } //set servicename to service cmd line, to have a custom name after relaunch as a service - svcConfig.Arguments = append(svcConfig.Arguments, "--service-name", *fServiceName) + svcConfig.Arguments = append(svcConfig.Arguments, "--service-name", t.serviceName) - if *fServiceAutoRestart { - svcConfig.Option = service.KeyValue{"OnFailure": "restart", "OnFailureDelayDuration": *fServiceRestartDelay} + if t.serviceAutoRestart { + svcConfig.Option = service.KeyValue{"OnFailure": "restart", "OnFailureDelayDuration": t.serviceRestartDelay} } - err := service.Control(s, *fService) + err := service.Control(s, t.service) if err != nil { - log.Fatal("E! " + err.Error()) + return fmt.Errorf("E! " + err.Error()) } - os.Exit(0) } else { logger.SetupLogging(logger.LogConfig{LogTarget: logger.LogTargetEventlog}) err = s.Run() - if err != nil { - log.Println("E! " + err.Error()) + return fmt.Errorf("E! " + err.Error()) } } + return nil } // Return true if Telegraf should create a Windows service. -func windowsRunAsService() bool { - if *fService != "" { +func (t *Telegraf) windowsRunAsService() bool { + if t.service != "" { return true } - if *fRunAsConsole { + if t.console { return false } diff --git a/config/config.go b/config/config.go index aae02af468aee..eb0721b631535 100644 --- a/config/config.go +++ b/config/config.go @@ -107,7 +107,7 @@ func NewConfig() *Config { } // Handle unknown version - version := internal.Version() + version := internal.Version if version == "" || version == "unknown" { version = "0.0.0-unknown" } diff --git a/config/deprecation.go b/config/deprecation.go index f8c13904b5a3a..c8a2a70e78144 100644 --- a/config/deprecation.go +++ b/config/deprecation.go @@ -18,8 +18,8 @@ import ( "github.com/influxdata/telegraf/plugins/processors" ) -// deprecationInfo contains all important information to describe a deprecated entity -type deprecationInfo struct { +// DeprecationInfo contains all important information to describe a deprecated entity +type DeprecationInfo struct { // Name of the plugin or plugin option Name string // LogLevel is the level of deprecation which currently corresponds to a log-level @@ -27,7 +27,7 @@ type deprecationInfo struct { info telegraf.DeprecationInfo } -func (di *deprecationInfo) determineEscalation(telegrafVersion *semver.Version) error { +func (di *DeprecationInfo) determineEscalation(telegrafVersion *semver.Version) error { di.LogLevel = telegraf.None if di.info.Since == "" { return nil @@ -64,12 +64,12 @@ func (di *deprecationInfo) determineEscalation(telegrafVersion *semver.Version) return nil } -// pluginDeprecationInfo holds all information about a deprecated plugin or it's options -type pluginDeprecationInfo struct { - deprecationInfo +// PluginDeprecationInfo holds all information about a deprecated plugin or it's options +type PluginDeprecationInfo struct { + DeprecationInfo // Options deprecated for this plugin - Options []deprecationInfo + Options []DeprecationInfo } func (c *Config) incrementPluginDeprecations(category string) { @@ -88,9 +88,9 @@ func (c *Config) incrementPluginOptionDeprecations(category string) { c.Deprecations[category] = newcounts } -func (c *Config) collectDeprecationInfo(category, name string, plugin interface{}, all bool) pluginDeprecationInfo { - info := pluginDeprecationInfo{ - deprecationInfo: deprecationInfo{ +func (c *Config) collectDeprecationInfo(category, name string, plugin interface{}, all bool) PluginDeprecationInfo { + info := PluginDeprecationInfo{ + DeprecationInfo: DeprecationInfo{ Name: category + "." + name, LogLevel: telegraf.None, }, @@ -100,19 +100,19 @@ func (c *Config) collectDeprecationInfo(category, name string, plugin interface{ switch category { case "aggregators": if pi, deprecated := aggregators.Deprecations[name]; deprecated { - info.deprecationInfo.info = pi + info.DeprecationInfo.info = pi } case "inputs": if pi, deprecated := inputs.Deprecations[name]; deprecated { - info.deprecationInfo.info = pi + info.DeprecationInfo.info = pi } case "outputs": if pi, deprecated := outputs.Deprecations[name]; deprecated { - info.deprecationInfo.info = pi + info.DeprecationInfo.info = pi } case "processors": if pi, deprecated := processors.Deprecations[name]; deprecated { - info.deprecationInfo.info = pi + info.DeprecationInfo.info = pi } } if err := info.determineEscalation(c.version); err != nil { @@ -138,7 +138,7 @@ func (c *Config) collectDeprecationInfo(category, name string, plugin interface{ if len(tags) < 1 || tags[0] == "" { return } - optionInfo := deprecationInfo{Name: field.Name} + optionInfo := DeprecationInfo{Name: field.Name} optionInfo.info.Since = tags[0] if len(tags) > 1 { @@ -190,10 +190,10 @@ func (c *Config) printUserDeprecation(category, name string, plugin interface{}) return nil } -func (c *Config) CollectDeprecationInfos(inFilter, outFilter, aggFilter, procFilter []string) map[string][]pluginDeprecationInfo { - infos := make(map[string][]pluginDeprecationInfo) +func (c *Config) CollectDeprecationInfos(inFilter, outFilter, aggFilter, procFilter []string) map[string][]PluginDeprecationInfo { + infos := make(map[string][]PluginDeprecationInfo) - infos["inputs"] = make([]pluginDeprecationInfo, 0) + infos["inputs"] = make([]PluginDeprecationInfo, 0) for name, creator := range inputs.Inputs { if len(inFilter) > 0 && !sliceContains(name, inFilter) { continue @@ -207,7 +207,7 @@ func (c *Config) CollectDeprecationInfos(inFilter, outFilter, aggFilter, procFil } } - infos["outputs"] = make([]pluginDeprecationInfo, 0) + infos["outputs"] = make([]PluginDeprecationInfo, 0) for name, creator := range outputs.Outputs { if len(outFilter) > 0 && !sliceContains(name, outFilter) { continue @@ -221,7 +221,7 @@ func (c *Config) CollectDeprecationInfos(inFilter, outFilter, aggFilter, procFil } } - infos["processors"] = make([]pluginDeprecationInfo, 0) + infos["processors"] = make([]PluginDeprecationInfo, 0) for name, creator := range processors.Processors { if len(procFilter) > 0 && !sliceContains(name, procFilter) { continue @@ -235,7 +235,7 @@ func (c *Config) CollectDeprecationInfos(inFilter, outFilter, aggFilter, procFil } } - infos["aggregators"] = make([]pluginDeprecationInfo, 0) + infos["aggregators"] = make([]PluginDeprecationInfo, 0) for name, creator := range aggregators.Aggregators { if len(aggFilter) > 0 && !sliceContains(name, aggFilter) { continue @@ -252,7 +252,7 @@ func (c *Config) CollectDeprecationInfos(inFilter, outFilter, aggFilter, procFil return infos } -func (c *Config) PrintDeprecationList(plugins []pluginDeprecationInfo) { +func (c *Config) PrintDeprecationList(plugins []PluginDeprecationInfo) { sort.Slice(plugins, func(i, j int) bool { return plugins[i].Name < plugins[j].Name }) for _, plugin := range plugins { diff --git a/docs/LICENSE_OF_DEPENDENCIES.md b/docs/LICENSE_OF_DEPENDENCIES.md index 67e31b8f1e115..989dc820f3014 100644 --- a/docs/LICENSE_OF_DEPENDENCIES.md +++ b/docs/LICENSE_OF_DEPENDENCIES.md @@ -81,6 +81,7 @@ following works: - github.com/couchbase/go-couchbase [MIT License](https://github.com/couchbase/go-couchbase/blob/master/LICENSE) - github.com/couchbase/gomemcached [MIT License](https://github.com/couchbase/gomemcached/blob/master/LICENSE) - github.com/couchbase/goutils [Apache License 2.0](https://github.com/couchbase/goutils/blob/master/LICENSE.md) +- github.com/cpuguy83/go-md2man [MIT License](https://github.com/cpuguy83/go-md2man/blob/master/LICENSE.md) - github.com/davecgh/go-spew [ISC License](https://github.com/davecgh/go-spew/blob/master/LICENSE) - github.com/denisenkom/go-mssqldb [BSD 3-Clause "New" or "Revised" License](https://github.com/denisenkom/go-mssqldb/blob/master/LICENSE.txt) - github.com/devigned/tab [MIT License](https://github.com/devigned/tab/blob/master/LICENSE) @@ -251,6 +252,7 @@ following works: - github.com/remyoudompheng/bigfft [BSD 3-Clause "New" or "Revised" License](https://github.com/remyoudompheng/bigfft/blob/master/LICENSE) - github.com/riemann/riemann-go-client [MIT License](https://github.com/riemann/riemann-go-client/blob/master/LICENSE) - github.com/robbiet480/go.nut [MIT License](https://github.com/robbiet480/go.nut/blob/master/LICENSE) +- github.com/russross/blackfriday [BSD 2-Clause "Simplified" License](https://github.com/russross/blackfriday/blob/master/LICENSE.txt) - github.com/safchain/ethtool [Apache License 2.0](https://github.com/safchain/ethtool/blob/master/LICENSE) - github.com/samuel/go-zookeeper [BSD 3-Clause Clear License](https://github.com/samuel/go-zookeeper/blob/master/LICENSE) - github.com/shirou/gopsutil [BSD 3-Clause Clear License](https://github.com/shirou/gopsutil/blob/master/LICENSE) @@ -272,6 +274,7 @@ following works: - github.com/tinylib/msgp [MIT License](https://github.com/tinylib/msgp/blob/master/LICENSE) - github.com/tklauser/go-sysconf [BSD 3-Clause "New" or "Revised" License](https://github.com/tklauser/go-sysconf/blob/master/LICENSE) - github.com/tklauser/numcpus [Apache License 2.0](https://github.com/tklauser/numcpus/blob/master/LICENSE) +- github.com/urfave/cli [MIT License](https://github.com/urfave/cli/blob/main/LICENSE) - github.com/vapourismo/knx-go [MIT License](https://github.com/vapourismo/knx-go/blob/master/LICENSE) - github.com/vishvananda/netlink [Apache License 2.0](https://github.com/vishvananda/netlink/blob/master/LICENSE) - github.com/vishvananda/netns [Apache License 2.0](https://github.com/vishvananda/netns/blob/master/LICENSE) @@ -285,6 +288,7 @@ following works: - github.com/xdg-go/stringprep [Apache License 2.0](https://github.com/xdg-go/stringprep/blob/master/LICENSE) - github.com/xdg/scram [Apache License 2.0](https://github.com/xdg-go/scram/blob/master/LICENSE) - github.com/xdg/stringprep [Apache License 2.0](https://github.com/xdg-go/stringprep/blob/master/LICENSE) +- github.com/xrash/smetrics [MIT License](https://github.com/xrash/smetrics/blob/master/LICENSE) - github.com/youmark/pkcs8 [MIT License](https://github.com/youmark/pkcs8/blob/master/LICENSE) - github.com/yuin/gopher-lua [MIT License](https://github.com/yuin/gopher-lua/blob/master/LICENSE) - github.com/yusufpapurcu/wmi [MIT License](https://github.com/yusufpapurcu/wmi/blob/master/LICENSE) diff --git a/go.mod b/go.mod index fb6276a40e5d4..85953a4e43d13 100644 --- a/go.mod +++ b/go.mod @@ -180,14 +180,6 @@ require ( modernc.org/sqlite v1.17.3 ) -require ( - github.com/imdario/mergo v0.3.12 // indirect - github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect - github.com/spf13/pflag v1.0.5 // indirect - github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect - gopkg.in/macaroon-bakery.v3 v3.0.0 // indirect -) - require ( cloud.google.com/go v0.102.1 // indirect cloud.google.com/go/compute v1.7.0 // indirect @@ -290,6 +282,7 @@ require ( github.com/hashicorp/go-rootcerts v1.0.2 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/hashicorp/serf v0.9.7 // indirect + github.com/imdario/mergo v0.3.12 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect github.com/jackc/pgconn v1.13.0 // indirect github.com/jackc/pgio v1.0.0 // indirect @@ -331,6 +324,7 @@ require ( github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/naoina/go-stringutil v0.1.0 // indirect @@ -362,6 +356,7 @@ require ( github.com/signalfx/com_signalfx_metrics_protobuf v0.0.2 // indirect github.com/signalfx/gohistogram v0.0.0-20160107210732-1ccfd2ff5083 // indirect github.com/signalfx/sapm-proto v0.7.2 // indirect + github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.4.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.0 // indirect @@ -374,6 +369,7 @@ require ( github.com/xdg-go/scram v1.1.1 // indirect github.com/xdg-go/stringprep v1.0.3 // indirect github.com/xdg/stringprep v1.0.3 // indirect + github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect github.com/yuin/gopher-lua v0.0.0-20200816102855-ee81675732da // indirect github.com/yusufpapurcu/wmi v1.2.2 // indirect @@ -401,6 +397,7 @@ require ( gopkg.in/httprequest.v1 v1.2.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.66.6 // indirect + gopkg.in/macaroon-bakery.v3 v3.0.0 // indirect gopkg.in/macaroon.v2 v2.1.0 // indirect gopkg.in/sourcemap.v1 v1.0.5 // indirect gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 // indirect diff --git a/internal/internal.go b/internal/internal.go index 022c6634f3639..c6e1391c5d249 100644 --- a/internal/internal.go +++ b/internal/internal.go @@ -29,9 +29,9 @@ var ( // Set via LDFLAGS -X var ( - version = "unknown" - branch = "" - commit = "" + Version = "unknown" + Branch = "" + Commit = "" ) type ReadWaitCloser struct { @@ -39,28 +39,23 @@ type ReadWaitCloser struct { wg sync.WaitGroup } -// Version returns the telegraf agent version -func Version() string { - return version -} - func FormatFullVersion() string { var parts = []string{"Telegraf"} - if version != "" { - parts = append(parts, version) + if Version != "" { + parts = append(parts, Version) } else { parts = append(parts, "unknown") } - if branch != "" || commit != "" { - if branch == "" { - branch = "unknown" + if Branch != "" || Commit != "" { + if Branch == "" { + Branch = "unknown" } - if commit == "" { - commit = "unknown" + if Commit == "" { + Commit = "unknown" } - git := fmt.Sprintf("(git: %s@%s)", branch, commit) + git := fmt.Sprintf("(git: %s@%s)", Branch, Commit) parts = append(parts, git) } @@ -70,7 +65,7 @@ func FormatFullVersion() string { // ProductToken returns a tag for Telegraf that can be used in user agents. func ProductToken() string { return fmt.Sprintf("Telegraf/%s Go/%s", - Version(), strings.TrimPrefix(runtime.Version(), "go")) + Version, strings.TrimPrefix(runtime.Version(), "go")) } // ReadLines reads contents from a file and splits them by new lines. @@ -82,8 +77,9 @@ func ReadLines(filename string) ([]string, error) { // ReadLines reads contents from file and splits them by new line. // The offset tells at which line number to start. // The count determines the number of lines to read (starting from offset): -// n >= 0: at most n lines -// n < 0: whole file +// +// n >= 0: at most n lines +// n < 0: whole file func ReadLinesOffsetN(filename string, offset uint, n int) ([]string, error) { f, err := os.Open(filename) if err != nil { @@ -244,9 +240,10 @@ func CompressWithGzip(data io.Reader) (io.ReadCloser, error) { // ParseTimestamp parses a Time according to the standard Telegraf options. // These are generally displayed in the toml similar to: -// json_time_key= "timestamp" -// json_time_format = "2006-01-02T15:04:05Z07:00" -// json_timezone = "America/Los_Angeles" +// +// json_time_key= "timestamp" +// json_time_format = "2006-01-02T15:04:05Z07:00" +// json_timezone = "America/Los_Angeles" // // The format can be one of "unix", "unix_ms", "unix_us", "unix_ns", or a Go // time layout suitable for time.Parse. @@ -295,7 +292,8 @@ func parseUnix(format string, timestamp interface{}) (time.Time, error) { // Returns the integers before and after an optional decimal point. Both '.' // and ',' are supported for the decimal point. The timestamp can be an int64, // float64, or string. -// ex: "42.5" -> (42, 5, nil) +// +// ex: "42.5" -> (42, 5, nil) func parseComponents(timestamp interface{}) (int64, int64, error) { switch ts := timestamp.(type) { case string: diff --git a/internal/usage.go b/internal/usage.go deleted file mode 100644 index 65fb378669df7..0000000000000 --- a/internal/usage.go +++ /dev/null @@ -1,66 +0,0 @@ -//go:build !windows -// +build !windows - -package internal - -const Usage = `Telegraf, The plugin-driven server agent for collecting and reporting metrics. - -Usage: - - telegraf [commands|flags] - -The commands & flags are: - - config print out full sample configuration to stdout - version print the version to stdout - - --aggregator-filter filter the aggregators to enable, separator is : - --config configuration file to load - --config-directory directory containing additional *.conf files - --watch-config Telegraf will restart on local config changes. Monitor changes - using either fs notifications or polling. Valid values: 'inotify' or 'poll'. - Monitoring is off by default. - --plugin-directory directory containing *.so files, this directory will be - searched recursively. Any Plugin found will be loaded - and namespaced. - --debug turn on debug logging - --deprecation-list print all deprecated plugins or plugin options. - --input-filter filter the inputs to enable, separator is : - --input-list print available input plugins. - --output-filter filter the outputs to enable, separator is : - --output-list print available output plugins. - --pidfile file to write our pid to - --pprof-addr
pprof address to listen on, don't activate pprof if empty - --processor-filter filter the processors to enable, separator is : - --quiet run in quiet mode - --section-filter filter config sections to output, separator is : - Valid values are 'agent', 'global_tags', 'outputs', - 'processors', 'aggregators' and 'inputs' - --sample-config print out full sample configuration - --once enable once mode: gather metrics once, write them, and exit - --test enable test mode: gather metrics once and print them. - No outputs are executed! - --test-wait wait up to this many seconds for service inputs to complete - in test or once mode. Implies --test if not used with --once. - --usage print usage for a plugin, ie, 'telegraf --usage mysql' - --version display the version and exit - -Examples: - - # generate a telegraf config file: - telegraf config > telegraf.conf - - # generate config with only cpu input & influxdb output plugins defined - telegraf config --input-filter cpu --output-filter influxdb - - # run a single telegraf collection, outputting metrics to stdout - telegraf --config telegraf.conf --test - - # run telegraf with all plugins defined in config file - telegraf --config telegraf.conf - - # run telegraf, enabling the cpu & memory input, and influxdb output plugins - telegraf --config telegraf.conf --input-filter cpu:mem --output-filter influxdb - - # run telegraf with pprof - telegraf --config telegraf.conf --pprof-addr localhost:6060` diff --git a/internal/usage_windows.go b/internal/usage_windows.go deleted file mode 100644 index 511bd5ca49d94..0000000000000 --- a/internal/usage_windows.go +++ /dev/null @@ -1,80 +0,0 @@ -//go:build windows -// +build windows - -package internal - -const Usage = `Telegraf, The plugin-driven server agent for collecting and reporting metrics. - -Usage: - - telegraf [commands|flags] - -The commands & flags are: - - config print out full sample configuration to stdout - version print the version to stdout - - --aggregator-filter filter the aggregators to enable, separator is : - --config configuration file to load - --config-directory directory containing additional *.conf files - --watch-config Telegraf will restart on local config changes. Monitor changes - using either fs notifications or polling. Valid values: 'inotify' or 'poll'. - Monitoring is off by default. - --debug turn on debug logging - --input-filter filter the inputs to enable, separator is : - --input-list print available input plugins. - --output-filter filter the outputs to enable, separator is : - --output-list print available output plugins. - --pidfile file to write our pid to - --pprof-addr
pprof address to listen on, don't activate pprof if empty - --processor-filter filter the processors to enable, separator is : - --quiet run in quiet mode - --sample-config print out full sample configuration - --section-filter filter config sections to output, separator is : - Valid values are 'agent', 'global_tags', 'outputs', - 'processors', 'aggregators' and 'inputs' - --once enable once mode: gather metrics once, write them, and exit - --test enable test mode: gather metrics once and print them - --test-wait wait up to this many seconds for service - inputs to complete in test or once mode - --usage print usage for a plugin, ie, 'telegraf --usage mysql' - --version display the version and exit - - --console run as console application (windows only) - --service operate on the service (windows only) - --service-name service name (windows only) - --service-display-name service display name (windows only) - --service-auto-restart auto restart service on failure (windows only) - --service-restart-delay delay before service auto restart, default is 5m (windows only) - -Examples: - - # generate a telegraf config file: - telegraf config > telegraf.conf - - # generate config with only cpu input & influxdb output plugins defined - telegraf --input-filter cpu --output-filter influxdb config - - # run a single telegraf collection, outputting metrics to stdout - telegraf --config telegraf.conf --test - - # run telegraf with all plugins defined in config file - telegraf --config telegraf.conf - - # run telegraf, enabling the cpu & memory input, and influxdb output plugins - telegraf --config telegraf.conf --input-filter cpu:mem --output-filter influxdb - - # run telegraf with pprof - telegraf --config telegraf.conf --pprof-addr localhost:6060 - - # run telegraf without service controller - telegraf --console install --config "C:\Program Files\Telegraf\telegraf.conf" - - # install telegraf service - telegraf --service install --config "C:\Program Files\Telegraf\telegraf.conf" - - # install telegraf service with custom name - telegraf --service install --service-name=my-telegraf --service-display-name="My Telegraf" - - # install telegraf service with auto restart and restart delay of 3 minutes - telegraf --service install --service-auto-restart --service-restart-delay 3m` diff --git a/plugins/inputs/influxdb/influxdb.go b/plugins/inputs/influxdb/influxdb.go index 4e88e99eec84f..1a0127674deab 100644 --- a/plugins/inputs/influxdb/influxdb.go +++ b/plugins/inputs/influxdb/influxdb.go @@ -19,6 +19,7 @@ import ( ) // DO NOT REMOVE THE NEXT TWO LINES! This is required to embed the sampleConfig data. +// //go:embed sample.conf var sampleConfig string @@ -126,11 +127,13 @@ type memstats struct { // Gathers data from a particular URL // Parameters: -// acc : The telegraf Accumulator to use -// url : endpoint to send request to +// +// acc : The telegraf Accumulator to use +// url : endpoint to send request to // // Returns: -// error: Any error that may have occurred +// +// error: Any error that may have occurred func (i *InfluxDB) gatherURL( acc telegraf.Accumulator, url string, @@ -147,7 +150,7 @@ func (i *InfluxDB) gatherURL( req.SetBasicAuth(i.Username, i.Password) } - req.Header.Set("User-Agent", "Telegraf/"+internal.Version()) + req.Header.Set("User-Agent", "Telegraf/"+internal.Version) resp, err := i.client.Do(req) if err != nil { diff --git a/plugins/inputs/internal/README.md b/plugins/inputs/internal/README.md index 90882d7490a04..042dfd7dd74c3 100644 --- a/plugins/inputs/internal/README.md +++ b/plugins/inputs/internal/README.md @@ -14,7 +14,7 @@ plugin. # collect_memstats = true ``` -## Measurements & Fields +## Metrics memstats are taken from the Go runtime: diff --git a/plugins/inputs/internal/internal.go b/plugins/inputs/internal/internal.go index 664d8b98acf1d..f2c63bff422d8 100644 --- a/plugins/inputs/internal/internal.go +++ b/plugins/inputs/internal/internal.go @@ -13,6 +13,7 @@ import ( ) // DO NOT REMOVE THE NEXT TWO LINES! This is required to embed the sampleConfig data. +// //go:embed sample.conf var sampleConfig string @@ -53,7 +54,7 @@ func (s *Self) Gather(acc telegraf.Accumulator) error { acc.AddFields("internal_memstats", fields, map[string]string{}) } - telegrafVersion := inter.Version() + telegrafVersion := inter.Version goVersion := strings.TrimPrefix(runtime.Version(), "go") for _, m := range selfstat.Metrics() {