Log4Qt快速入门——Log4Qt日志格式化源码解析
Log4Qt快速入门--Log4Qt日志格式化源码解析
一、Layout
1、Layout简介
Log4Qt提供了多种Layout对象,用于格式化日志输出,指定日志级别、线程名称、Logger名称、日期时间等信息。
Layout类是Log4Qt API中的抽象类。
PatternLayout:根据一个模式字符串输出日志事件;
SimpleLayout:输出日志事件的级别和消息;
TTCCLayout:输出日志事件的时间、线程名称、Logger名称和嵌套的诊断上下文信息。
PatternLayout和TTCCLayout通过PatternFormatter来实现格式化。当PatternFormatter解析模式字符串时,会根据发现的信息创建一个PatternConverter链,每个PatternConverter会处理LoggingEvent的某个成员。
转换字符:用于指定数据的类型,例如:类别、级别、日期、线程名称。Log4Qt中的转换字符有:
c:Logger 名称。
d{format_string}:日期。参数 format_string 可选,用于格式化日期。
m:消息内容
p:消息级别
r:启动程序的相对时间
t:线程名称
x:NDC(嵌套的诊断上下文)名称
X:MDC(映射的诊断上下文)名称
F:文件名称
M:方法名称
L:行号
l:位置信息
n:平台相关的行分隔符,Windows:\r\n,Linux:\n
2、NDC简介
NDC(Nested Diagnostic Context)即嵌套诊断上下文,是log4J用于存储上下文信息(context information)的类,NDC采用栈的机制push和pop上下文,每个线程有独立的上下文,如果要存储的上下文信息是堆栈式的在选择NDC。
NDC常用接口如下:static QString pop();
将NDC栈顶元素弹出static void push(const QString &rMessage);
将rMessage压入NDC栈static void clear();
清空NDC栈static int depth();
获取NDC栈的深度static void setMaxDepth(int maxDepth);
设置NDC栈的最大深度static QString peek();
获取NDC栈顶的数据
通常在context或servlet入口将线程相关的应用信息保存,在需要记录日志log信息等时将信息输出,并保证在当前线程的结束或servlet的出口使用clear()移除,防止信息泄露。
3、MDC简介
MDC(Mapped Diagnositc Context)即映射诊断上下文,是log4J用于存储上下文信息(context information)的类,MDC内部采用Hash容器实现,是线程独立的,但一个子线程会自动获得一个父线程MDC的copy,如果要存储的上下文信息是key/value式的选择MDC。
MDC常用接口如下:static void put(const QString &rKey, const QString &rValue);
将rKey/rValue数据存储到Hash容器中static void remove(const QString &rKey);
从Hash容器中删除key为rKey的key/valuestatic QString get(const QString &rKey);
从Hash容器中获取key为rKey的key/valuestatic QHash<QString, QString> context();
获取Hash容器中所有的内容
4、Layout常用接口
QString footer() const;
获取Layout对象的footerQString header() const;
获取Layout对象的headerQString name() const;
获取Layout对象的对象名称void setFooter(const QString &rFooter);
设置Layout对象的footervoid setHeader(const QString &rHeader);
设置Layout对象的headervoid setName(const QString &rName);
设置Layout对象的对象名称virtual QString format(const LoggingEvent &rEvent) = 0;
Layout对象的日志消息格式化接口,派生类PatternLayout、SimpleLayout、TTCCLayout通过format函数来确定具体要输出信息的格式。
二、PatternLayout
1、PatternLayout 简介
PatternLayout是Layout的一个派生类,如果想生成基于模式匹配的特定格式的日志信息,可以使用PatternLayout来进行格式化。
PatternLayout的枚举ConversionPattern定义了两个常用的模式:
enum ConversionPattern{ DEFAULT_CONVERSION_PATTERN,// "%m,%n" TTCC_CONVERSION_PATTERN,//"%r [%t] %p %c %x - %m%n"};
2、PatternLayout常用接口
QString conversionPattern() const;
获取PatternLayout对象的转换模式匹配字符串void setConversionPattern(const QString &rPattern);
设置PatternLayout对象的转换模式匹配字符串void setConversionPattern(ConversionPattern conversionPattern);
设置PatternLayout对象的转换模式匹配方式
3、format格式化实现
QString PatternLayout::format(const LoggingEvent &rEvent){ Q_ASSERT_X(mpPatternFormatter, "PatternLayout::format()", "mpPatternConverter must not be null"); return mpPatternFormatter->format(rEvent);}
调用PatternFormatter格式化函数根据模式转换器链表的每个模式转换器格式化信息。
QString PatternFormatter::format(const LoggingEvent &rLoggingEvent) const { QString result; PatternConverter *p_converter; Q_FOREACH(p_converter, mPatternConverters) p_converter->format(result, rLoggingEvent); return result; }
PatternFormatter使用模式匹配字符串创建一个模式转换器mPatternConverters,每个转换符对应一个模式转换器。
void PatternFormatter::createConverter(const QChar &rChar, const FormattingInfo &rFormattingInfo, const QString &rOption) { Q_ASSERT_X(mConversionCharacters.indexOf(rChar) >= 0, "PatternFormatter::createConverter", "Unknown conversion character" ); LogError e("Creating Converter for character '%1' min %2, max %3, left %4 and option '%5'"); e << QString(rChar) << FormattingInfo::intToString(rFormattingInfo.mMinLength) << FormattingInfo::intToString(rFormattingInfo.mMaxLength) << rFormattingInfo.mLeftAligned << rOption; logger()->trace(e); switch (rChar.toLatin1()) { case 'c': mPatternConverters << new LoggerPatternConverter(rFormattingInfo, parseIntegerOption(rOption)); break; case 'd': { QString option = rOption; if (rOption.isEmpty()) option = QLatin1String("ISO8601"); mPatternConverters << new DatePatternConverter(rFormattingInfo, option); break; } case 'm': mPatternConverters << new BasicPatternConverter(rFormattingInfo, BasicPatternConverter::MESSAGE_CONVERTER); break; case 'p': mPatternConverters << new BasicPatternConverter(rFormattingInfo, BasicPatternConverter::LEVEL_CONVERTER); break; case 'r': mPatternConverters << new DatePatternConverter(rFormattingInfo, QLatin1String("RELATIVE")); break; case 't': mPatternConverters << new BasicPatternConverter(rFormattingInfo, BasicPatternConverter::THREAD_CONVERTER); break; case 'x': mPatternConverters << new BasicPatternConverter(rFormattingInfo, BasicPatternConverter::NDC_CONVERTER); break; case 'X': mPatternConverters << new MDCPatternConverter(rFormattingInfo, rOption); break; default: Q_ASSERT_X(false, "PatternFormatter::createConverter", "Unknown pattern character"); } }
4、PatternLayout使用示例
#include #include #include #include #include int main(int argc, char *argv[]){ QCoreApplication a(argc, argv); // 获取rootLogger Log4Qt::Logger *logger = Log4Qt::Logger::rootLogger(); // 创建PatternLayout(根据模式字符串输出日志事件) Log4Qt::PatternLayout *layout = new Log4Qt::PatternLayout(); // 设置标头信息 layout->setHeader("----- start -----"); // 设置页脚信息 layout->setFooter("----- end -----"); // 设置转换模式 layout->setConversionPattern("%d{yyyy-MM-dd hh:mm:ss} [%p] - %m%n"); // 激活Layout layout->activateOptions(); // 创建ConsoleAppender Log4Qt::ConsoleAppender *appender = new Log4Qt::ConsoleAppender(layout, Log4Qt::ConsoleAppender::STDOUT_TARGET); appender->activateOptions(); logger->addAppender(appender); logger->setLevel(Log4Qt::Level::DEBUG_INT); logger->debug("Debug, Log4Qt!"); logger->info("Info, Log4Qt!"); // 关闭 logger logger->removeAllAppenders(); logger->loggerRepository()->shutdown(); return a.exec();}// output:// ----- start -----// 2018-10-11 21:25:30 [DEBUG] - Debug, Log4Qt!// 2018-10-11 21:25:30 [INFO] - Info, Log4Qt!// ----- end -----
5、配置PatternLayout
使用log4qt.properties配置文件配置PatternLayout:
# 定义 rootLoggerlog4j.rootLogger=DEBUG, console# 定义 ConsoleAppenderlog4j.appender.console=org.apache.log4j.ConsoleAppenderlog4j.appender.console.immediateFlush=truelog4j.appender.console.target=STDOUT_TARGET# 为 ConsoleAppender 定义 Layoutlog4j.appender.console.layout=org.apache.log4j.PatternLayoutlog4j.appender.console.layout.header=----- start -----log4j.appender.console.layout.footer=----- end -----log4j.appender.console.layout.conversionPattern=%d{yyyy-MM-dd hh:mm:ss} [%t] %p %c %x - %m%n
程序使用示例:
#include #include #include #include int main(int argc, char *argv[]){ QCoreApplication a(argc, argv); QThread::currentThread()->setObjectName("MainThread"); // 获取rootLogger Log4Qt::Logger* logger = Log4Qt::Logger::rootLogger(); // 打印消息 logger->debug("Debug, Log4Qt!"); logger->info("Info, Log4Qt!"); // 关闭rootLogger logger->removeAllAppenders(); logger->loggerRepository()->shutdown(); return a.exec();}// output:// ----- start -----// 2018-10-11 21:35:58 [MainThread] DEBUG root - Debug, Log4Qt!// 2018-10-11 21:35:58 [MainThread] INFO root - Info, Log4Qt!// ----- end -----
三、SimpleLayout
1、SimpleLayout简介
SimpleLayout 是Layout的一个派生类,对日志消息的格式化只包含日志的级别和消息内容。
2、format格式化实现
QString SimpleLayout::format(const LoggingEvent &rEvent) { return rEvent.level().toString() + QLatin1String(" - ") + rEvent.message() + Layout::endOfLine(); }
SimpleLayout对日志消息的格式化只包含日志的级别和消息内容。
3、SimpleLayout示例
#include #include #include #include #include int main(int argc, char *argv[]){ QCoreApplication a(argc, argv); // 创建SimpleLayout Log4Qt::Logger *logger = Log4Qt::Logger::rootLogger(); Log4Qt::SimpleLayout *layout = new Log4Qt::SimpleLayout(); layout->setFooter("end"); layout->setHeader("start"); layout->activateOptions(); // 创建ConsoleAppender Log4Qt::ConsoleAppender *appender = new Log4Qt::ConsoleAppender(layout, Log4Qt::ConsoleAppender::STDOUT_TARGET); appender->activateOptions(); logger->addAppender(appender); logger->setLevel(Log4Qt::Level::DEBUG_INT); logger->debug("Debug, Log4Qt!"); logger->info("Info, Log4Qt!"); // 关闭 logger logger->removeAllAppenders(); logger->loggerRepository()->shutdown(); return a.exec();}// output:// start// DEBUG - Debug, Log4Qt!// INFO - Info, Log4Qt!// end
4、配置SimpleLayout
使用log4qt.properties配置文件配置SimpleLayout:
# 定义 rootLoggerlog4j.rootLogger=DEBUG, console# 定义 ConsoleAppenderlog4j.appender.console=org.apache.log4j.ConsoleAppenderlog4j.appender.console.immediateFlush=truelog4j.appender.console.target=STDOUT_TARGET# 为 ConsoleAppender 定义 Layoutlog4j.appender.console.layout=org.apache.log4j.SimpleLayoutlog4j.appender.console.layout.header=----- start -----log4j.appender.console.layout.footer=----- end -----
程序使用示例:
#include #include #include int main(int argc, char *argv[]){ QCoreApplication a(argc, argv); // 获取 rootLogger Log4Qt::Logger* logger = Log4Qt::Logger::rootLogger(); // 打印消息 logger->debug("Hello, Log4Qt!"); logger->info("Hello, Log4Qt!"); // 关闭 rootLogger logger->removeAllAppenders(); logger->loggerRepository()->shutdown(); return a.exec();}// output:// ----- start -----// DEBUG - Hello, Log4Qt!// INFO - Hello, Log4Qt!// ----- end -----
四、TTCCLayout
1、TTCCLayout简介
TTCCLayout 是Layout的一个派生类,负责提供有关日志事件的详细信息,通常包含以下内容:
Time:从启动应用程序开始,以毫秒数计算的时间;
Thread:调用线程;
Category:用于创建日志事件的类别或Logger;
Context:NDC信息。NDC信息不会自动包含在LoggingEvent 对象中,必须专门包含NDC信息。因此,Context是TTCCLayout的一个可选输出,即使启用NDC设置,如果LoggingEvent 不包含任何NDC设置,TTCCLayout也可能不会显示任何NDC 数据。
TTCCLayout有多个可选参数,但即使不设置任何选项, TTCCLayout仍然会输出下列信息:
Level:日志消息的级别;
Message:日志消息本身。
TTCCLayout预定义了多种日期格式,定义如下:
enum DateFormat{ NONE,//没有日期格式 ISO8601,// yyyy-MM-dd hh:mm:ss.zzz ABSOLUTE,// HH:mm:ss.zzz DATE,//MMM YYYY HH:mm:ss.zzzz RELATIVE // 程序启动开始的毫秒数量};
2、TTCCLayout常用接口
bool categoryPrefixing() const;
获取是否格式化输出Logger名称bool contextPrinting() const;
获取是否格式化输出NDC信息QString dateFormat() const;
获取日期格式的字符串bool threadPrinting() const;
获取是否格式化输出线程名称void setCategoryPrefixing(bool categoryPrefixing);
设置指定Logger名称是否是格式化输出。void setContextPrinting(bool contextPrinting);
设置是否格式化输出NDC信息void setDateFormat(const QString &rDateFormat);
设置rDateFormat字符串表示的日期格式void setDateFormat(DateFormat dateFormat);
设置DateFormat枚举类型定义的日期格式类型void setThreadPrinting(bool threadPrinting);
设置是否格式化输出线程名称virtual QString format(const LoggingEvent &rEvent);
格式化日志信息接口static void Log4Qt::NDC::push(const QString &rMessage);
将rMessage信息压入NDC栈static QString Log4Qt::NDC::pop();
将NDC栈顶信息出栈
3、format格式化实现
QString TTCCLayout::format(const LoggingEvent &rEvent){ Q_ASSERT_X(mpPatternFormatter, "TTCCLayout::format()", "mpPatternConverter must not be null"); return mpPatternFormatter->format(rEvent);}
TTCCLayout的格式化与PatternLayout的格式化方法相同,都是根据模式匹配字符串创建的模式转换器链表的每个模式转换器格式化信息。
4、TTCCLayout示例
#include #include #include #include #include #include #include int main(int argc, char *argv[]){ QCoreApplication a(argc, argv); QThread::currentThread()->setObjectName("MainThread"); Log4Qt::Logger *logger = Log4Qt::Logger::rootLogger(); // 创建TTCCLayout Log4Qt::TTCCLayout *layout = new Log4Qt::TTCCLayout(); layout->setDateFormat("yyyy-mm-dd hh:mm:ss"); layout->activateOptions(); // 创建一ConsoleAppender Log4Qt::ConsoleAppender *appender = new Log4Qt::ConsoleAppender(layout, Log4Qt::ConsoleAppender::STDOUT_TARGET); appender->activateOptions(); logger->addAppender(appender); logger->setLevel(Log4Qt::Level::DEBUG_INT); // NDC信息 Log4Qt::NDC::push("Thread start"); logger->debug("Hello, Log4Qt!"); Log4Qt::NDC::pop(); logger->info("Hello, Log4Qt!"); // 关闭 logger logger->removeAllAppenders(); logger->loggerRepository()->shutdown(); return a.exec();}// output:// 2018-18-11 23:18:12 [MainThread] DEBUG root Thread start - Hello, Log4Qt!// 2018-18-11 23:18:12 [MainThread] INFO root - Hello, Log4Qt!
5、配置TTCCLayout
使用log4qt.properties配置文件配置TTCCLayout:
# 定义rootLoggerlog4j.rootLogger=DEBUG, console# 定义ConsoleAppenderlog4j.appender.console=org.apache.log4j.ConsoleAppenderlog4j.appender.console.immediateFlush=truelog4j.appender.console.target=STDOUT_TARGET# 为ConsoleAppender定义Layoutlog4j.appender.console.layout=org.apache.log4j.TTCCLayoutlog4j.appender.console.layout.categoryPrefixing=truelog4j.appender.console.layout.contextPrinting=truelog4j.appender.console.layout.threadPrinting=truelog4j.appender.console.layout.dateFormat=ISO8601
程序使用示例:
#include #include #include #include #include #include #include int main(int argc, char *argv[]){ QCoreApplication a(argc, argv); QThread::currentThread()->setObjectName("MainThread"); Log4Qt::Logger *logger = Log4Qt::Logger::rootLogger(); // NDC信息 Log4Qt::NDC::push("Thread start"); logger->debug("Hello, Log4Qt!"); Log4Qt::NDC::pop(); logger->info("Hello, Log4Qt!"); // 关闭 logger logger->removeAllAppenders(); logger->loggerRepository()->shutdown(); return a.exec();}// output:// 2018-10-11 23:22:09.936 [MainThread] DEBUG root Thread start - Hello, Log4Qt!// 2018-10-11 23:22:09.936 [MainThread] INFO root - Hello, Log4Qt!