Log4Qt快速入門——Log4Qt日誌格式化源碼解析
一、Layout
1、Layout簡介
Log4Qt提供了多種Layout對象,用於格式化日誌輸出,指定日誌級別、線程名稱、Logger名稱、日期時間等信息。
Layout類是Log4Qt API中的抽象類。
PatternLayout:根據一個模式字符串輸出日誌事件;
SimpleLayout:輸出日誌事件的級別和消息;
TTCCLayout:輸出日誌事件的時間、線程名稱、Logger名稱和嵌套的診斷上下文信息。
PatternLayout和TTCCLayout通過PatternFormatter來實現格式化。當PatternFormatter解析模式字符串時,會根據發現的信息創建一個PatternConverter鏈,每個PatternConverter會處理LoggingEvent的某個成員。
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。
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 <QCoreApplication>
#include <log4qt/logger.h>
#include <log4qt/patternlayout.h>
#include <log4qt/consoleappender.h>
#include <log4qt/loggerrepository.h>
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:
# 定義 rootLogger
log4j.rootLogger=DEBUG, console
# 定義 ConsoleAppender
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.immediateFlush=true
log4j.appender.console.target=STDOUT_TARGET
# 為 ConsoleAppender 定義 Layout
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.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 <QCoreApplication>
#include <log4qt/logger.h>
#include <log4qt/loggerrepository.h>
#include <QThread>
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 <QCoreApplication>
#include <log4qt/logger.h>
#include <log4qt/simplelayout.h>
#include <log4qt/consoleappender.h>
#include <log4qt/loggerrepository.h>
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:
# 定義 rootLogger
log4j.rootLogger=DEBUG, console
# 定義 ConsoleAppender
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.immediateFlush=true
log4j.appender.console.target=STDOUT_TARGET
# 為 ConsoleAppender 定義 Layout
log4j.appender.console.layout=org.apache.log4j.SimpleLayout
log4j.appender.console.layout.header=----- start -----
log4j.appender.console.layout.footer=----- end -----
程序使用示例:
#include <QCoreApplication>
#include <log4qt/logger.h>
#include <log4qt/loggerrepository.h>
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 <QCoreApplication>
#include <log4qt/logger.h>
#include <log4qt/ttcclayout.h>
#include <log4qt/consoleappender.h>
#include <log4qt/loggerrepository.h>
#include <log4qt/ndc.h>
#include <QThread>
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:
# 定義rootLogger
log4j.rootLogger=DEBUG, console
# 定義ConsoleAppender
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.immediateFlush=true
log4j.appender.console.target=STDOUT_TARGET
# 為ConsoleAppender定義Layout
log4j.appender.console.layout=org.apache.log4j.TTCCLayout
log4j.appender.console.layout.categoryPrefixing=true
log4j.appender.console.layout.contextPrinting=true
log4j.appender.console.layout.threadPrinting=true
log4j.appender.console.layout.dateFormat=ISO8601
程序使用示例:
#include <QCoreApplication>
#include <log4qt/logger.h>
#include <log4qt/ttcclayout.h>
#include <log4qt/consoleappender.h>
#include <log4qt/loggerrepository.h>
#include <log4qt/ndc.h>
#include <QThread>
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!
Log4Qt快速入門——Log4Qt日誌格式化源碼解析