easylogging++的那些事(四)源码分析(三)类printf接口
在上一篇我们分析了 VERBOSE日志宏。在 功能介绍 中我们介绍过使用 logger 类的 类 printf 接口来输出日志,今天来看看 logger 类的 类 printf 接口的实现。
接口创建宏
接口声明宏
除
verbose接口外的其他类printf接口(info,debug,warn,error,fatal,trace)的声明是基于LOGGER_LEVEL_WRITERS_SIGNATURES来完成的。
LOGGER_LEVEL_WRITERS_SIGNATURES宏的定义如下:#define LOGGER_LEVEL_WRITERS_SIGNATURES(FUNCTION_NAME) \ template <typename T, typename... Args> \ inline void FUNCTION_NAME(const char *, const T &, const Args &...); \ template <typename T> \ inline void FUNCTION_NAME(const T &);
接口实现宏
除
verbose接口外的其他接口(info,debug,warn,error,fatal,trace)的实现是基于LOGGER_LEVEL_WRITERS宏和LOGGER_LEVEL_WRITERS_DISABLED宏来完成的。
LOGGER_LEVEL_WRITERS宏的定义如下:#define LOGGER_LEVEL_WRITERS(FUNCTION_NAME, LOG_LEVEL) \ template <typename T, typename... Args> \ inline void Logger::FUNCTION_NAME(const char *s, const T &value, const Args &...args) \ { \ log(LOG_LEVEL, s, value, args...); \ } \ template <typename T> \ inline void Logger::FUNCTION_NAME(const T &value) \ { \ log(LOG_LEVEL, value); \ }
LOGGER_LEVEL_WRITERS_DISABLED宏的定义如下:#define LOGGER_LEVEL_WRITERS_DISABLED(FUNCTION_NAME, LOG_LEVEL) \ template <typename T, typename... Args> \ inline void Logger::FUNCTION_NAME(const char *, const T &, const Args &...) \ { \ return; \ } \ template <typename T> \ inline void Logger::FUNCTION_NAME(const T &) \ { \ return; \ }
LOGGER_LEVEL_WRITERS_DISABLED宏从定义上来看,啥也没干,主要用于日志级别的日志输出被禁用的情况。
基于接口创建宏创建的接口
宏展开
info 接口
info接口的声明如下:LOGGER_LEVEL_WRITERS_SIGNATURES(info)展开后为:
template <typename T, typename... Args> inline void info(const char *, const T &, const Args &...); template <typename T> inline void info(const T &);
info接口的实现如下:#if ELPP_INFO_LOG LOGGER_LEVEL_WRITERS(info, Level::Info) #else LOGGER_LEVEL_WRITERS_DISABLED(info, Level::Info) #endif // ELPP_INFO_LOG
LOGGER_LEVEL_WRITERS_SIGNATURES宏,LOGGER_LEVEL_WRITERS宏和LOGGER_LEVEL_WRITERS_DISABLED宏在前面接口创建宏中已经介绍过了。
ELPP_INFO_LOG宏在CLOG宏展开 中已经详细介绍过了,用于启用info级别日志。
ELPP_INFO_LOG值为0时,LOGGER_LEVEL_WRITERS_DISABLED(info, Level::Info)展开后为:template <typename T, typename... Args> inline void Logger::info(const char *, const T &, const Args &...) { return; } template <typename T> inline void Logger::info(const T &) { return; }
ELPP_INFO_LOG值为1时,LOGGER_LEVEL_WRITERS(info, Level::Info)展开后为:template <typename T, typename... Args> inline void Logger::info(const char *s, const T &value, const Args &...args) { log(Level::Info, s, value, args...); } template <typename T> inline void Logger::info(const T &value) { log(Level::Info, value); }debug 接口
debug接口的声明如下:LOGGER_LEVEL_WRITERS_SIGNATURES(debug)
debug接口的实现如下:#if ELPP_DEBUG_LOG LOGGER_LEVEL_WRITERS(debug, Level::Debug) #else LOGGER_LEVEL_WRITERS_DISABLED(debug, Level::Debug) #endif // ELPP_DEBUG_LOG
LOGGER_LEVEL_WRITERS_SIGNATURES宏,LOGGER_LEVEL_WRITERS宏和LOGGER_LEVEL_WRITERS_DISABLED宏在前面接口创建宏中已经介绍过了。
ELPP_DEBUG_LOG宏在CLOG宏展开 中已经详细介绍过了,用于启用debug级别日志。
ELPP_DEBUG_LOG值为0时,LOGGER_LEVEL_WRITERS_DISABLED(debug, Level::Debug)展开后为:template <typename T, typename... Args> inline void Logger::debug(const char *, const T &, const Args &...) { return; } template <typename T> inline void Logger::debug(const T &) { return; }
ELPP_DEBUG_LOG值为1时,LOGGER_LEVEL_WRITERS(debug, Level::Debug)展开后为:template <typename T, typename... Args> inline void Logger::debug(const char *s, const T &value, const Args &...args) { log(Level::Debug, s, value, args...); } template <typename T> inline void Logger::debug(const T &value) { log(Level::Debug, value); }warn 接口
warn接口的声明如下:LOGGER_LEVEL_WRITERS_SIGNATURES(warn)
warn接口的实现如下:#if ELPP_WARNING_LOG LOGGER_LEVEL_WRITERS(warn, Level::Warning) #else LOGGER_LEVEL_WRITERS_DISABLED(warn, Level::Warning) #endif // ELPP_WARNING_LOG
LOGGER_LEVEL_WRITERS_SIGNATURES宏,LOGGER_LEVEL_WRITERS宏和LOGGER_LEVEL_WRITERS_DISABLED宏在前面接口创建宏中已经介绍过了。
ELPP_WARNING_LOG宏在CLOG宏展开 中已经详细介绍过了,用于启用warn级别日志。
ELPP_WARNING_LOG值为0时,LOGGER_LEVEL_WRITERS_DISABLED(debug, Level::Debug)展开后为:template <typename T, typename... Args> inline void Logger::warn(const char *, const T &, const Args &...) { return; } template <typename T> inline void Logger::warn(const T &) { return; }
ELPP_WARNING_LOG值为 1 时,LOGGER_LEVEL_WRITERS(warn, Level::Warning)展开后为:template <typename T, typename... Args> inline void Logger::warn(const char *s, const T &value, const Args &...args) { log(Level::Warning, s, value, args...); } template <typename T> inline void Logger::warn(const T &value) { log(Level::Warning, value); }error 接口
error接口的声明如下:LOGGER_LEVEL_WRITERS_SIGNATURES(error)
error接口的实现如下:#if ELPP_ERROR_LOG LOGGER_LEVEL_WRITERS(error, Level::Error) #else LOGGER_LEVEL_WRITERS_DISABLED(error, Level::Error) #endif // ELPP_ERROR_LOG
LOGGER_LEVEL_WRITERS_SIGNATURES宏,LOGGER_LEVEL_WRITERS宏和LOGGER_LEVEL_WRITERS_DISABLED宏在前面接口创建宏中已经介绍过了。
ELPP_ERROR_LOG宏在CLOG宏展开 中已经详细介绍过了,用于启用error级别日志。
ELPP_ERROR_LOG值为0时,LOGGER_LEVEL_WRITERS_DISABLED(error, Level::Error)展开后为:template <typename T, typename... Args> inline void Logger::error(const char *, const T &, const Args &...) { return; } template <typename T> inline void Logger::error(const T &) { return; }上面的接口其实什么也没做。
ELPP_ERROR_LOG值为1时,LOGGER_LEVEL_WRITERS(warn, Level::Error)展开后为:template <typename T, typename... Args> inline void Logger::error(const char *s, const T &value, const Args &...args) { log(Level::Error, s, value, args...); } template <typename T> inline void Logger::error(const T &value) { log(Level::Error, value); }fatal 接口
fatal接口的声明如下:LOGGER_LEVEL_WRITERS_SIGNATURES(fatal)
fatal接口的实现如下:#if ELPP_FATAL_LOG LOGGER_LEVEL_WRITERS(fatal, Level::Fatal) #else LOGGER_LEVEL_WRITERS_DISABLED(fatal, Level::Fatal) #endif // ELPP_FATAL_LOG
LOGGER_LEVEL_WRITERS_SIGNATURES宏,LOGGER_LEVEL_WRITERS宏和LOGGER_LEVEL_WRITERS_DISABLED宏在前面接口创建宏中已经介绍过了。
ELPP_FATAL_LOG宏在CLOG宏展开 中已经详细介绍过了,用于启用fatal级别日志。
ELPP_FATAL_LOG值为0时,LOGGER_LEVEL_WRITERS_DISABLED(fatal, Level::Fatal)展开后为:template <typename T, typename... Args> inline void Logger::fatal(const char *, const T &, const Args &...) { return; } template <typename T> inline void Logger::fatal(const T &) { return; }
ELPP_FATAL_LOG值为 1 时,LOGGER_LEVEL_WRITERS(fatal, Level::Fatal)展开后为:template <typename T, typename... Args> inline void Logger::fatal(const char *s, const T &value, const Args &...args) { log(Level::Fatal, s, value, args...); } template <typename T> inline void Logger::fatal(const T &value) { log(Level::Fatal, value); }trace 接口
trace接口的声明如下:LOGGER_LEVEL_WRITERS_SIGNATURES(trace)
trace接口的实现如下:#if ELPP_TRACE_LOG LOGGER_LEVEL_WRITERS(trace, Level::Trace) #else LOGGER_LEVEL_WRITERS_DISABLED(trace, Level::Trace) #endif // ELPP_TRACE_LOG
LOGGER_LEVEL_WRITERS_SIGNATURES宏,LOGGER_LEVEL_WRITERS宏和LOGGER_LEVEL_WRITERS_DISABLED宏在前面接口创建宏中已经介绍过了。
ELPP_TRACE_LOG宏在CLOG宏展开 中中已经详细介绍过了,用于启用trace级别日志。
ELPP_TRACE_LOG值为0时,LOGGER_LEVEL_WRITERS_DISABLED(trace, Level::Trace)展开后为:template <typename T, typename... Args> inline void Logger::trace(const char *, const T &, const Args &...) { return; } template <typename T> inline void Logger::trace(const T &) { return; }
ELPP_TRACE_LOG值为1时,LOGGER_LEVEL_WRITERS(trace, Level::Trace)展开后为:template <typename T, typename... Args> inline void Logger::trace(const char *s, const T &value, const Args &...args) { log(Level::Trace, s, value, args...); } template <typename T> inline void Logger::trace(const T &value) { log(Level::Trace, value); }
源码分析
上面
类printf接口(info,debug,warn,error,fatal,trace)实际上都委托给了对应的 log 可变参函数模板来实现:template <typename T, typename... Args> inline void Logger::log(Level level, const char *s, const T &value, const Args &...args) { acquireLock(); // released in Writer! log_(level, 0, s, value, args...); } //log可变参函数模板的退出接口 template <typename T> inline void Logger::log(Level level, const T &log) { acquireLock(); // released in Writer! log_(level, 0, log); }这两个
log接口只不过是对应log_接口的增强类, 增强的地方在于加锁了 。实际功能再次委托给了对应 log_可变参函数模板:template <typename T, typename... Args> void Logger::log_(Level level, int vlevel, const char *s, const T &value, const Args &...args) { // MessageBuilder 类在 CLOG 宏其他相关类中中已经详细介绍过了, 用于支持各种类型的日志输出 base::MessageBuilder b; b.initialize(this); while (*s) { // 当前字符是% if (*s == base::consts::kFormatSpecifierChar) { if (*(s + 1) == base::consts::kFormatSpecifierChar) { // 下一个字符也是%,则跳过当前字符,并直接将%字符放入日志记录器的字符串流对象中 ++s; } else { // 下一个字符也是 v if (*(s + 1) == base::consts::kFormatSpecifierCharValue) { ++s; // 将当前的可变参放入日志记录器的字符串流对象中(类 printf 接口中,%v 这种形式的格式说明符可以支持所有的数据类型(需要 MessageBuilder 类提供了针对这种类型的日志输出支持)) b << value; // 跳过 "%v" 格式说明符, 继续递归解析剩余的格式化字符串 log_(level, vlevel, ++s, args...); return; } } } // 非“%v”, 非“%%”的其他形式的字符,直接放入日志记录器的字符串流对象中 b << *s++; } ELPP_INTERNAL_ERROR("Too many arguments provided. Unable to handle. Please provide more format specifiers", false); }上面的实现本身并不复杂,无非是对 s(相当于 printf 中的格式化字符串)逐个字符进行解析,调整后,放入日志记录器的字符串流对象中。
从上面的实现中可以看到:logger类的类printf接口只能解析“%v”和“%%”这种格式说明符,其他配置文件中支持的日志格式指示器,这里都不支持。下面这个接口是上面
log_可变参函数模板的退出接口。可变参函数模板一般都会定义这样的两个接口。template <typename T> void Logger::log_(Level level, int vlevel, const T &log) { // 是否 VERBOSE 日志 if (level == Level::Verbose) { // 当前文件在当前 VLEVEL 是否允许进行 VERBOSE 日志输出 if (ELPP->vRegistry()->allowed(vlevel, __FILE__)) { // 这里是通过 verbose 接口进到这里来的,verbose 接口入口处已经加锁了,所以这里 Writer 类里面的接口不用再次加锁了,所以传递 false base::Writer(Level::Verbose, "FILE", 0, "FUNCTION", base::DispatchAction::NormalLog, vlevel) .construct(this, false) << log; } else { // 不允许进行 VERBOSE 日志输出则清空日志记录器对应的字符串流对象 stream().str(ELPP_LITERAL("")); // verbose 接口入口处已经加锁了,这里需要释放锁 releaseLock(); } } else { // 分层日志 base::Writer(level, "FILE", 0, "FUNCTION").construct(this, false) << log; } }
el::base::Writer类我们在CLOG宏 Writer 对象的创建以及初始化、日志输出、日志信息保存 已经仔细介绍过了,这里就不多说了。
verbose 接口
verbose接口的声明如下:template <typename T, typename... Args> inline void verbose(int, const char *, const T &, const Args &...); template <typename T> inline void verbose(int, const T &);
verbose接口的实现如下:#if ELPP_VERBOSE_LOG template <typename T, typename... Args> inline void Logger::verbose(int vlevel, const char *s, const T &value, const Args &...args) { acquireLock(); // released in Writer! log_(el::Level::Verbose, vlevel, s, value, args...); } template <typename T> inline void Logger::verbose(int vlevel, const T &log) { acquireLock(); // released in Writer! log_(el::Level::Verbose, vlevel, log); } #else template <typename T, typename... Args> inline void Logger::verbose(int, const char *, const T &, const Args &...) { return; } template <typename T> inline void Logger::verbose(int, const T &) { return; } #endif // ELPP_VERBOSE_LOG
ELPP_VERBOSE_LOG宏在 VERBOSE日志宏 中已经介绍过了,用于启用VERBOSE日志。
ELPP_VERBOSE_LOG值为0时,两个verbose接口什么也没做。
ELPP_VERBOSE_LOG值为1时,两个verbose接口实际是委托给对应的log_接口来实现的。
log_接口在前面已经详细介绍过了,这里就不多说了。
至此,logger 类的类 printf 接口就介绍完了,下一篇我们开始介绍日志格式的配置与加载。
本文来自博客园,作者:节奏自由,转载请注明原文链接:https://www.cnblogs.com/DesignLife/p/16941827.html

浙公网安备 33010602011771号