#ifndef THREAD_LOCAL_LOGGER_H #define THREAD_LOCAL_LOGGER_H #include #include #include #include #include #include #include #include #include // 日志输出目标(新增:支持控制台/文件/两者) enum class LogTarget { CONSOLE, // 只写控制台 FILE, // 只写文件 BOTH // 控制台+文件(默认) }; // 日志级别 enum class LogLevel { DEBUG, INFO, WARN, ERROR, FATAL }; class ThreadLocalLogger { public: // 初始化当前线程的日志器(新增 log_target 参数) static void init( LogTarget target = LogTarget::BOTH, const std::string& log_dir = "./logs", LogLevel level = LogLevel::INFO, const std::string modStr = "", bool use_color = true // 控制台是否启用彩色输出 ) { ThreadLocalLogger& logger = getInstance(); logger.log_level_ = level; logger.target_ = target; logger.use_color_ = use_color; // 若需要写文件,才创建目录和打开文件 if (target == LogTarget::FILE || target == LogTarget::BOTH) { mkdir(log_dir.c_str()); std::string tid_str = std::to_string(getThreadId()); logger.log_file_path_ = log_dir + "/"+modStr+"_thread_" + tid_str + ".log"; logger.log_file_.open(logger.log_file_path_, std::ios::app | std::ios::out); if (!logger.log_file_.is_open()) { std::cerr << "线程 " << tid_str << " 打开日志文件失败:" << logger.log_file_path_ << std::endl; } } } // 销毁当前线程的日志器 static void destroy() { ThreadLocalLogger& logger = getInstance(); if (logger.log_file_.is_open()) { logger.log_file_.close(); } } // 日志输出接口(保持不变) template static void debug(const char* file, int line, Args&&... args) { log(LogLevel::DEBUG, file, line, std::forward(args)...); } template static void info(const char* file, int line, Args&&... args) { log(LogLevel::INFO, file, line, std::forward(args)...); } template static void warn(const char* file, int line, Args&&... args) { log(LogLevel::WARN, file, line, std::forward(args)...); } template static void error(const char* file, int line, Args&&... args) { log(LogLevel::ERROR, file, line, std::forward(args)...); } template static void fatal(const char* file, int line, Args&&... args) { log(LogLevel::FATAL, file, line, std::forward(args)...); std::exit(EXIT_FAILURE); } private: static ThreadLocalLogger& getInstance() { thread_local ThreadLocalLogger instance; return instance; } ThreadLocalLogger() = default; ~ThreadLocalLogger() { if (log_file_.is_open()) { log_file_.close(); } } ThreadLocalLogger(const ThreadLocalLogger&) = delete; ThreadLocalLogger& operator=(const ThreadLocalLogger&) = delete; // 核心日志输出函数(根据目标输出) template static void log(LogLevel level, const char* file, int line, Args&&... args) { ThreadLocalLogger& logger = getInstance(); if (level < logger.log_level_) { return; } // 生成日志前缀 std::string prefix = generatePrefix(level, file, line); // 格式化日志内容 std::stringstream content_ss; (content_ss << ... << std::forward(args)); std::string content = prefix + " " + content_ss.str() + "\n"; // 根据输出目标写入 if (logger.target_ == LogTarget::CONSOLE || logger.target_ == LogTarget::BOTH) { // 控制台输出(支持彩色) if (logger.use_color_) { std::cout << getColorCode(level) << content << "\033[0m"; // 重置颜色 } else { std::cout << content; } std::cout.flush(); } if (logger.target_ == LogTarget::FILE || logger.target_ == LogTarget::BOTH) { // 文件输出(无颜色) if (logger.log_file_.is_open()) { logger.log_file_ << content; logger.log_file_.flush(); } } } // 生成日志前缀(保持不变) static std::string generatePrefix(LogLevel level, const char* file, int line) { auto now = std::chrono::system_clock::now(); auto ms = std::chrono::duration_cast(now.time_since_epoch()) % 1000; std::time_t time = std::chrono::system_clock::to_time_t(now); std::tm tm = *std::localtime(&time); std::stringstream ss; ss << "[" << std::put_time(&tm, "%Y-%m-%d %H:%M:%S") << "." << std::setw(3) << std::setfill('0') << ms.count() << "] [" << levelToString(level) << "] " << "[tid:" << getThreadId() << "] " << "(" << getShortFileName(file) << ":" << line << ")"; return ss.str(); } // 日志级别转字符串(保持不变) static const char* levelToString(LogLevel level) { switch (level) { case LogLevel::DEBUG: return "DEBUG"; case LogLevel::INFO: return "INFO"; case LogLevel::WARN: return "WARN"; case LogLevel::ERROR: return "ERROR"; case LogLevel::FATAL: return "FATAL"; default: return "UNKNOWN"; } } // 控制台彩色输出(新增) static const char* getColorCode(LogLevel level) { switch (level) { case LogLevel::DEBUG: return "\033[34m"; // 蓝色 case LogLevel::INFO: return "\033[32m"; // 绿色 case LogLevel::WARN: return "\033[33m"; // 黄色 case LogLevel::ERROR: return "\033[31m"; // 红色 case LogLevel::FATAL: return "\033[41m"; // 红色背景 default: return ""; } } // 提取短文件名(保持不变) static const char* getShortFileName(const char* file) { const char* short_file = std::strrchr(file, '/'); return short_file ? short_file + 1 : file; } // 获取线程ID(保持不变) static uint64_t getThreadId() { std::thread::id tid = std::this_thread::get_id(); std::stringstream ss; ss << tid; uint64_t tid_num; ss >> tid_num; return tid_num; } // 创建目录(跨平台,保持不变) static int mkdir(const std::string& path) { #ifdef _WIN32 return ::_mkdir(path.c_str()); #else return ::mkdir(path.c_str(), S_IRWXU | S_IRWXG | S_IRWXO); #endif } // 线程私有成员变量(新增输出目标和彩色开关) LogLevel log_level_ = LogLevel::INFO; LogTarget target_ = LogTarget::BOTH; // 默认输出到控制台+文件 std::string log_file_path_; std::ofstream log_file_; bool use_color_ = true; // 控制台彩色开关 }; // 日志宏(保持不变) #define LOG_DEBUG(...) ThreadLocalLogger::debug(__FILE__, __LINE__, __VA_ARGS__) #define LOG_INFO(...) ThreadLocalLogger::info(__FILE__, __LINE__, __VA_ARGS__) #define LOG_WARN(...) ThreadLocalLogger::warn(__FILE__, __LINE__, __VA_ARGS__) #define LOG_ERROR(...) ThreadLocalLogger::error(__FILE__, __LINE__, __VA_ARGS__) #define LOG_FATAL(...) ThreadLocalLogger::fatal(__FILE__, __LINE__, __VA_ARGS__) #endif // THREAD_LOCAL_LOGGER_H