vector/cli.rs
1#![allow(missing_docs)]
2
3use std::{num::NonZeroU64, path::PathBuf};
4
5use clap::{ArgAction, CommandFactory, FromArgMatches, Parser};
6
7#[cfg(windows)]
8use crate::service;
9#[cfg(feature = "api-client")]
10use crate::tap;
11#[cfg(feature = "api-client")]
12use crate::top;
13use crate::{
14 config, convert_config, generate, generate_schema, get_version, graph, list, signal, unit_test,
15 validate,
16};
17
18#[derive(Parser, Debug)]
19#[command(rename_all = "kebab-case")]
20pub struct Opts {
21 #[command(flatten)]
22 pub root: RootOpts,
23
24 #[command(subcommand)]
25 pub sub_command: Option<SubCommand>,
26}
27
28impl Opts {
29 pub fn get_matches() -> Result<Self, clap::Error> {
30 let version = get_version();
31 let app = Opts::command().version(version);
32 Opts::from_arg_matches(&app.get_matches())
33 }
34
35 pub const fn log_level(&self) -> &'static str {
36 let (quiet_level, verbose_level) = match self.sub_command {
37 Some(SubCommand::Validate(_))
38 | Some(SubCommand::Graph(_))
39 | Some(SubCommand::Generate(_))
40 | Some(SubCommand::ConvertConfig(_))
41 | Some(SubCommand::List(_))
42 | Some(SubCommand::Test(_)) => {
43 if self.root.verbose == 0 {
44 (self.root.quiet + 1, self.root.verbose)
45 } else {
46 (self.root.quiet, self.root.verbose - 1)
47 }
48 }
49 _ => (self.root.quiet, self.root.verbose),
50 };
51 match quiet_level {
52 0 => match verbose_level {
53 0 => "info",
54 1 => "debug",
55 2..=255 => "trace",
56 },
57 1 => "warn",
58 2 => "error",
59 3..=255 => "off",
60 }
61 }
62}
63
64#[derive(Parser, Debug)]
65#[command(rename_all = "kebab-case")]
66pub struct RootOpts {
67 /// Read configuration from one or more files. Wildcard paths are supported.
68 /// File format is detected from the file name.
69 /// If zero files are specified, the deprecated default config path
70 /// `/etc/vector/vector.yaml` is targeted.
71 #[arg(
72 id = "config",
73 short,
74 long,
75 env = "VECTOR_CONFIG",
76 value_delimiter(',')
77 )]
78 pub config_paths: Vec<PathBuf>,
79
80 /// Read configuration from files in one or more directories.
81 /// File format is detected from the file name.
82 ///
83 /// Files not ending in .toml, .json, .yaml, or .yml will be ignored.
84 #[arg(
85 id = "config-dir",
86 short = 'C',
87 long,
88 env = "VECTOR_CONFIG_DIR",
89 value_delimiter(',')
90 )]
91 pub config_dirs: Vec<PathBuf>,
92
93 /// Read configuration from one or more files. Wildcard paths are supported.
94 /// TOML file format is expected.
95 #[arg(
96 id = "config-toml",
97 long,
98 env = "VECTOR_CONFIG_TOML",
99 value_delimiter(',')
100 )]
101 pub config_paths_toml: Vec<PathBuf>,
102
103 /// Read configuration from one or more files. Wildcard paths are supported.
104 /// JSON file format is expected.
105 #[arg(
106 id = "config-json",
107 long,
108 env = "VECTOR_CONFIG_JSON",
109 value_delimiter(',')
110 )]
111 pub config_paths_json: Vec<PathBuf>,
112
113 /// Read configuration from one or more files. Wildcard paths are supported.
114 /// YAML file format is expected.
115 #[arg(
116 id = "config-yaml",
117 long,
118 env = "VECTOR_CONFIG_YAML",
119 value_delimiter(',')
120 )]
121 pub config_paths_yaml: Vec<PathBuf>,
122
123 /// Exit on startup if any sinks fail healthchecks
124 #[arg(short, long, env = "VECTOR_REQUIRE_HEALTHY")]
125 pub require_healthy: Option<bool>,
126
127 /// Number of threads to use for processing (default is number of available cores)
128 #[arg(short, long, env = "VECTOR_THREADS")]
129 pub threads: Option<usize>,
130
131 /// Enable more detailed internal logging. Repeat to increase level. Overridden by `--quiet`.
132 #[arg(short, long, action = ArgAction::Count)]
133 pub verbose: u8,
134
135 /// Reduce detail of internal logging. Repeat to reduce further. Overrides `--verbose`.
136 #[arg(short, long, action = ArgAction::Count)]
137 pub quiet: u8,
138
139 /// Set the logging format
140 #[arg(long, default_value = "text", env = "VECTOR_LOG_FORMAT")]
141 pub log_format: LogFormat,
142
143 /// Control when ANSI terminal formatting is used.
144 ///
145 /// By default `vector` will try and detect if `stdout` is a terminal, if it is
146 /// ANSI will be enabled. Otherwise it will be disabled. By providing this flag with
147 /// the `--color always` option will always enable ANSI terminal formatting. `--color never`
148 /// will disable all ANSI terminal formatting. `--color auto` will attempt
149 /// to detect it automatically.
150 #[arg(long, default_value = "auto", env = "VECTOR_COLOR")]
151 pub color: Color,
152
153 /// Watch for changes in configuration file, and reload accordingly.
154 #[arg(short, long, env = "VECTOR_WATCH_CONFIG")]
155 pub watch_config: bool,
156
157 /// Method for configuration watching.
158 ///
159 /// By default, `vector` uses recommended watcher for host OS
160 /// - `inotify` for Linux-based systems.
161 /// - `kqueue` for unix/macos
162 /// - `ReadDirectoryChangesWatcher` for windows
163 ///
164 /// The `poll` watcher can be used in cases where `inotify` doesn't work, e.g., when attaching the configuration via NFS.
165 #[arg(
166 long,
167 default_value = "recommended",
168 env = "VECTOR_WATCH_CONFIG_METHOD"
169 )]
170 pub watch_config_method: WatchConfigMethod,
171
172 /// Poll for changes in the configuration file at the given interval.
173 ///
174 /// This setting is only applicable if `Poll` is set in `--watch-config-method`.
175 #[arg(
176 long,
177 env = "VECTOR_WATCH_CONFIG_POLL_INTERVAL_SECONDS",
178 default_value = "30"
179 )]
180 pub watch_config_poll_interval_seconds: NonZeroU64,
181
182 /// Set the internal log rate limit in seconds.
183 ///
184 /// This controls the time window for rate limiting Vector's own internal logs.
185 /// Within each time window, the first occurrence of a log is emitted, the second
186 /// shows a suppression warning, and subsequent occurrences are silent until the
187 /// window expires.
188 ///
189 /// Logs are grouped by their location in the code and contextual fields so different log instances can be rate
190 /// limited independently.
191 ///
192 /// Examples:
193 /// - 1: Very verbose, logs can repeat every second
194 /// - 10 (default): Logs can repeat every 10 seconds
195 /// - 60: Less verbose, logs can repeat every minute
196 #[arg(
197 short,
198 long,
199 env = "VECTOR_INTERNAL_LOG_RATE_LIMIT",
200 default_value = "10"
201 )]
202 pub internal_log_rate_limit: u64,
203
204 /// Set the duration in seconds to wait for graceful shutdown after SIGINT or SIGTERM are
205 /// received. After the duration has passed, Vector will force shutdown. To never force
206 /// shutdown, use `--no-graceful-shutdown-limit`.
207 #[arg(
208 long,
209 default_value = "60",
210 env = "VECTOR_GRACEFUL_SHUTDOWN_LIMIT_SECS",
211 group = "graceful-shutdown-limit"
212 )]
213 pub graceful_shutdown_limit_secs: NonZeroU64,
214
215 /// Never time out while waiting for graceful shutdown after SIGINT or SIGTERM received.
216 /// This is useful when you would like for Vector to attempt to send data until terminated
217 /// by a SIGKILL. Overrides/cannot be set with `--graceful-shutdown-limit-secs`.
218 #[arg(
219 long,
220 default_value = "false",
221 env = "VECTOR_NO_GRACEFUL_SHUTDOWN_LIMIT",
222 group = "graceful-shutdown-limit"
223 )]
224 pub no_graceful_shutdown_limit: bool,
225
226 /// Set runtime allocation tracing
227 #[cfg(feature = "allocation-tracing")]
228 #[arg(long, env = "ALLOCATION_TRACING", default_value = "false")]
229 pub allocation_tracing: bool,
230
231 /// Set allocation tracing reporting rate in milliseconds.
232 #[cfg(feature = "allocation-tracing")]
233 #[arg(
234 long,
235 env = "ALLOCATION_TRACING_REPORTING_INTERVAL_MS",
236 default_value = "5000"
237 )]
238 pub allocation_tracing_reporting_interval_ms: u64,
239
240 /// Disable probing and configuration of root certificate locations on the system for OpenSSL.
241 ///
242 /// The probe functionality manipulates the `SSL_CERT_FILE` and `SSL_CERT_DIR` environment variables
243 /// in the Vector process. This behavior can be problematic for users of the `exec` source, which by
244 /// default inherits the environment of the Vector process.
245 #[arg(long, env = "VECTOR_OPENSSL_NO_PROBE", default_value = "false")]
246 pub openssl_no_probe: bool,
247
248 /// Allow the configuration to run without any components. This is useful for loading in an
249 /// empty stub config that will later be replaced with actual components. Note that this is
250 /// likely not useful without also watching for config file changes as described in
251 /// `--watch-config`.
252 #[arg(long, env = "VECTOR_ALLOW_EMPTY_CONFIG", default_value = "false")]
253 pub allow_empty_config: bool,
254}
255
256impl RootOpts {
257 /// Return a list of config paths with the associated formats.
258 pub fn config_paths_with_formats(&self) -> Vec<config::ConfigPath> {
259 config::merge_path_lists(vec![
260 (&self.config_paths, None),
261 (&self.config_paths_toml, Some(config::Format::Toml)),
262 (&self.config_paths_json, Some(config::Format::Json)),
263 (&self.config_paths_yaml, Some(config::Format::Yaml)),
264 ])
265 .map(|(path, hint)| config::ConfigPath::File(path, hint))
266 .chain(
267 self.config_dirs
268 .iter()
269 .map(|dir| config::ConfigPath::Dir(dir.to_path_buf())),
270 )
271 .collect()
272 }
273
274 pub fn init_global(&self) {
275 if !self.openssl_no_probe {
276 unsafe {
277 openssl_probe::init_openssl_env_vars();
278 }
279 }
280
281 crate::metrics::init_global().expect("metrics initialization failed");
282 }
283}
284
285#[derive(Parser, Debug)]
286#[command(rename_all = "kebab-case")]
287pub enum SubCommand {
288 /// Validate the target config, then exit.
289 Validate(validate::Opts),
290
291 /// Convert a config file from one format to another.
292 /// This command can also walk directories recursively and convert all config files that are discovered.
293 /// Note that this is a best effort conversion due to the following reasons:
294 /// * The comments from the original config file are not preserved.
295 /// * Explicitly set default values in the original implementation might be omitted.
296 /// * Depending on how each source/sink config struct configures serde, there might be entries with null values.
297 ConvertConfig(convert_config::Opts),
298
299 /// Generate a Vector configuration containing a list of components.
300 Generate(generate::Opts),
301
302 /// Generate the configuration schema for this version of Vector. (experimental)
303 ///
304 /// A JSON Schema document will be generated that represents the valid schema for a
305 /// Vector configuration. This schema is based on the "full" configuration, such that for usages
306 /// where a configuration is split into multiple files, the schema would apply to those files
307 /// only when concatenated together.
308 ///
309 /// By default all output is writen to stdout. The `output_path` option can be used to redirect to a file.
310 GenerateSchema(generate_schema::Opts),
311
312 /// Output a provided Vector configuration file/dir as a single JSON object, useful for checking in to version control.
313 #[command(hide = true)]
314 Config(config::Opts),
315
316 /// List available components, then exit.
317 List(list::Opts),
318
319 /// Run Vector config unit tests, then exit. This command is experimental and therefore subject to change.
320 /// For guidance on how to write unit tests check out <https://vector.dev/guides/level-up/unit-testing/>.
321 Test(unit_test::Opts),
322
323 /// Output the topology as visual representation using the DOT language which can be rendered by GraphViz
324 Graph(graph::Opts),
325
326 /// Display topology and metrics in the console, for a local or remote Vector instance
327 #[cfg(feature = "api-client")]
328 Top(top::Opts),
329
330 /// Observe output log events from source or transform components. Logs are sampled at a specified interval.
331 #[cfg(feature = "api-client")]
332 Tap(tap::Opts),
333
334 /// Manage the vector service.
335 #[cfg(windows)]
336 Service(service::Opts),
337
338 /// Vector Remap Language CLI
339 Vrl(vrl::cli::Opts),
340}
341
342impl SubCommand {
343 pub async fn execute(
344 &self,
345 mut signals: signal::SignalPair,
346 color: bool,
347 ) -> exitcode::ExitCode {
348 match self {
349 Self::Config(c) => config::cmd(c),
350 Self::ConvertConfig(opts) => convert_config::cmd(opts),
351 Self::Generate(g) => generate::cmd(g),
352 Self::GenerateSchema(opts) => generate_schema::cmd(opts),
353 Self::Graph(g) => graph::cmd(g),
354 Self::List(l) => list::cmd(l),
355 #[cfg(windows)]
356 Self::Service(s) => service::cmd(s),
357 #[cfg(feature = "api-client")]
358 Self::Tap(t) => tap::cmd(t, signals.receiver).await,
359 Self::Test(t) => unit_test::cmd(t, &mut signals.handler).await,
360 #[cfg(feature = "api-client")]
361 Self::Top(t) => top::cmd(t).await,
362 Self::Validate(v) => validate::validate(v, color).await,
363 Self::Vrl(s) => {
364 let mut functions = vrl::stdlib::all();
365 functions.extend(vector_vrl_functions::all());
366 vrl::cli::cmd::cmd(s, functions)
367 }
368 }
369 }
370}
371
372#[derive(clap::ValueEnum, Debug, Clone, Copy, PartialEq, Eq)]
373pub enum Color {
374 Auto,
375 Always,
376 Never,
377}
378
379impl Color {
380 pub fn use_color(&self) -> bool {
381 match self {
382 #[cfg(unix)]
383 Color::Auto => {
384 use std::io::IsTerminal;
385 std::io::stdout().is_terminal()
386 }
387 #[cfg(windows)]
388 Color::Auto => false, // ANSI colors are not supported by cmd.exe
389 Color::Always => true,
390 Color::Never => false,
391 }
392 }
393}
394
395#[derive(clap::ValueEnum, Debug, Clone, Copy, PartialEq, Eq)]
396pub enum LogFormat {
397 Text,
398 Json,
399}
400
401#[derive(clap::ValueEnum, Debug, Clone, Copy, PartialEq, Eq)]
402pub enum WatchConfigMethod {
403 /// Recommended watcher for the current OS, usually `inotify` for Linux-based systems.
404 Recommended,
405 /// Poll-based watcher, typically used for watching files on EFS/NFS-like network storage systems.
406 /// The interval is determined by [`RootOpts::watch_config_poll_interval_seconds`].
407 Poll,
408}
409
410pub fn handle_config_errors(errors: Vec<String>) -> exitcode::ExitCode {
411 for error in errors {
412 error!(message = "Configuration error.", %error);
413 }
414
415 exitcode::CONFIG
416}