【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_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, 二进制+追加模式)总结
- 核心流程:日志宏调用 → 日志级别检查 → 日志格式化 → 日志输出(同步/异步)→ Sink写入(控制台/文件/滚动文件),其中异步日志通过
AsyncLooper的生产者-消费者模型实现。 - 关键依赖:
util.hpp提供的文件/时间工具是日志框架的基础(目录创建、时间戳、文件路径解析),Buffer类为异步日志提供高效的内存管理,Formatter类通过组合模式实现灵活的日志格式解析。 - 线程安全:同步日志通过
std::mutex保证输出安全,异步日志通过条件变量+互斥锁实现缓冲区的线程安全操作,滚动文件Sink通过文件流追加模式保证多线程写入不冲突。
