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