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