197 lines
5.4 KiB
C++
197 lines
5.4 KiB
C++
#include "rdp_log.h"
|
||
#include "parameters.h"
|
||
|
||
#include <cstdio>
|
||
#include <memory>
|
||
#include <string>
|
||
#include <vector>
|
||
#include <time.h>
|
||
#include <direct.h>
|
||
#include <io.h>
|
||
|
||
#include <spdlog/sinks/basic_file_sink.h>
|
||
#include <spdlog/sinks/stdout_sinks.h>
|
||
|
||
namespace
|
||
{
|
||
//日志行格式:与旧版 [时间戳][文件:行号] 风格一致,另加 spdlog 级别
|
||
const char *const kPattern = "[%Y-%m-%d %H:%M:%S.%e] [%l] [%s:%#] %v";
|
||
|
||
//日志模块内部状态:默认 logger(标准输出 + 日志文件)与当前日志文件路径
|
||
struct RdpLogState
|
||
{
|
||
std::shared_ptr<spdlog::logger> logger; //默认 logger(含标准输出 sink,成功时另含文件 sink)
|
||
std::string log_path; //当前日志文件全路径(降级为仅标准输出时为空)
|
||
bool initialized; //是否已初始化(含文件创建失败时的降级初始化)
|
||
|
||
RdpLogState() : initialized(false) {}
|
||
};
|
||
|
||
RdpLogState &State()
|
||
{
|
||
static RdpLogState s;
|
||
return s;
|
||
}
|
||
|
||
//创建日志目录(目录已存在时 _mkdir 返回 EEXIST,忽略;逻辑与原 rdp_log/rdp_record 一致)
|
||
void EnsureDir(const std::string &dir_with_separator)
|
||
{
|
||
std::string dir = dir_with_separator;
|
||
while (dir.size() > 0 && (dir[dir.size() - 1] == '\\' || dir[dir.size() - 1] == '/'))
|
||
{
|
||
dir.erase(dir.size() - 1);
|
||
}
|
||
if (dir.size() > 0)
|
||
{
|
||
_mkdir(dir.c_str());
|
||
}
|
||
}
|
||
|
||
//当前本地时间字符串,格式 "YYYYMMDD_HHMMSS"
|
||
bool TimeStamp(char *buf, size_t size)
|
||
{
|
||
time_t now = time(NULL);
|
||
struct tm local;
|
||
if (localtime_s(&local, &now) != 0)
|
||
{
|
||
return false;
|
||
}
|
||
return strftime(buf, size, "%Y%m%d_%H%M%S", &local) != 0;
|
||
}
|
||
|
||
//生成日志文件路径:RDP_LOG_DIR + rdp_YYYYMMDD_HHMMSS.log;
|
||
//同一秒内文件已存在(Destroy→GetB 重连、同目录多进程启动)时追加 _1~_999 后缀,不复用同一文件
|
||
bool MakeLogPath(std::string *path)
|
||
{
|
||
char stamp[32] = {0};
|
||
if (!TimeStamp(stamp, sizeof(stamp)))
|
||
{
|
||
return false;
|
||
}
|
||
const std::string base = std::string(RDP_LOG_DIR) + "rdp_" + stamp;
|
||
for (int suffix = 0; suffix < 1000; ++suffix)
|
||
{
|
||
std::string candidate = base;
|
||
if (suffix > 0)
|
||
{
|
||
candidate += "_" + std::to_string(suffix);
|
||
}
|
||
candidate += ".log";
|
||
if (_access(candidate.c_str(), 0) != 0)
|
||
{
|
||
*path = candidate;
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
//初始化 logger:统一日志格式,运行期级别放开到 trace(实际输出级别由编译期
|
||
//SPDLOG_ACTIVE_LEVEL 过滤),所有级别逐条 flush(日志量小,且调试场景要求异常退出也尽量不丢日志)
|
||
void ConfigureLogger(const std::shared_ptr<spdlog::logger> &logger)
|
||
{
|
||
logger->set_pattern(kPattern);
|
||
logger->set_level(spdlog::level::trace);
|
||
logger->flush_on(spdlog::level::trace);
|
||
spdlog::set_default_logger(logger);
|
||
}
|
||
|
||
//仅标准输出 sink 的降级 logger(日志文件创建失败时使用)
|
||
std::shared_ptr<spdlog::logger> MakeStdoutLogger()
|
||
{
|
||
std::vector<spdlog::sink_ptr> sinks;
|
||
sinks.push_back(std::make_shared<spdlog::sinks::stdout_sink_mt>());
|
||
std::shared_ptr<spdlog::logger> logger =
|
||
std::make_shared<spdlog::logger>("rdp", sinks.begin(), sinks.end());
|
||
ConfigureLogger(logger);
|
||
return logger;
|
||
}
|
||
}
|
||
|
||
namespace rdp_log
|
||
{
|
||
bool Init()
|
||
{
|
||
RdpLogState &st = State();
|
||
if (st.initialized)
|
||
{
|
||
return true; //已经初始化,避免重复创建日志文件
|
||
}
|
||
|
||
#if SPDLOG_ACTIVE_LEVEL > SPDLOG_LEVEL_CRITICAL
|
||
//编译期关闭日志(RDP_LOG_LEVEL=OFF):不初始化 logger、不创建日志文件
|
||
return false;
|
||
#else
|
||
try
|
||
{
|
||
EnsureDir(RDP_LOG_DIR);
|
||
|
||
std::string log_path;
|
||
if (!MakeLogPath(&log_path))
|
||
{
|
||
throw spdlog::spdlog_ex("no usable log file name in RDP_LOG_DIR");
|
||
}
|
||
|
||
std::vector<spdlog::sink_ptr> sinks;
|
||
sinks.push_back(std::make_shared<spdlog::sinks::stdout_sink_mt>());
|
||
//truncate=false:日志文件名已保证不存在(极端并发下万一被抢占则追加而非覆盖已有内容)
|
||
sinks.push_back(std::make_shared<spdlog::sinks::basic_file_sink_mt>(log_path, false));
|
||
|
||
std::shared_ptr<spdlog::logger> logger =
|
||
std::make_shared<spdlog::logger>("rdp", sinks.begin(), sinks.end());
|
||
ConfigureLogger(logger);
|
||
|
||
//spdlog 内部错误(如日志文件写失败)打到标准错误,不影响雷达数据处理
|
||
spdlog::set_error_handler([](const std::string &msg) {
|
||
std::fprintf(stderr, "[rdp_log] spdlog error: %s\n", msg.c_str());
|
||
});
|
||
|
||
st.logger = logger;
|
||
st.log_path = log_path;
|
||
st.initialized = true;
|
||
|
||
RDP_LOG_INFO("rdp_log enabled, log file: {}", log_path);
|
||
return true;
|
||
}
|
||
catch (const std::exception &e)
|
||
{
|
||
//日志文件创建失败(目录不可写等):降级为仅标准输出,不中断雷达数据处理
|
||
try
|
||
{
|
||
st.logger = MakeStdoutLogger();
|
||
st.log_path.clear();
|
||
st.initialized = true;
|
||
std::fprintf(stderr, "[rdp_log] cannot create log file, fallback to stdout only: %s\n",
|
||
e.what());
|
||
}
|
||
catch (...)
|
||
{
|
||
std::fprintf(stderr, "[rdp_log] logger init failed\n");
|
||
}
|
||
return false;
|
||
}
|
||
#endif
|
||
}
|
||
|
||
void Shutdown()
|
||
{
|
||
RdpLogState &st = State();
|
||
if (!st.initialized || !st.logger)
|
||
{
|
||
return;
|
||
}
|
||
|
||
RDP_LOG_INFO("rdp_log shutdown");
|
||
//flush 并释放默认 logger(随之关闭日志文件)
|
||
spdlog::shutdown();
|
||
st.logger.reset();
|
||
st.log_path.clear();
|
||
st.initialized = false;
|
||
}
|
||
|
||
std::string log_file_path()
|
||
{
|
||
return State().log_path;
|
||
}
|
||
}
|