4 #include <spdlog/common.h>
5 #include <spdlog/fmt/fmt.h>
6 #include <spdlog/sinks/ansicolor_sink.h>
7 #include <spdlog/sinks/basic_file_sink.h>
8 #include <spdlog/sinks/null_sink.h>
9 #include <spdlog/sinks/stdout_sinks.h>
13 std::map<std::string, std::shared_ptr<LogManager::log_sink>> LogManager::m_file_sinks;
15 LogManager* LogManager::m_instance =
nullptr;
17 LogManager::LogManager(
const std::filesystem::path& file_name)
20 std::filesystem::create_directories(m_file_path.parent_path());
23 {
"trace", spdlog::level::level_enum::trace},
24 {
"debug", spdlog::level::level_enum::debug},
25 {
"info", spdlog::level::level_enum::info},
26 {
"warn", spdlog::level::level_enum::warn},
27 {
"err", spdlog::level::level_enum::err},
28 {
"critical", spdlog::level::level_enum::critical},
29 {
"off", spdlog::level::level_enum::off},
34 spdlog::sinks_init_list stdout_init_list = {std::make_shared<spdlog::sinks::ansicolor_stdout_sink_mt>(), gui_sink->spdlog_sink};
38 {
"null", spdlog::create<spdlog::sinks::null_sink_mt>(
"null")},
40 {
"stdout", std::make_shared<spdlog::logger>(
"stdout", stdout_init_list)},
43 spdlog::details::registry::instance().initialize_logger(m_logger.at(
"stdout"));
45 spdlog::set_error_handler([](
const std::string& msg) {
throw std::invalid_argument(
"[!] internal log error: " + msg); });
53 LogManager::~LogManager()
55 std::cerr <<
"~LogManager" << std::endl;
56 for (
const auto& it : m_logger)
72 spdlog::set_pattern(format);
77 auto it = m_logger.find(channel);
78 if (it == m_logger.end())
80 if (channel !=
"stdout")
82 log_warning(
"stdout",
"log channel '{}' was not registered so far, creating default channel.", channel);
84 return add_channel(channel, m_default_sinks,
"info");
92 std::set<std::string> channels;
93 for (
const auto& it : m_logger)
95 channels.insert(it.first);
100 std::shared_ptr<spdlog::logger>
LogManager::add_channel(
const std::string& channel_name,
const std::vector<std::shared_ptr<log_sink>>& sinks,
const std::string& level)
102 if (
auto it = m_logger.find(channel_name); it != m_logger.end())
107 std::vector<std::shared_ptr<spdlog::sinks::sink>> vec;
108 for (
const auto& sink : sinks)
110 vec.push_back(sink->spdlog_sink);
113 auto channel = std::make_shared<spdlog::logger>(channel_name, vec.begin(), vec.end());
114 m_logger[channel_name] = channel;
115 m_logger[channel_name]->flush_on(spdlog::level::info);
117 spdlog::details::registry::instance().initialize_logger(m_logger.at(channel_name));
119 m_logger_sinks[channel_name] = sinks;
121 if (m_enforce_level.empty())
135 if (m_logger.find(channel_name) == m_logger.end())
139 m_logger[channel_name]->flush();
140 spdlog::drop(channel_name);
141 m_logger.erase(channel_name);
142 m_logger_sinks.erase(channel_name);
147 auto it_channel = m_logger.find(channel_name);
148 if (it_channel == m_logger.end())
150 return std::string(
"");
153 for (
const auto& it_level : m_level)
155 if (it_level.second == it_channel->second->level())
157 return it_level.first;
161 return std::string(
"");
166 auto it_channel = m_logger.find(channel_name);
167 auto it_level = m_level.find(level);
168 if (it_channel == m_logger.end() || it_level == m_level.end())
173 auto& channel = it_channel->second;
174 channel->set_level(it_level->second);
175 m_logger[channel_name] = channel;
185 for (
const auto& channel : m_logger)
198 for (
const auto& channel : m_logger)
206 return m_default_sinks;
211 m_default_sinks.erase(
212 std::remove_if(m_default_sinks.begin(), m_default_sinks.end(), [sink_type](
const std::shared_ptr<hal::LogManager::log_sink> sink) { return sink->sink_type == sink_type; }),
213 m_default_sinks.end());
218 auto sink = std::make_shared<log_sink>();
219 sink->is_file_sink =
false;
220 sink->sink_type =
"stdout";
224 sink->spdlog_sink = std::make_shared<spdlog::sinks::stdout_sink_mt>();
230 sink->spdlog_sink =
nullptr;
231 log_error(
"stdout",
"create_stdout_sink() has to be implemented for Windows.");
233 auto stdout_sink = std::make_shared<spdlog::sinks::ansicolor_stdout_sink_mt>();
234 stdout_sink->set_color(spdlog::level::trace, stdout_sink->green);
235 stdout_sink->set_color(spdlog::level::debug, stdout_sink->blue);
236 stdout_sink->set_color(spdlog::level::info, stdout_sink->reset);
237 stdout_sink->set_color(spdlog::level::warn, stdout_sink->yellow);
238 stdout_sink->set_color(spdlog::level::err, stdout_sink->red);
239 stdout_sink->set_color(spdlog::level::critical, stdout_sink->red_bold);
240 sink->spdlog_sink = stdout_sink;
249 std::filesystem::path path = file_name;
250 if (file_name.empty())
255 auto it = m_file_sinks.find(path.string());
256 if (it != m_file_sinks.end())
261 auto sink = std::make_shared<log_sink>();
262 sink->is_file_sink =
true;
263 sink->sink_type =
"file";
264 sink->truncate = truncate;
267 sink->spdlog_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(path.string(), truncate);
269 m_file_sinks[path.string()] = sink;
276 auto sink = std::make_shared<log_sink>();
277 sink->spdlog_sink = std::make_shared<log_gui_sink>();
278 sink->is_file_sink =
false;
279 sink->sink_type =
"gui";
285 std::set<std::string> levels;
286 for (
const auto& it : m_level)
287 levels.insert(it.first);
293 log_info(
"core",
"setting log file to '{}'.", file_path.string());
297 std::vector<std::string> channels_to_recreate;
298 for (
const auto& it : m_logger_sinks)
300 auto name = it.first;
301 auto sinks = it.second;
303 for (
const auto& sink : sinks)
305 if (sink->is_file_sink && (sink->path == m_file_path))
307 channels_to_recreate.push_back(
name);
315 for (
const auto&
name : channels_to_recreate)
319 std::vector<std::shared_ptr<log_sink>> new_sinks;
320 for (
const auto& sink : m_logger_sinks[
name])
322 if (sink->is_file_sink && (sink->path == m_file_path))
325 new_sinks.push_back(sink);
332 for (
u32 sink_idx = 0; sink_idx < m_default_sinks.size(); sink_idx++)
334 if (m_default_sinks.at(sink_idx)->is_file_sink && m_default_sinks.at(sink_idx)->path == m_file_path)
336 m_default_sinks.at(sink_idx) =
create_file_sink(file_path, m_default_sinks.at(sink_idx)->truncate);
341 for (
auto it = m_file_sinks.cbegin(); it != m_file_sinks.cend(); )
343 if (it->second.unique())
344 m_file_sinks.erase(it++);
350 m_file_path = file_path;
362 auto start =
"--log." + channel;
363 auto level_string = start +
".level";
364 auto enabled_string = start +
".enabled";
371 return m_descriptions;
376 bool default_enabled =
true;
377 if (
args.is_option_set(
"--log.enabled"))
379 auto arg =
args.get_parameter(
"--log.enabled");
380 default_enabled = (arg ==
"true" || arg ==
"1");
389 if (
args.is_option_set(
"--log.level"))
391 auto level =
args.get_parameter(
"--log.level");
392 if (m_level.find(level) != m_level.end())
394 m_enforce_level = level;
402 log_warning(
"core",
"default log level {} provided is not valid.", level);
407 if (
args.is_option_set(
"--log." + channel +
".enabled"))
409 auto arg =
args.get_parameter(
"--log." + channel +
".enabled");
410 enabled = (arg ==
"true" || arg ==
"1");
417 if (
args.is_option_set(
"--log." + channel +
".level"))
419 auto level =
args.get_parameter(
"--log." + channel +
".level");
420 if (m_level.find(level) != m_level.end())
426 log_warning(
"core",
"log level {} provided for {} is not valid.", level, channel);
433 return m_gui_callback;
441 spdlog::memory_buf_t formatted;
442 formatter_->format(msg, formatted);
void set_level_of_channel(const std::string &channel_name, const std::string &level)
std::shared_ptr< spdlog::logger > add_channel(const std::string &channel_name, const std::vector< std::shared_ptr< log_sink >> &sinks, const std::string &level="info")
void activate_channel(const std::string &channel_name)
void deactivate_all_channels()
void deactivate_channel(const std::string &channel_name)
void remove_channel(const std::string &channel_name)
ProgramOptions & get_option_descriptions()
std::string get_level_of_channel(const std::string &channel_name) const
static std::shared_ptr< log_sink > create_gui_sink()
void activate_all_channels()
std::vector< std::shared_ptr< hal::LogManager::log_sink > > get_default_sinks()
std::set< std::string > get_channels() const
void set_format_pattern(const std::string &format)
std::shared_ptr< spdlog::logger > get_channel(const std::string &channel_name="stdout")
static std::shared_ptr< log_sink > create_file_sink(const std::filesystem::path &file_name="", const bool truncate=false)
void remove_sink_from_default(const std::string &sink_type)
void handle_options(ProgramArguments &args)
CallbackHook< void(const spdlog::level::level_enum &, const std::string &, const std::string &)> & get_gui_callback()
static std::shared_ptr< log_sink > create_stdout_sink(const bool colored=true)
static LogManager * get_instance(const std::filesystem::path &file_name="")
std::set< std::string > get_available_log_levels() const
void set_file_name(const std::filesystem::path &file_name)
std::vector< std::tuple< std::set< std::string >, std::string > > get_options() const
bool add(const std::string &flag, const std::string &description, const std::initializer_list< std::string > ¶meters={})
static const std::string A_REQUIRED_PARAMETER
constant to specify that a parameter is required and does not have a default value.
void sink_it_(const spdlog::details::log_msg &msg) override
#define log_error(channel,...)
#define log_info(channel,...)
#define log_warning(channel,...)
std::filesystem::path get_default_log_directory(std::filesystem::path source_file)