* Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
* MindIE 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 "log.h"
#include <sys/utsname.h>
#include <cstdlib>
#include <regex>
#include <stdexcept>
#include "common_util.h"
#include "log_utils.h"
#include "spdlog.h"
namespace mindie_llm {
constexpr size_t MAX_MSG_SIZE = 2048;
std::unordered_map<LoggerType, std::shared_ptr<Log>> Log::loggerMap;
std::once_flag Log::atbLogInitFlag{};
const std::unordered_set<std::string> SpecialChar = {"\n", "\r", "\u007f", "\b", "\f", "\t", "\v", "\u000b", "%08",
"%09", "%0a", "%0b", "%0c", "%0d", "%7f", "//", "\\", "&"};
Log::Log(const std::shared_ptr<LogConfig> logConfig)
: logConfig_(logConfig ? std::make_shared<LogConfig>(*logConfig) : nullptr) {}
std::shared_ptr<Log> Log::GetInstance(LoggerType loggerType) {
if (loggerType == LoggerType::ATB) {
std::call_once(atbLogInitFlag, [] {
CreateInstance(LoggerType::ATB);
mindie_llm::LogLevelDynamicHandler::Init(5000);
});
}
auto logger = loggerMap.find(loggerType);
if (logger != loggerMap.end()) {
return logger->second;
}
return nullptr;
}
void Log::CreateAllLoggers() {
for (int i = 0; i < static_cast<int>(LoggerType::MAX_LOGGER_TYPE); i++) {
LoggerType loggerType = static_cast<LoggerType>(i);
if (loggerType == LoggerType::ATB) {
continue;
}
CreateInstance(loggerType);
}
}
void Log::CreateInstance(LoggerType loggerType) {
auto logger = loggerMap.find(loggerType);
if (logger != loggerMap.end()) {
return;
}
std::string name = LogUtils::GetLoggerNameStr(loggerType);
std::shared_ptr<LogConfig> logConfig = std::make_shared<LogConfig>();
if (!logConfig || logConfig->Init(loggerType) != LOG_OK) {
throw std::runtime_error(name + " failed to init the logConfig.");
}
if (logConfig->ValidateSettings() != LOG_OK) {
throw std::runtime_error(name + " log params validation failed.");
}
std::shared_ptr<Log> targetLogger = std::make_shared<Log>(logConfig);
if (targetLogger == nullptr) {
throw std::runtime_error(name + " failed to create logger.");
}
if (targetLogger->Initialize(loggerType) != LOG_OK) {
throw std::runtime_error(name + " failed to initialize inner logger.");
}
loggerMap[loggerType] = targetLogger;
}
const std::shared_ptr<LogConfig> Log::GetLogConfig(LoggerType loggerType) {
std::shared_ptr<Log> logger = GetInstance(loggerType);
if (logger != nullptr) {
return logger->logConfig_;
}
throw std::runtime_error(LogUtils::GetLoggerNameStr(loggerType) + " logger is null, failed to get LogConfig.");
}
void Log::LogMessage(LoggerType loggerType, LogLevel level, const std::string &message) {
std::shared_ptr<Log> logger = GetInstance(loggerType);
if (logger == nullptr || logger->innerLogger_ == nullptr) {
throw std::runtime_error(LogUtils::GetLoggerNameStr(loggerType) + " logger is null.");
}
if (message.empty() || level < 0 || level > MAX_LOG_LEVEL_LIMIT) {
throw std::runtime_error(LogUtils::GetLoggerNameStr(loggerType) + " invalid log params.");
}
std::string truncatedMsg = message;
if (message.size() > MAX_MSG_SIZE) {
truncatedMsg = message.substr(0, MAX_MSG_SIZE);
truncatedMsg += "...[TRUNCATED]";
}
SanitizeMessage(truncatedMsg);
logger->innerLogger_->log(level, "{}", truncatedMsg.c_str());
if (loggerType == LoggerType::SECURITY || loggerType == LoggerType::DEBUG) {
logger->innerLogger_->flush();
}
}
void Log::SetFileEventHandle(spdlog::file_event_handlers &handlers) const {
handlers.after_open = [](spdlog::filename_t filename, const std::FILE *fstream) {
std::string baseDir = "/";
std::string errMsg;
std::string regularPath;
if (!FileUtils::RegularFilePath(filename, baseDir, errMsg, true, regularPath)) {
std::cerr << "Regular file failed by " << errMsg << std::endl;
throw std::runtime_error("LLM Regular log file path failed");
}
chmod(regularPath.c_str(), MAX_OPEN_LOG_FILE_PERM);
(void)fstream;
};
handlers.before_close = [](spdlog::filename_t filename, const std::FILE *fstream) {
std::string baseDir = "/";
std::string errMsg;
std::string regularPath;
if (!FileUtils::RegularFilePath(filename, baseDir, errMsg, true, regularPath)) {
std::cerr << "Regular file failed by " << errMsg << std::endl;
throw std::runtime_error("LLM Regular log file path failed");
}
chmod(regularPath.c_str(), MAX_CLOSE_LOG_FILE_PERM);
(void)fstream;
};
}
bool Log::ShouldPrintToStdout(LoggerType loggerType) { return loggerType != LoggerType::MINDIE_LLM_TOKEN; }
int Log::Initialize(LoggerType loggerType) {
std::vector<spdlog::sink_ptr> sinks;
try {
if (logConfig_->logToStdOut_ && ShouldPrintToStdout(loggerType)) {
auto stdoutSink = std::make_shared<spdlog::sinks::stdout_sink_mt>();
stdoutSink->set_pattern(GetLogPattern(loggerType));
sinks.push_back(stdoutSink);
}
if (logConfig_->logToFile_) {
spdlog::file_event_handlers handlers;
SetFileEventHandle(handlers);
auto fileSink = std::make_shared<GenericRotationFileSink>(
logConfig_->logFilePath_, logConfig_->logFileSize_, logConfig_->logFileCount_ - 1, handlers,
logConfig_->baseDir_);
sinks.push_back(fileSink);
spdlog::flush_every(std::chrono::seconds(1));
}
if (loggerType == LoggerType::ATB) {
innerLogger_ =
std::make_shared<spdlog::logger>(LogUtils::GetLoggerNameStr(loggerType), sinks.begin(), sinks.end());
} else {
innerLogger_ = std::make_shared<spdlog::async_logger>(LogUtils::GetLoggerNameStr(loggerType), sinks.begin(),
sinks.end(), spdlog::thread_pool(),
spdlog::async_overflow_policy::block);
}
innerLogger_->set_level(static_cast<spdlog::level::level_enum>(logConfig_->logLevel_));
innerLogger_->set_pattern("%Y-%m-%d %H:%M:%S.%f %t %v");
innerLogger_->info(GetLoggerFormat(loggerType));
innerLogger_->set_pattern(GetLogPattern(loggerType));
innerLogger_->flush_on(spdlog::level::err);
} catch (const spdlog::spdlog_ex &e) {
std::stringstream errMsg;
errMsg << "Failed to create inner logger: " << e.what();
throw std::runtime_error(errMsg.str());
}
return LOG_OK;
}
std::string Log::GetLogPattern(LoggerType loggerType) {
std::string moduleName = LogUtils::GetModuleName(loggerType);
std::string pattern = "[%Y-%m-%d %H:%M:%S.%f] %v";
if (loggerType <= LoggerType::ATB) {
if (logConfig_->logVerbose_) {
pattern = "[%Y-%m-%d %H:%M:%S.%f] [%P] [%t] [" + moduleName + "] %v";
}
} else if (loggerType == LoggerType::SECURITY || loggerType == LoggerType::DEBUG) {
pattern = "[%Y-%m-%d %H:%M:%S.%f] %v";
if (logConfig_->logVerbose_) {
pattern = "[%Y-%m-%d %H:%M:%S.%f] [%P] [%t] [" + moduleName + "] %v";
}
if (loggerType == LoggerType::SECURITY) {
pattern = "[%Y-%m-%d %H:%M:%S.%f] [%P] %v";
}
}
return pattern;
}
std::string Log::GetLoggerFormat(LoggerType loggerType) {
return spdlog::fmt_lib::format(
"LLM log default format: [yyyy-mm-dd hh:mm:ss.uuuuuu][processid] [threadid] [{}] "
"[loglevel] [file:line] [status code] msg",
LogUtils::GetModuleName(loggerType));
}
void Log::Flush() {
for (const auto &pair : loggerMap) {
const auto &logger = pair.second;
if (logger && logger->innerLogger_) {
logger->innerLogger_->flush();
}
}
}
void Log::GetErrorCode(std::ostringstream &oss, const std::string &args) {
std::ostringstream errorcode;
errorcode << args;
std::string errorcodeStr = errorcode.str();
if (errorcodeStr.size() > 0) {
auto it = ERROR_CODE_MAPPING.find(errorcodeStr);
if (it != ERROR_CODE_MAPPING.end()) {
oss << "[" << it->second << "] ";
} else {
std::cout << "ErrorCode not found in errorCodeMap!" << std::endl;
}
}
}
std::string Log::GetLevelStr(const LogLevel level) {
switch (level) {
case LogLevel::critical:
return "[CRITICAL] ";
case LogLevel::err:
return "[ERROR] ";
case LogLevel::warn:
return "[WARN] ";
case LogLevel::info:
return "[INFO] ";
case LogLevel::debug:
return "[DEBUG] ";
default:
return "[] ";
}
}
std::string Log::GetUserName() {
struct utsname buf;
if (uname(&buf) != 0) {
*buf.nodename = '\0';
}
return buf.nodename;
}
void Log::SanitizeMessage(std::string &msg) {
for (const auto &bad : SpecialChar) {
size_t pos = 0;
while ((pos = msg.find(bad, pos)) != std::string::npos) {
msg.replace(pos, bad.size(), "_");
pos += 1;
}
}
}
void Log::SetAllLogLevel(LogLevel level) {
for (int i = 0; i < static_cast<int>(LoggerType::MAX_LOGGER_TYPE); i++) {
LoggerType loggerType = static_cast<LoggerType>(i);
SetLogLevel(loggerType, level);
}
}
void Log::SetLogLevel(LoggerType loggerType, LogLevel level) {
auto logger = loggerMap.find(loggerType);
if (logger == loggerMap.end()) {
return;
}
GetLogConfig(loggerType)->logLevel_ = level;
std::shared_ptr<spdlog::logger> innerLog = logger->second->innerLogger_;
if (innerLog) {
innerLog->set_level(level);
for (auto &sink : innerLog->sinks()) {
sink->set_level(level);
}
innerLog->info("Log level changed to: {}", GetLevelStr(level));
}
}
}