文章目录
一、完整函数调用链
覆盖日志框架的主要使用场景(同步日志输出、异步日志输出、日志器创建、日志格式化、文件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通过文件流追加模式保证多线程写入不冲突。