From d082ded7ad526b7295eb574f086a950f6ff92830 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Lecrenier?= Date: Tue, 3 Jan 2023 11:55:34 +0100 Subject: [PATCH] Explicitly restrict log level options to those that are documented Fixes https://github.com/meilisearch/meilisearch/issues/3292 --- config.toml | 2 +- .../src/analytics/segment_analytics.rs | 2 +- meilisearch/src/main.rs | 5 +- meilisearch/src/option.rs | 70 ++++++++++++++++--- 4 files changed, 65 insertions(+), 14 deletions(-) diff --git a/config.toml b/config.toml index cfc95a54b..6f60c8774 100644 --- a/config.toml +++ b/config.toml @@ -28,7 +28,7 @@ http_payload_size_limit = "100 MB" log_level = "INFO" # Defines how much detail should be present in Meilisearch's logs. -# Meilisearch currently supports five log levels, listed in order of increasing verbosity: `ERROR`, `WARN`, `INFO`, `DEBUG`, `TRACE` +# Meilisearch currently supports six log levels, listed in order of increasing verbosity: `OFF`, `ERROR`, `WARN`, `INFO`, `DEBUG`, `TRACE` # https://docs.meilisearch.com/learn/configuration/instance_options.html#log-level max_index_size = "100 GiB" diff --git a/meilisearch/src/analytics/segment_analytics.rs b/meilisearch/src/analytics/segment_analytics.rs index e1540c093..b1fd06dc1 100644 --- a/meilisearch/src/analytics/segment_analytics.rs +++ b/meilisearch/src/analytics/segment_analytics.rs @@ -306,7 +306,7 @@ impl From for Infos { max_index_size, max_task_db_size, http_payload_size_limit, - log_level, + log_level: log_level.to_string(), max_indexing_memory, max_indexing_threads, with_configuration_file: config_file_path.is_some(), diff --git a/meilisearch/src/main.rs b/meilisearch/src/main.rs index e3309dde8..8438e1c41 100644 --- a/meilisearch/src/main.rs +++ b/meilisearch/src/main.rs @@ -7,6 +7,7 @@ use actix_web::web::Data; use actix_web::HttpServer; use index_scheduler::IndexScheduler; use meilisearch::analytics::Analytics; +use meilisearch::option::LogLevel; use meilisearch::{analytics, create_app, setup_meilisearch, Opt}; use meilisearch_auth::{generate_master_key, AuthController, MASTER_KEY_MIN_SIZE}; @@ -16,8 +17,8 @@ static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc; /// does all the setup before meilisearch is launched fn setup(opt: &Opt) -> anyhow::Result<()> { let mut log_builder = env_logger::Builder::new(); - log_builder.parse_filters(&opt.log_level); - if opt.log_level == "info" { + log_builder.parse_filters(&opt.log_level.to_string()); + if matches!(opt.log_level, LogLevel::Info) { // if we are in info we only allow the warn log_level for milli log_builder.filter_module("milli", log::LevelFilter::Warn); } diff --git a/meilisearch/src/option.rs b/meilisearch/src/option.rs index 8dfa6a3b3..01b781984 100644 --- a/meilisearch/src/option.rs +++ b/meilisearch/src/option.rs @@ -1,6 +1,7 @@ use std::convert::TryFrom; use std::env::VarError; use std::ffi::OsStr; +use std::fmt::Display; use std::io::{BufReader, Read}; use std::num::ParseIntError; use std::ops::Deref; @@ -63,12 +64,65 @@ const DEFAULT_HTTP_PAYLOAD_SIZE_LIMIT: &str = "100 MB"; const DEFAULT_SNAPSHOT_DIR: &str = "snapshots/"; const DEFAULT_SNAPSHOT_INTERVAL_SEC: u64 = 86400; const DEFAULT_DUMP_DIR: &str = "dumps/"; -const DEFAULT_LOG_LEVEL: &str = "INFO"; const MEILI_MAX_INDEXING_MEMORY: &str = "MEILI_MAX_INDEXING_MEMORY"; const MEILI_MAX_INDEXING_THREADS: &str = "MEILI_MAX_INDEXING_THREADS"; const DEFAULT_LOG_EVERY_N: usize = 100000; +#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize)] +#[serde(rename_all = "UPPERCASE")] +pub enum LogLevel { + Off, + Error, + Warn, + #[default] + Info, + Debug, + Trace, +} + +#[derive(Debug)] +pub struct LogLevelError { + pub given_log_level: String, +} +impl Display for LogLevelError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + writeln!( + f, + "Log level '{}' is invalid. Accepted values are 'OFF', 'ERROR', 'WARN', 'INFO', 'DEBUG', and 'TRACE'.", + self.given_log_level + ) + } +} +impl Display for LogLevel { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + LogLevel::Off => Display::fmt("OFF", f), + LogLevel::Error => Display::fmt("ERROR", f), + LogLevel::Warn => Display::fmt("WARN", f), + LogLevel::Info => Display::fmt("INFO", f), + LogLevel::Debug => Display::fmt("DEBUG", f), + LogLevel::Trace => Display::fmt("TRACE", f), + } + } +} +impl std::error::Error for LogLevelError {} +impl FromStr for LogLevel { + type Err = LogLevelError; + + fn from_str(s: &str) -> Result { + match s.trim().to_lowercase().as_str() { + "off" => Ok(LogLevel::Off), + "error" => Ok(LogLevel::Error), + "warn" => Ok(LogLevel::Warn), + "info" => Ok(LogLevel::Info), + "debug" => Ok(LogLevel::Debug), + "trace" => Ok(LogLevel::Trace), + _ => Err(LogLevelError { given_log_level: s.to_owned() }), + } + } +} + #[derive(Debug, Clone, Parser, Deserialize)] #[clap(version, next_display_order = None)] #[serde(rename_all = "snake_case", deny_unknown_fields)] @@ -225,10 +279,10 @@ pub struct Opt { /// Defines how much detail should be present in Meilisearch's logs. /// - /// Meilisearch currently supports five log levels, listed in order of increasing verbosity: ERROR, WARN, INFO, DEBUG, TRACE. - #[clap(long, env = MEILI_LOG_LEVEL, default_value_t = default_log_level())] - #[serde(default = "default_log_level")] - pub log_level: String, + /// Meilisearch currently supports six log levels, listed in order of increasing verbosity: OFF, ERROR, WARN, INFO, DEBUG, TRACE. + #[clap(long, env = MEILI_LOG_LEVEL, default_value_t)] + #[serde(default)] + pub log_level: LogLevel, /// Generates a string of characters that can be used as a master key and exits. /// @@ -377,7 +431,7 @@ impl Opt { snapshot_interval_sec.to_string(), ); export_to_env_if_not_present(MEILI_DUMP_DIR, dump_dir); - export_to_env_if_not_present(MEILI_LOG_LEVEL, log_level); + export_to_env_if_not_present(MEILI_LOG_LEVEL, log_level.to_string()); #[cfg(feature = "metrics")] { export_to_env_if_not_present( @@ -698,10 +752,6 @@ fn default_dump_dir() -> PathBuf { PathBuf::from(DEFAULT_DUMP_DIR) } -fn default_log_level() -> String { - DEFAULT_LOG_LEVEL.to_string() -} - fn default_log_every_n() -> usize { DEFAULT_LOG_EVERY_N }