如何使用abseil为全局设置日志

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;
}

关键修复点

  1. 独立的文件流

    • 每个日志文件使用独立的 FILE*,避免 freopen 的冲突。

  2. RAII 管理文件资源

    • 在 LogManager 的析构函数中调用 ShutdownAllLogging,确保所有文件正确关闭。

  3. 线程安全

    • 使用 std::mutex 保护日志系统的初始化和关闭操作。

  4. 日志输出

    • 在关闭文件之前,输出一条日志记录,表示文件即将关闭。


测试用例

  1. 正常流程

    • 运行程序,检查日志文件是否正确生成和关闭。

    • 确保日志内容清晰,没有混合或丢失。

  2. 多线程测试

    • 在多个线程中同时调用 InitializeLogging 和 ShutdownLogging,确保没有竞争条件。

  3. 异常流程

    • 尝试打开不存在的目录下的日志文件,检查错误处理逻辑。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值