1. 使用独立的文件流
为每个日志文件创建独立的 FILE*,避免 freopen 的冲突。
2. 使用 RAII 管理文件资源
通过 RAII(资源获取即初始化)模式管理文件资源,确保文件正确关闭。
3. 线程安全
使用 std::mutex 确保日志系统的初始化和关闭是线程安全的。
4. 修复后的完整代码
#include <unordered_map>
#include <unordered_set>
#include <mutex>
#include <absl/log/log.h>
#include <cstdio>
#include <memory>
class LogManager {
public:
static LogManager& GetInstance() {
static LogManager instance;
return instance;
}
// 初始化日志系统并打开日志文件
void InitializeLogging(const std::string& logFile) {
std::lock_guard<std::mutex> lock(mutex_);
if (initializedLogs_.find(logFile) == initializedLogs_.end()) {
// 初始化 Abseil 日志系统(仅一次)
static bool isAbseilInitialized = false;
if (!isAbseilInitialized) {
absl::InitializeLog();
isAbseilInitialized = true;
}
// 打开日志文件
FILE* file = fopen(logFile.c_str(), "a");
if (file) {
logFiles_[logFile] = file; // 保存文件指针
LOG(INFO) << "Log file opened: " << logFile;
initializedLogs_.insert(logFile);
} else {
LOG(ERROR) << "Failed to open log file: " << logFile;
}
}
}
// 关闭日志文件并释放资源
void ShutdownLogging(const std::string& logFile) {
std::lock_guard<std::mutex> lock(mutex_);
auto it = logFiles_.find(logFile);
if (it != logFiles_.end()) {
fclose(it->second); // 关闭文件
logFiles_.erase(it); // 从映射中移除
LOG(INFO) << "Log file closed: " << logFile;
} else {
LOG(WARNING) << "Log file not found: " << logFile;
}
}
// 关闭所有日志文件
void ShutdownAllLogging() {
std::lock_guard<std::mutex> lock(mutex_);
for (auto& [logFile, file] : logFiles_) {
fclose(file); // 关闭文件
LOG(INFO) << "Log file closed: " << logFile;
}
logFiles_.clear(); // 清空映射
}
private:
LogManager() = default;
~LogManager() {
ShutdownAllLogging(); // 确保所有文件在析构时关闭
}
std::mutex mutex_;
std::unordered_map<std::string, FILE*> logFiles_; // 日志文件映射
std::unordered_set<std::string> initializedLogs_; // 已初始化的日志文件
};
// 示例类:ScreenCapture
class ScreenCapture {
public:
ScreenCapture() : savedImagePath("") {
LogManager::GetInstance().InitializeLogging("logs/screen_capture.log");
LOG(INFO) << "ScreenCapture initialized.";
}
~ScreenCapture() {
LogManager::GetInstance().ShutdownLogging("logs/screen_capture.log");
LOG(INFO) << "ScreenCapture destroyed.";
}
private:
std::string savedImagePath;
};
// 示例类:CMojoCall
class CMojoCall {
public:
CMojoCall() {
LogManager::GetInstance().InitializeLogging("logs/mojocall.log");
LOG(INFO) << "CMojoCall initialized.";
}
~CMojoCall() {
LogManager::GetInstance().ShutdownLogging("logs/mojocall.log");
LOG(INFO) << "CMojoCall destroyed.";
}
private:
HMODULE m_mod = 0;
};
int main() {
// 初始化日志系统
LogManager::GetInstance().InitializeLogging("logs/screen_capture.log");
LogManager::GetInstance().InitializeLogging("logs/mojocall.log");
// 程序逻辑
LOG(INFO) << "Application started.";
{
ScreenCapture screenCapture;
CMojoCall mojoCall;
} // 对象析构时自动关闭日志文件
// 关闭所有日志文件
LogManager::GetInstance().ShutdownAllLogging();
return 0;
}
关键修复点
-
独立的文件流:
-
每个日志文件使用独立的
FILE*,避免freopen的冲突。
-
-
RAII 管理文件资源:
-
在
LogManager的析构函数中调用ShutdownAllLogging,确保所有文件正确关闭。
-
-
线程安全:
-
使用
std::mutex保护日志系统的初始化和关闭操作。
-
-
日志输出:
-
在关闭文件之前,输出一条日志记录,表示文件即将关闭。
-
测试用例
-
正常流程:
-
运行程序,检查日志文件是否正确生成和关闭。
-
确保日志内容清晰,没有混合或丢失。
-
-
多线程测试:
-
在多个线程中同时调用
InitializeLogging和ShutdownLogging,确保没有竞争条件。
-
-
异常流程:
-
尝试打开不存在的目录下的日志文件,检查错误处理逻辑。
-
1317

被折叠的 条评论
为什么被折叠?



