C++静态日志类startLog调用log无写入问题求助
startLog内部调用log无写入的问题 我正在实现一个静态日志类,其中startLog函数负责删除同名文件并准备写入,log函数负责写入日志内容。目前单独调用log能正常写入,但在startLog内部调用log时,日志内容并未写入目标文件。
期望日志内容(log.log)
[INFO] 2022-07-29 20:04:07.?????? | log.cpp:34: void startLog (const std::string&) | Log opening ... log.log
[DEBUG] 2022-07-29 20:04:07.465570 | main.cpp:15: int main(int, char**) | TEST1 - START!
[DEBUG] 2022-07-29 20:04:08.547214 | main.cpp:22: int main(int, char**) | TEST1 - END!
实际日志内容(log.log)
[DEBUG] 2022-07-29 20:04:07.465570 | main.cpp:15: int main(int, char**) | TEST1 - START!
[DEBUG] 2022-07-29 20:04:08.547214 | main.cpp:22: int main(int, char**) | TEST1 - END!
main.cpp
#include "log.hpp" #include <iostream> int main(int, char*[]) { std::string logFile {"log.log"}; Log::startLog (logFile); Log::startLog ("log1.txt"); int testCycles {100'000}; Log::log ("TEST1 - START!", Log::DEBUG, logFile); for (int i = 0; i < testCycles; ++i) { Log::log("TEST" + std::to_string(i), Log::INFO, "log1.txt"); } Log::log ("TEST1 - END!", Log::DEBUG, logFile); return EXIT_SUCCESS; }
log.hpp
#ifndef LOG_HPP #define LOG_HPP #include <fstream> #include <source_location> #include <string> #include <string_view> class Log { public: enum { INFO, DEBUG, WARNING, ERROR, FATAL, }; static void log (std::string_view errorMsg, int severity = Log::INFO, const std::string& logFile = s_logFile, const std::source_location location = std::source_location::current()); static void startLog (const std::string& logFile = "log.log"); static std::string getLogFile (){return s_logFile;}; static void setLogLevel (int logLevel); private: static inline std::string s_logFile; static inline std::fstream s_outf {s_logFile, std::ios::app}; static inline std::string s_logMsg [] = {"[INFO]", "[DEBUG]", "[WARNING]","[ERROR]", "[FATAL]"}; static inline int s_logLevel = Log::INFO; Log (){}; }; #endif // LOG_HPP_INCLUDED
log.cpp
#include "log.hpp" #include "utilities.hpp" #include <filesystem> #include <fstream> #include <iomanip> #include <string_view> void Log::log (std::string_view errorMsg, int severity, const std::string& logFile, const std::source_location location) { if (severity < Log::INFO) severity = Log::INFO; else if (severity > Log::FATAL) severity = Log::FATAL; if (severity < s_logLevel) return; if (logFile != s_logFile) { s_outf.close(); s_outf.open (logFile, std::ios::app); s_logFile = std::move (logFile); } s_outf << std::left << std::setw(10) << s_logMsg[severity] << Util::getTimeMillisec() << " | " << std::filesystem::path(location.file_name()).filename().string() << ":" << location.line() << ": " << location.function_name() << " | " << errorMsg <<'\n'; // should be optimized in C++23 using location.resource_file to get filename instead of using std::filesystem } void Log::startLog (const std::string& logFile) { s_logFile = logFile; remove (s_logFile.c_str()); log ("Log opening ... " + s_logFile); } void Log::setLogLevel (int logLevel) { if (logLevel < 0) s_logLevel = 0; else if (logLevel > Log::FATAL) s_logLevel = Log::FATAL; else s_logLevel = logLevel; }
注:utilities.hpp和utilities.cpp用于格式化时间戳,功能正常,此处未附上代码。
问题原因分析
- 静态成员初始化顺序问题:静态成员
s_outf初始化时,s_logFile还是空字符串,导致s_outf初始化为无效的文件流。 startLog逻辑漏洞:设置s_logFile并删除文件后,调用log时因logFile与s_logFile相等,不会执行open操作,直接使用了之前无效的s_outf流,写入失败。- 文件打开模式问题:删除文件后未重新以正确模式打开流,
app模式无法自动修复无效的流句柄。
修复方案
1. 修正startLog函数
删除文件后主动重新打开流,确保流有效:
void Log::startLog(const std::string& logFile) { s_logFile = logFile; s_outf.close(); remove(s_logFile.c_str()); s_outf.open(s_logFile, std::ios::out | std::ios::app); log("Log opening ... " + s_logFile); }
2. 调整静态成员初始化逻辑
延迟初始化文件流,避免空路径初始化:
在log.hpp中修改静态成员声明:
private: static inline std::string s_logFile; static inline std::fstream s_outf; // 不在这里初始化 // 其他成员不变
在log函数中增加流有效性检查:
void Log::log(std::string_view errorMsg, int severity, const std::string& logFile, const std::source_location location) { if (severity < Log::INFO) severity = Log::INFO; else if (severity > Log::FATAL) severity = Log::FATAL; if (severity < s_logLevel) return; if (logFile != s_logFile || !s_outf.is_open()) { s_outf.close(); s_outf.open(logFile, std::ios::out | std::ios::app); s_logFile = logFile; } // 写入逻辑不变 }
3. 增加流有效性判断(可选)
写入前检查流状态,避免无效操作:
if (!s_outf) { std::cerr << "Failed to write log to file: " << logFile << std::endl; return; }
修复后验证
修改后,startLog内部调用log时,文件流已被正确打开,日志内容会正常写入目标文件,符合预期输出。
内容的提问来源于stack exchange,提问作者Tomáš Nadrchal

