From 3ef3367fb535a41f08ff5ea87a62849d661a45e0 Mon Sep 17 00:00:00 2001 From: user_ch-ml <1303079376@qq.com> Date: 星期三, 02 七月 2025 15:59:58 +0800 Subject: [PATCH] Merge branch 'master' of ssh://115.28.86.8:29418/~admin/智能网联_25-0305_617_v1 --- Server/王鹏哲/code/logger.h | 334 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ Server/王鹏哲/log/日志_王鹏哲_20250701.doc | 0 Server/王鹏哲/log/日志_王鹏哲_20250629.doc | 0 Server/王鹏哲/log/日志_王鹏哲_20250630.doc | 0 4 files changed, 334 insertions(+), 0 deletions(-) diff --git "a/Server/\347\216\213\351\271\217\345\223\262/code/logger.h" "b/Server/\347\216\213\351\271\217\345\223\262/code/logger.h" new file mode 100644 index 0000000..10cf437 --- /dev/null +++ "b/Server/\347\216\213\351\271\217\345\223\262/code/logger.h" @@ -0,0 +1,334 @@ +#pragma once +#include "common.h" +#include <cstring> // 用于strerror +#include <cstdio> // 用于std::rename, std::remove +#include <utility> // 用于std::forward +// 日志级别枚举 +enum class LogLevel { + TRACE, // 最详细的跟踪信息 + DEBUG, // 调试信息 + INFO, // 常规信息 + WARNING, // 警告信息 + ERRORL, // 错误信息 + FATAL // 严重错误 +}; + +// 将日志级别转换为字符串 +inline const char* levelToString(LogLevel level) { + switch (level) { + case LogLevel::TRACE: return "TRACE"; + case LogLevel::DEBUG: return "DEBUG"; + case LogLevel::INFO: return "INFO"; + case LogLevel::WARNING: return "WARNING"; + case LogLevel::ERRORL: return "ERROR"; + case LogLevel::FATAL: return "FATAL"; + default: return "UNKNOWN"; + } +} + +// 日志配置结构 +struct LogConfig { + LogLevel minLevel = LogLevel::INFO; // 最小日志级别 + bool consoleOutput = true; // 是否输出到控制台 + bool fileOutput = false; // 是否输出到文件 + std::string logFilePath = "temp.log";// 日志文件路径 + bool includeTimestamp = true; // 是否包含时间戳 + bool includeThreadId = false; // 是否包含线程ID + size_t maxFileSize = 10 * 1024 * 1024; // 最大文件大小 (10MB) + int maxBackupFiles = 5; // 最大备份文件数 +}; + +class Logger { +public: + // 获取日志实例 + static Logger& instance() { + static Logger instance; + return instance; + } + + // 配置日志系统 + void configure(const LogConfig& config) { + std::lock_guard<std::mutex> lock(configMutex_); + config_ = config; + + // 如果启用文件输出,确保文件已打开 + if (config.fileOutput) { + openLogFile(); + } + } + + // 记录日志 (模板方法,支持任何可流式输出的类型) + template<typename... Args> + void log(LogLevel level, Args&&... args) { + if (shutdown_) return; + + // 复制配置以最小化锁范围 + LogConfig currentConfig; + { + std::lock_guard<std::mutex> lock(configMutex_); + if (level < config_.minLevel) return; + currentConfig = config_; + } + + // 构造日志消息 + std::ostringstream oss; + formatLogPrefix(oss, level, currentConfig); + + using expander = int[]; + (void)expander { + 0, (void(oss << std::forward<Args>(args)), 0)... + }; + + // 添加换行符 + oss << '\n'; + std::string message = oss.str(); + + // 直接写入日志 + /*writeLog(message);*/ + + // 将日志消息加入队列 + { + std::lock_guard<std::mutex> lock(queueMutex_); + logQueue_.push(std::move(message)); + } + + // 通知日志线程有新消息 + queueCond_.notify_one(); + } + // 便捷日志方法 + template<typename... Args> void trace(Args&&... args) { + log(LogLevel::TRACE, std::forward<Args>(args)...); + } + + template<typename... Args> void debug(Args&&... args) { + log(LogLevel::DEBUG, std::forward<Args>(args)...); + } + + template<typename... Args> void info(Args&&... args) { + log(LogLevel::INFO, std::forward<Args>(args)...); + } + + template<typename... Args> void warning(Args&&... args) { + log(LogLevel::WARNING, std::forward<Args>(args)...); + } + + template<typename... Args> void error(Args&&... args) { + log(LogLevel::ERRORL, std::forward<Args>(args)...); + } + + template<typename... Args> void fatal(Args&&... args) { + log(LogLevel::FATAL, std::forward<Args>(args)...); + flush(); + std::exit(EXIT_FAILURE); + } + + // 刷新日志缓冲区 + void flush() { + std::lock_guard<std::mutex> lock(fileMutex_); + if (logFile_.is_open()) { + logFile_.flush(); + } + } + + // 析构函数 + ~Logger() { + shutdown(); + flush(); + } + // 安全关闭日志系统 + void shutdown() { + if (shutdown_) return; + + shutdown_ = true; + queueCond_.notify_all(); // 唤醒线程以处理退出 + + if (workerThread_.joinable()) { + workerThread_.join(); // 等待线程结束 + } + + flush(); // 最终刷新 + } + // 删除拷贝构造函数和赋值运算符 + Logger(const Logger&) = delete; + Logger& operator=(const Logger&) = delete; + +private: + LogConfig config_; // 日志配置 + std::ofstream logFile_; // 日志文件流 + std::mutex configMutex_; // 配置互斥锁 + std::mutex fileMutex_; // 文件操作互斥锁 + std::atomic<bool> shutdown_{ false }; // 关闭标志 + // 新增线程安全队列相关成员 + std::queue<std::string> logQueue_; // 日志消息队列 + std::mutex queueMutex_; // 队列互斥锁 + std::condition_variable queueCond_; // 队列条件变量 + std::thread workerThread_; // 日志写入线程 + + // 私有构造函数 + Logger() { + // 启动日志写入线程 + workerThread_ = std::thread(&Logger::processLogs, this); + }; + // 日志处理线程函数 + void processLogs() { + while (true) { + std::unique_lock<std::mutex> lock(queueMutex_); + + // 等待新日志或关闭信号 + queueCond_.wait(lock, [this] { + return !logQueue_.empty() || shutdown_; + }); + + // 处理关闭信号 + if (shutdown_ && logQueue_.empty()) { + break; + } + + // 取出所有待处理日志 + std::queue<std::string> tempQueue; + std::swap(logQueue_, tempQueue); + lock.unlock(); + + // 处理所有取出的日志 + while (!tempQueue.empty()) { + writeLog(tempQueue.front()); + tempQueue.pop(); + } + } + } + + // 格式化日志前缀 + void formatLogPrefix(std::ostringstream& oss, LogLevel level, const LogConfig& config) { + // 日志级别标签 + oss << "[" << levelToString(level) << "] "; + + // 时间戳 + if (config.includeTimestamp) { + auto now = std::chrono::system_clock::now(); + auto in_time_t = std::chrono::system_clock::to_time_t(now); + auto ms = std::chrono::duration_cast<std::chrono::milliseconds>( + now.time_since_epoch()) % 1000; + + std::tm tm; +#ifdef _WIN32 + localtime_s(&tm, &in_time_t); +#else + localtime_r(&in_time_t, &tm); +#endif + + oss << std::put_time(&tm, "%Y-%m-%d %H:%M:%S"); + oss << '.' << std::setfill('0') << std::setw(3) << ms.count() << " "; + } + + // 线程ID + if (config.includeThreadId) { + oss << "[Thread:" << std::this_thread::get_id() << "] "; + } + } + + // 打开日志文件 + void openLogFile() { + // 关闭当前文件(如果已打开) + if (logFile_.is_open()) { + logFile_.close(); + } + + // 检查文件大小,必要时轮转 + std::ifstream in(config_.logFilePath, std::ios::binary | std::ios::ate); + if (in) { + auto size = in.tellg(); + if (static_cast<size_t>(size) >= config_.maxFileSize) { + rotateLogFiles(); + } + } + in.close(); + + // 打开新日志文件 + logFile_.open(config_.logFilePath, std::ios::out | std::ios::app); + if (!logFile_.is_open()) { + std::cerr << "Failed to open log file: " + << config_.logFilePath << " - " + << errno << std::endl; + } + } + + // 轮转日志文件 + void rotateLogFiles() { + // 删除最旧的备份文件 + if (config_.maxBackupFiles > 0) { + std::string oldestFile = config_.logFilePath + "." + std::to_string(config_.maxBackupFiles); + std::remove(oldestFile.c_str()); + + // 重命名现有备份文件 + for (int i = config_.maxBackupFiles - 1; i >= 1; i--) { + std::string oldName = config_.logFilePath + "." + std::to_string(i); + std::string newName = config_.logFilePath + "." + std::to_string(i + 1); + + if (fileExists(oldName)) { + std::rename(oldName.c_str(), newName.c_str()); + } + } + + // 重命名当前日志文件为备份1 + if (fileExists(config_.logFilePath)) { + std::string newName = config_.logFilePath + ".1"; + std::rename(config_.logFilePath.c_str(), newName.c_str()); + } + } + } + + // 检查文件是否存在 + bool fileExists(const std::string& path) { + std::ifstream f(path); + return f.good(); + } + + // 实际写入日志 + void writeLog(const std::string& message) { + LogConfig currentConfig; + { + std::lock_guard<std::mutex> configLock(configMutex_); + currentConfig = config_; + } + + // 输出到控制台 + if (currentConfig.consoleOutput) { + if (message.find("[ERROR]") != std::string::npos || + message.find("[FATAL]") != std::string::npos) { + std::cerr << message; + } + else { + std::cout << message; + } + } + + // 输出到文件 + if (currentConfig.fileOutput) { + std::lock_guard<std::mutex> fileLock(fileMutex_); + if (!logFile_.is_open() || !logFile_.good()) { + openLogFile(); + } + + if (logFile_.good()) { + logFile_ << message; + logFile_.flush(); // 实时刷新到磁盘 + + // 检查文件大小 + auto pos = logFile_.tellp(); + if (static_cast<size_t>(pos) >= currentConfig.maxFileSize) { + logFile_.close(); + rotateLogFiles(); + logFile_.open(currentConfig.logFilePath, std::ios::out | std::ios::app); + } + } + } + } +}; + +// 日志宏定义 - 提供文件名和行号信息 +#define LOG_TRACE(...) Logger::instance().trace("(", __FILE__, ":", __LINE__, ") ", __VA_ARGS__) +#define LOG_DEBUG(...) Logger::instance().debug("(", __FILE__, ":", __LINE__, ") ", __VA_ARGS__) +#define LOG_INFO(...) Logger::instance().info(__VA_ARGS__) +#define LOG_WARN(...) Logger::instance().warning("(", __FILE__, ":", __LINE__, ") ", __VA_ARGS__) +#define LOG_ERROR(...) Logger::instance().error("(", __FILE__, ":", __LINE__, ") ", __VA_ARGS__) +#define LOG_FATAL(...) Logger::instance().fatal("(", __FILE__, ":", __LINE__, ") ", __VA_ARGS__) \ No newline at end of file diff --git "a/Server/\347\216\213\351\271\217\345\223\262/log/\346\227\245\345\277\227_\347\216\213\351\271\217\345\223\262_20250629.doc" "b/Server/\347\216\213\351\271\217\345\223\262/log/\346\227\245\345\277\227_\347\216\213\351\271\217\345\223\262_20250629.doc" new file mode 100644 index 0000000..25b1c97 --- /dev/null +++ "b/Server/\347\216\213\351\271\217\345\223\262/log/\346\227\245\345\277\227_\347\216\213\351\271\217\345\223\262_20250629.doc" Binary files differ diff --git "a/Server/\347\216\213\351\271\217\345\223\262/log/\346\227\245\345\277\227_\347\216\213\351\271\217\345\223\262_20250630.doc" "b/Server/\347\216\213\351\271\217\345\223\262/log/\346\227\245\345\277\227_\347\216\213\351\271\217\345\223\262_20250630.doc" new file mode 100644 index 0000000..78def34 --- /dev/null +++ "b/Server/\347\216\213\351\271\217\345\223\262/log/\346\227\245\345\277\227_\347\216\213\351\271\217\345\223\262_20250630.doc" Binary files differ diff --git "a/Server/\347\216\213\351\271\217\345\223\262/log/\346\227\245\345\277\227_\347\216\213\351\271\217\345\223\262_20250701.doc" "b/Server/\347\216\213\351\271\217\345\223\262/log/\346\227\245\345\277\227_\347\216\213\351\271\217\345\223\262_20250701.doc" new file mode 100644 index 0000000..61462ad --- /dev/null +++ "b/Server/\347\216\213\351\271\217\345\223\262/log/\346\227\245\345\277\227_\347\216\213\351\271\217\345\223\262_20250701.doc" Binary files differ -- Gitblit v1.8.0