feat: Migrate to urfave/cli (#11700)

This commit is contained in:
Sebastian Spaink 2022-08-24 21:46:58 -05:00 committed by GitHub
parent 7feb2722a7
commit a57434eb4e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 1190 additions and 639 deletions

View File

@ -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

View File

@ -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.

355
cmd/telegraf/main.go Normal file
View File

@ -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)
}
}

463
cmd/telegraf/main_test.go Normal file
View File

@ -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)
}

View File

@ -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)
}

45
cmd/telegraf/pprof.go Normal file
View File

@ -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
}

View File

@ -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
}

View File

@ -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
func (i *sliceFlags) String() string {
s := strings.Join(*i, " ")
return "[" + s + "]"
}
func (i *sliceFlags) Set(value string) error {
*i = append(*i, value)
return nil
}
// 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")
var stop chan struct{}
func reloadLoop(
inputFilters []string,
outputFilters []string,
) {
type GlobalFlags struct {
config []string
configDir []string
testWait int
watchConfig string
pidFile string
plugindDir string
test bool
debug bool
once bool
quiet bool
}
type WindowFlags struct {
service string
serviceName string
serviceDisplayName string
serviceRestartDelay string
serviceAutoRestart bool
console bool
}
type App interface {
Init(<-chan error, Filters, GlobalFlags, WindowFlags)
Run() error
}
type Telegraf struct {
pprofErr <-chan error
inputFilters []string
outputFilters []string
GlobalFlags
WindowFlags
}
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,
)
}

View File

@ -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{}
}

View File

@ -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) {
// Register the eventlog logging target for windows.
logger.RegisterEventLogger(*fServiceName)
if runtime.GOOS == "windows" && windowsRunAsService() {
runAsWindowsService(
inputFilters,
outputFilters,
)
} else {
stop = make(chan struct{})
reloadLoop(
inputFilters,
outputFilters,
)
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.
err := logger.RegisterEventLogger(t.serviceName)
if err != nil {
return err
}
if !t.windowsRunAsService() {
stop = make(chan struct{})
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
}

View File

@ -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"
}

View File

@ -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 {

View File

@ -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)

13
go.mod
View File

@ -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

View File

@ -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:

View File

@ -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> filter the aggregators to enable, separator is :
--config <file> configuration file to load
--config-directory <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> filter the inputs to enable, separator is :
--input-list print available input plugins.
--output-filter <filter> filter the outputs to enable, separator is :
--output-list print available output plugins.
--pidfile <file> file to write our pid to
--pprof-addr <address> pprof address to listen on, don't activate pprof if empty
--processor-filter <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 <plugin> 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`

View File

@ -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> filter the aggregators to enable, separator is :
--config <file> configuration file to load
--config-directory <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> filter the inputs to enable, separator is :
--input-list print available input plugins.
--output-filter <filter> filter the outputs to enable, separator is :
--output-list print available output plugins.
--pidfile <file> file to write our pid to
--pprof-addr <address> pprof address to listen on, don't activate pprof if empty
--processor-filter <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 <plugin> print usage for a plugin, ie, 'telegraf --usage mysql'
--version display the version and exit
--console run as console application (windows only)
--service <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`

View File

@ -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 {

View File

@ -14,7 +14,7 @@ plugin.
# collect_memstats = true
```
## Measurements & Fields
## Metrics
memstats are taken from the Go runtime:
<https://golang.org/pkg/runtime/#MemStats>

View File

@ -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() {