2025-04-28 12:25:20 +08:00
|
|
|
|
/**
|
|
|
|
|
* @file XNLogger.h
|
|
|
|
|
* @author jinchao
|
|
|
|
|
* @brief 日志类
|
|
|
|
|
* @version 1.0
|
|
|
|
|
* @date 2025-01-08
|
|
|
|
|
*
|
|
|
|
|
* @copyright Copyright (c) 2025 COMAC
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
#pragma once
|
2025-05-20 15:39:40 +08:00
|
|
|
|
#include <string>
|
|
|
|
|
#include <mutex>
|
|
|
|
|
#include <fstream>
|
|
|
|
|
#include <chrono>
|
|
|
|
|
#include <filesystem>
|
2025-04-28 12:25:20 +08:00
|
|
|
|
#include <type_traits>
|
2025-05-20 15:39:40 +08:00
|
|
|
|
#include <iostream>
|
|
|
|
|
#include <iomanip>
|
|
|
|
|
#include <sstream>
|
2025-04-28 12:25:20 +08:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 日志类
|
|
|
|
|
*/
|
|
|
|
|
class XNLogger
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
/**
|
|
|
|
|
* @brief 日志等级
|
|
|
|
|
*/
|
|
|
|
|
enum LogLevel { Debug, Info, Warning, Error, Time };
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 获取日志类实例
|
|
|
|
|
* @return 日志类实例
|
|
|
|
|
*/
|
|
|
|
|
static XNLogger &instance()
|
|
|
|
|
{
|
|
|
|
|
static XNLogger instance;
|
|
|
|
|
return instance;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 日志输出
|
|
|
|
|
* @param level 日志等级
|
|
|
|
|
* @param message 日志消息
|
|
|
|
|
*/
|
2025-05-20 15:39:40 +08:00
|
|
|
|
void log(LogLevel level, const std::string &message);
|
2025-04-28 12:25:20 +08:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 启用控制台输出
|
|
|
|
|
* @param level 日志等级
|
|
|
|
|
* @param enable 是否启用
|
|
|
|
|
*/
|
|
|
|
|
void enableConsoleOutput(LogLevel level, bool enable);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 启用文件输出
|
|
|
|
|
* @param level 日志等级
|
|
|
|
|
* @param enable 是否启用
|
|
|
|
|
*/
|
|
|
|
|
void enableFileOutput(LogLevel level, bool enable);
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
/**
|
|
|
|
|
* @brief 构造函数
|
|
|
|
|
*/
|
2025-05-20 15:39:40 +08:00
|
|
|
|
XNLogger();
|
2025-04-28 12:25:20 +08:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 析构函数
|
|
|
|
|
*/
|
|
|
|
|
~XNLogger();
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 禁止拷贝构造
|
|
|
|
|
*/
|
2025-05-20 15:39:40 +08:00
|
|
|
|
XNLogger(const XNLogger &) = delete;
|
2025-04-28 12:25:20 +08:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 禁止赋值
|
|
|
|
|
*/
|
2025-05-20 15:39:40 +08:00
|
|
|
|
XNLogger &operator=(const XNLogger &) = delete;
|
2025-04-28 12:25:20 +08:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 日志文件路径
|
|
|
|
|
*/
|
2025-05-20 15:39:40 +08:00
|
|
|
|
std::string logFilePath;
|
2025-04-28 12:25:20 +08:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 控制台输出控制
|
|
|
|
|
*/
|
|
|
|
|
bool consoleOutputEnabled[5];
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 文件输出控制
|
|
|
|
|
*/
|
|
|
|
|
bool fileOutputEnabled[5];
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 日志文件
|
|
|
|
|
*/
|
2025-05-20 15:39:40 +08:00
|
|
|
|
std::ofstream logFile;
|
2025-04-28 12:25:20 +08:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 互斥锁
|
|
|
|
|
*/
|
2025-05-20 15:39:40 +08:00
|
|
|
|
std::mutex mutex;
|
2025-04-28 12:25:20 +08:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 日志等级转换为字符串
|
|
|
|
|
* @param level 日志等级
|
|
|
|
|
* @return 日志等级字符串
|
|
|
|
|
*/
|
2025-05-20 15:39:40 +08:00
|
|
|
|
std::string logLevelToString(LogLevel level) const;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 获取当前时间字符串
|
|
|
|
|
* @return 格式化的时间字符串
|
|
|
|
|
*/
|
|
|
|
|
std::string getCurrentTimeString() const;
|
2025-04-28 12:25:20 +08:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 控制台输出字体恢复颜色常量
|
|
|
|
|
*/
|
2025-05-20 15:39:40 +08:00
|
|
|
|
const std::string COLOR_RESET = "\033[0m";
|
2025-04-28 12:25:20 +08:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 调试颜色常量
|
|
|
|
|
*/
|
2025-05-20 15:39:40 +08:00
|
|
|
|
const std::string COLOR_DEBUG = "\033[34m"; // 蓝色
|
2025-04-28 12:25:20 +08:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 信息颜色常量
|
|
|
|
|
*/
|
2025-05-20 15:39:40 +08:00
|
|
|
|
const std::string COLOR_INFO = "\033[32m"; // 绿色
|
2025-04-28 12:25:20 +08:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 警告颜色常量
|
|
|
|
|
*/
|
2025-05-20 15:39:40 +08:00
|
|
|
|
const std::string COLOR_WARNING = "\033[33m"; // 黄色
|
2025-04-28 12:25:20 +08:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 错误颜色常量
|
|
|
|
|
*/
|
2025-05-20 15:39:40 +08:00
|
|
|
|
const std::string COLOR_ERROR = "\033[31m"; // 红色
|
2025-04-28 12:25:20 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 日志辅助类
|
|
|
|
|
*/
|
|
|
|
|
class XNLoggerHelper
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
/**
|
|
|
|
|
* @brief 带参数的日志输出
|
|
|
|
|
* @tparam Args 参数类型
|
|
|
|
|
* @param level 日志等级
|
|
|
|
|
* @param message 日志消息
|
|
|
|
|
* @param args 参数
|
|
|
|
|
*/
|
|
|
|
|
template <typename... Args>
|
|
|
|
|
inline static typename std::enable_if<(sizeof...(Args) > 0), void>::type
|
2025-05-20 15:39:40 +08:00
|
|
|
|
log(XNLogger::LogLevel level, const std::string &message, Args... args)
|
2025-04-28 12:25:20 +08:00
|
|
|
|
{
|
2025-05-20 15:39:40 +08:00
|
|
|
|
std::string formattedMessage = formatMessage(message, args...);
|
2025-04-28 12:25:20 +08:00
|
|
|
|
XNLogger::instance().log(level, formattedMessage);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 不带参数的日志输出
|
|
|
|
|
* @param level 日志等级
|
|
|
|
|
* @param message 日志消息
|
|
|
|
|
*/
|
2025-05-20 15:39:40 +08:00
|
|
|
|
inline static void log(XNLogger::LogLevel level, const std::string &message)
|
2025-04-28 12:25:20 +08:00
|
|
|
|
{
|
|
|
|
|
XNLogger::instance().log(level, message);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
2025-06-06 17:03:35 +08:00
|
|
|
|
/**
|
|
|
|
|
* @brief 将参数转换为字符串
|
|
|
|
|
* @tparam T 参数类型
|
|
|
|
|
* @param arg 要转换的参数
|
|
|
|
|
* @return 转换后的字符串
|
|
|
|
|
*/
|
2025-04-28 12:25:20 +08:00
|
|
|
|
template <typename T>
|
2025-05-20 15:39:40 +08:00
|
|
|
|
static std::string convertToString(const T &arg)
|
2025-04-28 12:25:20 +08:00
|
|
|
|
{
|
2025-06-06 17:03:35 +08:00
|
|
|
|
if constexpr (std::is_arithmetic_v<T>) {
|
2025-05-20 15:39:40 +08:00
|
|
|
|
return std::to_string(arg); // 处理数值类型
|
2025-06-06 17:03:35 +08:00
|
|
|
|
} else if constexpr (std::is_same_v<T, std::string>) {
|
|
|
|
|
return arg;
|
|
|
|
|
} else if constexpr (std::is_convertible_v<T, std::string>) {
|
|
|
|
|
return std::string(arg);
|
|
|
|
|
} else if constexpr (std::is_same_v<T, char *> || std::is_same_v<T, const char *>) {
|
2025-05-20 15:39:40 +08:00
|
|
|
|
return std::string(arg);
|
2025-06-06 17:03:35 +08:00
|
|
|
|
} else {
|
|
|
|
|
static_assert(std::is_arithmetic_v<T> || std::is_same_v<T, std::string>
|
|
|
|
|
|| std::is_convertible_v<T, std::string> || std::is_same_v<T, char *>
|
|
|
|
|
|| std::is_same_v<T, const char *>,
|
|
|
|
|
"A01021001: 不支持的类型转换,详见XNLogger.cpp:line 199");
|
2025-04-28 12:25:20 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-06 17:03:35 +08:00
|
|
|
|
/**
|
|
|
|
|
* @brief 格式化日志消息,顺序替换%1、%2、%3……
|
|
|
|
|
* @tparam Args 参数类型
|
|
|
|
|
* @param message 日志消息
|
|
|
|
|
* @param args 参数包
|
|
|
|
|
* @return 格式化后的消息
|
|
|
|
|
*/
|
|
|
|
|
template <typename... Args>
|
|
|
|
|
static std::string formatMessage(const std::string &message, Args &&...args)
|
2025-04-28 12:25:20 +08:00
|
|
|
|
{
|
2025-06-06 17:03:35 +08:00
|
|
|
|
static_assert(sizeof...(Args) <= 9,
|
|
|
|
|
"A01021002: 单条日志参数数量超过限制,详见XNLogger.cpp:line 216");
|
2025-05-20 15:39:40 +08:00
|
|
|
|
|
2025-06-06 17:03:35 +08:00
|
|
|
|
std::string result = message;
|
|
|
|
|
// 使用初始化列表展开参数包
|
|
|
|
|
int index = 1;
|
|
|
|
|
// 使用lambda和std::initializer_list展开参数包
|
|
|
|
|
(void)std::initializer_list<int>{(
|
|
|
|
|
[&result, &index](const auto &value) {
|
|
|
|
|
std::string placeholder = "%" + std::to_string(index++);
|
|
|
|
|
size_t pos = result.find(placeholder);
|
|
|
|
|
if (pos != std::string::npos) {
|
|
|
|
|
result.replace(pos, placeholder.length(), convertToString(value));
|
|
|
|
|
}
|
|
|
|
|
}(args),
|
|
|
|
|
0)...};
|
|
|
|
|
return result;
|
2025-04-28 12:25:20 +08:00
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 宏定义,用于输出调试日志
|
|
|
|
|
*/
|
|
|
|
|
#define LOG_DEBUG(message, ...) XNLoggerHelper::log(XNLogger::Debug, message, ##__VA_ARGS__)
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 宏定义,用于输出信息日志
|
|
|
|
|
*/
|
|
|
|
|
#define LOG_INFO(message, ...) XNLoggerHelper::log(XNLogger::Info, message, ##__VA_ARGS__)
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 宏定义,用于输出警告日志
|
|
|
|
|
*/
|
|
|
|
|
#define LOG_WARNING(message, ...) XNLoggerHelper::log(XNLogger::Warning, message, ##__VA_ARGS__)
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @brief 宏定义,用于输出错误日志
|
|
|
|
|
*/
|
|
|
|
|
#define LOG_ERROR(message, ...) XNLoggerHelper::log(XNLogger::Error, message, ##__VA_ARGS__)
|