github.com/influxdata/telegraf@v1.30.3/cmd/telegraf/main.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "io" 6 "log" 7 "os" 8 "sort" 9 "strings" 10 11 "github.com/awnumar/memguard" 12 "github.com/urfave/cli/v2" 13 14 "github.com/influxdata/telegraf/config" 15 "github.com/influxdata/telegraf/internal" 16 "github.com/influxdata/telegraf/internal/goplugin" 17 "github.com/influxdata/telegraf/logger" 18 _ "github.com/influxdata/telegraf/plugins/aggregators/all" 19 "github.com/influxdata/telegraf/plugins/inputs" 20 _ "github.com/influxdata/telegraf/plugins/inputs/all" 21 "github.com/influxdata/telegraf/plugins/outputs" 22 _ "github.com/influxdata/telegraf/plugins/outputs/all" 23 _ "github.com/influxdata/telegraf/plugins/parsers/all" 24 _ "github.com/influxdata/telegraf/plugins/processors/all" 25 _ "github.com/influxdata/telegraf/plugins/secretstores/all" 26 _ "github.com/influxdata/telegraf/plugins/serializers/all" 27 ) 28 29 type TelegrafConfig interface { 30 CollectDeprecationInfos([]string, []string, []string, []string) map[string][]config.PluginDeprecationInfo 31 PrintDeprecationList([]config.PluginDeprecationInfo) 32 } 33 34 type Filters struct { 35 section []string 36 input []string 37 output []string 38 aggregator []string 39 processor []string 40 secretstore []string 41 } 42 43 func appendFilter(a, b string) string { 44 if a != "" && b != "" { 45 return fmt.Sprintf("%s:%s", a, b) 46 } 47 if a != "" { 48 return a 49 } 50 return b 51 } 52 53 func processFilterFlags(ctx *cli.Context) Filters { 54 var section, input, output, aggregator, processor, secretstore string 55 56 // Support defining filters before and after the command 57 // The old style was: 58 // ./telegraf --section-filter inputs --input-filter cpu config >test.conf 59 // The new style is: 60 // ./telegraf config --section-filter inputs --input-filter cpu >test.conf 61 // To support the old style, check if the parent context has the filter flags defined 62 if len(ctx.Lineage()) >= 2 { 63 parent := ctx.Lineage()[1] // ancestor contexts in order from child to parent 64 section = parent.String("section-filter") 65 input = parent.String("input-filter") 66 output = parent.String("output-filter") 67 aggregator = parent.String("aggregator-filter") 68 processor = parent.String("processor-filter") 69 secretstore = parent.String("secretstore-filter") 70 } 71 72 // If both the parent and command filters are defined, append them together 73 section = appendFilter(section, ctx.String("section-filter")) 74 input = appendFilter(input, ctx.String("input-filter")) 75 output = appendFilter(output, ctx.String("output-filter")) 76 aggregator = appendFilter(aggregator, ctx.String("aggregator-filter")) 77 processor = appendFilter(processor, ctx.String("processor-filter")) 78 secretstore = appendFilter(secretstore, ctx.String("secretstore-filter")) 79 80 sectionFilters := deleteEmpty(strings.Split(section, ":")) 81 inputFilters := deleteEmpty(strings.Split(input, ":")) 82 outputFilters := deleteEmpty(strings.Split(output, ":")) 83 aggregatorFilters := deleteEmpty(strings.Split(aggregator, ":")) 84 processorFilters := deleteEmpty(strings.Split(processor, ":")) 85 secretstoreFilters := deleteEmpty(strings.Split(secretstore, ":")) 86 return Filters{sectionFilters, inputFilters, outputFilters, aggregatorFilters, processorFilters, secretstoreFilters} 87 } 88 89 func deleteEmpty(s []string) []string { 90 var r []string 91 for _, str := range s { 92 if str != "" { 93 r = append(r, str) 94 } 95 } 96 return r 97 } 98 99 // runApp defines all the subcommands and flags for Telegraf 100 // this abstraction is used for testing, so outputBuffer and args can be changed 101 func runApp(args []string, outputBuffer io.Writer, pprof Server, c TelegrafConfig, m App) error { 102 pluginFilterFlags := []cli.Flag{ 103 &cli.StringFlag{ 104 Name: "section-filter", 105 Usage: "filter the sections to print, separator is ':'. " + 106 "Valid values are 'agent', 'global_tags', 'outputs', 'processors', 'aggregators' and 'inputs'", 107 }, 108 &cli.StringFlag{ 109 Name: "input-filter", 110 Usage: "filter the inputs to enable, separator is ':'", 111 }, 112 &cli.StringFlag{ 113 Name: "output-filter", 114 Usage: "filter the outputs to enable, separator is ':'", 115 }, 116 &cli.StringFlag{ 117 Name: "aggregator-filter", 118 Usage: "filter the aggregators to enable, separator is ':'", 119 }, 120 &cli.StringFlag{ 121 Name: "processor-filter", 122 Usage: "filter the processors to enable, separator is ':'", 123 }, 124 &cli.StringFlag{ 125 Name: "secretstore-filter", 126 Usage: "filter the secret-stores to enable, separator is ':'", 127 }, 128 } 129 130 extraFlags := append(pluginFilterFlags, cliFlags()...) 131 132 // This function is used when Telegraf is run with only flags 133 action := func(cCtx *cli.Context) error { 134 // We do not expect any arguments this is likely a misspelling of 135 // a command... 136 if cCtx.NArg() > 0 { 137 return fmt.Errorf("unknown command %q", cCtx.Args().First()) 138 } 139 140 err := logger.SetupLogging(logger.LogConfig{}) 141 if err != nil { 142 return err 143 } 144 145 // Deprecated: Use execd instead 146 // Load external plugins, if requested. 147 if cCtx.String("plugin-directory") != "" { 148 log.Printf("I! Loading external plugins from: %s", cCtx.String("plugin-directory")) 149 if err := goplugin.LoadExternalPlugins(cCtx.String("plugin-directory")); err != nil { 150 return err 151 } 152 } 153 154 // switch for flags which just do something and exit immediately 155 switch { 156 // print available input plugins 157 case cCtx.Bool("deprecation-list"): 158 filters := processFilterFlags(cCtx) 159 infos := c.CollectDeprecationInfos( 160 filters.input, filters.output, filters.aggregator, filters.processor, 161 ) 162 outputBuffer.Write([]byte("Deprecated Input Plugins:\n")) 163 c.PrintDeprecationList(infos["inputs"]) 164 outputBuffer.Write([]byte("Deprecated Output Plugins:\n")) 165 c.PrintDeprecationList(infos["outputs"]) 166 outputBuffer.Write([]byte("Deprecated Processor Plugins:\n")) 167 c.PrintDeprecationList(infos["processors"]) 168 outputBuffer.Write([]byte("Deprecated Aggregator Plugins:\n")) 169 c.PrintDeprecationList(infos["aggregators"]) 170 return nil 171 // print available output plugins 172 case cCtx.Bool("output-list"): 173 outputBuffer.Write([]byte("DEPRECATED: use telegraf plugins outputs\n")) 174 outputBuffer.Write([]byte("Available Output Plugins:\n")) 175 names := make([]string, 0, len(outputs.Outputs)) 176 for k := range outputs.Outputs { 177 names = append(names, k) 178 } 179 sort.Strings(names) 180 for _, k := range names { 181 fmt.Fprintf(outputBuffer, " %s\n", k) 182 } 183 return nil 184 // print available input plugins 185 case cCtx.Bool("input-list"): 186 outputBuffer.Write([]byte("DEPRECATED: use telegraf plugins inputs\n")) 187 outputBuffer.Write([]byte("Available Input Plugins:\n")) 188 names := make([]string, 0, len(inputs.Inputs)) 189 for k := range inputs.Inputs { 190 names = append(names, k) 191 } 192 sort.Strings(names) 193 for _, k := range names { 194 fmt.Fprintf(outputBuffer, " %s\n", k) 195 } 196 return nil 197 // print usage for a plugin, ie, 'telegraf --usage mysql' 198 case cCtx.String("usage") != "": 199 err := PrintInputConfig(cCtx.String("usage"), outputBuffer) 200 err2 := PrintOutputConfig(cCtx.String("usage"), outputBuffer) 201 if err != nil && err2 != nil { 202 return fmt.Errorf("%w and %w", err, err2) 203 } 204 return nil 205 // DEPRECATED 206 case cCtx.Bool("version"): 207 fmt.Fprintf(outputBuffer, "%s\n", internal.FormatFullVersion()) 208 return nil 209 // DEPRECATED 210 case cCtx.Bool("sample-config"): 211 filters := processFilterFlags(cCtx) 212 213 printSampleConfig(outputBuffer, filters) 214 return nil 215 } 216 217 if cCtx.String("pprof-addr") != "" { 218 pprof.Start(cCtx.String("pprof-addr")) 219 } 220 221 filters := processFilterFlags(cCtx) 222 223 g := GlobalFlags{ 224 config: cCtx.StringSlice("config"), 225 configDir: cCtx.StringSlice("config-directory"), 226 testWait: cCtx.Int("test-wait"), 227 watchConfig: cCtx.String("watch-config"), 228 pidFile: cCtx.String("pidfile"), 229 plugindDir: cCtx.String("plugin-directory"), 230 password: cCtx.String("password"), 231 oldEnvBehavior: cCtx.Bool("old-env-behavior"), 232 test: cCtx.Bool("test"), 233 debug: cCtx.Bool("debug"), 234 once: cCtx.Bool("once"), 235 quiet: cCtx.Bool("quiet"), 236 unprotected: cCtx.Bool("unprotected"), 237 } 238 239 w := WindowFlags{ 240 service: cCtx.String("service"), 241 serviceName: cCtx.String("service-name"), 242 serviceDisplayName: cCtx.String("service-display-name"), 243 serviceRestartDelay: cCtx.String("service-restart-delay"), 244 serviceAutoRestart: cCtx.Bool("service-auto-restart"), 245 console: cCtx.Bool("console"), 246 } 247 248 m.Init(pprof.ErrChan(), filters, g, w) 249 return m.Run() 250 } 251 252 commands := append( 253 getConfigCommands(pluginFilterFlags, outputBuffer), 254 getSecretStoreCommands(m)..., 255 ) 256 commands = append(commands, getPluginCommands(outputBuffer)...) 257 258 app := &cli.App{ 259 Name: "Telegraf", 260 Usage: "The plugin-driven server agent for collecting & reporting metrics.", 261 Writer: outputBuffer, 262 Flags: append( 263 []cli.Flag{ 264 // String slice flags 265 &cli.StringSliceFlag{ 266 Name: "config", 267 Usage: "configuration file to load", 268 }, 269 &cli.StringSliceFlag{ 270 Name: "config-directory", 271 Usage: "directory containing additional *.conf files", 272 }, 273 // Int flags 274 &cli.IntFlag{ 275 Name: "test-wait", 276 Usage: "wait up to this many seconds for service inputs to complete in test mode", 277 }, 278 // 279 // String flags 280 &cli.StringFlag{ 281 Name: "usage", 282 Usage: "print usage for a plugin, ie, 'telegraf --usage mysql'", 283 }, 284 &cli.StringFlag{ 285 Name: "pprof-addr", 286 Usage: "pprof host/IP and port to listen on (e.g. 'localhost:6060')", 287 }, 288 &cli.StringFlag{ 289 Name: "watch-config", 290 Usage: "monitoring config changes [notify, poll] of --config and --config-directory options", 291 }, 292 &cli.StringFlag{ 293 Name: "pidfile", 294 Usage: "file to write our pid to", 295 }, 296 &cli.StringFlag{ 297 Name: "password", 298 Usage: "password to unlock secret-stores", 299 }, 300 // 301 // Bool flags 302 &cli.BoolFlag{ 303 Name: "old-env-behavior", 304 Usage: "switch back to pre v1.27 environment replacement behavior", 305 }, 306 &cli.BoolFlag{ 307 Name: "once", 308 Usage: "run one gather and exit", 309 }, 310 &cli.BoolFlag{ 311 Name: "debug", 312 Usage: "turn on debug logging", 313 }, 314 &cli.BoolFlag{ 315 Name: "quiet", 316 Usage: "run in quiet mode", 317 }, 318 &cli.BoolFlag{ 319 Name: "unprotected", 320 Usage: "do not protect secrets in memory", 321 }, 322 &cli.BoolFlag{ 323 Name: "test", 324 Usage: "enable test mode: gather metrics, print them out, and exit. " + 325 "Note: Test mode only runs inputs, not processors, aggregators, or outputs", 326 }, 327 // TODO: Change "deprecation-list, input-list, output-list" flags to become a subcommand "list" that takes 328 // "input,output,aggregator,processor, deprecated" as parameters 329 &cli.BoolFlag{ 330 Name: "deprecation-list", 331 Usage: "print all deprecated plugins or plugin options", 332 }, 333 &cli.BoolFlag{ 334 Name: "input-list", 335 Usage: "print available input plugins", 336 }, 337 &cli.BoolFlag{ 338 Name: "output-list", 339 Usage: "print available output plugins", 340 }, 341 // 342 // !!! The following flags are DEPRECATED !!! 343 // Already covered with the subcommand `./telegraf version` 344 &cli.BoolFlag{ 345 Name: "version", 346 Usage: "DEPRECATED: display the version and exit", 347 }, 348 // Already covered with the subcommand `./telegraf config` 349 &cli.BoolFlag{ 350 Name: "sample-config", 351 Usage: "DEPRECATED: print out full sample configuration", 352 }, 353 // Using execd plugin to add external plugins is preferred (less size impact, easier for end user) 354 &cli.StringFlag{ 355 Name: "plugin-directory", 356 Usage: "DEPRECATED: path to directory containing external plugins", 357 }, 358 // !!! 359 }, extraFlags...), 360 Action: action, 361 Commands: append([]*cli.Command{ 362 { 363 Name: "version", 364 Usage: "print current version to stdout", 365 Action: func(*cli.Context) error { 366 fmt.Fprintf(outputBuffer, "%s\n", internal.FormatFullVersion()) 367 return nil 368 }, 369 }, 370 }, commands...), 371 } 372 373 // Make sure we safely erase secrets 374 defer memguard.Purge() 375 376 if err := app.Run(args); err != nil { 377 log.Printf("E! %s", err) 378 return err 379 } 380 return nil 381 } 382 383 func main() { 384 // #13481: disables gh:99designs/keyring kwallet.go from connecting to dbus 385 os.Setenv("DISABLE_KWALLET", "1") 386 387 agent := Telegraf{} 388 pprof := NewPprofServer() 389 c := config.NewConfig() 390 if err := runApp(os.Args, os.Stdout, pprof, c, &agent); err != nil { 391 os.Exit(1) 392 } 393 }