From e51975f9139c8a10847b3972395bc32014ae8342 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Wo=CC=81jcik?= <3044353+pwojcikdev@users.noreply.github.com> Date: Thu, 18 Jan 2024 00:19:37 +0100 Subject: [PATCH] ENV LEVELS --- nano/lib/logging.cpp | 82 ++++++++++++++++++++++++++++++++++---- nano/lib/logging.hpp | 7 ++-- nano/lib/logging_enums.cpp | 6 +-- nano/lib/logging_enums.hpp | 6 +-- nano/lib/utility.hpp | 12 ++++++ nano/node/cli.cpp | 2 +- 6 files changed, 97 insertions(+), 18 deletions(-) diff --git a/nano/lib/logging.cpp b/nano/lib/logging.cpp index 68c2ccc27a..342f402e35 100644 --- a/nano/lib/logging.cpp +++ b/nano/lib/logging.cpp @@ -269,22 +269,32 @@ spdlog::level::level_enum nano::to_spdlog_level (nano::log::level level) nano::log_config nano::log_config::cli_default () { - log_config config; + log_config config{}; config.default_level = nano::log::level::critical; + config.console.to_cerr = true; // Use cerr to avoid interference with CLI output that goes to stdout + config.file.enable = false; return config; } nano::log_config nano::log_config::daemon_default () { - log_config config; + log_config config{}; config.default_level = nano::log::level::info; return config; } nano::log_config nano::log_config::tests_default () { - log_config config; - config.default_level = nano::log::level::critical; + log_config config{}; + config.default_level = nano::log::level::off; + return config; +} + +nano::log_config nano::log_config::sample_config () +{ + log_config config{}; + config.default_level = nano::log::level::info; + config.levels = default_levels (nano::log::level::info); // Populate with default levels return config; } @@ -349,7 +359,7 @@ void nano::log_config::deserialize (nano::tomlconfig & toml) if (toml.has_key ("default_level")) { auto default_level_l = toml.get ("default_level"); - default_level = nano::log::to_level (default_level_l); + default_level = nano::log::parse_level (default_level_l); } if (toml.has_key ("console")) @@ -376,7 +386,7 @@ void nano::log_config::deserialize (nano::tomlconfig & toml) try { auto & [name_str, level_str] = level; - auto logger_level = nano::log::to_level (level_str); + auto logger_level = nano::log::parse_level (level_str); auto logger_id = parse_logger_id (name_str); levels[logger_id] = logger_level; @@ -399,14 +409,14 @@ nano::log_config::logger_id_t nano::log_config::parse_logger_id (const std::stri auto pos = logger_name.find ("::"); if (pos == std::string::npos) { - return { nano::log::to_type (logger_name), nano::log::detail::all }; + return { nano::log::parse_type (logger_name), nano::log::detail::all }; } else { auto logger_type = logger_name.substr (0, pos); auto logger_detail = logger_name.substr (pos + 1); - return { nano::log::to_type (logger_type), nano::log::to_detail (logger_detail) }; + return { nano::log::parse_type (logger_type), nano::log::parse_detail (logger_detail) }; } } @@ -430,6 +440,62 @@ nano::log_config nano::load_log_config (nano::log_config fallback, const std::fi try { auto config = nano::load_config_file (fallback, config_filename, data_path, config_overrides); + + // Parse default log level from environment variable, e.g. "NANO_LOG=debug" + auto env_level = nano::get_env ("NANO_LOG"); + if (env_level) + { + try + { + config.default_level = nano::log::parse_level (*env_level); + + std::cerr << "Using default log level from NANO_LOG environment variable: " << *env_level << std::endl; + } + catch (std::invalid_argument const & ex) + { + std::cerr << "Invalid log level from NANO_LOG environment variable: " << ex.what () << std::endl; + } + } + + // Parse per logger levels from environment variable, e.g. "NANO_LOG_LEVELS=ledger=debug,node=trace" + auto env_levels = nano::get_env ("NANO_LOG_LEVELS"); + if (env_levels) + { + std::map levels; + for (auto const & env_level_str : nano::util::split (*env_levels, ',')) + { + try + { + // Split 'logger_name=level' into a pair of 'logger_name' and 'level' + auto arr = nano::util::split (env_level_str, '='); + if (arr.size () != 2) + { + throw std::invalid_argument ("Invalid entry: " + env_level_str); + } + + auto name_str = arr[0]; + auto level_str = arr[1]; + + auto logger_id = nano::log_config::parse_logger_id (name_str); + auto logger_level = nano::log::parse_level (level_str); + + levels[logger_id] = logger_level; + + std::cerr << "Using logger log level from NANO_LOG_LEVELS environment variable: " << name_str << "=" << level_str << std::endl; + } + catch (std::invalid_argument const & ex) + { + std::cerr << "Invalid log level from NANO_LOG_LEVELS environment variable: " << ex.what () << std::endl; + } + } + + // Merge with existing levels + for (auto const & [logger_id, level] : levels) + { + config.levels[logger_id] = level; + } + } + return config; } catch (std::runtime_error const & ex) diff --git a/nano/lib/logging.hpp b/nano/lib/logging.hpp index a9b10c6693..9906b45f28 100644 --- a/nano/lib/logging.hpp +++ b/nano/lib/logging.hpp @@ -27,7 +27,7 @@ class log_config final nano::log::level flush_level{ nano::log::level::error }; using logger_id_t = std::pair; - std::map levels{ default_levels (default_level) }; + std::map levels; struct console_config { @@ -50,10 +50,11 @@ class log_config final static log_config cli_default (); static log_config daemon_default (); static log_config tests_default (); + static log_config sample_config (); // For auto-generated sample config files -private: - logger_id_t parse_logger_id (std::string const &); + static logger_id_t parse_logger_id (std::string const &); +private: /// Returns placeholder log levels for all loggers static std::map default_levels (nano::log::level); }; diff --git a/nano/lib/logging_enums.cpp b/nano/lib/logging_enums.cpp index a82996aa70..b1a55f4485 100644 --- a/nano/lib/logging_enums.cpp +++ b/nano/lib/logging_enums.cpp @@ -47,7 +47,7 @@ const std::vector & nano::log::all_types () return all; } -nano::log::level nano::log::to_level (std::string_view name) +nano::log::level nano::log::parse_level (std::string_view name) { auto value = magic_enum::enum_cast (name); if (value.has_value ()) @@ -64,7 +64,7 @@ nano::log::level nano::log::to_level (std::string_view name) } } -nano::log::type nano::log::to_type (std::string_view name) +nano::log::type nano::log::parse_type (std::string_view name) { auto value = magic_enum::enum_cast (name); if (value.has_value ()) @@ -77,7 +77,7 @@ nano::log::type nano::log::to_type (std::string_view name) } } -nano::log::detail nano::log::to_detail (std::string_view name) +nano::log::detail nano::log::parse_detail (std::string_view name) { auto value = magic_enum::enum_cast (name); if (value.has_value ()) diff --git a/nano/lib/logging_enums.hpp b/nano/lib/logging_enums.hpp index 0440307823..508364dd47 100644 --- a/nano/lib/logging_enums.hpp +++ b/nano/lib/logging_enums.hpp @@ -128,13 +128,13 @@ std::string_view to_string (nano::log::detail); std::string_view to_string (nano::log::level); /// @throw std::invalid_argument if the input string does not match a log::level -nano::log::level to_level (std::string_view); +nano::log::level parse_level (std::string_view); /// @throw std::invalid_argument if the input string does not match a log::type -nano::log::type to_type (std::string_view); +nano::log::type parse_type (std::string_view); /// @throw std::invalid_argument if the input string does not match a log::detail -nano::log::detail to_detail (std::string_view); +nano::log::detail parse_detail (std::string_view); std::vector const & all_levels (); std::vector const & all_types (); diff --git a/nano/lib/utility.hpp b/nano/lib/utility.hpp index 5ec06f6e9e..331c0534ab 100644 --- a/nano/lib/utility.hpp +++ b/nano/lib/utility.hpp @@ -245,6 +245,18 @@ std::string join (Container const & container, std::string_view delimiter, Func return join (container.begin (), container.end (), delimiter, transform); } +inline std::vector split (const std::string & str, char delimiter) +{ + std::stringstream ss{ str }; + std::vector result; + std::string item; + while (std::getline (ss, item, delimiter)) + { + result.push_back (item); + } + return result; +} + template std::string to_str (T const & val) { diff --git a/nano/node/cli.cpp b/nano/node/cli.cpp index 2636f5a144..26fe004923 100644 --- a/nano/node/cli.cpp +++ b/nano/node/cli.cpp @@ -674,7 +674,7 @@ std::error_code nano::handle_node_options (boost::program_options::variables_map else if (type == "log") { valid_type = true; - nano::log_config config{ nano::log_config::daemon_default () }; + nano::log_config config = nano::log_config::sample_config (); config.serialize_toml (toml); } else if (type == "tls")