You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用g3log实现按函数或按模块分别生成日志文件

g3log按模块/函数拆分日志文件实现示例

核心实现逻辑:g3log通过自定义Sink扩展日志输出能力,要实现按模块/函数拆分日志文件,只需要在自定义Sink中根据日志携带的模块标记、函数名信息,将日志路由到对应文件即可。


步骤1:实现自定义Sink类

CustomSink.h

#pragma once
#include <string>
#include <fstream>
#include <unordered_map>
#include <mutex>
#include <g2log.hpp>

class CustomSink {
public:
    // 拆分模式枚举:按模块拆分 / 按函数拆分
    enum class SplitMode : int {
        BY_MODULE = 0,
        BY_FUNCTION = 1
    };

    explicit CustomSink(SplitMode mode, const std::string& log_dir = "./logs/");
    ~CustomSink();

    // g3log日志回调入口,所有日志都会传入该函数处理
    void callback(g2::LogMessageMover message);

private:
    SplitMode m_split_mode;
    std::string m_log_dir;
    std::unordered_map<std::string, std::ofstream> m_file_stream_map;
    std::mutex m_file_op_mutex;

    // 根据拆分key获取对应的文件流,不存在则自动创建
    std::ofstream& get_file_stream(const std::string& split_key);
};

CustomSink.cpp

#include "CustomSink.h"
#include <filesystem>
#include <stdexcept>
#include <algorithm>

namespace fs = std::filesystem;

CustomSink::CustomSink(SplitMode mode, const std::string& log_dir) 
    : m_split_mode(mode), m_log_dir(log_dir) {
    // 自动创建日志目录
    if (!fs::exists(m_log_dir)) {
        fs::create_directories(m_log_dir);
    }
    if (!fs::is_directory(m_log_dir)) {
        throw std::runtime_error("无效的日志存储路径: " + m_log_dir);
    }
}

CustomSink::~CustomSink() {
    std::lock_guard<std::mutex> lock(m_file_op_mutex);
    for (auto& [key, file] : m_file_stream_map) {
        if (file.is_open()) {
            file.close();
        }
    }
}

std::ofstream& CustomSink::get_file_stream(const std::string& split_key) {
    auto iter = m_file_stream_map.find(split_key);
    if (iter == m_file_stream_map.end()) {
        std::string file_path = m_log_dir + split_key + ".log";
        auto [new_iter, insert_success] = m_file_stream_map.emplace(
            split_key, 
            std::ofstream(file_path, std::ios::app | std::ios::out)
        );
        if (!insert_success || !new_iter->second.is_open()) {
            throw std::runtime_error("日志文件创建失败: " + file_path);
        }
        iter = new_iter;
    }
    return iter->second;
}

void CustomSink::callback(g2::LogMessageMover message) {
    auto log_msg = message.get();
    std::string split_key;

    if (m_split_mode == SplitMode::BY_MODULE) {
        // 解析日志前缀的[模块名]标记,无标记则归入default.log
        size_t tag_start = log_msg.message().find('[');
        size_t tag_end = log_msg.message().find(']', tag_start);
        if (tag_start != std::string::npos && tag_end != std::string::npos) {
            split_key = log_msg.message().substr(tag_start + 1, tag_end - tag_start - 1);
        } else {
            split_key = "default";
        }
    } else if (m_split_mode == SplitMode::BY_FUNCTION) {
        // 直接取函数名作为拆分key,替换非法字符避免文件名错误
        split_key = log_msg.function();
        std::replace(split_key.begin(), split_key.end(), ':', '_');
        std::replace(split_key.begin(), split_key.end(), '<', '_');
        std::replace(split_key.begin(), split_key.end(), '>', '_');
    }

    std::lock_guard<std::mutex> lock(m_file_op_mutex);
    auto& output_file = get_file_stream(split_key);
    // 自定义日志输出格式,可根据需求调整
    output_file << log_msg.timestamp() << " [" << log_msg.level() << "] " 
                << log_msg.message() << " (" << log_msg.file() << ":" << log_msg.line() << ")\n";
    output_file.flush();
}

步骤2:定义模块日志宏(按模块分类时需要)

在公共头文件中添加如下宏,打日志时指定模块即可自动标记:

// 参数1:模块名,参数2:日志等级(DEBUG/INFO/WARNING/ERROR/FATAL)
#define LOG_MODULE(module, level) G2_LOG_##level(__FILE__, __LINE__, __FUNCTION__, "") << "[" #module "] "

步骤3:修改日志初始化代码

// main.cpp
#include<g2log.hpp>
#include<g2logworker.hpp>
#include <std2_make_unique.hpp>
 
#include "CustomSink.h"
 
int main(int argc, char**argv) {
   using namespace g2;
   // 创建无默认Sink的日志worker,如果需要保留全局默认日志,也可以用createWithDefaultLogger创建后再加自定义Sink
   auto log_worker = LogWorker::createWithNoSink();
   // 初始化自定义Sink:可选择按模块拆分SplitMode::BY_MODULE,或按函数拆分SplitMode::BY_FUNCTION
   auto custom_sink = std2::make_unique<CustomSink>(CustomSink::SplitMode::BY_MODULE, "./logs/");
   // 绑定Sink回调
   log_worker->addSink(std::move(custom_sink), &CustomSink::callback);
 
   // 初始化全局日志
   g2::initializeLogging(log_worker.get());

   // 测试按模块打日志
   LOG_MODULE(DB, INFO) << "数据库连接成功,端口3306";
   LOG_MODULE(NETWORK, DEBUG) << "收到客户端请求,IP=192.168.1.10";
   LOG_MODULE(UI, WARNING) << "图标资源加载失败,使用默认占位图";

   // 按函数分类时直接用原生LOG宏即可
   // LOG(INFO) << "函数日志测试";
   
   // 程序退出时worker会自动释放资源,无需手动关闭
   return 0;
}

注意事项

  • 按模块分类时必须使用LOG_MODULE宏打日志,未标记模块的日志会默认存入default.log
  • 按函数分类时如果函数名过长、包含特殊字符,可额外添加映射表将函数名转换为短别名,避免文件名异常
  • 高并发场景下可在Sink中添加异步写队列,避免日志写盘阻塞业务线程
  • 若需要同时保留全局统一日志,可保留原有createWithDefaultLogger初始化逻辑,再额外添加自定义Sink即可,多个Sink之间互不影响

内容的提问来源于stack exchange,提问作者chan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.27 07:09:00