Explicitly restrict log level options to those that are documented

Fixes https://github.com/meilisearch/meilisearch/issues/3292
This commit is contained in:
Loïc Lecrenier 2023-01-03 11:55:34 +01:00
parent 947f08793a
commit d082ded7ad
4 changed files with 65 additions and 14 deletions

View File

@ -28,7 +28,7 @@ http_payload_size_limit = "100 MB"
log_level = "INFO" log_level = "INFO"
# Defines how much detail should be present in Meilisearch's logs. # 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 # https://docs.meilisearch.com/learn/configuration/instance_options.html#log-level
max_index_size = "100 GiB" max_index_size = "100 GiB"

View File

@ -306,7 +306,7 @@ impl From<Opt> for Infos {
max_index_size, max_index_size,
max_task_db_size, max_task_db_size,
http_payload_size_limit, http_payload_size_limit,
log_level, log_level: log_level.to_string(),
max_indexing_memory, max_indexing_memory,
max_indexing_threads, max_indexing_threads,
with_configuration_file: config_file_path.is_some(), with_configuration_file: config_file_path.is_some(),

View File

@ -7,6 +7,7 @@ use actix_web::web::Data;
use actix_web::HttpServer; use actix_web::HttpServer;
use index_scheduler::IndexScheduler; use index_scheduler::IndexScheduler;
use meilisearch::analytics::Analytics; use meilisearch::analytics::Analytics;
use meilisearch::option::LogLevel;
use meilisearch::{analytics, create_app, setup_meilisearch, Opt}; use meilisearch::{analytics, create_app, setup_meilisearch, Opt};
use meilisearch_auth::{generate_master_key, AuthController, MASTER_KEY_MIN_SIZE}; 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 /// does all the setup before meilisearch is launched
fn setup(opt: &Opt) -> anyhow::Result<()> { fn setup(opt: &Opt) -> anyhow::Result<()> {
let mut log_builder = env_logger::Builder::new(); let mut log_builder = env_logger::Builder::new();
log_builder.parse_filters(&opt.log_level); log_builder.parse_filters(&opt.log_level.to_string());
if opt.log_level == "info" { if matches!(opt.log_level, LogLevel::Info) {
// if we are in info we only allow the warn log_level for milli // if we are in info we only allow the warn log_level for milli
log_builder.filter_module("milli", log::LevelFilter::Warn); log_builder.filter_module("milli", log::LevelFilter::Warn);
} }

View File

@ -1,6 +1,7 @@
use std::convert::TryFrom; use std::convert::TryFrom;
use std::env::VarError; use std::env::VarError;
use std::ffi::OsStr; use std::ffi::OsStr;
use std::fmt::Display;
use std::io::{BufReader, Read}; use std::io::{BufReader, Read};
use std::num::ParseIntError; use std::num::ParseIntError;
use std::ops::Deref; 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_DIR: &str = "snapshots/";
const DEFAULT_SNAPSHOT_INTERVAL_SEC: u64 = 86400; const DEFAULT_SNAPSHOT_INTERVAL_SEC: u64 = 86400;
const DEFAULT_DUMP_DIR: &str = "dumps/"; 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_MEMORY: &str = "MEILI_MAX_INDEXING_MEMORY";
const MEILI_MAX_INDEXING_THREADS: &str = "MEILI_MAX_INDEXING_THREADS"; const MEILI_MAX_INDEXING_THREADS: &str = "MEILI_MAX_INDEXING_THREADS";
const DEFAULT_LOG_EVERY_N: usize = 100000; 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<Self, Self::Err> {
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)] #[derive(Debug, Clone, Parser, Deserialize)]
#[clap(version, next_display_order = None)] #[clap(version, next_display_order = None)]
#[serde(rename_all = "snake_case", deny_unknown_fields)] #[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. /// 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.
#[clap(long, env = MEILI_LOG_LEVEL, default_value_t = default_log_level())] #[clap(long, env = MEILI_LOG_LEVEL, default_value_t)]
#[serde(default = "default_log_level")] #[serde(default)]
pub log_level: String, pub log_level: LogLevel,
/// Generates a string of characters that can be used as a master key and exits. /// 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(), snapshot_interval_sec.to_string(),
); );
export_to_env_if_not_present(MEILI_DUMP_DIR, dump_dir); 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")] #[cfg(feature = "metrics")]
{ {
export_to_env_if_not_present( export_to_env_if_not_present(
@ -698,10 +752,6 @@ fn default_dump_dir() -> PathBuf {
PathBuf::from(DEFAULT_DUMP_DIR) PathBuf::from(DEFAULT_DUMP_DIR)
} }
fn default_log_level() -> String {
DEFAULT_LOG_LEVEL.to_string()
}
fn default_log_every_n() -> usize { fn default_log_every_n() -> usize {
DEFAULT_LOG_EVERY_N DEFAULT_LOG_EVERY_N
} }