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}