#include "ServerLogger.h" #include #include #include #include #include #include #include // 格式化用的辅助函数 struct DurationView { long double value; const char* unit; }; inline std::tm ToTm(std::chrono::system_clock::time_point tp) { return fmt::localtime(std::chrono::system_clock::to_time_t(tp)); } inline std::string FormatTime(const std::tm& tm, const std::string& spec) { char buf[128] = {}; const char* fmt = spec.empty() ? "%Y-%m-%d %H:%M:%S" : spec.c_str(); std::strftime(buf, sizeof(buf), fmt, &tm); return buf; } inline DurationView NormalizeDuration(std::chrono::nanoseconds ns) { long double v = (long double)ns.count(); constexpr long double ns_1 = 1.0L; constexpr long double us = 1000.0L; constexpr long double ms = 1000000.0L; constexpr long double s = 1000000000.0L; constexpr long double m = 60.0L * s; constexpr long double h = 60.0L * m; constexpr long double d = 24.0L * h; if (v >= d) return { v / d, "d" }; if (v >= h) return { v / h, "h" }; if (v >= m) return { v / m, "m" }; if (v >= s) return { v / s, "s" }; if (v >= ms) return { v / ms, "ms" }; if (v >= us) return { v / us, "us" }; return { v / ns_1, "ns" }; } // ==================== 1. 锁死在 .cpp 内部的隐藏格式化器 ==================== // 让 ServerLogger.cpp 内部的 fmt 彻底看懂 uns::LogArg 变体 template <> struct fmt::formatter { constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } template auto format(const uns::LogArg& la, FormatContext& ctx) const { return std::visit([&] (auto&& arg) -> decltype(ctx.out()) { using T = std::decay_t; // A. 针对容器的运行时展开 if constexpr (std::is_same_v) { std::vector items; // 【修正】去掉瞎编的 container_ptr,直接传入 items 容器供内部闭包填充 arg.to_log_args(arg.ptr, items); fmt::format_to(ctx.out(), "["); for (size_t i = 0; i < items.size(); ++i) { if (i > 0) fmt::format_to(ctx.out(), ", "); fmt::format_to(ctx.out(), fmt::runtime("{}"), items[i]); } return fmt::format_to(ctx.out(), "]"); } // B. 针对键值对的运行时展开 else if constexpr (std::is_same_v) { std::vector items; arg.to_log_args(arg.ptr, items); // 假设 PairCapturer 也是相同的解包逻辑 if (items.size() >= 2) fmt::format_to(ctx.out(), "{}: {}", items[0], items[1]); return ctx.out(); } // C. 针对延迟执行函数的运行时展开 else if constexpr (std::is_same_v) { // 【注意】请根据你 FuncCapturer 内部实际的求值函数名修改(如 .to_string() 或 .eval()) return fmt::format_to(ctx.out(), "Function{{ptr: {}, type: {}}}", arg.code_ptr, arg.is_closure ? "Closure" : "PureFunc"); } // D. 时间相关的内容 else if constexpr (std::is_same_v) { auto out = ctx.out(); try { if (arg.mode == uns::TimeMode::Point) { std::tm tm = ToTm(arg.tp); std::string s = FormatTime(tm, arg.format_spec); return fmt::format_to(out, "{}", s); } else if (arg.mode == uns::TimeMode::TMPoint) { std::string s = FormatTime(arg._tm, arg.format_spec); return fmt::format_to(out, "{}", s); } else { auto norm = NormalizeDuration(arg.duration); return fmt::format_to(out, "{:.3f}{}", norm.value, norm.unit); } } catch (...) { return fmt::format_to(out, "[time_error]"); } } // E. 基础原生类型 else return fmt::format_to(ctx.out(), "{}", arg); }, la.value); } }; std::string uns::toBinary(long number, int bits) { bool negitive = (number < 0); unsigned long positive = (negitive ? -number : number); return (negitive ? "-" + toBinary(positive, bits) : toBinary(positive, bits)); } std::string uns::toBinary(std::uint32_t number, int bits) { return toBinary(static_cast(number), bits); } std::string uns::toBinary(unsigned long number, int bits) { std::string res; while (true) { res += std::to_string(number % 2); number = number / 2; if (number == 0) break; } std::reverse(res.begin(), res.end()); while (res.size() < bits) res = "0" + res; return "0b" + res; } bool StartsWith(std::string_view str, std::string_view prefix) { if (str.size() < prefix.size()) return false; return str.compare(0, prefix.size(), prefix) == 0; } bool IsSizeUnit(std::string_view str) { if (str == "bit") return true; if (str.empty()) return false; char last = str.back(); return last == 'b' || last == 'B'; } bool IsNonNegativeInteger(std::string_view str, int& value) { if (str.empty()) return false; int result = 0; auto [ptr, ec] = std::from_chars(str.data(), str.data() + str.size(), result); if (ec != std::errc() || ptr != str.data() + str.size()) return false; if (result < 0) return false; value = result; return true; } std::pair ParseSizeFormat(std::string_view input) { constexpr std::pair default_value = { "B", 2 }; if (!StartsWith(input, "siz")) return { std::string(default_value.first), default_value.second }; std::string_view body = input.substr(3); if (body.empty() || body.front() != '-') return { std::string(default_value.first), default_value.second }; body.remove_prefix(1); size_t first_dash = body.find('-'); if (first_dash == std::string_view::npos) { // siz-xx 或 siz-x if (IsSizeUnit(body)) return { std::string(body), 2 }; int precision = 0; if (IsNonNegativeInteger(body, precision)) return { "B", precision }; return { std::string(default_value.first), default_value.second }; } // siz-xx-y std::string_view unit = body.substr(0, first_dash); std::string_view precision_str = body.substr(first_dash + 1); if (!IsSizeUnit(unit)) return { std::string(default_value.first), default_value.second }; int precision = 0; if (!IsNonNegativeInteger(precision_str, precision)) return { std::string(default_value.first), default_value.second }; return { std::string(unit), precision }; } std::string FormatFileSize(size_t size, const std::string& unit, int precision) { static constexpr double K = 1024.0; static constexpr std::array units = { "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" }; static constexpr std::array iec_units = { "B", "KIB", "MIB", "GIB", "TIB", "PIB", "EIB", "ZIB", "YIB" }; std::string input_unit = unit; std::transform(input_unit.begin(), input_unit.end(), input_unit.begin(), [] (unsigned char c) { return static_cast(std::toupper(c)); }); double bytes = static_cast(size); if ((input_unit == "BIT") || (input_unit == "BITS")) bytes /= 8.0; else { size_t unit_index = 0; bool found = false; for (size_t i = 0; i < units.size(); ++i) { if ((input_unit == units[i]) || (input_unit == iec_units[i])) { unit_index = i; found = true; break; } } if (!found) return std::format("{} {}", size, input_unit); for (size_t i = 0; i < unit_index; ++i) bytes *= K; } size_t output_index = 0; while ((bytes >= K) && (output_index < (units.size() - 1))) { bytes /= K; ++output_index; } if (precision < 0) precision = 0; if (std::fabs(bytes - std::round(bytes)) < std::numeric_limits::epsilon()) return fmt::format("{} {}", static_cast(std::round(bytes)), units[output_index]); std::string value = fmt::format("{:.{}f}", bytes, precision); return fmt::format("{} {}", value, units[output_index]); } std::string ServerLogger::GenerateLogHeader(uns::ServerLogLevel LogLevel) { std::string hstr; // 在日志行最开头添加对应日志级别的颜色控制码 switch (LogLevel) { case uns::llDebug: hstr += "\033[36m"; // 青色 break; case uns::llInfo: hstr += "\033[32m"; // 绿色 break; case uns::llWarning: hstr += "\033[33m"; // 黄色 break; case uns::llError: hstr += "\033[31m"; // 红色 break; case uns::llFatal: hstr += "\033[1;37;41m"; // 亮白字 + 红底 (极度醒目) break; case uns::llTrace: default: break; // TRACE 与未知级别保持默认颜色,不追加转义码 } time_t lt = time(NULL); tm* loctim = localtime(<); char timestr[250] = {}; sprintf(timestr, "{%04d-%02d-%02d %02d:%02d:%02d} ", loctim->tm_year + 1900, loctim->tm_mon + 1, loctim->tm_mday, loctim->tm_hour, loctim->tm_min, loctim->tm_sec); hstr += timestr; switch (LogLevel) { case uns::llTrace: hstr += "[TRACE] "; break; case uns::llDebug: hstr += "[DEBUG] "; break; case uns::llInfo: hstr += "[INFO] "; break; case uns::llWarning: hstr += "[WARNING] "; break; case uns::llError: hstr += "[ERROR] "; break; case uns::llFatal: hstr += "[FATAL] "; break; default: hstr += "[UNKNOWN] "; break; } return hstr; } std::string ServerLogger::GenerateFileInfo(std::string filename, int line_num) { // 使用 stringstream 替代 new/memset/sprintf std::ostringstream oss; oss << "(" << filename << " -> LINE=" << line_num << ") "; return oss.str(); } // 高性能、100% 异常安全的 ANSI 颜色码剥离函数 inline std::string StripAnsiCodes(const std::string& input) noexcept { std::string result; // 预分配内存,避免多次 Realloc(即使底层内存极度匮乏,noexcept 也会兜底) try { result.reserve(input.size()); } catch (...) { // 极罕见的内存耗尽情况,直接降级返回原串或空串,绝不崩溃 return input; } bool in_escape = false; for (char c : input) { if (c == '\033') // 遇到转义字符 '\033' (ESC) { in_escape = true; continue; } if (in_escape) { // ANSI 颜色控制码以 'm' 结尾(例如 \033[31m 或 \033[0m) if (c == 'm') in_escape = false; continue; // 跳过转义序列内的所有字符 } result.push_back(c); } return result; } void ServerLogger::WriteBatchToOutputs(const std::deque& batch) { if (batch.empty()) return; std::time_t now = std::time(nullptr); for (const auto& item : batch) { if (item.empty()) continue; std::fwrite(item.c_str(), 1, item.size(), stdout); if (LogStream.is_open()) LogStream << StripAnsiCodes(item); RotateIfNeeded(now, true); } std::fflush(stdout); if (LogStream.is_open()) LogStream.flush(); } void ServerLogger::WorkerLoop() { constexpr std::size_t kMaxBatchCount = 128; while (WorkerRunning) { std::deque local_batch; { std::unique_lock lock(QueueMutex); QueueCV.wait(lock, [this] () { return !LogQueue.empty() || !WorkerRunning; }); if (!WorkerRunning && LogQueue.empty()) break; std::size_t count = 0; while (!LogQueue.empty() && (count < kMaxBatchCount)) { local_batch.push_back(std::move(LogQueue.front())); LogQueue.pop_front(); count++; } } if (!local_batch.empty()) WriteBatchToOutputs(local_batch); } // 退出前把剩余日志尽量写完 for (;;) { std::deque local_batch; { std::lock_guard lock(QueueMutex); if (LogQueue.empty()) break; std::size_t count = 0; while (!LogQueue.empty() && (count < kMaxBatchCount)) { local_batch.push_back(std::move(LogQueue.front())); LogQueue.pop_front(); count++; } } WriteBatchToOutputs(local_batch); } ThreadRunningFlag = false; } std::string ServerLogger::MakeRotatedFileName(const std::string& base, std::time_t t, int osl_index) { tm* loctim = std::localtime(&t); std::ostringstream oss; oss << base; if (RotatePeriod == uns::RP_Hourly) oss << "_" << std::setw(4) << (loctim->tm_year + 1900) << std::setw(2) << std::setfill('0') << (loctim->tm_mon + 1) << std::setw(2) << std::setfill('0') << loctim->tm_mday << "_" << std::setw(2) << std::setfill('0') << loctim->tm_hour; else if (RotatePeriod == uns::RP_Daily) oss << "_" << std::setw(4) << (loctim->tm_year + 1900) << std::setw(2) << std::setfill('0') << (loctim->tm_mon + 1) << std::setw(2) << std::setfill('0') << loctim->tm_mday; if (osl_index > 0) oss << "_OSL" << osl_index; oss << ".log"; return oss.str(); } void ServerLogger::RotateIfNeeded(std::time_t now, bool check_size_after_write) { //控制台模式不进行轮转 if (LogFileName.empty()) return; // 时间轮转优先:若 period 发生变化则重建文件并重置 OSL 索引 if (RotatePeriod != uns::RP_None) { std::time_t new_period_start = (RotatePeriod == uns::RP_Hourly ? (now / 3600) * 3600 : (now / 86400) * 86400); if (new_period_start != CurrentFilePeriodStart) { // 时间轮转:关闭流并打开新的 period 文件,重置 OSL 索引 if (LogStream.is_open()) { LogStream.flush(); LogStream.close(); } CurrentOSLIndex = 0; std::string actual_file = MakeRotatedFileName(LogFileName.empty() ? "UNSC" : LogFileName, now, CurrentOSLIndex); LogStream.open(actual_file.c_str(), std::ios::out | std::ios::app); CurrentFilePeriodStart = new_period_start; // 完成时间轮转后不做 size 检查(新文件刚创建,肯定小于阈值) return; } } // 如果要求检查大小(一般在写入之后调用),并且 MaxFileSizeBytes > 0,则进行大小轮转 if (check_size_after_write && (MaxFileSizeBytes > 0) && LogStream.is_open()) { // 尝试使用 tellp 获取当前文件位置 std::streampos pos = LogStream.tellp(); std::size_t filesize = 0; if (pos != static_cast(-1)) filesize = static_cast(pos); else { // 备用:通过打开文件获取大小 // 构造当前文件名(基于当前 period 和索引) std::string current_file = MakeRotatedFileName(LogFileName.empty() ? "UNSC" : LogFileName, now, CurrentOSLIndex); std::ifstream ifs(current_file.c_str(), std::ios::binary | std::ios::ate); if (ifs.is_open()) { filesize = static_cast(ifs.tellg()); ifs.close(); } } if (filesize >= MaxFileSizeBytes) { // 增加 OSL 索引并打开新文件 if (LogStream.is_open()) { LogStream.flush(); LogStream.close(); } CurrentOSLIndex++; std::string new_file = MakeRotatedFileName(LogFileName.empty() ? "UNSC" : LogFileName, now, CurrentOSLIndex); LogStream.open(new_file.c_str(), std::ios::out | std::ios::app); } } } size_t GetUnsignedInteger(const uns::LogVariant& value) { return std::visit([] (const auto& v) -> size_t { using T = std::decay_t; if constexpr (std::is_integral_v && !std::is_same_v && !std::is_same_v) return static_cast(v); else return 0; }, value); } inline std::string RewriteFormatString(const std::string& real_format, const uns::LogArg* args, size_t count, fmt::dynamic_format_arg_store& store) { std::string out; out.reserve(real_format.size()); size_t arg_index = 0; for (size_t i = 0; i < real_format.size();) { char c = real_format[i]; // escaped {{ if ((c == '{') && ((i + 1) < real_format.size()) && (real_format[i + 1] == '{')) { out += "{{"; i += 2; continue; } // escaped }} if ((c == '}') && ((i + 1) < real_format.size()) && (real_format[i + 1] == '}')) { out += "}}"; i += 2; continue; } if (c == '{') { size_t j = i + 1; while ((j < real_format.size()) && (real_format[j] != '}')) ++j; if (j >= real_format.size()) { out += '{'; ++i; continue; } std::string_view inside(real_format.data() + i + 1, j - i - 1); if (arg_index < count) { uns::LogArg arg = args[arg_index]; // 只处理 time spec if (!inside.empty() && (inside[0] == ':')) { if (std::holds_alternative(arg.value)) { auto& tc = std::get(arg.value); tc.format_spec = std::string(inside.substr(1)); arg.value = tc; store.push_back(arg); } else store.push_back(args[arg_index]); } else if (StartsWith(inside, "siz")) { auto [u, p] = ParseSizeFormat(inside); store.push_back(FormatFileSize(GetUnsignedInteger(arg.value), u, p)); } else store.push_back(args[arg_index]); } out += "{}"; ++arg_index; i = j + 1; continue; } out += c; ++i; } // debug // std::string debug = "[RewriteFormatString] RAW=|" + real_format + "|, OUT=|" + out + "|\n"; // std::fwrite(debug.c_str(), 1, debug.size(), stdout); return out; } void ServerLogger::LogImpl(uns::ServerLogLevel level, const std::string& format, const uns::LogArg* args, size_t count) { std::string formatted; try { if (count == 0) formatted = format; else { fmt::dynamic_format_arg_store> store; for (size_t i = 0; i < count; ++i) { std::visit([&] (auto&& val) { using DeT = std::decay_t; if constexpr (std::is_arithmetic_v || std::is_convertible_v || std::is_pointer_v) store.push_back(val); else // 【修正】这里必须传入 args[i](即 LogArg 本身) 这样才能正确触发上面我们写好的 fmt::formatter 路由 store.push_back(fmt::format("{}", args[i])); }, args[i].value); } // 【修正】显式提供模板参数 ,彻底解决 std::string 导致的推导失败 formatted = fmt::vsprintf(format, store); } } catch (const fmt::format_error& e) { formatted = std::string("<< log format error: ") + e.what() + " >> " + format; } std::string logstr = GenerateLogHeader(level); logstr += formatted; logstr += "\033[0m\n"; std::unique_lock lock(QueueMutex); LogQueue.push_back(logstr); lock.unlock(); QueueCV.notify_one(); } void ServerLogger::LogFImpl(uns::ServerLogLevel level, const std::string& filename, int line_num, const std::string& format, const uns::LogArg* args, size_t count) { std::string formatted; std::string real_format = GenerateFileInfo(filename, line_num) + format; try { if (count == 0) formatted = real_format; else { fmt::dynamic_format_arg_store> store; for (size_t i = 0; i < count; ++i) { std::visit([&] (auto&& val) { using DeT = std::decay_t; if constexpr (std::is_arithmetic_v || std::is_convertible_v || std::is_pointer_v) store.push_back(val); else store.push_back(fmt::format("{}", args[i])); }, args[i].value); } // 【修正】显式提供模板参数 formatted = fmt::vsprintf(real_format, store); } } catch (const fmt::format_error& e) { formatted = std::string("<< log format error: ") + e.what() + " >> " + real_format; } std::string logstr = GenerateLogHeader(level); logstr += formatted; logstr += "\033[0m\n"; std::unique_lock lock(QueueMutex); LogQueue.push_back(logstr); lock.unlock(); QueueCV.notify_one(); } void ServerLogger::LogFMTImpl(uns::ServerLogLevel level, const std::string& format, const uns::LogArg* args, size_t count) { std::string formatted; try { if (count == 0) formatted = format; else { // 现代 {} 风格:动态包装擦除后的类型数组 fmt::dynamic_format_arg_store store; std::string rewrite_format = RewriteFormatString(format, args, count, store); formatted = fmt::vformat(rewrite_format, store); } } catch (const fmt::format_error& e) { formatted = std::string("<< log format error: ") + e.what() + " >> " + format; } std::string logstr = GenerateLogHeader(level); logstr += formatted; logstr += "\033[0m\n"; std::unique_lock lock(QueueMutex); LogQueue.push_back(logstr); lock.unlock(); QueueCV.notify_one(); } void ServerLogger::LogFMT_FImpl(uns::ServerLogLevel level, const std::string& filename, int line_num, const std::string& format, const uns::LogArg* args, size_t count) { std::string formatted; std::string real_format = GenerateFileInfo(filename, line_num) + format; try { if (count == 0) formatted = real_format; else { fmt::dynamic_format_arg_store store; std::string rewrite_format = RewriteFormatString(real_format, args, count, store); formatted = fmt::vformat(rewrite_format, store); } } catch (const fmt::format_error& e) { formatted = std::string("<< log format error: ") + e.what() + " >> " + real_format; } std::string logstr = GenerateLogHeader(level); logstr += formatted; logstr += "\033[0m\n"; std::unique_lock lock(QueueMutex); LogQueue.push_back(logstr); lock.unlock(); QueueCV.notify_one(); } ServerLogger::ServerLogger() { //Default log off //InitLogger(uns::llOff); CurrentLevel = uns::llOff; WorkerRunning = false; RotatePeriod = uns::RP_None; CurrentFilePeriodStart = 0; MaxFileSizeBytes = 50 * 1024 * 1024; CurrentOSLIndex = 0; return; } ServerLogger::ServerLogger(uns::ServerLogLevel log_level, std::string file, uns::LogRotationPeriod rotation, std::size_t max_bytes) { InitLogger(log_level, file, rotation, max_bytes); CurrentLevel = log_level; return; } ServerLogger::~ServerLogger() { // 优雅关闭工作线程并刷新 CloseLog(); return; } bool ServerLogger::InitLogger(uns::ServerLogLevel log_level, const std::string& file, uns::LogRotationPeriod rotation, std::size_t max_bytes) { WorkerRunning = false; RotatePeriod = rotation; CurrentFilePeriodStart = 0; CurrentLevel = log_level; CurrentOSLIndex = 0; MaxFileSizeBytes = max_bytes; // 强制开启大小轮转,默认为 50MiB(可由调用者覆盖) LogFileName = file; //初始化轮转 if (!LogFileName.empty()) { time_t now = time(nullptr); //取整点 CurrentFilePeriodStart = (RotatePeriod == uns::RP_Hourly ? ((now / 3600) * 3600) : (RotatePeriod == uns::RP_Daily ? ((now / 86400) * 86400) : now)); std::string actual_file = MakeRotatedFileName(LogFileName.empty() ? "UNSC" : LogFileName, now, CurrentOSLIndex); LogStream.open(actual_file.c_str(), std::ios::out | std::ios::app); // 如果当前文件已超过大小限制,生成下一个 OSL 文件 if (LogStream.is_open()) { // 尝试获取当前位置(文件大小),并在必要时轮转 std::streampos pos = LogStream.tellp(); std::size_t filesize = 0; if (pos != static_cast(-1)) filesize = static_cast(pos); else { // 备用方案:用 ifstream 直接获取文件大小 std::ifstream ifs(actual_file.c_str(), std::ios::binary | std::ios::ate); if (ifs.is_open()) { filesize = static_cast(ifs.tellg()); ifs.close(); } } if (filesize >= MaxFileSizeBytes) { // increase osl index and open new file LogStream.close(); CurrentOSLIndex = 1; actual_file = MakeRotatedFileName(LogFileName.empty() ? "UNSC" : LogFileName, now, CurrentOSLIndex); LogStream.open(actual_file.c_str(), std::ios::out | std::ios::app); } } } //启动日志线程 bool started = true; if (!WorkerRunning) { WorkerRunning = true; try { WorkerThread = std::thread(&ServerLogger::WorkerLoop, this); } catch (...) { WorkerRunning = false; started = false; } } return ((LogStream.is_open() || LogFileName.empty()) && started); } void ServerLogger::CloseLog() { //关闭工作线程 WorkerRunning = false; QueueCV.notify_all(); if (WorkerThread.joinable()) WorkerThread.join(); while (ThreadRunningFlag) std::this_thread::sleep_for(std::chrono::milliseconds(5)); //关闭文件流 if (LogStream.is_open()) LogStream.close(); CurrentLevel = uns::llOff; } void ServerLogger::DisableLog() { CurrentLevel = uns::llOff; } void ServerLogger::FlushLogBuffer() { //触发写入 QueueCV.notify_all(); //等待队列清空 while (true) { std::unique_lock lock(QueueMutex); if (LogQueue.empty()) break; lock.unlock(); std::this_thread::sleep_for(std::chrono::milliseconds(5)); } if (LogStream.is_open()) LogStream.flush(); } void ServerLogger::EnableLog(uns::ServerLogLevel level) { CurrentLevel = (level == uns::llOff ? uns::llAll : level); } ServerLogger GlobalServerLogger;