LogStream设计了一个实现streaming log宏的基准接口表达,并通过继承实现对用户提供了两个扩展点
#include "babylon/logging/log_stream.h"
using babylon::LogStream;
// 通过继承方式实现一个有实际意义的LogStream
class SomeLogStream : public LogStream {
// 基类构造必须传入一个可用的std::streambuf的子类
// 所有写入动作最终都会生效到这个缓冲区中
SomeLogStream() : LogStream(stringbuf) {}
// 一次日志事务的开头结尾进行的额外动作
virtual void do_begin() noexcept override {
*this << ... // 典型用法是实现日志头的前置输出
}
virtual void do_end() noexcept override {
write('\n'); // 一般对于文本日志都需要追加结尾换行
// 没有默认实现主要为了能够同样表达非文本日志
... // 一般最后都需要提交到日志系统的实际后端实现
}
...
// 这里以std::stringbuf为例,实际上可以是任意自定义的流缓冲区
std::stringbuf stringbuf;
};
// 一般无需直接使用LogStream,而是通过Logger的包装宏使用
// 包装宏会自动完成begin/end的调用管理
LogStream& ls = ...
ls.begin();
ls << ...
ls.end();
// 除了流操作符,也支持printf-like的格式化动作
// 实际格式化功能由absl::Format提供
ls.format("some spec %d", value);
实际最终日志打印动作可见的对向是Logger,而非直接使用LogStream。Logger主要提供了两个能力
Logger本身仅供使用,创建一个Logger通过LoggerBuilder完成
#include "babylon/logging/logger.h"
using babylon::Logger;
using babylon::LoggerBuilder;
using babylon::LogSeverity;
LoggerBuilder builder;
// 对所有日志等级设置统一的LogStream
// 由于最终会对每个线程创建一次
// 所以传入的不是创建好的实例,而是创建实例的函数
builder.set_log_stream_creator([] {
auto ls = ...
return std::unique_ptr<LogStream>(ls);
});
// 也可以对某个级别设置单独的LogStream
builder.set_log_stream_creator(LogSeverity::INFO, [] {
auto ls = ...
return std::unique_ptr<LogStream>(ls);
});
// 设置最低日志等级,低于最低等级无论设置与否都会按照空LogStream处理
// 同时日志宏包装也会识别最低等级进行整段流操作符的省略
// LogSeverity包含{DEBUG, INFO, WARNING, FATAL}四个等级
builder.set_min_severity(LogSeverity min_severity);
// 按照设置实际构造一个可以使用的Logger
Logger logger = builder.build();
// 使用logger,打印置顶级别的日志
// ***部分可以追加其他业务通用头,也同样遵守noflush时只输出一次的规则
// ...部分是正常的日志流输入
BABYLON_LOG_STREAM(logger, INFO, ***) << ...
LoggerManager维护了层级化的Logger树,初始化阶段用来配置,运行时用来获取获取各个层级节点的Logger,配置和运行阶段通过明确的初始化动作完成转换
#include "babylon/logging/logger.h"
using babylon::LoggerManager;
// LoggerManager通过全局单例使用
auto& manager = LoggerManager::instance();
// 根据name取得某个层级的logger
// name根据层级查找配置,例如对于name = "a.b.c"
// 会依次尝试"a.b.c" -> "a.b" -> "a" -> root
// 层次同时兼容"a::b::c" -> "a::b" -> "a" -> root
auto& logger = manager.get_logger("...");
// 直接取得root logger
auto& root_logger = manager.get_root_logger();
// 在任何设置动作发生之前,取得的Logger都是默认状态,全部输出到标准错误
// 构造计划生效的builder
LoggerBuilder&& builder = ...
// 设置root logger
manager.set_root_builder(builder);
// 设置某个层级的logger
manager.set_builder("a.b.c", builder);
// 所有设置只有在apply之后才会统一生效
manager.apply();
// 完成设置后,『之前』取得的Logger也会从默认状态,变更为正确的状态
// 变更过程是线程安全的,正在使用中的Logger也会被正确处理
// 『之后』取得的Logger直接就会是正确设置的状态
// 默认宏内部也会使用root logger
BABYLON_LOG(INFO) << ...
// 使用指定的logger
BABYLON_LOG_STREAM(logger, INFO) << ...
// 一般在语句结束后,就会视为结束进行日志提交
// 支持使用noflush来进行渐进组装
BABYLON_LOG(INFO) << "some " << ::babylon::noflush;
BABYLON_LOG(INFO) << "thing"; // 输出[header] some thing
// 支持类printf格式化功能,底层由abseil-cpp库提供支持
BABYLON_LOG(INFO).format("hello %s", world).format(" +%d", 10086);