Skip to content

Commit

Permalink
LOG LEVELS CONFIG
Browse files Browse the repository at this point in the history
  • Loading branch information
pwojcikdev committed Jan 13, 2024
1 parent 0b7de38 commit bac95f9
Show file tree
Hide file tree
Showing 5 changed files with 188 additions and 55 deletions.
104 changes: 70 additions & 34 deletions nano/lib/logging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ void nano::release_logging ()
* nlogger
*/

nano::nlogger::nlogger (const nano::log_config & config, std::string identifier)
nano::nlogger::nlogger (nano::log_config config, std::string identifier) :
config{ std::move (config) },
identifier{ std::move (identifier) }
{
debug_assert (logging_initialized, "initialize_logging must be called before creating a logger");

Expand Down Expand Up @@ -124,7 +126,18 @@ spdlog::logger & nano::nlogger::get_logger (nano::log::type tag)
std::shared_ptr<spdlog::logger> nano::nlogger::make_logger (nano::log::type tag)
{
auto spd_logger = std::make_shared<spdlog::logger> (std::string{ to_string (tag) }, sinks.begin (), sinks.end ());

spdlog::initialize_logger (spd_logger);

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));
}

return spd_logger;
}

Expand Down Expand Up @@ -182,7 +195,7 @@ nano::log_config nano::log_config::tests_default ()

nano::error nano::log_config::serialize (nano::tomlconfig & toml) const
{
toml.put ("level", std::string{ to_string (default_level) });
toml.put ("default_level", std::string{ to_string (default_level) });

nano::tomlconfig console_config;
console_config.put ("enable", console.enable);
Expand All @@ -196,17 +209,26 @@ nano::error nano::log_config::serialize (nano::tomlconfig & toml) const
file_config.put ("rotation_count", file.rotation_count);
toml.put_child ("file", file_config);

nano::tomlconfig levels_config;
for (auto const & [logger_id, level] : levels)
{
auto logger_name = to_string (logger_id.first);
levels_config.put (std::string{ logger_name }, std::string{ to_string (level) });
}
toml.put_child ("levels", levels_config);

return toml.get_error ();
}

// TODO: Move handling of deserialization exceptions outside of this function
nano::error nano::log_config::deserialize (nano::tomlconfig & toml)
{
try
{
if (toml.has_key ("level"))
if (toml.has_key ("default_level"))
{
auto default_level_l = toml.get<std::string> ("level");
default_level = parse_level (default_level_l);
auto default_level_l = toml.get<std::string> ("default_level");
default_level = nano::log::to_level (default_level_l);
}

if (toml.has_key ("console"))
Expand All @@ -224,50 +246,64 @@ nano::error nano::log_config::deserialize (nano::tomlconfig & toml)
file.max_size = file_config.get<std::size_t> ("max_size");
file.rotation_count = file_config.get<std::size_t> ("rotation_count");
}
}

catch (std::runtime_error const & ex)
if (toml.has_key ("levels"))
{
auto levels_config = toml.get_required_child ("levels");
for (auto & level : levels_config.get_values<std::string> ())
{
try
{
auto & [name_str, level_str] = level;
auto logger_level = nano::log::to_level (level_str);
auto logger_id = parse_logger_id (name_str);

levels[logger_id] = logger_level;
}
catch (std::invalid_argument const & ex)
{
// Ignore but warn about invalid logger names
nano::log::warn ("Processing log config: {}", ex.what ());
}
}
}
}
catch (std::invalid_argument const & ex)
{
toml.get_error ().set (ex.what ());
}

return toml.get_error ();
}

nano::log::level nano::log_config::parse_level (const std::string & level)
/**
* 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)
{
if (level == "off")
{
return nano::log::level::off;
}
else if (level == "critical")
{
return nano::log::level::critical;
}
else if (level == "error")
{
return nano::log::level::error;
}
else if (level == "warn")
{
return nano::log::level::warn;
}
else if (level == "info")
auto pos = logger_name.find ("::");
if (pos == std::string::npos)
{
return nano::log::level::info;
return { nano::log::to_type (logger_name), nano::log::detail::all };
}
else if (level == "debug")
{
return nano::log::level::debug;
}
else if (level == "trace")
else
{
return nano::log::level::trace;
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) };
}
else
}

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;
for (auto const & type : nano::log::all_types ())
{
throw std::runtime_error ("Invalid log level: " + level + ". Must be one of: off, critical, error, warn, info, debug, trace");
result.emplace (std::make_pair (type, nano::log::detail::all), default_level);
}
return result;
}

nano::error nano::read_log_config_toml (const std::filesystem::path & data_path, nano::log_config & config, const std::vector<std::string> & config_overrides)
Expand Down
19 changes: 13 additions & 6 deletions nano/lib/logging.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ class log_config final
nano::error serialize (nano::tomlconfig &) const;
nano::error deserialize (nano::tomlconfig &);

private:
nano::log::level parse_level (std::string const &);

public:
nano::log::level default_level{ nano::log::level::info };

using logger_id_t = std::pair<nano::log::type, nano::log::detail>;
std::map<logger_id_t, nano::log::level> levels{ default_levels (default_level) };

struct console_config
{
bool enable{ true };
Expand All @@ -41,12 +41,16 @@ class log_config final
console_config console;
file_config file;

// TODO: Per logger type levels

public: // Predefined defaults
static log_config cli_default ();
static log_config daemon_default ();
static log_config tests_default ();

private:
logger_id_t parse_logger_id (std::string const &);

/// Returns placeholder log levels for all loggers
static std::map<logger_id_t, nano::log::level> default_levels (nano::log::level);
};

nano::error read_log_config_toml (std::filesystem::path const & data_path, nano::log_config & config, std::vector<std::string> const & overrides);
Expand All @@ -62,7 +66,7 @@ spdlog::level::level_enum to_spdlog_level (nano::log::level);
class nlogger final
{
public:
nlogger (nano::log_config const &, std::string identifier = "");
nlogger (nano::log_config, std::string identifier = "");

// Disallow copies
nlogger (nlogger const &) = delete;
Expand Down Expand Up @@ -105,6 +109,9 @@ class nlogger final
}

private:
const nano::log_config config;
const std::string identifier;

std::vector<spdlog::sink_ptr> sinks;
std::unordered_map<nano::log::type, std::shared_ptr<spdlog::logger>> spd_loggers;
std::shared_mutex mutex;
Expand Down
75 changes: 72 additions & 3 deletions nano/lib/logging_enums.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,86 @@

#include <magic_enum.hpp>

std::string_view nano::to_string (nano::log::type tag)
std::string_view nano::log::to_string (nano::log::type tag)
{
return magic_enum::enum_name (tag);
}

std::string_view nano::to_string (nano::log::detail detail)
std::string_view nano::log::to_string (nano::log::detail detail)
{
return magic_enum::enum_name (detail);
}

std::string_view nano::to_string (nano::log::level level)
std::string_view nano::log::to_string (nano::log::level level)
{
return magic_enum::enum_name (level);
}

const std::vector<nano::log::level> & nano::log::all_levels ()
{
static std::vector<nano::log::level> all = [] () {
std::vector<nano::log::level> result;
for (auto const & lvl : magic_enum::enum_values<nano::log::level> ())
{
result.push_back (lvl);
}
return result;
}();
return all;
}

const std::vector<nano::log::type> & nano::log::all_types ()
{
static std::vector<nano::log::type> all = [] () {
std::vector<nano::log::type> result;
for (auto const & lvl : magic_enum::enum_values<nano::log::type> ())
{
result.push_back (lvl);
}
return result;
}();
return all;
}

nano::log::level nano::log::to_level (std::string_view name)
{
auto value = magic_enum::enum_cast<nano::log::level> (name);
if (value.has_value ())
{
return value.value ();
}
else
{
auto all_levels_str = nano::util::join (nano::log::all_levels (), ", ", [] (auto const & lvl) {
return to_string (lvl);
});

throw std::invalid_argument ("Invalid log level: " + std::string (name) + ". Must be one of: " + all_levels_str);
}
}

nano::log::type nano::log::to_type (std::string_view name)
{
auto value = magic_enum::enum_cast<nano::log::type> (name);
if (value.has_value ())
{
return value.value ();
}
else
{
throw std::invalid_argument ("Invalid log type: " + std::string (name));
}
}

nano::log::detail nano::log::to_detail (std::string_view name)
{
auto value = magic_enum::enum_cast<nano::log::detail> (name);
if (value.has_value ())
{
return value.value ();
}
else
{
throw std::invalid_argument ("Invalid log detail: " + std::string (name));
}
}
32 changes: 20 additions & 12 deletions nano/lib/logging_enums.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <cstdint>
#include <string_view>
#include <vector>

namespace nano::log
{
Expand Down Expand Up @@ -69,13 +70,6 @@ enum class type
bootstrap_legacy,
};

enum class category
{
all = 0, // reserved

work_generation,
};

enum class detail
{
all = 0, // reserved
Expand Down Expand Up @@ -112,17 +106,31 @@ enum class detail

};

enum class preset
// TODO: Additionally categorize logs by categories which can be enabled/disabled independently
enum class category
{
cli,
daemon,
tests,
all = 0, // reserved

work_generation,
// ...
};
}

namespace nano
namespace nano::log
{
std::string_view to_string (nano::log::type);
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);

/// @throw std::invalid_argument if the input string does not match a log::type
nano::log::type to_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);

std::vector<nano::log::level> const & all_levels ();
std::vector<nano::log::type> const & all_types ();
}
13 changes: 13 additions & 0 deletions nano/lib/tomlconfig.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,19 @@ class tomlconfig : public nano::configbase
return *this;
}

template <typename T>
std::vector<std::pair<std::string, T>> get_values ()
{
std::vector<std::pair<std::string, T>> result;
for (auto & entry : *tree)
{
T target{};
get_config (true, entry.first, target, target);
result.push_back ({ entry.first, target });
}
return result;
}

protected:
template <typename T, typename = std::enable_if_t<nano::is_lexical_castable<T>::value>>
tomlconfig & get_config (bool optional, std::string const & key, T & target, T default_value = T ())
Expand Down

0 comments on commit bac95f9

Please sign in to comment.