GrabBag/VrUtils/Src/VrLog.cpp

380 lines
13 KiB
C++
Raw Normal View History

2025-11-26 22:44:38 +08:00
#include "VrLog.h"
2025-07-23 01:35:14 +08:00
#include <iostream>
#include <cstring>
#include <stdarg.h>
2025-12-27 09:34:02 +08:00
#include <clocale>
2026-01-04 23:11:31 +08:00
#include <mutex>
#include <atomic>
2025-07-23 01:35:14 +08:00
#include <log4cpp/Category.hh>
#include <log4cpp/Appender.hh>
#include <log4cpp/FileAppender.hh>
#include <log4cpp/Priority.hh>
#include <log4cpp/PatternLayout.hh>
#include <log4cpp/RollingFileAppender.hh>
#include <log4cpp/PropertyConfigurator.hh>
#include "VrFileUtils.h"
#ifdef _WIN32
#include <windows.h>
#include <direct.h>
2025-08-31 21:08:28 +08:00
#include <shlobj.h>
2025-07-23 01:35:14 +08:00
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <WinSock2.h>
#pragma comment(lib,"Ws2_32.lib")
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#endif
////定义Log宏
///******************************************************************************************************************/
2025-09-29 00:56:53 +08:00
#ifdef _WIN32
#define PATH_SEP "\\"
#else
#define PATH_SEP "/"
#endif
#define LOG_CONFIG_FILE "config.ini"
#define LOG_PRINT_FILE "AppLog.log"
2026-01-04 23:11:31 +08:00
// 使用函数内静态变量实现真正的全局单例(跨静态库安全)
struct VrLogState {
log4cpp::PatternLayout* pLayout = nullptr;
log4cpp::RollingFileAppender* pRollFileAppender = nullptr;
std::atomic<bool> isInitialized{false};
std::atomic<bool> isShutdown{false}; // 标记日志系统是否已关闭(程序退出时)
std::atomic<bool> isTimeEnabled{true};
std::once_flag initFlag;
std::once_flag uninitFlag;
};
// 函数内静态变量,确保跨编译单元的唯一性
static VrLogState& GetLogState() {
static VrLogState state;
return state;
}
// 兼容旧代码的访问方式
#define m_pLayout (GetLogState().pLayout)
#define m_prollfileAppender (GetLogState().pRollFileAppender)
#define g_isLogInitialized (GetLogState().isInitialized)
#define g_isLogShutdown (GetLogState().isShutdown)
#define g_isTimeEnabled (GetLogState().isTimeEnabled)
2025-09-29 00:56:53 +08:00
2025-07-23 01:35:14 +08:00
#ifdef STM32_UCOSIII
#define LOG_TIME do{ \
OS_ERR err;\
OS_TICK time = OSTimeGet(&err); \
printf("[%10u]",time); \
}while(0)
#else
#define LOG_TIME do { \
std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); \
std::time_t timestamp = std::chrono::system_clock::to_time_t(now); \
std::tm local_time = *std::localtime(&timestamp); \
long long milliseconds = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count(); \
printf("%d-%02d-%02d %02d:%02d:%02d.%03lld", local_time.tm_year + 1900 , local_time.tm_mon + 1, local_time.tm_mday, local_time.tm_hour, local_time.tm_min, local_time.tm_sec, milliseconds % 1000); \
} while(0)
#endif
#ifdef __ANDROID__
#include <android/log.h>
#define MY_LOG_VERBOSE(fmt, ...) __android_log_print(ANDROID_LOG_VERBOSE, "APPV", ##__VA_ARGS__)
#define MY_LOG_DEBUG(fmt, ...) __android_log_print(ANDROID_LOG_DEBUG, "APPD", ##__VA_ARGS__)
#define MY_LOG_INFO(fmt, ...) __android_log_print(ANDROID_LOG_INFO, "APPI", ##__VA_ARGS__)
#define MY_LOG_WARNING(fmt, ...) __android_log_print(ANDROID_LOG_WARN, "APPW", ##__VA_ARGS__)
#define MY_LOG_ERRO(fmt, ...) __android_log_print(ANDROID_LOG_ERROR, "APPE", ##__VA_ARGS__)
#else
2025-12-27 09:34:02 +08:00
// 辅助函数截断文件路径最多显示15个字符12个字符 + "..."
static inline const char* TruncateFilePath(const char* filePath) {
static thread_local char buffer[20]; // 线程局部存储,避免多线程问题
if (!filePath) return "";
size_t len = strlen(filePath);
if (len <= 15) {
// 如果长度不超过15直接返回
return filePath;
} else {
// 如果超过15个字符取前13个字符 + "..."
strncpy(buffer, filePath, 13);
buffer[13] = '.';
buffer[14] = '.';
buffer[15] = '\0';
return buffer;
}
}
#define MY_LOG_VERBOSE(filePath, nLine, fmt, ...) do { if(g_isTimeEnabled) LOG_TIME; printf(" V[%15s:%4u] " fmt"", TruncateFilePath(filePath), nLine ,##__VA_ARGS__); fflush(stdout); } while(0)
#define MY_LOG_DEBUG(filePath, nLine, fmt, ...) do { if(g_isTimeEnabled) LOG_TIME; printf(" D[%15s:%4u] " fmt"", TruncateFilePath(filePath), nLine ,##__VA_ARGS__); fflush(stdout); } while(0)
#define MY_LOG_INFO(filePath, nLine, fmt, ...) do { if(g_isTimeEnabled) LOG_TIME; printf(" I[%15s:%4u] " fmt"", TruncateFilePath(filePath), nLine ,##__VA_ARGS__); fflush(stdout); } while(0)
#define MY_LOG_WARNING(filePath, nLine, fmt, ...) do { if(g_isTimeEnabled) LOG_TIME; printf(" W[%15s:%4u] " fmt"", TruncateFilePath(filePath), nLine ,##__VA_ARGS__); fflush(stdout); } while(0)
#define MY_LOG_ERRO(filePath, nLine, fmt, ...) do { if(g_isTimeEnabled) LOG_TIME; printf(" E[%15s:%4u] " fmt"", TruncateFilePath(filePath), nLine ,##__VA_ARGS__); fflush(stdout); } while(0)
2025-07-23 01:35:14 +08:00
#endif // __ANROID__
///******************************************************************************************************************/
// 动态获取日志路径(包含应用名称)
static std::string GetLogPath()
{
2025-08-31 21:08:28 +08:00
std::string appName = "YJApp"; // 默认应用名称
2025-07-23 01:35:14 +08:00
// 尝试从进程路径获取应用名称
#ifdef _WIN32
2025-08-31 21:08:28 +08:00
// 获取用户文档目录
// 如果无法获取用户目录,则使用原来的方式
2025-07-23 01:35:14 +08:00
char exePath[MAX_PATH];
if (GetModuleFileNameA(NULL, exePath, MAX_PATH) != 0) {
std::string fullPath(exePath);
size_t pos = fullPath.find_last_of("\\");
if (pos != std::string::npos) {
std::string fileName = fullPath.substr(pos + 1);
size_t dotPos = fileName.find_last_of(".");
if (dotPos != std::string::npos) {
appName = fileName.substr(0, dotPos);
}
}
}
2025-08-31 21:08:28 +08:00
char userPath[MAX_PATH];
if (SHGetFolderPathA(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, userPath) == S_OK) {
std::string userDir(userPath);
return userDir + "\\" + appName + "\\Log";
}
2025-07-23 01:35:14 +08:00
return ".\\" + appName + "\\Log";
#else
// Linux/Android平台
char exePath[1024];
ssize_t len = readlink("/proc/self/exe", exePath, sizeof(exePath) - 1);
if (len != -1) {
exePath[len] = '\0';
std::string fullPath(exePath);
size_t pos = fullPath.find_last_of("/");
if (pos != std::string::npos) {
appName = fullPath.substr(pos + 1);
}
}
#ifdef __ANDROID__
return "/sdcard/" + appName + "/Log";
#else
return appName + "/Log";
#endif
#endif
}
// 自动初始化和清理类,确保程序启动时初始化日志,退出时清理日志资源
class VrLogAutoCleaner {
public:
VrLogAutoCleaner() {
VrLogUtils::InitLog();
}
~VrLogAutoCleaner() {
VrLogUtils::UninitLog();
}
};
// 静态实例,程序启动时自动初始化日志,程序退出时自动清理日志
static VrLogAutoCleaner g_logAutoCleaner;
2026-01-04 23:11:31 +08:00
// 实际执行初始化的内部函数
static void DoInitLog()
2025-07-23 01:35:14 +08:00
{
2025-12-27 09:34:02 +08:00
#ifdef _WIN32
// 设置控制台代码页为 UTF-8解决中文乱码
SetConsoleOutputCP(CP_UTF8);
SetConsoleCP(CP_UTF8);
// 设置 C 运行时的 locale 为 UTF-8
setlocale(LC_ALL, ".UTF-8");
#endif
2025-07-23 01:35:14 +08:00
// if file exist then load file config
std::string logPath = GetLogPath();
std::string logConfig = logPath + PATH_SEP + LOG_CONFIG_FILE;
if (CVrFileUtils::IsFileExist(logConfig.c_str()))
{
// load profile
try
{
log4cpp::PropertyConfigurator::configure(logConfig);
}
catch (log4cpp::ConfigureFailure& f)
{
std::cout << "Load Log Profile Error" << f.what() << std::endl;
}
}
else
{
if (!CVrFileUtils::IsDirExist(logPath.c_str()))
{
CVrFileUtils::CreatNewDir(logPath.c_str());
}
// create file appender
2026-01-04 23:11:31 +08:00
VrLogState& state = GetLogState();
if (nullptr == state.pLayout)
2025-07-23 01:35:14 +08:00
{
2026-01-04 23:11:31 +08:00
state.pLayout = new log4cpp::PatternLayout;
2025-07-23 01:35:14 +08:00
// 设置布局格式,可以控制是否自动换行
// %d{%Y-%m-%d %H:%M:%S.%l} [%p] %c - %m%n
// 其中 %n 表示换行符,如果想要控制换行可以去掉或修改这个
2026-01-04 23:11:31 +08:00
state.pLayout->setConversionPattern("%d{%Y-%m-%d %H:%M:%S.%l} [%p] %m");
2025-07-23 01:35:14 +08:00
}
2026-01-04 23:11:31 +08:00
if (nullptr == state.pRollFileAppender)
2025-07-23 01:35:14 +08:00
{
2026-01-04 23:11:31 +08:00
state.pRollFileAppender = new log4cpp::RollingFileAppender("AppLogAppender", logPath + PATH_SEP + LOG_PRINT_FILE,
2025-07-23 01:35:14 +08:00
1024 * 1024, // 单个文件大小1M
10); // 10个文件
2026-01-04 23:11:31 +08:00
state.pRollFileAppender->setLayout(state.pLayout);
2025-07-23 01:35:14 +08:00
// add appender to category
log4cpp::Category& root = log4cpp::Category::getRoot();
2026-01-04 23:11:31 +08:00
root.addAppender(state.pRollFileAppender);
2025-07-23 01:35:14 +08:00
// set priority
root.setPriority(log4cpp::Priority::DEBUG);
}
}
2026-01-04 23:11:31 +08:00
2025-07-23 01:35:14 +08:00
g_isLogInitialized = true;
}
2026-01-04 23:11:31 +08:00
/// 初始化log
void VrLogUtils::InitLog()
2025-07-23 01:35:14 +08:00
{
2026-01-04 23:11:31 +08:00
// 如果已经关闭,不再初始化
if (g_isLogShutdown) {
2025-07-23 01:35:14 +08:00
return;
}
2026-01-04 23:11:31 +08:00
// 使用 call_once 确保只执行一次初始化(跨静态库安全)
std::call_once(GetLogState().initFlag, DoInitLog);
}
2025-07-23 01:35:14 +08:00
2026-01-04 23:11:31 +08:00
// 实际执行清理的内部函数
static void DoUninitLog()
{
// 先设置标志,防止其他线程继续调用日志
2025-07-23 01:35:14 +08:00
g_isLogInitialized = false;
2026-01-04 23:11:31 +08:00
g_isLogShutdown = true; // 标记已关闭,防止 EchoLog 重新初始化
// 注意:不调用 log4cpp::Category::shutdown()
// 原因静态对象析构顺序不确定log4cpp 内部的静态对象可能在
// VrLogAutoCleaner 之前就已经被析构,此时调用 shutdown() 会崩溃。
// 程序退出时,操作系统会自动回收所有内存,不需要手动清理。
// 只置空指针,不删除对象(对象由 log4cpp 内部管理)
VrLogState& state = GetLogState();
state.pLayout = nullptr;
state.pRollFileAppender = nullptr;
}
/// 关闭log
void VrLogUtils::UninitLog()
{
// 使用 call_once 确保只执行一次清理(跨静态库安全)
std::call_once(GetLogState().uninitFlag, DoUninitLog);
2025-07-23 01:35:14 +08:00
}
2025-09-29 00:56:53 +08:00
/// 开启/关闭时间戳
void VrLogUtils::EnableTime(bool bEnable)
{
if (nullptr != m_pLayout)
{
m_pLayout->setConversionPattern(bEnable ? "%d{%Y-%m-%d %H:%M:%S.%l} [%p] %m" : "%p %m");
}
g_isTimeEnabled = false;
}
2025-07-23 01:35:14 +08:00
/// 输出log
void VrLogUtils::EchoLog(VrLogLevel eLogLevel, const char* sFilePath, const int nLine, const char* sLogGroup, const char* sFormat, ...)
{
2026-01-04 23:11:31 +08:00
// 如果日志系统已关闭(程序退出),直接返回,不要重新初始化
if (g_isLogShutdown) {
return;
}
2025-07-23 01:35:14 +08:00
// 由于VrLogAutoCleaner会在程序启动时自动初始化这里只做保险检查
if (!g_isLogInitialized) {
InitLog();
}
// load log info
va_list args;
va_start(args, sFormat);
char szLogInfo[1024] = { 0 };
#ifdef _WIN32
vsprintf_s(szLogInfo, sFormat, args);
#else
vsprintf(szLogInfo, sFormat, args);
#endif
va_end(args);
log4cpp::Category& root = log4cpp::Category::getRoot();
switch (eLogLevel)
{
case KELOGLEVEL_None:
break;
case KELOGLEVEL_Verbose:
MY_LOG_VERBOSE(sFilePath, nLine, "%s", szLogInfo);
if (nullptr != m_pLayout && nullptr != m_prollfileAppender)
{
LOG4CPP_DEBUG(root, szLogInfo);
}
break;
case KELOGLEVEL_Debug:
MY_LOG_DEBUG(sFilePath, nLine, "%s", szLogInfo);
if (nullptr != m_pLayout && nullptr != m_prollfileAppender)
{
LOG4CPP_DEBUG(root, szLogInfo);
}
break;
case KELOGLEVEL_Info:
MY_LOG_INFO(sFilePath, nLine, "%s", szLogInfo);
if (nullptr != m_pLayout && nullptr != m_prollfileAppender)
{
LOG4CPP_INFO(root, szLogInfo);
}
break;
case KELOGLEVEL_Warning:
MY_LOG_WARNING(sFilePath, nLine, "%s", szLogInfo);
if (nullptr != m_pLayout && nullptr != m_prollfileAppender)
{
LOG4CPP_WARN(root, szLogInfo);
}
break;
case KELOGLEVEL_Error:
MY_LOG_ERRO(sFilePath, nLine, "%s", szLogInfo);
if (nullptr != m_pLayout && nullptr != m_prollfileAppender)
{
LOG4CPP_ERROR(root, szLogInfo);
}
break;
default:
break;
}
}
/// 修改log level default info
void VrLogUtils::AlterLogLevel(VrLogLevel eLogLevel)
{
}
/// 修改log输出形式 默认都输出
void VrLogUtils::AlterLogType(VrLogType eLogType)
{
}