日誌庫Log

日誌庫Log

[email protected]

2014年7月1日

2014年9月12日添加QT原生日誌函數

1  概述

在系統集成中,大多數以庫或可執行文件的形式集成(在windows中,dll形式的庫在集成時有很多問題,最著名的就是dll地獄,還有C++導出類等其它問題),集成中的調試信息很難通過程序的中調試程序輸出(如TRACE(),OutputDebugString()等。爲了能夠在運行中獲取調試信息,一般將程序的信息輸出到文件,以日誌的形式進行保存。

常用的日誌庫有log4c(可移植性好,文檔少),log4cpp(使用方便,文檔資源全,最接近log4j,推薦),log4cplus(功能全,複雜),log4qt(可以直接操作QT類)。Qt中可以使用通過的庫,原生的日誌使用qDebug()等的處理函數,將其記錄到文件中作爲日誌。

2 log4c

2.1 庫

官網:http://log4c.sourceforge.net/

下載源碼編譯,或直接使用預編譯的版本。

2.2 操作

log4c移植於log4j(java),用於日誌輸出的純C庫,操作方法與log4j相同。

log4c通過log4crc文件指定相關的參數。

在程序中使用log4c_init():初始化。log4c_finit():去初始化。來初始化和釋放資源。

log4c_categary_get(name):從rc文件中獲取指定的category。

log4c_categary_log(pCat,type,str):依據rc文件的配置輸出log信息。

2.3 配置

categary:類別,不同的log對象的種類。

在RC文件中指定名稱、優先級和appender。

appender:輸出位置。

在RC文件中指定名稱、類別和layout、rollingpolicy。

layout:輸出格式。

在RC文件中指定名稱和類型。、

rollingpolicy:輸出文件策略。

在RC文件中指定文件的名稱、類型、最大、最小。

2.4 示例

 

參考:

入門:http://www.cnblogs.com/jyli/archive/2010/02/11/1660606.html

詳細:http://xueqi.iteye.com/blog/1570013

 

3 log4cpp

3.1 庫

官網:http://log4cpp.sourceforge.net/

下載源碼編譯,無預編譯的版本。

3.2 編譯

下載後有各種庫的(如:msvc10等)工程文件,使用相關的程序(如:vs2012)打開(debug版對應不同的VC版本需要編譯成不同的文件,但是release版本可以通用),編譯,編譯lig4cpp(動態庫)和libcpplib(靜態庫)。

編譯完成後在工程文件目錄下有debug和release兩個目錄,將相關的lib、dll拷貝出來,源文件下有include目錄,就可以用了。

注意:在使用時需要與客戶程序相同的vc版本。

編譯完成後可以使用doxygen生成幫助文件。

參考:http://blog.csdn.net/kingskyleader/article/details/7320826

http://sogo6.iteye.com/blog/1154315

http://www.ibm.com/developerworks/cn/linux/l-log4cpp/

3.3 原理

log4cpp將所不同的日誌類型視爲不同的類別(Category)。每個日誌可以指定不同的輸出(Appender),輸出的格式使用佈局(Layout)設定。

類別是進行日誌操作的實體類,可以使用派生的子類直接繼承父類。所有類別的父類是rootCategory。類別需要指定輸出的優先級別(Priority),小於此級別的日誌將不輸出。

日誌具有不同的優先級,可以設置日誌輸出的優先級範圍。

在使用配置文件時,將所有的類屬性以鍵值對的方式進行配置。

3.4 方法

3.4.1類別Category:用於標識不同的日誌對象。

1)   Category需要指定日誌優先級(Priority,只有大於指定Priority的日誌纔會記錄)和輸出目標(Appender,可以有多個輸出Appender)。

2)   Category組織爲樹,子Category繼承父Category的Appender,使用additivity=false,可以重新生成Appender列表。

3)   由於在源程序的不同文件中可能需要使用同一個日誌輸出,所以使用靜態單例的方法,根據不同的名稱,生成不同的類別。

3.4.2輸出Appender:輸出日誌到指定的目標。

1)   Appender也是樹形組織的類別,可以繼承。預定義有多種類型(參見源碼),常用的有:consoleAppender(輸出到std::cout),Win32DebugAppender(VC IDE中的輸出窗口==OutputDebugString),FileAppender(文件輸出),RollingFileAppender(循環文件輸出)。

2)   Appender需要指定類型及其參數,並設置輸出佈局(Layout)。

3.4.3佈局Layout:設置日誌格式。

Layout需要指定類型及其參數。

3.4.4優先級Priority:設置需要輸出的級別範圍。

NOTSET < DEBUG < INFO < NOTICE < WARN< ERROR < CRIT < ALERT < FATAL = EMER

示例:

    usingnamespacelog4cpp;

    Appender*myAppender=newFileAppender("myapp","myapp.log");

    myAppender->setLayout(newBasicLayout);

    Category&myCat=Category::getInstance("mycat");

    myCat.setPriority(Priority::DEBUG);

    myCat.addAppender(myAppender)

myCat.debug("mycat.myapp.basiclayout.");

3.4.5配置文件

可以直接讀取配置文件,使用配置文件中的參數,生成指定的類,使用時直接獲取類進行輸出,操作方便,配置靈活。

配置參數參見:配置

3.5 操作

3.5.1程序生成

可以利用程序生成各種參數完成日誌記錄。

修改時不方便,僅用於原理演示。

示例:

#include<log4cpp/Category.hh>

#include<log4cpp/FileAppender.hh>

#include<log4cpp/BasicLayout.hh>

#pragmacomment(lib,"log4cpp.lib")

intmain(intargc,char*argv[])

{

    usingnamespacelog4cpp;

    log4cpp::Layout*pLayout=newlog4cpp::BasicLayout;

    Appender*pAppender=newFileAppender("FileAppender","../test.log");

    pAppender->setLayout(pLayout);

    Category&warn_log=  Category::getInstance("mywarn");

    warn_log.setPriority(Priority::DEBUG);

    warn_log.addAppender(pAppender);

    warn_log.debug("%s,%d","hello,log4cpp!",12);

    warn_log.shutdown();

    return0;

}

3.5.2配置文件生成(推薦)

這是最常用,最方便的方式。

首先在配置文件中生成需要配置,然後加載配置文件,自動生成各種對象,程序只需要記錄日誌。

示例

//log4cpp.property

#註釋

#category

log4cpp.rootCategory=DEBUG,rootAppender

log4cpp.category.mywarn=WARN,simpAppender

log4cpp.category.cfglog=ERROR,debugAppender

 

#Appender

log4cpp.appender.rootAppender=ConsoleAppender

log4cpp.appender.rootAppender.layout=BasicLayout

 

log4cpp.appender.simpAppender=FileAppender

log4cpp.appender.simpAppender.layout=BasicLayout

log4cpp.appender.simpAppender.fileName=filelog.log

 

log4cpp.appender.debugAppender=FileAppender

log4cpp.appender.debugAppender.layout=BasicLayout

log4cpp.appender.debugAppender.fileName=debuglog.log

 

#Layout

//main.cpp

#include<log4cpp/Category.hh>

#include<log4cpp/PropertyConfigurator.hh>

#pragmacomment(lib,"log4cpp.lib")

#include<log4cpp/FileAppender.hh>

#include<log4cpp/BasicLayout.hh>

 

intmain(intargc,char*argv[])

{

    log4cpp::PropertyConfigurator::configure("../log/log4cpp.property");

    log4cpp::Category&cfglog=log4cpp::Category::getInstance("cfglog");

    cfglog.debug("cfglog");

    cfglog.warn("warnlog");

    cfglog.emerg("emerglog");

    return0;

}

 

3.6 配置

注意配置文件中大小寫敏感,尤其是一些屬性,必須按照規定的大小寫,遵守camel命名法。

3.6.1預定義信息

   const char* constTimeStampComponent::FORMAT_ISO8601 = "%Y-%m-%d %H:%M:%S,%l";

    constchar* const TimeStampComponent::FORMAT_ABSOLUTE = "%H:%M:%S,%l";

    constchar* const TimeStampComponent::FORMAT_DATE = "%d %b %Y %H:%M:%S,%l";

 

    constchar* PatternLayout::DEFAULT_CONVERSION_PATTERN = "%m%n";//default

    constchar* PatternLayout::SIMPLE_CONVERSION_PATTERN = "%p - %m%n";//simple

    constchar* PatternLayout::BASIC_CONVERSION_PATTERN = "%R %p %c %x: %m%n";//basic

    constchar* PatternLayout::TTCC_CONVERSION_PATTERN = "%r [%t] %p %c %x -%m%n";//ttcc

3.6.2PatternLayout的ConversionPattern轉義字符表

詳見src/patternlayout.cpp。

1)   "%",轉義字符,相當於printf中的\。

2)   "%m",輸出信息message。

3)   "%n",換行。

4)   "%c",Category的名稱。

5)   "%d",時間戳。

6)   "%p",當前記錄行的Priority(僅指本條日誌)。

7)   "%r",程序啓動以來的毫秒數(從0起算)。

8)   "%R",程序啓動以來的秒數(從1970年起算)。

9)   "%t",線程ID。

10)  "%u",CPU時間。

11)  "%x",nested diagnostic context。NDC爲區分不同線程設計。一個線程一個NDC,目標是獲取不同線程的嵌套關係,相同輸出的情況。

3.6.3RollingFileAppender

回捲文件。指定每個文件大小和文件數目。循環使用。

fileName:文件名。

maxFileSize:單文件最大字節數。

maxBackupIndex:最大文件後綴號(起始文件不存在後綴(後綴爲0,省略),其它文件從1開始)。

示例

#註釋

#category

log4cpp.rootCategory=DEBUG,rootAppender

log4cpp.category.log=NOTSET,debugAppender

 

#Appender

log4cpp.appender.rootAppender=ConsoleAppender

log4cpp.appender.rootAppender.layout=BasicLayout

 

log4cpp.appender.debugAppender=RollingFileAppender

log4cpp.appender.debugAppender.fileName=debuglog.log

log4cpp.appender.debugAppender.maxFileSize=1024

log4cpp.appender.debugAppender.maxBackupIndex=2

 

#Layout

log4cpp.appender.debugAppender.layout=PatternLayout

log4cpp.appender.debugAppender.layout.ConversionPattern=[%t-%p:%d][%r-%u][%x]%m%n

3.6.4參考

NDC:http://blog.csdn.net/mrliu20082009/article/details/7407319

配置:http://ustcfxx.iteye.com/blog/505390

 

3.7 常用調試示例

//log4cpp.property

#註釋

#root日誌

log4cpp.rootCategory=DEBUG,rootAppender

log4cpp.appender.rootAppender=ConsoleAppender

log4cpp.appender.rootAppender.layout=BasicLayout

 

#調試信息日誌:log

log4cpp.category.log=NOTSET,debugAppender

log4cpp.appender.debugAppender=RollingFileAppender

log4cpp.appender.debugAppender.fileName=debuglog.log

log4cpp.appender.debugAppender.maxFileSize=102400

log4cpp.appender.debugAppender.maxBackupIndex=2

log4cpp.appender.debugAppender.layout=PatternLayout

log4cpp.appender.debugAppender.layout.ConversionPattern=[%t-%p:%d][%r-%u][%x]%m%n

 

//main.cpp

#include<log4cpp/Category.hh>

#include<log4cpp/PropertyConfigurator.hh>

#pragmacomment(lib,"log4cpp.lib")

#include<log4cpp\NDC.hh>

intmain(intargc,char*argv[])

{

    log4cpp::PropertyConfigurator::configure("../log/log4cpp.property");

    log4cpp::Category&cfglog=log4cpp::Category::getInstance("log");

    cfglog.debug("debuglog");

    cfglog.warn("warnlog");

    DWORDdwID=GetCurrentThreadId();

    printf("id =%lu\n",dwID);

    cfglog.debug(log4cpp::NDC::get());

    log4cpp::NDC::push("NDCtest");

    for(inti=0;i<1000;i++)

    {

        cfglog.debug("debug:%d",i);

    }

    cfglog.emerg("emerglog");

    return0;

}

 

 

4 log4cplus

與log4cpp基本相同,區別是log4cpp中的category在log4cplus中是logger。

log4cplus較log4cpp更加細緻,支持也更加全面(android、ios),但操作也相應的複雜。

 

5 log4qt

可以直接操作QT的類,但是自2009以後就沒有更新了。

6 QT原生日誌:qInstallMsgHandler(推薦)

6.1 目的:使用QT方便,快捷,靈活的生成日誌文件。

1)   方便的生成日誌文件。

2)   可以細化爲不同的級別。

3)   可以生成時間,進程id,級別、消息的日誌。

4)   可以生成捲動日誌。

6.2 原理:使用qInstallMsgHandler將QDebug的輸出進行事件處理,然後輸出爲文件。

qInstallMsgHandler()可以將一個回調函數綁定的調試函數,當執行調試函數時會觸發此函數,進行操作(記錄到文件)。

qInstallMsgHandler()是一個全局函數,接受QtMsgHandler類型的函數指針。

QtMsgHandler是一個函數指針類型:typedefvoid (*QtMsgHandler)(QtMsgType,const char *)。

注意:Qt5處理qInstallMessageHandler和QtMessageHandler替代當前函數。

6.3 方法:使用qInstallMsgHandler將QDebug的輸出進行事件處理,然後輸出爲文件。

1)   生成處理函數:QtMsgHandler

2)   註冊處理函數:qInstallMsgHandler

3)   輸出調試信息:qDebug,qFatal….

4)   取消處理函數:qInstallMsgHandler(NULL)

5)   設置消息文件及格式:

內容和格式:只要設置輸出的String就可以了。

時間:QDateTime

線程ID:GetCurrentThreadId()(windows)

級別:qDebug….

消息:輸入參數msg

rollingfile:測試文件大小,然後自動rolling。如果有未滿文件,則順序查找,然後寫入,如果文件已滿,則查找最舊文件寫入。

參考:http://blog.chinaunix.net/uid-20698826-id-4232440.html   

6.4 示例

/**
*@file  logging.h
*@author[email protected]
*@version1.0
*@date2014-09-1215:26:47
*@brieflogfileprocess
*@details
*createlogfileandconfigthelogformat.
*
*@history
*/
 
#ifndefLOGGING_H
#defineLOGGING_H
#include<QFile>
#include<QTextStream>
#include<QDebug>
#include<QDateTime>
#include<QThread>
#include<Windows.h>
#include<QFileInfo>
namespacegutang{
namespacelogging{
 
///createlogfilenamewithnameprefixandindex
QStringcreateLogName(constQString&strName,inti);
///findtheoldlogfiletodeleteandlognew
QStringfindOldestFile(constQString&strName,intiMax);
///getthelogfilewhichcouldbelogged
QFile*getLogFile(constQString&strName,intiMaxSize,intiMax);
///QMsgHanderimplememt
voidmyHandler(QtMsgTypetMsg,constchar*msg);
 
}
}
 
#endif//LOGGING_H

 

 

 

/**
*@file  logging.cpp
*@author[email protected]
*@version1.0
*@date2014-09-1215:26:47
*@brieflogfileprocess
*@details
*createlogfileandconfigthelogformat.
*
*@history
*/
 
#include"logging.h"
namespacegutang{
namespacelogging{
 
staticintS_Current_Index=0;///currentlogfileindex
 
/**
*@briefcreateLogName
*
*createlogfilename.
*ifindexis0,filenamewillbe:name.log.
*ifindexisnot0,filenamewillbe:name+index.log.
*@paramstrName
*@parami
*@return
*@author[email protected]
*@date2014-09-1215:15:34
*/
QStringcreateLogName(constQString&strName,inti)
{
    QStringstrFileName;
    if(0==i){
        strFileName=QString("%1%2.log").arg(strName).arg("");
    }
    else{
        strFileName=QString("%1%2.log").arg(strName).arg(i);
    }
    returnstrFileName;
}
 
/**
*@brieffindOldestFile
*
*findtheoldestlogfilethatshouldbedelete.
*@paramstrName
*@paramiMax
*@return
*@author[email protected]
*@date2014-09-1215:15:34
*/
QStringfindOldestFile(constQString&strName,intiMax)
{
    inti=0;
    QDateTimedtLast=QDateTime::currentDateTime();
    QStringstrLast;
    while(i<iMax)
    {
        QStringstrFileName=createLogName(strName,i);
        QFileInfofi(strFileName);
        QDateTimedt=fi.lastModified();
        if(dtLast>dt){
            dtLast=dt;
            strLast=strFileName;
        }
        ++i;
    }
    returnstrLast;
}
 
/**
*@briefgetLogFile
*
*getthelogfilewhichwillbeloged.
*@paramstrNamefilenameprefix
*@paramiMaxSizemaxfilesizeperlogfile
*@paramiMaxmaxlogfilecount
*@return
*@author[email protected]
*@date2014-09-1215:15:34
*/
QFile*getLogFile(constQString&strName,intiMaxSize,intiMax)
{
    QFile*pFileLog=NULL;
    //rollingfile
    inti=S_Current_Index;
//    intiMax=8;
    boolbFull=false;
    while(NULL==pFileLog)
    {
        QStringstrFileName;
        if(i>=iMax)
        {
            bFull=true;
            //findthelastmodified
            strFileName=findOldestFile(strName,iMax);
            QFile*pfLog=newQFile(strFileName);
            pfLog->open(QIODevice::WriteOnly|QIODevice::Truncate);
            pFileLog=pfLog;
            break;
        }
        else{
            strFileName=createLogName(strName,i);
        }
 
        QFile*pfLog=newQFile(strFileName);
        pfLog->open(QIODevice::WriteOnly|QIODevice::Append);
 
        if(pfLog->size()<iMaxSize)
        {
            pFileLog=pfLog;
            break;
        }
        else
        {
            pfLog->close();
            deletepfLog;
            pfLog=NULL;
        }
        ++i;
    }
    S_Current_Index=i;
    returnpFileLog;
}
 
/**
*@briefmyHandler
*@paramtMsg
*@parammsg
*@author[email protected]
*@date2014-09-1215:15:34
*/
voidmyHandler(QtMsgTypetMsg,constchar*msg)
{
    ///
    ///howto?
    ///1.ifanyfileisnotfull,sequecelywritelog.
    ///2.ifallfilesarefull,findtheoldestoneandwritelog.
    ///
 
    //filesetting
    intiMaxFileSize=1024*1204;//maxfilesize=10M
    intiMaxCount=8;//maxfilecount
    QStringstrName("mylog");//logfilenameprefix
    QStringstrLog("[%1-%2%3]%4\r\n");//logformat:[time-prioritythreadid]msg
 
    //time
    QDateTimet=QDateTime::currentDateTime();
    strLog=strLog.arg(t.toString("yyyy-MM-ddhh:mm:ss.zzz"));
 
    //priority
    switch(tMsg){
    caseQtDebugMsg:
        strLog=strLog.arg("Debug");
        break;
    caseQtWarningMsg:
        strLog=strLog.arg("Warning");
        break;
    caseQtCriticalMsg:
        strLog=strLog.arg("Critiacl");
        break;
    caseQtFatalMsg:
        strLog=strLog.arg("Fatal");
        break;
    default:
        strLog=strLog.arg("Unknown:");
        break;
    }
 
    //threadid
    DWORDdwId=::GetCurrentThreadId();
    strLog=strLog.arg(dwId);
 
    //msg
    strLog=strLog.arg(msg);
 
    //savetofile
    QFile*pFileLog=NULL;
//    //singlefile
//    QStringstrFileName=QString("%1%2.log").arg(strName).arg(i);
//    QFile*pfLog=newQFile(strFileName);
//    pFileLog=pfLog;
//    pFileLog->open(QIODevice::WriteOnly|QIODevice::Append);
    pFileLog=getLogFile(strName,iMaxFileSize,iMaxCount);
    if(NULL==pFileLog){
        return;
    }
 
    QTextStreamtsLog(pFileLog);
    tsLog<<strLog;
    pFileLog->close();
    deletepFileLog;
    pFileLog=NULL;
}
}
}

 

 

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