upload
This commit is contained in:
@@ -0,0 +1,704 @@
|
||||
#include "ServerLogger.h"
|
||||
#include <fmt/format.h>
|
||||
#include <fmt/chrono.h>
|
||||
#include <fmt/printf.h>
|
||||
#include <fmt/args.h>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
// 格式化用的辅助函数
|
||||
|
||||
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<uns::LogArg>
|
||||
{
|
||||
constexpr auto parse(format_parse_context& ctx)
|
||||
{
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(const uns::LogArg& la, FormatContext& ctx) const
|
||||
{
|
||||
return std::visit([&] (auto&& arg) -> decltype(ctx.out())
|
||||
{
|
||||
using T = std::decay_t<decltype(arg)>;
|
||||
|
||||
// A. 针对容器的运行时展开
|
||||
if constexpr (std::is_same_v<T, uns::RangeCapturer>)
|
||||
{
|
||||
std::vector<uns::LogArg> 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<T, uns::PairCapturer>)
|
||||
{
|
||||
std::vector<uns::LogArg> 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<T, uns::FuncCapturer>)
|
||||
{
|
||||
// 【注意】请根据你 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<T, uns::TimeCapturer>)
|
||||
{
|
||||
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<unsigned long>(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;
|
||||
}
|
||||
|
||||
std::string ServerLogger::GenerateLogHeader(uns::ServerLogLevel LogLevel)
|
||||
{
|
||||
std::string hstr;
|
||||
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();
|
||||
}
|
||||
|
||||
void ServerLogger::WriteBatchToOutputs(const std::deque<std::string>& 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 << 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<std::string> local_batch;
|
||||
{
|
||||
std::unique_lock<std::mutex> 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<std::string> local_batch;
|
||||
{
|
||||
std::lock_guard<std::mutex> 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<std::streampos>(-1))
|
||||
filesize = static_cast<std::size_t>(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<std::size_t>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline std::string RewriteFormatString(const std::string& real_format, const uns::LogArg* args, size_t count, fmt::dynamic_format_arg_store<fmt::format_context>& 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<uns::TimeCapturer>(arg.value))
|
||||
{
|
||||
auto& tc = std::get<uns::TimeCapturer>(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
|
||||
store.push_back(args[arg_index]);
|
||||
}
|
||||
|
||||
out += "{}";
|
||||
++arg_index;
|
||||
i = j + 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
out += c;
|
||||
++i;
|
||||
}
|
||||
|
||||
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<fmt::basic_printf_context<char>> store;
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
std::visit([&] (auto&& val)
|
||||
{
|
||||
using DeT = std::decay_t<decltype(val)>;
|
||||
if constexpr (std::is_arithmetic_v<DeT> || std::is_convertible_v<DeT, fmt::string_view> || std::is_pointer_v<DeT>)
|
||||
store.push_back(val);
|
||||
else // 【修正】这里必须传入 args[i](即 LogArg 本身) 这样才能正确触发上面我们写好的 fmt::formatter<uns::LogArg> 路由
|
||||
store.push_back(fmt::format("{}", args[i]));
|
||||
}, args[i].value);
|
||||
}
|
||||
// 【修正】显式提供模板参数 <char>,彻底解决 std::string 导致的推导失败
|
||||
formatted = fmt::vsprintf<char>(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 += "\n";
|
||||
|
||||
std::unique_lock<std::mutex> 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<fmt::basic_printf_context<char>> store;
|
||||
for (size_t i = 0; i < count; ++i)
|
||||
{
|
||||
std::visit([&] (auto&& val)
|
||||
{
|
||||
using DeT = std::decay_t<decltype(val)>;
|
||||
if constexpr (std::is_arithmetic_v<DeT> || std::is_convertible_v<DeT, fmt::string_view> || std::is_pointer_v<DeT>)
|
||||
store.push_back(val);
|
||||
else
|
||||
store.push_back(fmt::format("{}", args[i]));
|
||||
}, args[i].value);
|
||||
}
|
||||
// 【修正】显式提供模板参数 <char>
|
||||
formatted = fmt::vsprintf<char>(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 += "\n";
|
||||
|
||||
std::unique_lock<std::mutex> 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<fmt::format_context> 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 += "\n";
|
||||
|
||||
std::unique_lock<std::mutex> 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<fmt::format_context> 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 += "\n";
|
||||
|
||||
std::unique_lock<std::mutex> 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<std::streampos>(-1))
|
||||
filesize = static_cast<std::size_t>(pos);
|
||||
else
|
||||
{
|
||||
// 备用方案:用 ifstream 直接获取文件大小
|
||||
std::ifstream ifs(actual_file.c_str(), std::ios::binary | std::ios::ate);
|
||||
if (ifs.is_open())
|
||||
{
|
||||
filesize = static_cast<std::size_t>(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<std::mutex> 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;
|
||||
Reference in New Issue
Block a user