Appender是全部Appender的抽象類,是對記錄日誌形式的抽象。Log4Qt(Qt4版本)中Appender繼承體系以下:數據庫
virtual Filter *filter() const = 0; virtual QString name() const = 0; virtual Layout *layout() const = 0; virtual bool requiresLayout() const = 0; virtual void setLayout(Layout *pLayout) = 0; virtual void setName(const QString &rName) = 0; virtual void addFilter(Filter *pFilter) = 0; virtual void clearFilters() = 0; virtual void close() = 0; virtual void doAppend(const LoggingEvent &rEvent) = 0;
AppenderSkeleton繼承自Appender類,實現了Appender的通用功能,但沒有實現繼承自Appender的部分接口,因此仍然是一個抽象類,不能實例化。AppenderSkeleton的全部函數都是線程安全的。緩存
virtual Filter *filter() const; virtual Layout *layout() const; bool isActive() const; bool isClosed() const; virtual QString name() const; Level threshold() const; virtual void setLayout(Layout *pLayout); virtual void setName(const QString &rName); void setThreshold(Level level); virtual void activateOptions(); virtual void addFilter(Filter *pFilter); virtual void clearFilters(); virtual void close(); virtual void doAppend(const LoggingEvent &rEvent); Filter* firstFilter() const; bool isAsSevereAsThreshold(Level level) const;
自定義Appender能夠從AppenderSkeleton派生,需要實現如下三個接口:安全
virtual void append(const LoggingEvent &rEvent) = 0; virtual bool requiresLayout() const = 0; virtual QDebug debug(QDebug &rDebug) const = 0;
append接口負責處理LoggingEvent對象,將格式化的日誌信息輸出到不一樣的輸出地,如文本流、文件流、數據庫等,若是須要將日誌信息重定向到QWidget組件,須要在append函數發送信號,日誌信息做爲信號參數,在相應的QWidget組件的槽函數接收處理日誌信息。
也能夠根據須要從WriterAppender、ConsoleAppender、
FileAppender、RollingFileAppender、DailyRollingFileAppender派生類進行實現。app
WriterAppender類繼承自AppenderSkeleton類,在其實現的append函數中會將LoggingEvent對象的日誌信息輸出到QTextStream對象。WriterAppender的全部函數是線程安全的。ide
void WriterAppender::append(const LoggingEvent &rEvent) { QString message(layout()->format(rEvent)); //輸出格式化的日誌信息到QTextStream對象 *mpWriter << message; if (handleIoErrors()) return; // 是否刷新 if (immediateFlush()) { mpWriter->flush(); if (handleIoErrors()) return; } }
QTextCodec *encoding() const;
獲取輸出文本流的編碼器bool immediateFlush() const;
獲取是否當即刷新QTextStream *writer() const;
獲取輸出文本流對象void setEncoding(QTextCodec *pTextCodec);
設置文本流的編碼器void setImmediateFlush(bool immediateFlush);
設置是否當即刷新void setWriter(QTextStream *pTextStream);
設置輸出文本流virtual void close();
關閉文本流,若是有設置頁腳,會打印出頁腳信息函數
ConsoleAppender類繼承自WriterAppender類,ConsoleAppender定義了標準輸出、標準錯誤兩種控制檯的輸出目的地。ConsoleAppender的全部函數是線程安全的。ui
enum Target { STDOUT_TARGET,//標準輸出 STDERR_TARGET//標準錯誤 };
在ConsoleAppender配置完成後,須要對其配置選項進行激活,ConsoleAppender的activateOptions函數中會將文本流指向相應控制檯的文本流對象。this
void ConsoleAppender::activateOptions() { QMutexLocker locker(&mObjectGuard); closeStream(); if (mTarget == STDOUT_TARGET) mpTextStream = new QTextStream(stdout); else mpTextStream = new QTextStream(stderr); // 調用WriterAppender的setWriter函數, // 將日誌信息重定向到控制檯對應的文本流 setWriter(mpTextStream); WriterAppender::activateOptions(); }
當Logger進行日誌輸出時,會在WriterAppender的append函數將格式化的日誌信息輸出到相應控制檯對應的文本流,完成輸出目的地的重定向。編碼
QString target() const;
獲取輸出目的地void setTarget(const QString &rTarget);
設置rTarget字符串爲輸出目的地void setTarget(Target target);
設置target爲輸出目的地virtual void activateOptions();
激活ConsoleAppender設置的選項virtual void close();
關閉ConsoleAppenderspa
#include <QCoreApplication> #include <QTextCodec> #include <log4qt/logger.h> #include <log4qt/ttcclayout.h> #include <log4qt/consoleappender.h> #include <log4qt/loggerrepository.h> #include <QThread> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QThread::currentThread()->setObjectName("MainThread"); // 建立TTCCLayout Log4Qt::Logger *logger = Log4Qt::Logger::rootLogger(); Log4Qt::TTCCLayout *layout = new Log4Qt::TTCCLayout(); layout->setDateFormat("yyyy-mm-dd hh:mm:ss"); // 激活選項 layout->activateOptions(); // 建立ConsoleAppender Log4Qt::ConsoleAppender *appender = new Log4Qt::ConsoleAppender(); appender->setLayout(layout); // 設置編碼 appender->setEncoding(QTextCodec::codecForName("UTF-8")); // 設置輸出目的地爲stdout appender->setTarget(Log4Qt::ConsoleAppender::STDOUT_TARGET); appender->setImmediateFlush(true); // 設置閾值級別爲INFO appender->setThreshold(Log4Qt::Level::INFO_INT); // 激活選項 appender->activateOptions(); logger->addAppender(appender); // 設置級別爲 DEBUG logger->setLevel(Log4Qt::Level::DEBUG_INT); // 輸出信息 logger->debug("你好, Log4Qt!"); logger->info("你好, Qt!"); // 關閉 logger logger->removeAllAppenders(); logger->loggerRepository()->shutdown(); return a.exec(); } // output: // 2018-03-12 18:03:48 [MainThread] INFO root - 你好, Qt!
FileAppender類繼承自WriterAppender類,用於將日誌輸出到文件。
FileAppender的全部函數是線程安全的。
在FileAppender配置完成後,須要對其配置選項進行激活,FileAppender的activateOptions函數中會打開指定的輸出文件,並將WriterAppender的文本流綁定到輸出文件的文件流,實現將WriterAppender的文本流重定向到輸出文件中。
void FileAppender::activateOptions() { QMutexLocker locker(&mObjectGuard); if (mFileName.isEmpty()) { LogError e = LOG4QT_QCLASS_ERROR(QT_TR_NOOP("Activation of Appender '%1' that requires file and has no file set"), APPENDER_ACTIVATE_MISSING_FILE_ERROR); e << name(); logger()->error(e); return; } closeFile(); // 打開文件 openFile(); WriterAppender::activateOptions(); } void FileAppender::openFile() { Q_ASSERT_X(mpFile == 0 && mpTextStream == 0, "FileAppender::openFile()", "Opening file without closing previous file"); QFileInfo file_info(mFileName); QDir parent_dir = file_info.dir(); if (!parent_dir.exists()) { logger()->trace("Creating missing parent directory for file %1", mFileName); QString name = parent_dir.dirName(); parent_dir.cdUp(); parent_dir.mkdir(name); } mpFile = new QFile(mFileName); QFile::OpenMode mode = QIODevice::WriteOnly | QIODevice::Text; //配置文件流的寫入模式 if (mAppendFile) mode |= QIODevice::Append; else mode |= QIODevice::Truncate; if (!mBufferedIo) mode |= QIODevice::Unbuffered; if (!mpFile->open(mode)) { LogError e = LOG4QT_QCLASS_ERROR(QT_TR_NOOP("Unable to open file '%1' for appender '%2'"), APPENDER_OPENING_FILE_ERROR); e << mFileName << name(); e.addCausingError(LogError(mpFile->errorString(), mpFile->error())); logger()->error(e); return; } // 將文件流綁定文本流 mpTextStream = new QTextStream(mpFile); // 將WriterAppender的文本流重定向到文本文件的文件流對應的文本流 // 完成輸出目的地的重定向 setWriter(mpTextStream); logger()->debug("Opened file '%1' for appender '%2'", mpFile->fileName(), name()); }
當Logger進行日誌輸出時,會在WriterAppender的append函數將格式化的日誌信息輸出到文本流綁定的輸出文件中,完成輸出目的地的重定向。
bool appendFile() const;
獲取是否追加文件QString file() const;
獲取輸出目的地的文件名bool bufferedIo() const;
獲取是否爲緩存IOvoid setAppendFile(bool append);
設置是否爲追加文件void setBufferedIo(bool buffered);
設置是否爲緩存IOvoid setFile(const QString &rFileName);
設置輸出目的地的文件名virtual void close();
關閉文件
四、FileAppender示例
#include <QCoreApplication> #include <QTextCodec> #include <log4qt/logger.h> #include <log4qt/ttcclayout.h> #include <log4qt/fileappender.h>.h> #include <log4qt/loggerrepository.h> #include <QThread> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QThread::currentThread()->setObjectName("MainThread"); // 建立TTCCLayout Log4Qt::Logger *logger = Log4Qt::Logger::rootLogger(); Log4Qt::TTCCLayout *layout = new Log4Qt::TTCCLayout(); layout->setDateFormat("yyyy-mm-dd hh:mm:ss"); // 激活選項 layout->activateOptions(); // 建立ConsoleAppender Log4Qt::FileAppender *appender = new Log4Qt::FileAppender; // 設置輸出目的地爲應用程序所在目錄下的logFile.log appender->setFile("logFile.log"); appender->setLayout(layout); // 設置編碼 appender->setEncoding(QTextCodec::codecForName("UTF-8")); appender->setImmediateFlush(true); // 設置閾值級別爲INFO appender->setThreshold(Log4Qt::Level::INFO_INT); // 激活選項 appender->activateOptions(); logger->addAppender(appender); // 設置級別爲 DEBUG logger->setLevel(Log4Qt::Level::DEBUG_INT); // 輸出信息 logger->debug("你好, Log4Qt!"); logger->info("你好, Qt!"); // 關閉 logger logger->removeAllAppenders(); logger->loggerRepository()->shutdown(); return a.exec(); } // logFile: // 2018-06-12 18:06:45 [MainThread] INFO root - 你好, Qt!
RollingFileAppender類繼承自FileAppender類,是對FileAppender功能的擴展。RollingFileAppender容許輸出的日誌文件達到指定大小時進行日誌文件的滾動備份。
RollingFileAppender的全部函數都是線程安全的。
在RollingFileAppender配置完成後,須要對其配置選項進行激活,RollingFileAppender調用FileAppender::activateOptions函數中會中會打開指定的輸出文件,並將WriterAppender的文本流綁定到輸出文件的文件流,實現將WriterAppender的文本流重定向到輸出文件中。
RollingFileAppender實現了append函數。
void RollingFileAppender::append(const LoggingEvent &rEvent) { // Q_ASSERT_X(, "RollingFileAppender::append()", "Lock must be held by caller") // 使用FileAppender將輸出文件綁定到WidgetAppender的輸出文本流 FileAppender::append(rEvent); // 若是日誌文件大小已經大於日誌文件指定的最大值,進行會滾備份操做 if (writer()->device()->size() > this->mMaximumFileSize) rollOver(); }
當Logger進行日誌輸出時,RollingFileAppender使用FileAppender::append(實際爲WriterAppender::append)函數將將格式化的日誌信息輸出到文本流綁定的輸出文件中,完成輸出目的地的重定向。若是輸出文件的大小大於指定的最大值時,進行會滾備份操做。
int maxBackupIndex() const;
獲取最大備份索引qint64 maximumFileSize() const;
獲取輸出日誌文件大小的最大值void setMaxBackupIndex(int maxBackupIndex);
設置備份文件索引的最大值void setMaximumFileSize(qint64 maximumFileSize);
設置單個輸出日誌文件的最大值爲maximumFileSize字節void setMaxFileSize(const QString &rMaxFileSize);
設置單個輸出日誌文件的最大值爲rMaxFileSize的值,可使用KB,MB,GB等單位
#include <QCoreApplication> #include <QTextCodec> #include <log4qt/logger.h> #include <log4qt/ttcclayout.h> #include <log4qt/rollingfileappender.h>.h>.h> #include <log4qt/loggerrepository.h> #include <QThread> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QThread::currentThread()->setObjectName("MainThread"); // 建立TTCCLayout Log4Qt::Logger *logger = Log4Qt::Logger::rootLogger(); Log4Qt::TTCCLayout *layout = new Log4Qt::TTCCLayout(); layout->setDateFormat("yyyy-mm-dd hh:mm:ss"); // 激活選項 layout->activateOptions(); // 建立ConsoleAppender Log4Qt::RollingFileAppender *appender = new Log4Qt::RollingFileAppender; // 設置輸出目的地爲應用程序所在目錄下的logFile.log appender->setFile("logFile.log"); // 設置日誌爲追加方式寫入輸出文件 appender->setAppendFile(true); // 設置備份文件的最大數量爲10個 appender->setMaxBackupIndex(10); // 設置輸出文件的最大值爲1KB appender->setMaxFileSize("1KB"); appender->setLayout(layout); // 設置編碼 appender->setEncoding(QTextCodec::codecForName("UTF-8")); appender->setImmediateFlush(true); // 設置閾值級別爲INFO appender->setThreshold(Log4Qt::Level::INFO_INT); // 激活選項 appender->activateOptions(); logger->addAppender(appender); // 設置級別爲 DEBUG logger->setLevel(Log4Qt::Level::DEBUG_INT); // 輸出信息 for(int i = 0 ; i < 100; i++) { logger->debug("你好, Log4Qt!"); logger->info("你好, Qt!"); } // 關閉 logger logger->removeAllAppenders(); logger->loggerRepository()->shutdown(); return a.exec(); }
程序最近的日誌輸出到logFile.log,並備份有5個文件,分別爲logFile.log.一、logFile.log.二、logFile.log.三、logFile.log.四、logFile.log.5
DailyRollingFileAppender類繼承自FileAppender類,是對FileAppender功能的擴展。DailyRollingFileAppender容許輸出的日誌文件按照指定的頻率進行會滾備份。
DailyRollingFileAppender的全部函數都是線程安全的。
DailyRollingFileAppender定義了六種日期模式。
enum DatePattern { MINUTELY_ROLLOVER = 0,// 每分鐘,'yyyy-MM-dd-hh-mm" HOURLY_ROLLOVER,// 每小時,yyyy-MM-dd-hh HALFDAILY_ROLLOVER,// 每半天,yyyy-MM-dd-a DAILY_ROLLOVER,// 天天,yyyy-MM-dd WEEKLY_ROLLOVER,// 每週,yyyy-ww MONTHLY_ROLLOVER// 每個月,yyyy-MM };
在DailyRollingFileAppender配置完成後,須要對其配置選項進行激活,DailyRollingFileAppender的activateOptions函數中會計算輸出文件回滾的頻率,並調用FileAppender::activateOptions()函數將WriterAppender的文本流綁定到輸出文件的文件流,實現將WriterAppender的文本流重定向到輸出文件中。
void DailyRollingFileAppender::activateOptions() { QMutexLocker locker(&mObjectGuard); // 計算輸出文件回滾的頻率, computeFrequency(); if (!mActiveDatePattern.isEmpty()) { //計算輸出文件回滾的時間 computeRollOverTime(); // 調用FileAppender::activateOptions重定向輸出文本流到輸出文件 FileAppender::activateOptions(); } }
DailyRollingFileAppender實現了append函數。
void DailyRollingFileAppender::append(const LoggingEvent &rEvent) { // 若是當前時間大於輸出文件回滾時間,進行輸出文件回滾 if (QDateTime::currentDateTime() > mRollOverTime) rollOver(); // 調用FileAppender::append將格式化的日誌輸出到輸文件 FileAppender::append(rEvent); }
當Logger進行日誌輸出時,DailyRollingFileAppender會在append函數內處理LoggingEvent對象。若是當前時間大於輸出文件須要進行回滾的時間,DailyRollingFileAppender會進行輸出文件的回滾備份操做,建立新的日誌輸出文件。而後調用FileAppender::append函數將格式化的日誌輸出到指定的輸出文件中。
QString datePattern() const;
獲取日期匹配模式字符串void setDatePattern(DatePattern datePattern);
設置日期匹配模式void setDatePattern(const QString &rDatePattern);
設置rDatePattern爲日期匹配模式
#include <QCoreApplication> #include <QTextCodec> #include <log4qt/logger.h> #include <log4qt/ttcclayout.h> #include <log4qt/dailyrollingfileappender.h>> #include <log4qt/loggerrepository.h> #include <QThread> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QThread::currentThread()->setObjectName("MainThread"); // 建立TTCCLayout Log4Qt::Logger *logger = Log4Qt::Logger::rootLogger(); Log4Qt::TTCCLayout *layout = new Log4Qt::TTCCLayout(); layout->setDateFormat("yyyy-mm-dd hh:mm:ss"); // 激活選項 layout->activateOptions(); // 建立ConsoleAppender Log4Qt::DailyRollingFileAppender *appender = new Log4Qt::DailyRollingFileAppender; // 設置輸出目的地爲應用程序所在目錄下的logFile.log appender->setFile("logFile.log"); // 設置日誌文件天天回滾 //appender->setDatePattern(Log4Qt::DailyRollingFileAppender::MINUTELY_ROLLOVER); appender->setDatePattern("'.'yyyy-MM-dd-hh-mm"); // 設置日誌爲追加方式寫入輸出文件 appender->setAppendFile(true); appender->setLayout(layout); // 設置編碼 appender->setEncoding(QTextCodec::codecForName("UTF-8")); appender->setImmediateFlush(true); // 設置閾值級別爲INFO appender->setThreshold(Log4Qt::Level::INFO_INT); // 激活選項 appender->activateOptions(); logger->addAppender(appender); // 設置級別爲 DEBUG logger->setLevel(Log4Qt::Level::DEBUG_INT); // 輸出信息 for(int i = 0; i < 10; i++) { logger->debug("你好, Log4Qt!"); logger->info("你好, Qt!"); for(int i = 0; i < 1000000000; i++) ; logger->debug("你好, Log4Qt2!"); logger->info("你好, Qt2!"); } // 關閉 logger logger->removeAllAppenders(); logger->loggerRepository()->shutdown(); return a.exec(); }
程序執行時,日誌輸出到logFile.log,輸出日誌每分鐘會回滾備份一次,備份文件名稱以下:logFile.log.2018-10-12-18-30。
將Log4Qt源碼工程下的src/log4qt目錄拷貝到本身的工程中,並在本身的工程文件中添加以下配置:
# 定義所需的宏 DEFINES += LOG4QT_LIBRARY # 將Log4Qt源碼工程下的src/log4qt目錄拷貝到本身的工程src目錄中 # 定義Log4Qt源碼根目錄 LOG4QT_ROOT_PATH = $$PWD/log4qt # 指定編譯項目時應該被搜索的#include目錄 INCLUDEPATH += $$LOG4QT_ROOT_PATH # 將Log4Qt源代碼添加至項目中 include($$LOG4QT_ROOT_PATH/log4qt.pri)
WidgetAppender從AppenderSkeleton進行實現。
WidgetAppender.h文件:
#ifndef WIDGETAPPENDER_H #define WIDGETAPPENDER_H #include <QObject> #include <QWidget> #include "appenderskeleton.h" #include <QDebug> namespace Log4Qt { /** * @brief WidgetAppender繼承自AppenderSkeleton類,用於將日誌信息重定向到QWidget組件 * @author scorpio * @note WidgetAppender的使用注意事項: * 一、必須使用setLogWidget接口設置日誌信息的重定向位置,即輸出窗口組件 * 二、必須實現一個槽函數onAppendLog(const QString& msg),用於接收WidgetAppender * 發送的logAppend(const QString& msg)信號,參數msg即接收的日誌信息,輸出窗口組件 * 須要將msg輸出到相應窗體。 */ class WidgetAppender : public AppenderSkeleton { Q_OBJECT public: WidgetAppender(QObject *parent = NULL); ~WidgetAppender(); /** * @brief 設置日誌信息輸出的QWidget組件 * @param widget,輸入參數,日誌信息輸出窗口,須要開發者本身實現 */ void setLogWidget(const QWidget& widget); signals: /** * @brief 新增長一條日誌信息的信號 * @param msg,輸入參數,格式化後的日誌信息 * @note logAppend信號由QWidget輸出窗口組件接收,開發者須要在輸出窗口組件實現 * 槽函數onAppendLog(const QString& msg)。 */ void logAppend(const QString& msg); protected: virtual bool requiresLayout() const; virtual void append(const Log4Qt::LoggingEvent &rEvent); #ifndef QT_NO_DEBUG_STREAM virtual QDebug debug(QDebug &rDebug) const; #endif //QT_NO_DEBUG_STREAM private: QWidget *m_logWidget; }; } #endif // WIDGETAPPENDER_H
WidgetAppender.cpp文件:
#include "WidgetAppender.h" #include "loggingevent.h" #include <log4qt/ttcclayout.h> namespace Log4Qt { WidgetAppender::WidgetAppender(QObject *parent): AppenderSkeleton(parent) { m_logWidget = NULL; } WidgetAppender::~WidgetAppender() { } void WidgetAppender::setLogWidget(const QWidget &widget) { m_logWidget = const_cast<QWidget*>(&widget); //鏈接槽函數到輸出窗口的onAppendLog(const QString&)槽函數 connect(this, SIGNAL(logAppend(const QString&)), m_logWidget, SLOT(onAppendLog(const QString&))); } bool WidgetAppender::requiresLayout() const { return true; } void WidgetAppender::append(const LoggingEvent &rEvent) { // 格式化日誌信息 QString message = dynamic_cast<TTCCLayout*>(layout())->format(rEvent); emit logAppend(message); } #ifndef QT_NO_DEBUG_STREAM QDebug WidgetAppender::debug(QDebug &rDebug) const { return rDebug.space(); } #endif //QT_NO_DEBUG_STREAM }
Widget.h文件:
#ifndef WIDGET_H #define WIDGET_H #include <QWidget> #include <QTextEdit> #include <QMutex> #include <QVBoxLayout> class Widget : public QWidget { Q_OBJECT public: Widget(QWidget *parent = 0); ~Widget(); private slots: /** * @brief 接收日誌信息的槽函數 * @param log,輸入參數,格式化後的日誌信息 */ void onAppendLog(const QString& log); private: QTextEdit* m_logEdit; QMutex m_mutex; }; #endif // WIDGET_H
Widget.cpp文件:
#include "Widget.h" Widget::Widget(QWidget *parent) : QWidget(parent) { m_logEdit = new QTextEdit(); QVBoxLayout* layout = new QVBoxLayout; layout->addWidget(m_logEdit); setLayout(layout); resize(600, 400); } Widget::~Widget() { } void Widget::onAppendLog(const QString &log) { QMutexLocker lock(&m_mutex); //將日誌信息輸出到窗口組件 m_logEdit->insertPlainText(log); }
#include "Widget.h" #include <QApplication> #include <log4qt/basicconfigurator.h> #include <log4qt/logger.h> #include <log4qt/WidgetAppender.h> #include <log4qt/ttcclayout.h> #include <log4qt/logmanager.h> int main(int argc, char *argv[]) { QApplication a(argc, argv); Widget w; w.show(); Log4Qt::BasicConfigurator::configure(); Log4Qt::LogManager::setHandleQtMessages(true); Log4Qt::Logger *logger = Log4Qt::Logger::rootLogger(); logger->removeAllAppenders(); Log4Qt::WidgetAppender *appender = new Log4Qt::WidgetAppender(); appender->setName("WidgetAppender"); Log4Qt::TTCCLayout *layout = new Log4Qt::TTCCLayout(Log4Qt::TTCCLayout::ISO8601); layout->setThreadPrinting(true); appender->setLayout(layout); appender->activateOptions(); //設置日誌信息輸出的窗口組件 appender->setLogWidget(w); logger->addAppender(appender); logger->warn("hello Log4Qt"); logger->info("hello Log4Qt"); logger->debug("hello Log4Qt"); logger->info("你好 Log4Qt"); logger->removeAllAppenders(); return a.exec(); } // output: // 2018-10-09 10:27:18.542 [] WARN root - hello Log4Qt // 2018-10-09 10:27:18.542 [] INFO root - hello Log4Qt // 2018-10-09 10:27:18.542 [] DEBUG root - hello Log4Qt // 2018-10-09 10:27:18.542 [] INFO root - 你好 Log4Qt