* Copyright (c) Huawei Technologies Co., Ltd. 2025-2026. 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.
*/
#ifndef SAFE_LOG_H
#define SAFE_LOG_H
#include <array>
#include <atomic>
#include <cstddef>
#include <cstring>
#include <fstream>
#include <functional>
#include <mutex>
#include <sstream>
#include <string>
#include <thread>
#include <vector>
#include "safe_envvar.h"
#include "safe_io.h"
#include "string_utils.h"
namespace mindie_llm {
static const std::string ALL_COMPONENT = "__all__";
enum class LogSeverity : uint8_t { DEBUG, INFO, WARN, ERROR, CRITICAL, AUDIT, __COUNT__ };
const std::array<std::string, static_cast<uint8_t>(LogSeverity::__COUNT__) - 1>& GetLogSeverityNameArray();
const std::unordered_set<std::string>& GetAllLogSeverity();
bool String2LogSeverity(const std::string& level, LogSeverity& out);
enum class LogType : uint8_t { GENERAL = 0, REQUEST, TOKEN, TOKENIZER, __COUNT__ };
const std::array<std::string, static_cast<uint8_t>(LogType::__COUNT__)>& GetLogTypeNameArray();
bool String2LogType(const std::string& s, LogType& out);
enum class LogComponent : uint8_t { LLM = 0, LLMMODELS, SERVER, __COUNT__ };
const std::array<std::string, static_cast<uint8_t>(LogComponent::__COUNT__)>& GetComponentNameArray();
const std::string& Component2String(LogComponent c);
bool String2Component(const std::string& s, LogComponent& out);
struct MsgPkg {
LogComponent component;
LogType type;
std::string msg;
};
struct ComponentConfig {
std::atomic<LogSeverity> minLevel;
std::atomic<bool> toStdout;
std::atomic<bool> toFile;
std::atomic<bool> verbose;
};
struct LogSink {
std::string filePath;
std::string basePath;
std::ofstream ofs;
size_t curSize;
};
class LogManager {
public:
static LogManager& GetInstance();
bool IsPrintLog(LogComponent comp, LogSeverity level);
void Push(LogComponent comp, LogType type, std::string&& formattedLog);
ComponentConfig& GetComponentConfig(LogComponent comp);
public:
template <class T, class Parser, class Setter>
void LoadByComponentByEnv(const char* envKey, const std::string& defaultVal,
const std::unordered_set<std::string>& validValues, Parser parser, Setter setter) {
std::string val;
Result r = EnvVar::GetInstance().Get(envKey, defaultVal, val);
if (!r.IsOk()) {
throw std::runtime_error(r.message());
}
LoadByComponentByString<T>(val, validValues, parser, setter);
}
template <class T, class Parser, class Setter>
void LoadByComponentByString(const std::string& val, const std::unordered_set<std::string>& validValues,
Parser parser, Setter setter) {
auto kv = ParseKeyValueString(val, validValues, ALL_COMPONENT, ';', ':');
for (size_t i = 0; i < componentCfgs_.size(); ++i) {
auto& cfg = componentCfgs_[i];
const std::string& componentName = Component2String(static_cast<LogComponent>(i));
if (kv.count(ALL_COMPONENT)) {
const T value = parser(kv.at(ALL_COMPONENT));
setter(cfg, value);
}
if (kv.count(componentName)) {
const T value = parser(kv.at(componentName));
setter(cfg, value);
}
}
}
private:
LogManager();
~LogManager();
void Init();
void LoadComponentConfigs();
bool IsAnyComponentToFile() const;
void GetLogRotate();
void GetLogDirs();
void OpenLogFiles();
void CreateLogFilePath(LogType type);
void Writer();
void FlushLoop();
uint32_t GetLogFileSizeCutOff(LogType type) const;
uint32_t GetLogFileNumCutOff(LogType type) const;
void RotateLogs(LogType type);
void Stop();
private:
std::atomic<bool> isRunning_{false};
std::array<ComponentConfig, static_cast<size_t>(LogComponent::__COUNT__)> componentCfgs_;
std::string logDir_;
uint32_t logFileSize_;
uint32_t logFileNum_;
using BufferArray = std::array<std::vector<MsgPkg>, static_cast<size_t>(LogType::__COUNT__)>;
BufferArray buffers_;
std::array<std::mutex, static_cast<size_t>(LogType::__COUNT__)> bufferMutex_;
std::array<LogSink, static_cast<size_t>(LogType::__COUNT__)> sinks_;
std::thread flushThread_;
};
class Logger {
public:
Logger() = default;
explicit Logger(LogComponent comp, LogSeverity level);
bool ShouldLog() const;
template <typename T>
Logger& operator<<(const T& v) {
stream_ << v;
return *this;
}
void AssembleAndPush(LogType type, const char* file, size_t line, std::string& stack);
void Reset();
private:
LogComponent component_;
LogSeverity level_;
std::ostringstream stream_;
};
class LogLine {
public:
LogLine(LogComponent comp, LogSeverity level, const char* file, size_t line);
~LogLine();
LogLine(const LogLine&) = delete;
LogLine& operator=(const LogLine&) = delete;
LogLine(LogLine&&) = delete;
LogLine& operator=(LogLine&&) = delete;
template <typename T>
LogLine& operator<<(const T& v) {
if (enabled_) {
logger_ << v;
}
return *this;
}
LogLine& SetType(LogType t) {
type_ = t;
return *this;
}
private:
std::string BuildStackTrace();
private:
Logger& logger_;
bool enabled_{false};
LogType type_{LogType::GENERAL};
const char* file_;
size_t line_;
std::string stack_;
};
Logger& GetThreadLogger(LogComponent comp, LogSeverity level);
struct DynamicLogConfig {
std::string logSeverity;
int validHours{2};
std::string validTimeStamp;
};
struct DynamicLogDiff {
bool logSeverityChanged{false};
bool validHoursChanged{false};
bool validTimeStampChanged{false};
};
class DynamicLogManager {
public:
static DynamicLogManager& GetInstance();
private:
DynamicLogManager();
~DynamicLogManager();
void Init();
void Stop();
void GetDefaultLogSeverity();
void Monitor();
void GetAndSetLogConfig();
std::string GetConfigPath() const;
DynamicLogConfig LoadLogConfig(const std::string& configPath);
std::string GetLogSeverity(const Json& logConfig) const;
int GetTimeInterval(const Json& logConfig, int lastHours) const;
std::string GetTimeStamp(const Json& logConfig, const std::string& lastTs) const;
bool IsValidTimeFormat(const std::string& timeStr) const;
bool ParseTime(const std::string& s, std::time_t& out) const;
bool IsGreaterThanNow(const std::string& timeStr) const;
DynamicLogDiff DiffConfig(const DynamicLogConfig& current, const DynamicLogConfig& last);
void ResetToDefaultLogSeverity();
void ApplyLogSeverity(const std::string& severity);
void UpdateValidTimeStamp(DynamicLogConfig& cfg);
bool IsWithinValidRange(const DynamicLogConfig& cfg) const;
private:
const std::string keyLogConfig = "LogConfig";
const std::string keyLogSeverity = "dynamicLogLevel";
const std::string keyTimeInterval = "dynamicLogLevelValidHours";
const std::string keyTimeStamp = "dynamicLogLevelValidTime";
private:
std::atomic<bool> isRunning_{false};
std::thread monitorThread_;
std::mutex mtx_;
static constexpr uint8_t monitorInterval_{5};
static constexpr int defaultHours_{2};
std::string defaultLogSeverity_{"info"};
std::string lastLogSeverity_;
int lastValidHours_{defaultHours_};
std::string lastValidTimeStamp_;
};
void InitSystemLog();
}
#define LOG_DEBUG_LLM \
mindie_llm::LogLine(mindie_llm::LogComponent::LLM, mindie_llm::LogSeverity::DEBUG, __FILE__, __LINE__)
#define LOG_INFO_LLM \
mindie_llm::LogLine(mindie_llm::LogComponent::LLM, mindie_llm::LogSeverity::INFO, __FILE__, __LINE__)
#define LOG_WARN_LLM \
mindie_llm::LogLine(mindie_llm::LogComponent::LLM, mindie_llm::LogSeverity::WARN, __FILE__, __LINE__)
#define LOG_ERROR_LLM \
mindie_llm::LogLine(mindie_llm::LogComponent::LLM, mindie_llm::LogSeverity::ERROR, __FILE__, __LINE__)
#define LOG_CRITICAL_LLM \
mindie_llm::LogLine(mindie_llm::LogComponent::LLM, mindie_llm::LogSeverity::CRITICAL, __FILE__, __LINE__)
#define LOG_AUDIT_LLM \
mindie_llm::LogLine(mindie_llm::LogComponent::LLM, mindie_llm::LogSeverity::AUDIT, __FILE__, __LINE__)
#define LOG_DEBUG_MODEL \
mindie_llm::LogLine(mindie_llm::LogComponent::LLMMODELS, mindie_llm::LogSeverity::DEBUG, __FILE__, __LINE__)
#define LOG_INFO_MODEL \
mindie_llm::LogLine(mindie_llm::LogComponent::LLMMODELS, mindie_llm::LogSeverity::INFO, __FILE__, __LINE__)
#define LOG_WARN_MODEL \
mindie_llm::LogLine(mindie_llm::LogComponent::LLMMODELS, mindie_llm::LogSeverity::WARN, __FILE__, __LINE__)
#define LOG_ERROR_MODEL \
mindie_llm::LogLine(mindie_llm::LogComponent::LLMMODELS, mindie_llm::LogSeverity::ERROR, __FILE__, __LINE__)
#define LOG_CRITICAL_MODEL \
mindie_llm::LogLine(mindie_llm::LogComponent::LLMMODELS, mindie_llm::LogSeverity::CRITICAL, __FILE__, __LINE__)
#define LOG_AUDIT_MODEL \
mindie_llm::LogLine(mindie_llm::LogComponent::LLMMODELS, mindie_llm::LogSeverity::AUDIT, __FILE__, __LINE__)
#define LOG_DEBUG_SERVER \
mindie_llm::LogLine(mindie_llm::LogComponent::SERVER, mindie_llm::LogSeverity::DEBUG, __FILE__, __LINE__)
#define LOG_INFO_SERVER \
mindie_llm::LogLine(mindie_llm::LogComponent::SERVER, mindie_llm::LogSeverity::INFO, __FILE__, __LINE__)
#define LOG_WARN_SERVER \
mindie_llm::LogLine(mindie_llm::LogComponent::SERVER, mindie_llm::LogSeverity::WARN, __FILE__, __LINE__)
#define LOG_ERROR_SERVER \
mindie_llm::LogLine(mindie_llm::LogComponent::SERVER, mindie_llm::LogSeverity::ERROR, __FILE__, __LINE__)
#define LOG_CRITICAL_SERVER \
mindie_llm::LogLine(mindie_llm::LogComponent::SERVER, mindie_llm::LogSeverity::CRITICAL, __FILE__, __LINE__)
#define LOG_AUDIT_SERVER \
mindie_llm::LogLine(mindie_llm::LogComponent::SERVER, mindie_llm::LogSeverity::AUDIT, __FILE__, __LINE__)
#endif