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 /// Set the logging format
141 #[arg(long, default_value = "text", env = "VECTOR_LOG_FORMAT")]
142 pub log_format: LogFormat,
143
144 /// Control when ANSI terminal formatting is used.
145 ///
146 /// By default `vector` will try and detect if `stdout` is a terminal, if it is
147 /// ANSI will be enabled. Otherwise it will be disabled. By providing this flag with
148 /// the `--color always` option will always enable ANSI terminal formatting. `--color never`
149 /// will disable all ANSI terminal formatting. `--color auto` will attempt
150 /// to detect it automatically.
151 #[arg(long, default_value = "auto", env = "VECTOR_COLOR")]
152 pub color: Color,
153
154 /// Watch for changes in configuration file, and reload accordingly.
155 #[arg(short, long, env = "VECTOR_WATCH_CONFIG")]
156 pub watch_config: bool,
157
158 /// Method for configuration watching.
159 ///
160 /// By default, `vector` uses recommended watcher for host OS
161 /// - `inotify` for Linux-based systems.
162 /// - `kqueue` for unix/macos
163 /// - `ReadDirectoryChangesWatcher` for windows
164 ///
165 /// The `poll` watcher can be used in cases where `inotify` doesn't work, e.g., when attaching the configuration via NFS.
166 #[arg(
167 long,
168 default_value = "recommended",
169 env = "VECTOR_WATCH_CONFIG_METHOD"
170 )]
171 pub watch_config_method: WatchConfigMethod,
172
173 /// Poll for changes in the configuration file at the given interval.
174 ///
175 /// This setting is only applicable if `Poll` is set in `--watch-config-method`.
176 #[arg(
177 long,
178 env = "VECTOR_WATCH_CONFIG_POLL_INTERVAL_SECONDS",
179 default_value = "30"
180 )]
181 pub watch_config_poll_interval_seconds: NonZeroU64,
182
183 /// Set the internal log rate limit in seconds.
184 ///
185 /// This controls the time window for rate limiting Vector's own internal logs.
186 /// Within each time window, the first occurrence of a log is emitted, the second
187 /// shows a suppression warning, and subsequent occurrences are silent until the
188 /// window expires.
189 ///
190 /// Logs are grouped by their location in the code and the `component_id` field, so logs
191 /// from different components are rate limited independently.
192 ///
193 /// Examples:
194 /// - 1: Very verbose, logs can repeat every second
195 /// - 10 (default): Logs can repeat every 10 seconds
196 /// - 60: Less verbose, logs can repeat every minute
197 #[arg(
198 short,
199 long,
200 env = "VECTOR_INTERNAL_LOG_RATE_LIMIT",
201 default_value = "10"
202 )]
203 pub internal_log_rate_limit: u64,
204
205 /// Set the duration in seconds to wait for graceful shutdown after SIGINT or SIGTERM are
206 /// received. After the duration has passed, Vector will force shutdown. To never force
207 /// shutdown, use `--no-graceful-shutdown-limit`.
208 #[arg(
209 long,
210 default_value = "60",
211 env = "VECTOR_GRACEFUL_SHUTDOWN_LIMIT_SECS",
212 group = "graceful-shutdown-limit"
213 )]
214 pub graceful_shutdown_limit_secs: NonZeroU64,
215
216 /// Never time out while waiting for graceful shutdown after SIGINT or SIGTERM received.
217 /// This is useful when you would like for Vector to attempt to send data until terminated
218 /// by a SIGKILL. Overrides/cannot be set with `--graceful-shutdown-limit-secs`.
219 #[arg(
220 long,
221 default_value = "false",
222 env = "VECTOR_NO_GRACEFUL_SHUTDOWN_LIMIT",
223 group = "graceful-shutdown-limit"
224 )]
225 pub no_graceful_shutdown_limit: bool,
226
227 /// Set runtime allocation tracing
228 #[cfg(feature = "allocation-tracing")]
229 #[arg(long, env = "ALLOCATION_TRACING", default_value = "false")]
230 pub allocation_tracing: bool,
231
232 /// Set allocation tracing reporting rate in milliseconds.
233 #[cfg(feature = "allocation-tracing")]
234 #[arg(
235 long,
236 env = "ALLOCATION_TRACING_REPORTING_INTERVAL_MS",
237 default_value = "5000"
238 )]
239 pub allocation_tracing_reporting_interval_ms: u64,
240
241 /// Disable probing and configuration of root certificate locations on the system for OpenSSL.
242 ///
243 /// The probe functionality manipulates the `SSL_CERT_FILE` and `SSL_CERT_DIR` environment variables
244 /// in the Vector process. This behavior can be problematic for users of the `exec` source, which by
245 /// default inherits the environment of the Vector process.
246 #[arg(long, env = "VECTOR_OPENSSL_NO_PROBE", default_value = "false")]
247 pub openssl_no_probe: bool,
248
249 /// Allow the configuration to run without any components. This is useful for loading in an
250 /// empty stub config that will later be replaced with actual components. Note that this is
251 /// likely not useful without also watching for config file changes as described in
252 /// `--watch-config`.
253 #[arg(long, env = "VECTOR_ALLOW_EMPTY_CONFIG", default_value = "false")]
254 pub allow_empty_config: bool,
255}
256
257impl RootOpts {
258 /// Return a list of config paths with the associated formats.
259 pub fn config_paths_with_formats(&self) -> Vec<config::ConfigPath> {
260 config::merge_path_lists(vec![
261 (&self.config_paths, None),
262 (&self.config_paths_toml, Some(config::Format::Toml)),
263 (&self.config_paths_json, Some(config::Format::Json)),
264 (&self.config_paths_yaml, Some(config::Format::Yaml)),
265 ])
266 .map(|(path, hint)| config::ConfigPath::File(path, hint))
267 .chain(
268 self.config_dirs
269 .iter()
270 .map(|dir| config::ConfigPath::Dir(dir.to_path_buf())),
271 )
272 .collect()
273 }
274
275 pub fn init_global(&self) {
276 if !self.openssl_no_probe {
277 unsafe {
278 openssl_probe::init_openssl_env_vars();
279 }
280 }
281
282 crate::metrics::init_global().expect("metrics initialization failed");
283 }
284}
285
286#[derive(Parser, Debug)]
287#[command(rename_all = "kebab-case")]
288pub enum SubCommand {
289 /// Validate the target config, then exit.
290 Validate(validate::Opts),
291
292 /// Convert a config file from one format to another.
293 /// This command can also walk directories recursively and convert all config files that are discovered.
294 /// Note that this is a best effort conversion due to the following reasons:
295 /// * The comments from the original config file are not preserved.
296 /// * Explicitly set default values in the original implementation might be omitted.
297 /// * Depending on how each source/sink config struct configures serde, there might be entries with null values.
298 ConvertConfig(convert_config::Opts),
299
300 /// Generate a Vector configuration containing a list of components.
301 Generate(generate::Opts),
302
303 /// Generate the configuration schema for this version of Vector. (experimental)
304 ///
305 /// A JSON Schema document will be generated that represents the valid schema for a
306 /// Vector configuration. This schema is based on the "full" configuration, such that for usages
307 /// where a configuration is split into multiple files, the schema would apply to those files
308 /// only when concatenated together.
309 ///
310 /// By default all output is writen to stdout. The `output_path` option can be used to redirect to a file.
311 GenerateSchema(generate_schema::Opts),
312
313 /// Output a provided Vector configuration file/dir as a single JSON object, useful for checking in to version control.
314 #[command(hide = true)]
315 Config(config::Opts),
316
317 /// List available components, then exit.
318 List(list::Opts),
319
320 /// Run Vector config unit tests, then exit. This command is experimental and therefore subject to change.
321 /// For guidance on how to write unit tests check out <https://vector.dev/guides/level-up/unit-testing/>.
322 Test(unit_test::Opts),
323
324 /// Output the topology as visual representation using the DOT language which can be rendered by GraphViz
325 Graph(graph::Opts),
326
327 /// Display topology and metrics in the console, for a local or remote Vector instance
328 #[cfg(feature = "top")]
329 Top(top::Opts),
330
331 /// Observe output log events from source or transform components. Logs are sampled at a specified interval.
332 #[cfg(feature = "api-client")]
333 Tap(tap::Opts),
334
335 /// Manage the vector service.
336 #[cfg(windows)]
337 Service(service::Opts),
338
339 /// Vector Remap Language CLI
340 Vrl(vrl::cli::Opts),
341}
342
343impl SubCommand {
344 pub async fn execute(
345 &self,
346 mut signals: signal::SignalPair,
347 color: bool,
348 ) -> exitcode::ExitCode {
349 match self {
350 Self::Config(c) => config::cmd(c),
351 Self::ConvertConfig(opts) => convert_config::cmd(opts),
352 Self::Generate(g) => generate::cmd(g),
353 Self::GenerateSchema(opts) => generate_schema::cmd(opts),
354 Self::Graph(g) => graph::cmd(g),
355 Self::List(l) => list::cmd(l),
356 #[cfg(windows)]
357 Self::Service(s) => service::cmd(s),
358 #[cfg(feature = "api-client")]
359 Self::Tap(t) => tap::cmd(t, signals.receiver).await,
360 Self::Test(t) => unit_test::cmd(t, &mut signals.handler).await,
361 #[cfg(feature = "top")]
362 Self::Top(t) => top::cmd(t).await,
363 Self::Validate(v) => validate::validate(v, color).await,
364 Self::Vrl(s) => {
365 let mut functions = vrl::stdlib::all();
366 functions.extend(vector_vrl_functions::all());
367 vrl::cli::cmd::cmd(s, functions)
368 }
369 }
370 }
371}
372
373#[derive(clap::ValueEnum, Debug, Clone, Copy, PartialEq, Eq)]
374pub enum Color {
375 Auto,
376 Always,
377 Never,
378}
379
380impl Color {
381 pub fn use_color(&self) -> bool {
382 match self {
383 #[cfg(unix)]
384 Color::Auto => {
385 use std::io::IsTerminal;
386 std::io::stdout().is_terminal()
387 }
388 #[cfg(windows)]
389 Color::Auto => false, // ANSI colors are not supported by cmd.exe
390 Color::Always => true,
391 Color::Never => false,
392 }
393 }
394}
395
396#[derive(clap::ValueEnum, Debug, Clone, Copy, PartialEq, Eq)]
397pub enum LogFormat {
398 Text,
399 Json,
400}
401
402#[derive(clap::ValueEnum, Debug, Clone, Copy, PartialEq, Eq)]
403pub enum WatchConfigMethod {
404 /// Recommended watcher for the current OS, usually `inotify` for Linux-based systems.
405 Recommended,
406 /// Poll-based watcher, typically used for watching files on EFS/NFS-like network storage systems.
407 /// The interval is determined by [`RootOpts::watch_config_poll_interval_seconds`].
408 Poll,
409}
410
411pub fn handle_config_errors(errors: Vec<String>) -> exitcode::ExitCode {
412 for error in errors {
413 error!(message = "Configuration error.", %error, internal_log_rate_limit = false);
414 }
415
416 exitcode::CONFIG
417}