【C++】深入解析日志框架调用链

文章目录

一、完整函数调用链

覆盖日志框架的主要使用场景(同步日志输出、异步日志输出、日志器创建、日志格式化、文件Sink输出等)。

场景1:全局根日志器输出DEBUG日志(最常用场景)
复制代码
// 1. 宏调用(bitlog.h)
LOGD("debug msg: %s", "test")
└── LOG_DEBUG(ApeLog::rootLogger(), fmt, ##__VA_ARGS__)
    └── (logger)->debug(fmt, ##__VA_ARGS__)
        └── Logger::debug(const char* file, size_t line, const char* fmt, ...)  // logger.hpp
            ├── Logger::shouldLog(LogLevel::value::DEBUG)  // 检查日志级别
            ├── va_start/va_end(初始化可变参数)
            └── Logger::log(LogLevel::value::DEBUG, file, line, fmt, al)  // 核心日志处理
                ├── vasprintf(格式化可变参数为字符串)
                ├── LogMsg构造函数(message.hpp)
                │   └── util::date::now()  // 获取时间戳(util.hpp)
                │   └── std::this_thread::get_id()  // 获取线程ID
                ├── Formatter::format(ss, log_msg)  // 格式化日志(formatter.hpp)
                │   └── 遍历Formatter::_items,依次调用FormatItem::format
                │       ├── TimeFormatItem::format → localtime_r + strftime
                │       ├── ThreadFormatItem::format → 输出线程ID
                │       ├── LevelFormatItem::format → LogLevel::toString
                │       ├── NameFormatItem::format → 输出日志器名称
                │       ├── CFileFormatItem::format → 输出文件名
                │       ├── CLineFormatItem::format → 输出行号
                │       ├── MsgFormatItem::format → 输出日志消息
                │       └── NLineFormatItem::format → 输出换行
                └── Logger::logIt(ss.str())  // 纯虚函数,由子类实现
                    └── SyncLogger::logIt(const std::string& msg)  // 同步日志器输出
                        ├── std::unique_lock<std::mutex>(加锁)
                        └── 遍历Logger::_sinks,调用LogSink::log
                            └── StdoutSink::log(const char* data, size_t len)  // 控制台输出(sink.hpp)
                                └── std::cout.write(data, len)
场景2:创建异步日志器并输出INFO日志
复制代码
// 1. 构建异步日志器(logger.hpp)
GlobalLoggerBuilder builder;
builder.buildLoggerName("async_logger");
builder.buildLoggerType(Logger::Type::LOGGER_ASYNC);
builder.buildFormatter("%d{%Y-%m-%d} %t [%p] %m%n");
builder.buildSink<FileSink>("./logs/async.log");
Logger::ptr logger = builder.build();
├── GlobalLoggerBuilder::build()  // 构建并注册日志器
    ├── 检查日志器名称非空
    ├── 检查格式化器(无则创建默认Formatter)
    │   └── Formatter::Formatter(pattern)
    │       └── Formatter::parsePattern()  // 解析格式字符串
    │           └── Formatter::createItem → 创建对应FormatItem实例
    ├── 检查Sink(无则添加StdoutSink)
    ├── 创建AsyncLogger实例
    │   └── AsyncLogger::AsyncLogger(...)
    │       └── Logger::Logger(...)  // 父类构造
    │       └── _looper = std::make_shared<AsyncLooper>(回调函数)  // looper.hpp
    │           └── AsyncLooper::AsyncLooper(cb)
    │               └── _thread = std::thread(&AsyncLooper::worker_loop, this)  // 启动工作线程
    └── loggerManager::getInstance().addLogger(...)  // 注册到管理器

// 2. 输出INFO日志
LOG_INFO(logger, "info msg: %d", 123)
└── Logger::info(...)
    ├── Logger::shouldLog(LogLevel::value::INFO)
    ├── Logger::log(...)  // 同场景1,格式化日志
    └── Logger::logIt(...)
        └── AsyncLogger::logIt(const std::string& msg)  // 异步日志器输出
            └── _looper->push(msg)  // 推送消息到异步缓冲区
                ├── AsyncLooper::push(const std::string& msg)  // looper.hpp
                │   ├── std::unique_lock<std::mutex>(加锁)
                │   ├── _push_cond.wait(等待缓冲区有空间)
                │   ├── _tasks_push.push(msg.c_str(), msg.size())  // 写入缓冲区
                │   └── _pop_cond.notify_all()(唤醒工作线程)
                └── 工作线程执行AsyncLooper::worker_loop()
                    ├── _pop_cond.wait(等待有消息)
                    ├── _tasks_push.swap(_tasks_pop)(交换缓冲区)
                    ├── 调用回调函数(AsyncLogger::backendLogIt)
                    │   └── 遍历_sinks,调用LogSink::log
                    │       └── FileSink::log(const char* data, size_t len)  // sink.hpp
                    │           ├── FileSink::initLogFile()(文件未打开则初始化)
                    │           │   ├── util::file::path(filename)  // 获取文件目录(util.hpp)
                    │           │   └── util::file::create_directory(path)  // 创建目录
                    │           │       ├── util::file::exists(path)  // 检查目录是否存在
                    │           │       └── mkdir(系统调用创建目录)
                    │           └── _ofs.write(data, len)  // 写入文件
                    └── _tasks_pop.reset()(重置缓冲区)
场景3:滚动文件Sink输出日志(RollSink)
复制代码
logger->warn("warn msg")
└── Logger::warn(...)
    ├── Logger::log(...)
    └── Logger::logIt(...)
        └── SyncLogger::logIt(...)
            └── RollSink::log(const char* data, size_t len)  // sink.hpp
                ├── RollSink::initLogFile()  // 检查是否需要滚动文件
                │   ├── 判断文件大小是否超过_max_fsize
                │   ├── RollSink::createFilename()  // 创建带时间戳的文件名
                │   │   ├── time(NULL) + localtime_r(获取本地时间)
                │   │   └── std::stringstream拼接文件名
                │   ├── util::file::create_directory(util::file::path(_basename))  // 创建目录
                │   └── _ofs.open(name, 二进制+追加模式)
                ├── _ofs.write(data, len)  // 写入文件
                └── _cur_fsize += len  // 更新当前文件大小
场景4:缓冲区自动扩容(Buffer)
复制代码
AsyncLooper::push(msg) → _tasks_push.push(msg.c_str(), msg.size())
└── Buffer::push(const char* data, size_t len)  // buffer.hpp
    ├── Buffer::ensureEnoughSpace(len)  // 检查并扩容
    │   ├── 判断len > writeAbleSize()
    │   ├── 计算new_capacity(分阈值扩容:<10MB翻倍,≥10MB+1MB)
    │   └── _v.resize(new_capacity)  // vector扩容
    ├── std::copy(data, data+len, &_v[_writer_idx])  // 拷贝数据
    └── _writer_idx += len  // 移动写指针

二、关键工具函数调用链(util.hpp)

复制代码
// 1. 创建多级目录
util::file::create_directory(path)
├── util::file::exists(path)  // 检查路径是否存在
│   └── stat(path.c_str(), &st)  // 系统调用
├── 遍历路径,逐段解析目录
├── util::file::exists(subdir)  // 检查子目录是否存在
└── mkdir(subdir.c_str(), 0755)  // 系统调用创建目录

// 2. 获取文件所在目录
util::file::path(name)
├── name.find_last_of("/\\")  // 查找最后一个分隔符
└── name.substr(0, pos+1)  // 截取目录部分

// 3. 获取当前时间戳
util::date::now()
└── (size_t)time(nullptr)  // 系统调用

三、核心依赖调用链(辅助场景)

复制代码
// 1. 日志器管理器(单例)
loggerManager::getInstance()
├── static loggerManager log_mgr;  // 单例初始化
├── loggerManager::loggerManager()  // 私有构造
    ├── LocalLoggerBuilder::build()  // 创建根日志器
    └── _loggers.insert("root", _root_logger)  // 注册根日志器

// 2. Sink工厂创建实例
SinkFactory::create<FileSink>(filename)  // sink.hpp
└── std::make_shared<FileSink>(filename)
    └── FileSink::FileSink(filename)
        ├── util::file::path(filename)
        ├── util::file::create_directory(path)
        └── _ofs.open(filename, 二进制+追加模式)

总结

  1. 核心流程 :日志宏调用 → 日志级别检查 → 日志格式化 → 日志输出(同步/异步)→ Sink写入(控制台/文件/滚动文件),其中异步日志通过AsyncLooper的生产者-消费者模型实现。
  2. 关键依赖util.hpp提供的文件/时间工具是日志框架的基础(目录创建、时间戳、文件路径解析),Buffer类为异步日志提供高效的内存管理,Formatter类通过组合模式实现灵活的日志格式解析。
  3. 线程安全 :同步日志通过std::mutex保证输出安全,异步日志通过条件变量+互斥锁实现缓冲区的线程安全操作,滚动文件Sink通过文件流追加模式保证多线程写入不冲突。
相关推荐
m0_528174452 小时前
多平台UI框架C++开发
开发语言·c++·算法
爱搞事的程小猿2 小时前
qt系统字体方案
c++·qt
ShineWinsu2 小时前
对于Linux:基础指令的介绍—中
linux·运维·服务器·c++·面试·笔试·系统
catchadmin2 小时前
告别阻塞!用 PHP TrueAsync 实现 PHP 脚本提速 10 倍
开发语言·php
草莓熊Lotso2 小时前
MySQL CRUD 核心指南:查询、插入、更新、删除全实战
android·开发语言·数据库·c++·人工智能·mysql
轩情吖2 小时前
MySQL之表的约束
android·数据库·c++·后端·mysql·开发·约束
窝子面2 小时前
LeetCode练题一:async 和await 和 promise
开发语言·前端·javascript
qq_334903152 小时前
编译器内建函数使用
开发语言·c++·算法
阿贵---2 小时前
C++中的中介者模式
开发语言·c++·算法