* Copyright (c) 2024 Huawei Technologies Co., Ltd.
* openUBMC is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
#include <algorithm>
#include <fstream>
#include <memory>
#include <mc/json.h>
#include <mc/log.h>
#include <mc/log/log_level.h>
#include <mc/log/log_manager.h>
#include <mc/log/log_message.h>
#include <mc/log/logger.h>
#include <mc/reflect.h>
#include <mc/variant.h>
#include <unordered_set>
namespace mc {
namespace log {
log_manager& log_manager::instance() {
return mc::singleton_leaky<log_manager>::instance_with_creator([]() {
return new log_manager();
});
}
log_manager::log_manager() {
logger default_logger(MC_LOG_DEFAULT_LOGGER);
mc::dict default_config{
{"stream", "std_out"},
{"use_color", true},
{"flush", true},
{"level_colors", mc::variants{mc::dict{{"level", "debug"}, {"color", "cyan"}},
mc::dict{{"level", "info"}, {"color", "green"}},
mc::dict{{"level", "warn"}, {"color", "blue"}},
mc::dict{{"level", "error"}, {"color", "red"}},
mc::dict{{"level", "fatal"}, {"color", "magenta"}}}}};
appender_ptr console_appender =
appender_factory::instance().create("default_console", "console", default_config);
if (console_appender) {
default_logger.add_appender(console_appender);
}
default_logger.set_level(mc::log::level::info);
m_loggers[MC_LOG_DEFAULT_LOGGER] = default_logger;
}
logger log_manager::get_logger(const char* name) {
std::lock_guard<std::mutex> lock(m_mutex);
auto it = m_loggers.find(name);
if (it != m_loggers.end()) {
return it->second;
}
logger new_logger(name);
m_loggers.emplace(name, name);
return new_logger;
}
bool log_manager::load_appender(const std::string& lib_path, const std::string& appender_name) {
return appender_factory::instance().load(lib_path, appender_name);
}
void log_manager::load_appenders(const std::string& dir_path) {
appender_factory::instance().load_all(dir_path);
}
bool log_manager::load_single_appender(const appender_config& app_config) {
if (!app_config.lib_path.empty()) {
if (!appender_factory::instance().load(app_config.lib_path, app_config.type)) {
elog("Failed to load appender[${name}] dynamic library: ${lib_path}",
("name", app_config.name)("lib_path", app_config.lib_path));
return false;
}
}
appender_ptr appender = appender_factory::instance().get_or_create_appender(
app_config.name, app_config.type, app_config.properties);
if (!appender) {
elog("Failed to create or configure appender[${name}]", ("name", app_config.name));
return false;
}
return true;
}
bool log_manager::load_appenders_from_config(const std::vector<appender_config>& appender_configs) {
bool has_error = false;
for (const auto& app_config : appender_configs) {
if (!load_single_appender(app_config)) {
has_error = true;
continue;
}
}
return !has_error;
}
void log_manager::update_existing_logger(logger& log, const logger_config& log_config) {
log.set_level(log_config.level);
std::vector<std::string> to_remove;
for (const auto& appender : log.get_appenders()) {
const std::string& name = appender->get_name();
if (std::find(log_config.appenders.begin(), log_config.appenders.end(), name) ==
log_config.appenders.end()) {
to_remove.push_back(name);
}
}
std::vector<std::string> to_add;
for (const auto& config_name : log_config.appenders) {
if (!log.find_appender(config_name)) {
to_add.push_back(config_name);
}
}
if (to_remove.empty() && to_add.empty()) {
return;
}
for (const auto& name : to_remove) {
if (log.remove_appender(name)) {
ilog("Removed appender[${name}] from logger[${logger}]",
("logger", log.get_name())("name", name));
}
}
for (const auto& name : to_add) {
appender_ptr appender = appender_factory::instance().get_appender(name);
if (appender) {
log.add_appender(appender);
ilog("Added appender[${name}] to logger[${logger}]",
("logger", log.get_name())("name", name));
} else {
wlog("Failed to add appender to logger[${logger}]: appender[${name}] not found",
("logger", log.get_name())("name", name));
}
}
}
logger log_manager::create_new_logger(const logger_config& log_config) {
logger new_logger(log_config.name);
new_logger.set_level(log_config.level);
for (const auto& app_name : log_config.appenders) {
appender_ptr appender = appender_factory::instance().get_appender(app_name);
if (appender) {
new_logger.add_appender(appender);
ilog("Added appender[${name}] to new logger[${logger}]",
("logger", new_logger.get_name())("name", app_name));
} else {
wlog("Failed to add appender to new logger[${logger}]: appender[${name}] not found",
("logger", new_logger.get_name())("name", app_name));
}
}
return new_logger;
}
bool log_manager::apply_config(const logging_config& config) {
if (!load_appenders_from_config(config.appenders)) {
wlog("Some appenders failed to load, continuing with other configurations");
}
for (const auto& log_config : config.loggers) {
try {
auto it = m_loggers.find(log_config.name);
if (it != m_loggers.end()) {
update_existing_logger(it->second, log_config);
} else {
m_loggers[log_config.name] = create_new_logger(log_config);
}
} catch (const std::exception& e) {
elog("Failed to update logger[${name}] configuration: ${error}",
("name", log_config.name)("error", e.what()));
continue;
}
}
return true;
}
void log_manager::set_dlog_level(level lvl) {
std::vector<std::string> logger_names;
{
std::lock_guard<std::mutex> lock(m_mutex);
for (auto& logger_entry : m_loggers) {
logger_entry.second.set_level(lvl);
logger_names.push_back(logger_entry.first);
}
auto default_logger_it = m_loggers.find(MC_LOG_DEFAULT_LOGGER);
if (default_logger_it != m_loggers.end()) {
default_logger_it->second.set_level(lvl);
}
}
for (const auto& logger_name : logger_names) {
ilog("Set logger ${name} log level to ${level}",
("name", logger_name)("level", mc::log::to_string(lvl)));
}
}
}
}
MC_REFLECT(mc::log::appender_config, (name)(type)(lib_path)(properties))
MC_REFLECT(mc::log::logger_config, (name)(level)(appenders))
MC_REFLECT(mc::log::logging_config, (appenders)(loggers))