#include "pch.h" #include "aLog.h" #include "spdlog/sinks/stdout_color_sinks.h" #include "spdlog/sinks/basic_file_sink.h" #include "spdlog/sinks/rotating_file_sink.h" #include "spdlog/sinks/basic_file_sink.h" #include "spdlog/async.h" //app全局通用的logger实例 std::shared_ptr alog; static std::wstring _LogFilePath; static std::wstring _LogFilePath2; spdlog::sink_ptr pConsoleSink; spdlog::sink_ptr pFileSink; spdlog::sink_ptr pFileSink2; // 用于console log static FILE* _stream=nullptr; static bool _consoleAttached = false; // 附着到父进程的控制台 static bool _consoleAllocated = false; // 我们新创建的控制台 static void _initConsoleLog() { // 若已存在控制台,直接使用;否则尝试附着父进程控制台,失败再创建 if (GetConsoleWindow()) { // 已有控制台(通常是控制台子系统) } else if (AttachConsole(ATTACH_PARENT_PROCESS)) { _consoleAttached = true; } else if (AllocConsole()) { _consoleAllocated = true; } // 若成功获得控制台,重定向 stdout 并设置 UTF-8 if (GetConsoleWindow()) { //freopen_s(&_stream, "CONOUT$", "w", stdout); SetConsoleOutputCP(CP_UTF8); // 尝试启用虚拟终端处理(让 ANSI 颜色也能生效,兼容使用 ansicolor sink 的场景) HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); if (hOut != INVALID_HANDLE_VALUE) { DWORD mode = 0; if (GetConsoleMode(hOut, &mode)) { // 添加虚拟终端 & 已处理输出标志(如果父控制台支持则颜色更稳定) SetConsoleMode(hOut, mode | ENABLE_PROCESSED_OUTPUT | ENABLE_VIRTUAL_TERMINAL_PROCESSING); } } pConsoleSink = std::make_shared(); pConsoleSink->set_level(spdlog::level::debug); } } static void _deinitConsoleLog() { pConsoleSink.reset(); if (_stream) { fclose(_stream); _stream = nullptr; } // 只在我们附着或创建过控制台时释放 if (_consoleAttached || _consoleAllocated) { FreeConsole(); _consoleAttached = false; _consoleAllocated = false; } } void InitRollFileLog(std::shared_ptr& log, const char * logname, int flag, const fs::path& logDir, const std::wstring& filename, int logfilesize_K, int logfilenum) { spdlog::drop(logname); // 删除原来的logger std::time_t t = std::time(nullptr); // 创建异步logger, 默认必须有一个文件log auto name = fmt::format(L"{}_{:%Y%m%d_%H%M%S}.log", filename, fmt::localtime(t)); _LogFilePath = (logDir / name).wstring(); log = spdlog::create_async_nb(logname, _LogFilePath, size_t(logfilesize_K) * 1024, logfilenum); pFileSink = log->sinks()[0]; // 添加console log if (flag & INIT_CONSOLE_LOG) { _initConsoleLog(); if(pConsoleSink) log->sinks().push_back(pConsoleSink); } pFileSink->set_level(spdlog::level::debug); if(flag & INIT_ERROR_FILE_LOG) { auto name = fmt::format(L"{}_{:%Y%m%d_%H%M%S}_error.log", filename, fmt::localtime(t)); _LogFilePath2 = (logDir / name).wstring(); pFileSink2 = std::make_shared(_LogFilePath2, size_t(logfilesize_K) * 1024, logfilenum); pFileSink2->set_level(spdlog::level::warn); log->sinks().push_back(pFileSink2); } log->set_level(spdlog::level::debug); log->flush_on(spdlog::level::err); //记录error立即flush一次 spdlog::flush_every(std::chrono::seconds(5)); //5秒钟flush一次 } void InitBasicFileLog(std::shared_ptr& log, const char* logname, int flag, const fs::path& logDir, const std::wstring& filename) { spdlog::drop(logname); // 删除原来的logger std::time_t t = std::time(nullptr); // 创建异步logger, 默认必须有一个文件log auto name = fmt::format(L"{}_{:%Y%m%d_%H%M%S}.log", filename, fmt::localtime(t)); _LogFilePath = (logDir / name).wstring(); log = spdlog::create_async_nb(logname, _LogFilePath); pFileSink = log->sinks()[0]; // 添加console log if (flag & INIT_CONSOLE_LOG) { _initConsoleLog(); log->sinks().push_back(pConsoleSink); } pFileSink->set_level(spdlog::level::debug); if (flag & INIT_ERROR_FILE_LOG) { auto name = fmt::format(L"{}_{:%Y%m%d_%H%M%S}_error.log", filename, fmt::localtime(t)); _LogFilePath2 = (logDir / name).wstring(); pFileSink2 = std::make_shared(_LogFilePath2); pFileSink2->set_level(spdlog::level::warn); log->sinks().push_back(pFileSink2); } log->set_level(spdlog::level::debug); log->flush_on(spdlog::level::err); //记录error立即flush一次 spdlog::flush_every(std::chrono::seconds(5)); //5秒钟flush一次 } // ---- 这两段用于简单的程序,只开console log ----- // 仅开Console log void aLogInitConsole() { spdlog::drop(""); // 删除默认logger // 创建异步logger alog = spdlog::create_async_nb(""); pConsoleSink = alog->sinks()[0]; pConsoleSink->set_level(spdlog::level::debug); _initConsoleLog(); alog->set_level(spdlog::level::debug); alog->flush_on(spdlog::level::err); //记录error立即flush一次 spdlog::flush_every(std::chrono::seconds(5)); //5秒钟flush一次 } void LogDeinit(std::shared_ptr& log, bool byelog) { // 关闭前打印一条信息 if(byelog) alog->info("Log正常关闭"); alog->flush(); Sleep(100); // 确保已经输出了 alog->sinks().clear(); if (pConsoleSink) { _deinitConsoleLog(); } if (pFileSink) { pFileSink.reset(); } if (pFileSink2) { pFileSink.reset(); } spdlog::shutdown(); } // ---- 这两段用于5C,运行过程中随时开启、关闭console log ----- // 如果没开启Console log,打开 void openConsoleLog() { if (!pConsoleSink) { _initConsoleLog(); } alog->sinks().push_back(pConsoleSink); } // 如果已经打开了Console Log,关闭掉 void closeConsoleLog() { if (pConsoleSink) {//开启了console要关闭 for (auto it = alog->sinks().begin(); it != alog->sinks().end(); it++) { if (pConsoleSink == *it) { alog->warn("已停止刷新Log到Console"); alog->flush(); Sleep(100); // 确保已经输出了 alog->sinks().erase(it); // 假设没有别的线程在输出 Sleep(100); // 确保已经输出了 _deinitConsoleLog(); break; } } } }