Qt輕量級日誌庫QsLog的使用

C加加下的日誌庫有很多,如log4cpp、Easylogging++等,Qt下也有log4qt。

不過我們今天要說的是QsLog,它是一個基於Qt的輕量級開源日誌庫。

git地址:https://github.com/victronenergy/QsLog

輕到什麼程度,請看如下:

在這裏插入圖片描述

代碼文件就是這麼點。花點時間閱讀一下,秒改造成你自己的私有輪子。

雖然輕量級,但是對於基本的日誌功能需求還是夠用的。

特徵

  • 六個日誌級別(從跟蹤到致命)
  • 運行時可配置的日誌級別閾值。
  • 關閉日誌記錄時的最小開銷。
  • 支持多個目標,附帶文件和調試目標。
  • 線程安全
  • 支持現成的常見Qt類型的日誌記錄。
  • 小依賴:直接把它放到你的項目中。

接下來,我們研究下它都有哪些功能。

一、QsLog使用方式

1. 源碼集成

在你的工程中,直接包含QsLog.pri文件,進行源碼集成。

當然你也可以包含QsLog.pri後,編譯爲xx.dll,在應用工程中去調用xx.dll。

2. 動態庫集成

編譯QsLogSharedLibrary.pro,生成動態鏈接庫QsLog2.dll,在你的工程中進行調用。

二、日誌級別

支持六個日誌級別,優先級從低到高依次爲:Trace、Debug、Info、Warn、Error、Fatal、Off。如下:

enum Level
{
    TraceLevel = 0,
    DebugLevel,
    InfoLevel,
    WarnLevel,
    ErrorLevel,
    FatalLevel,
    OffLevel
};
  • Trace:跟蹤,最低等級的,用於打開所有日誌記錄。
  • Debug:調試,打印一些細粒度調試運行信息。
  • Info:信息,打印粗粒度信息,突出強調程序的運行過程。打印一些感興趣或者重要的信息,可用於環境中輸出程序運行的一些重要信息,但不能濫用,避免打印過多日誌。
  • Warn:警告,表明會出現潛在錯誤的情形,有些信息不是錯誤信息,但是也要給程序員的一些提示。
  • Error:錯誤,指出雖然發生錯誤,但仍然不影響系統的繼續運行。打印錯誤和異常信息。
  • Fatal:致命的,發生嚴重錯誤,將導致應用程序的退出。這個級別比較高,程序直接停止運行了。
  • Off:最高等級的,用於關閉所有日誌記錄。

可以通過setLoggingLevel()設置記錄日誌的級別。

void setLoggingLevel(Level newLevel)

注意:

一般我們可以將日誌級別保存到配置文件,以便程序發佈後,可通過修改配置來改變記錄日誌級別。

三、日誌輸出目的地

QsLog的使用很簡單,在我們自己的工程中直接include它的QsLog.pri文件,然後源文件中包含QsLog.h就可以使用了。

1. 輸出到文件(支持文件分割)

// 測試文件爲目的地
void test_output_file()
{
    // 初始化日誌機制
    Logger& logger = Logger::instance();
    logger.setLoggingLevel(QsLogging::TraceLevel);

    // 添加文件爲目的地
    const QString sLogPath(QDir(QApplication::applicationDirPath()).filePath("log.txt"));
    DestinationPtr fileDestination(DestinationFactory::MakeFileDestination(
      sLogPath, EnableLogRotation, MaxSizeBytes(512*1024), MaxOldLogCount(5)));
    logger.addDestination(fileDestination);

    // 打印日誌
    QLOG_TRACE() << "1-trace msg";
    QLOG_DEBUG() << "2-debug msg";
    QLOG_INFO() << "3-info msg";
    QLOG_WARN() << "4-warn msg";
    QLOG_ERROR() << "5-error msg";
    QLOG_FATAL()  << "6-fatal msg";

    QsLogging::Logger::destroyInstance();
}
  • 代碼很簡單,setLoggingLevel()設置記錄的日誌級別;
    目前爲TraceLevel,則日誌級別比TraceLevel高的都會輸出到文件;
    若爲ErrorLevel,則只有"5-error msg"和"6-fatal msg"這2條會輸出。

  • 然後設置文件名和輸出目的地。其中目的地是由DestinationFactory::MakeFileDestination()函數進行構造的,其原型爲:

static DestinationPtr MakeFileDestination(
    const QString& filePath,
    LogRotationOption rotation = DisableLogRotation,
    const MaxSizeBytes &sizeInBytesToRotateAfter = MaxSizeBytes(),
    const MaxOldLogCount &oldLogsToKeep = MaxOldLogCount());

函數參數含義:

filePath: 日誌文件名

rotation: 取值DisableLogRotation和EnableLogRotation,

前者表示禁止日誌文件分割,即日誌始終往一個文件中寫入。

後者表示啓用日誌文件分割,此時sizeInBytesToRotateAfter和oldLogsToKeep參數纔有意義。

sizeInBytesToRotateAfter: 每個日誌文件的字節數大小限制,即到達此大小後,自動新建文件,在新文件中進行寫入。

oldLogsToKeep: 舊日誌文件保留(備份)個數,超過此數量,自動刪除最久遠文件,備份文件最多支持10個。

若爲2,則如下三個文件中內容,按照時間先後順序排列爲:log.txt.2->log.txt.1->log.txt,在log.txt中爲最新日誌,log.txt.1爲次新,log.txt.2爲最久遠日誌。

若此時log.txt超過sizeInBytesToRotateAfter限制,則會發生log.txt.2被刪除,log.txt.1被改名log.txt.2,log.txt被改名log.txt.1,新建log.txt。

在這裏插入圖片描述

運行效果:

在這裏插入圖片描述

2. 輸出到控制檯stdout

// 測試stdout爲目的地
void test_output_stdout()
{
    // 初始化日誌機制
    Logger& logger = Logger::instance();
    logger.setLoggingLevel(QsLogging::TraceLevel);

    // 添加stdout爲目的地
    DestinationPtr debugDestination(DestinationFactory::MakeDebugOutputDestination());
    logger.addDestination(debugDestination);

    // 打印日誌
    QLOG_TRACE() << "1-trace msg";
    QLOG_DEBUG() << "2-debug msg";
    QLOG_INFO() << "3-info msg";
    QLOG_WARN() << "4-warn msg";
    QLOG_ERROR() << "5-error msg";
    QLOG_FATAL()  << "6-fatal msg";

    QsLogging::Logger::destroyInstance();
}

運行效果:

在這裏插入圖片描述

3. 輸出到處理函數

void logFunction(const QString &message, QsLogging::Level level)
{
    qDebug() << "From log function: " << qPrintable(message) << " " << static_cast<int>(level);
}

// 測試函數爲目的地
void test_output_function()
{
    // 初始化日誌機制
    Logger& logger = Logger::instance();
    logger.setLoggingLevel(QsLogging::TraceLevel);

    // 添加函數爲目的地
    DestinationPtr functorDestination(DestinationFactory::MakeFunctorDestination(&logFunction));
    logger.addDestination(functorDestination);

    // 打印日誌
    QLOG_TRACE() << "1-trace msg";
    QLOG_DEBUG() << "2-debug msg";
    QLOG_INFO() << "3-info msg";
    QLOG_WARN() << "4-warn msg";
    QLOG_ERROR() << "5-error msg";
    QLOG_FATAL()  << "6-fatal msg";

    QsLogging::Logger::destroyInstance();
}

輸出到函數,該函數需要定義爲如下類型:

typedef void (*LogFunction)(const QString &message, Level level);

運行效果:

在這裏插入圖片描述

4. 輸出到QTextEdit控件

除了上面的輸出方式,還可以輸出到一個QObject對象上,主要是通過信號槽機制,將打印日誌發送到QObject的槽函數進行處理。

void MainWindow::writeLog(const QString &message, int level)
{
    ui->textEdit->append(message + " " + QString::number(level));
}
// 測試QObject爲目的地
void test_output_qobject(MainWindow* window)
{
    // 初始化日誌機制
    Logger& logger = Logger::instance();
    logger.setLoggingLevel(QsLogging::TraceLevel);

    // 添加QObject爲目的地
    DestinationPtr objectDestination(DestinationFactory::MakeFunctorDestination(window, SLOT(writeLog(QString,int))));
    logger.addDestination(objectDestination);

    // 打印日誌
    QLOG_TRACE() << "1-trace msg";
    QLOG_DEBUG() << "2-debug msg";
    QLOG_INFO() << "3-info msg";
    QLOG_WARN() << "4-warn msg";
    QLOG_ERROR() << "5-error msg";
    QLOG_FATAL()  << "6-fatal msg";

    QsLogging::Logger::destroyInstance();
}

輸出到QObject時,需要定義其槽函數,爲如下類型:

void xxxx(const QString &message, int level)

寫本例子時,發現TRACE信息不能輸出到QObject,是因爲QsLogDestFunctor.cpp文件中,write函數有個bug,如下:

void QsLogging::FunctorDestination::write(const QString &message, QsLogging::Level level)
{
    if (mLogFunction)
        mLogFunction(message, level);

    if (level > QsLogging::TraceLevel)
        emit logMessageReady(message, static_cast<int>(level));
}

應將>改爲>=,修改後即可解決,如下:

void QsLogging::FunctorDestination::write(const QString &message, QsLogging::Level level)
{
    if (mLogFunction)
        mLogFunction(message, level);

    if (level >= QsLogging::TraceLevel)
        emit logMessageReady(message, static_cast<int>(level));
}

修改後的QsLog庫,下載鏈接:

https://download.csdn.net/download/u011832525/12520461

運行效果:

在這裏插入圖片描述

小結:

總結下,日誌打印輸出目的地可以有4種,分別是:

  • 輸出到文件;
  • 輸出到控制檯stdout;
  • 輸出到函數;
  • 輸出到QObject。

並且可以添加任意多個目的地址,比如輸出到文件的同時,還要輸出到控制檯進行顯示,以方便查看調試打印信息。

四、打印源文件名稱和行號

在QsLog.pri文件中

DEFINES += QS_LOG_LINE_NUMBERS

打開此宏定義,重新編譯,即可打印帶源文件名稱和行號的日誌。如下:

在這裏插入圖片描述

運行效果:

在這裏插入圖片描述

五、禁止日誌記錄

禁用日誌記錄,有時候關閉日誌記錄是有必要的。可以通過3種方式實現:

  • 全局地,在編譯時,通過在QsLog.pri文件,打開DEFINES += QS_LOG_DISABLE宏定義。
  • 全局地,在運行時,通過將日誌級別設置爲關閉,即setLoggingLevel(QsLogging::OffLevel)。
  • 在編譯時,通過在目標文件中包含QsLogDisableForThisFile.h,爲每個文件創建一個。

六、線程安全

使用日誌宏進行打印日誌是線程安全的,日誌宏如下:

QLOG_TRACE() << "1-trace msg";
QLOG_DEBUG() << "2-debug msg";
QLOG_INFO() << "3-info msg";
QLOG_WARN() << "4-warn msg";
QLOG_ERROR() << "5-error msg";
QLOG_FATAL()  << "6-fatal msg";

這在前面我們已經使用過了。

如setLoggingLevel()、addDestination()函數不是線程安全的。

七、日誌的異步打印

所謂的異步打印,其實就是單獨開一個線程來專門寫日誌。

在QsLog.pri文件中

DEFINES += QS_LOG_SEPARATE_THREAD

打開此宏定義,重新編譯,日誌內容就會在單獨的線程中排隊並寫入。

八、總結

以下內容來自QsLog的git倉庫介紹:

QsLog is an easy to use logger that is based on Qt’s QDebug class. QsLog is released as open source, under the MIT license.

###Contribution policy### Bug fixes are welcome, larger changes however are not encouraged at this point due to the lack of time on my side for reviewing and integrating them. Your best bet in this case would be to open a ticket for your change or forking the project and implementing your change there, with the possibility of having it integrated in the future. All contributions will be credited, license of the contributions should be MIT.

意思就是有問題,歡迎提bug,不過我沒啥時間,修不修復就看我的心情了。如果你想玩,最好自己fork倉庫,以後,萬一未來哪天心情好了,想繼續迭代版本,你敲的代碼還是有可能merge進來的,到時候給你頒朵小紅花,不過要遵守老麻的許可噢!

看完本文後,有需要的客官大老爺,請自行斟酌。

本文工程代碼地址:

https://gitee.com/bailiyang/cdemo/tree/master/Qt/47QsLog


===================================================

===================================================

業餘時間不定期更新一些想法、思考文章,歡迎關注,共同探討,沉澱技術!

            

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章