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

发布时间:2026/7/14 15:31:51

【C++】深入解析日志框架调用链 文章目录一、完整函数调用链场景1全局根日志器输出DEBUG日志最常用场景场景2创建异步日志器并输出INFO日志场景3滚动文件Sink输出日志RollSink场景4缓冲区自动扩容Buffer二、关键工具函数调用链util.hpp三、核心依赖调用链辅助场景总结一、完整函数调用链覆盖日志框架的主要使用场景同步日志输出、异步日志输出、日志器创建、日志格式化、文件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_lockstd::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.buildSinkFileSink(./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_sharedAsyncLooper(回调函数) // 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_lockstd::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输出日志RollSinklogger-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缓冲区自动扩容BufferAsyncLooper::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翻倍≥10MB1MB │ └── _v.resize(new_capacity) // vector扩容 ├── std::copy(data, datalen, _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, pos1) // 截取目录部分 // 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::createFileSink(filename) // sink.hpp └── std::make_sharedFileSink(filename) └── FileSink::FileSink(filename) ├── util::file::path(filename) ├── util::file::create_directory(path) └── _ofs.open(filename, 二进制追加模式)总结核心流程日志宏调用 → 日志级别检查 → 日志格式化 → 日志输出同步/异步→ Sink写入控制台/文件/滚动文件其中异步日志通过AsyncLooper的生产者-消费者模型实现。关键依赖util.hpp提供的文件/时间工具是日志框架的基础目录创建、时间戳、文件路径解析Buffer类为异步日志提供高效的内存管理Formatter类通过组合模式实现灵活的日志格式解析。线程安全同步日志通过std::mutex保证输出安全异步日志通过条件变量互斥锁实现缓冲区的线程安全操作滚动文件Sink通过文件流追加模式保证多线程写入不冲突。

相关新闻