Logger id lookup improvements

This commit is contained in:
Piotr Wójcik 2024-01-23 12:42:29 +01:00
commit 1dd8ab4746
2 changed files with 95 additions and 64 deletions

View file

@ -3,6 +3,7 @@
#include <nano/lib/utility.hpp>
#include <fmt/chrono.h>
#include <magic_enum.hpp>
#include <spdlog/pattern_formatter.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/rotating_file_sink.h>
@ -24,8 +25,8 @@ nano::log_config nano::logger::global_config{};
std::vector<spdlog::sink_ptr> nano::logger::global_sinks{};
// By default, use only the tag as the logger name, since only one node is running in the process
std::function<std::string (nano::log::type tag, std::string identifier)> nano::logger::global_name_formatter{ [] (auto tag, auto identifier) {
return std::string{ to_string (tag) };
std::function<std::string (nano::log::logger_id, std::string identifier)> nano::logger::global_name_formatter{ [] (nano::log::logger_id logger_id, std::string identifier) {
return to_string (logger_id);
} };
void nano::logger::initialize (nano::log_config fallback, std::optional<std::filesystem::path> data_path, std::vector<std::string> const & config_overrides)
@ -96,8 +97,8 @@ void nano::logger::initialize_for_tests (nano::log_config fallback)
initialize_common (config, /* store log file in current workdir */ std::filesystem::current_path ());
// Use tag and identifier as the logger name, since multiple nodes may be running in the same process
global_name_formatter = [] (nano::log::type tag, std::string identifier) {
return fmt::format ("{}::{}", identifier, to_string (tag));
global_name_formatter = [] (nano::log::logger_id logger_id, std::string identifier) {
return fmt::format ("{}::{}", identifier, to_string (logger_id));
};
// Setup formatter to include information about node identifier `[%i]` and tag `[%n]`
@ -209,13 +210,13 @@ nano::logger::~logger ()
flush ();
}
spdlog::logger & nano::logger::get_logger (nano::log::type tag)
spdlog::logger & nano::logger::get_logger (nano::log::type type, nano::log::detail detail)
{
// This is a two-step process to avoid exclusively locking the mutex in the common case
{
std::shared_lock lock{ mutex };
if (auto it = spd_loggers.find (tag); it != spd_loggers.end ())
if (auto it = spd_loggers.find ({ type, detail }); it != spd_loggers.end ())
{
return *it->second;
}
@ -224,33 +225,44 @@ spdlog::logger & nano::logger::get_logger (nano::log::type tag)
{
std::unique_lock lock{ mutex };
auto [it, inserted] = spd_loggers.emplace (tag, make_logger (tag));
auto [it, inserted] = spd_loggers.emplace (std::make_pair (type, detail), make_logger ({ type, detail }));
return *it->second;
}
}
std::shared_ptr<spdlog::logger> nano::logger::make_logger (nano::log::type tag)
std::shared_ptr<spdlog::logger> nano::logger::make_logger (nano::log::logger_id logger_id)
{
auto const & config = global_config;
auto const & sinks = global_sinks;
auto name = global_name_formatter (tag, identifier);
auto name = global_name_formatter (logger_id, identifier);
auto spd_logger = std::make_shared<spdlog::logger> (name, sinks.begin (), sinks.end ());
if (auto it = config.levels.find ({ tag, nano::log::detail::all }); it != config.levels.end ())
{
spd_logger->set_level (to_spdlog_level (it->second));
}
else
{
spd_logger->set_level (to_spdlog_level (config.default_level));
}
spd_logger->set_level (to_spdlog_level (find_level (logger_id)));
spd_logger->flush_on (to_spdlog_level (config.flush_level));
return spd_logger;
}
nano::log::level nano::logger::find_level (nano::log::logger_id logger_id) const
{
auto const & config = global_config;
auto const & [type, detail] = logger_id;
// Check for a specific level for this logger
if (auto it = config.levels.find (logger_id); it != config.levels.end ())
{
return it->second;
}
// Check for a default level for this logger type
if (auto it = config.levels.find ({ type, nano::log::detail::all }); it != config.levels.end ())
{
return it->second;
}
// Use the default level
return config.default_level;
}
spdlog::level::level_enum nano::logger::to_spdlog_level (nano::log::level level)
{
switch (level)
@ -400,7 +412,7 @@ void nano::log_config::deserialize (nano::tomlconfig & toml)
{
auto & [name_str, level_str] = level;
auto logger_level = nano::log::parse_level (level_str);
auto logger_id = parse_logger_id (name_str);
auto logger_id = nano::log::parse_logger_id (name_str);
levels[logger_id] = logger_level;
}
@ -413,29 +425,9 @@ void nano::log_config::deserialize (nano::tomlconfig & toml)
}
}
/**
* Parse `logger_name[:logger_detail]` into a pair of `log::type` and `log::detail`
* @throw std::invalid_argument if `logger_name` or `logger_detail` are invalid
*/
nano::log_config::logger_id_t nano::log_config::parse_logger_id (const std::string & logger_name)
std::map<nano::log::logger_id, nano::log::level> nano::log_config::default_levels (nano::log::level default_level)
{
auto pos = logger_name.find ("::");
if (pos == std::string::npos)
{
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::parse_type (logger_type), nano::log::parse_detail (logger_detail) };
}
}
std::map<nano::log_config::logger_id_t, nano::log::level> nano::log_config::default_levels (nano::log::level default_level)
{
std::map<nano::log_config::logger_id_t, nano::log::level> result;
std::map<nano::log::logger_id, nano::log::level> result;
for (auto const & type : nano::log::all_types ())
{
result.emplace (std::make_pair (type, nano::log::detail::all), default_level);
@ -475,7 +467,7 @@ nano::log_config nano::load_log_config (nano::log_config fallback, const std::fi
auto env_levels = nano::get_env ("NANO_LOG_LEVELS");
if (env_levels)
{
std::map<nano::log_config::logger_id_t, nano::log::level> levels;
std::map<nano::log::logger_id, nano::log::level> levels;
for (auto const & env_level_str : nano::util::split (*env_levels, ','))
{
try
@ -490,7 +482,7 @@ nano::log_config nano::load_log_config (nano::log_config fallback, const std::fi
auto name_str = arr[0];
auto level_str = arr[1];
auto logger_id = nano::log_config::parse_logger_id (name_str);
auto logger_id = nano::log::parse_logger_id (name_str);
auto logger_level = nano::log::parse_level (level_str);
levels[logger_id] = logger_level;
@ -517,4 +509,37 @@ nano::log_config nano::load_log_config (nano::log_config fallback, const std::fi
std::cerr << "Unable to load log config. Using defaults. Error: " << ex.what () << std::endl;
}
return fallback;
}
std::string nano::log::to_string (nano::log::logger_id logger_id)
{
auto const & [type, detail] = logger_id;
if (detail == nano::log::detail::all)
{
return fmt::format ("{}", to_string (type));
}
else
{
return fmt::format ("{}::{}", to_string (type), to_string (detail));
}
}
/**
* Parse `logger_name[:logger_detail]` into a pair of `log::type` and `log::detail`
* @throw std::invalid_argument if `logger_name` or `logger_detail` are invalid
*/
nano::log::logger_id nano::log::parse_logger_id (const std::string & logger_name)
{
auto pos = logger_name.find ("::");
if (pos == std::string::npos)
{
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::parse_type (logger_type), nano::log::parse_detail (logger_detail) };
}
}

View file

@ -10,6 +10,14 @@
#include <spdlog/spdlog.h>
namespace nano::log
{
using logger_id = std::pair<nano::log::type, nano::log::detail>;
std::string to_string (logger_id);
logger_id parse_logger_id (std::string const &);
}
namespace nano
{
class log_config final
@ -26,8 +34,7 @@ public:
nano::log::level default_level{ nano::log::level::info };
nano::log::level flush_level{ nano::log::level::error };
using logger_id_t = std::pair<nano::log::type, nano::log::detail>;
std::map<logger_id_t, nano::log::level> levels;
std::map<nano::log::logger_id, nano::log::level> levels;
struct console_config
{
@ -52,11 +59,9 @@ public: // Predefined defaults
static log_config tests_default ();
static log_config sample_config (); // For auto-generated sample config files
static logger_id_t parse_logger_id (std::string const &);
private:
/// Returns placeholder log levels for all loggers
static std::map<logger_id_t, nano::log::level> default_levels (nano::log::level);
static std::map<nano::log::logger_id, nano::log::level> default_levels (nano::log::level);
};
nano::log_config load_log_config (nano::log_config fallback, std::filesystem::path const & data_path, std::vector<std::string> const & config_overrides = {});
@ -79,56 +84,57 @@ private:
static bool global_initialized;
static nano::log_config global_config;
static std::vector<spdlog::sink_ptr> global_sinks;
static std::function<std::string (nano::log::type tag, std::string identifier)> global_name_formatter;
static std::function<std::string (nano::log::logger_id, std::string identifier)> global_name_formatter;
static void initialize_common (nano::log_config const &, std::optional<std::filesystem::path> data_path);
public:
template <class... Args>
void log (nano::log::level level, nano::log::type tag, spdlog::format_string_t<Args...> fmt, Args &&... args)
void log (nano::log::level level, nano::log::type type, spdlog::format_string_t<Args...> fmt, Args &&... args)
{
get_logger (tag).log (to_spdlog_level (level), fmt, std::forward<Args> (args)...);
get_logger (type).log (to_spdlog_level (level), fmt, std::forward<Args> (args)...);
}
template <class... Args>
void debug (nano::log::type tag, spdlog::format_string_t<Args...> fmt, Args &&... args)
void debug (nano::log::type type, spdlog::format_string_t<Args...> fmt, Args &&... args)
{
get_logger (tag).debug (fmt, std::forward<Args> (args)...);
get_logger (type).debug (fmt, std::forward<Args> (args)...);
}
template <class... Args>
void info (nano::log::type tag, spdlog::format_string_t<Args...> fmt, Args &&... args)
void info (nano::log::type type, spdlog::format_string_t<Args...> fmt, Args &&... args)
{
get_logger (tag).info (fmt, std::forward<Args> (args)...);
get_logger (type).info (fmt, std::forward<Args> (args)...);
}
template <class... Args>
void warn (nano::log::type tag, spdlog::format_string_t<Args...> fmt, Args &&... args)
void warn (nano::log::type type, spdlog::format_string_t<Args...> fmt, Args &&... args)
{
get_logger (tag).warn (fmt, std::forward<Args> (args)...);
get_logger (type).warn (fmt, std::forward<Args> (args)...);
}
template <class... Args>
void error (nano::log::type tag, spdlog::format_string_t<Args...> fmt, Args &&... args)
void error (nano::log::type type, spdlog::format_string_t<Args...> fmt, Args &&... args)
{
get_logger (tag).error (fmt, std::forward<Args> (args)...);
get_logger (type).error (fmt, std::forward<Args> (args)...);
}
template <class... Args>
void critical (nano::log::type tag, spdlog::format_string_t<Args...> fmt, Args &&... args)
void critical (nano::log::type type, spdlog::format_string_t<Args...> fmt, Args &&... args)
{
get_logger (tag).critical (fmt, std::forward<Args> (args)...);
get_logger (type).critical (fmt, std::forward<Args> (args)...);
}
private:
const std::string identifier;
std::unordered_map<nano::log::type, std::shared_ptr<spdlog::logger>> spd_loggers;
std::map<nano::log::logger_id, std::shared_ptr<spdlog::logger>> spd_loggers;
std::shared_mutex mutex;
private:
spdlog::logger & get_logger (nano::log::type tag);
std::shared_ptr<spdlog::logger> make_logger (nano::log::type tag);
spdlog::logger & get_logger (nano::log::type, nano::log::detail = nano::log::detail::all);
std::shared_ptr<spdlog::logger> make_logger (nano::log::logger_id);
nano::log::level find_level (nano::log::logger_id) const;
static spdlog::level::level_enum to_spdlog_level (nano::log::level);
};