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