log4cxx 的編譯安裝過程和使用
簡介
log4cxx是Java社群著名的log4j的c++移植版,用於為C++程式提供日誌功能,以便開發者對目標程式進行除錯和審計,log4cxx是apache軟體基金會的開源專案,基於APR實現跨平臺支援。一個良好的日誌系統不管是開發、除錯和維護,對一個專案來說是多麼的重要,我想做過開發的同學深知這點。我用過的日誌框架比較少,所以在這裡不做與其它日誌框架的比較,類似的日誌框架還有GLog、boost log,如果有興趣可以去研究一下。
其實剛工作時,9c的pass9專案中就需要日誌系統,只是那時候還不知道log4cxx這個好東西,只能自己動手實現了一個簡單,主要是用到了c函式的可變引數,值得注意的是執行緒安全的問題,所以在寫檔案時要加互斥鎖,為了提高效能,還需注意檔案塊大小問題,當然了,功能特別簡單,只有控制檯和檔案輸出,有等級、時間、執行緒號、檔名、行號、函式名這些輸出。
下載與安裝
2. 將 3 個壓縮包解壓到同一個目錄.
3. 將 apr-1.2.11 重新命名為 apr, 將 apr-util-1.2.10 重新命名為 apr-util.
最終如下:
4. 使用 cmd, 切換至 apache-log4cxx-0.10.0 目錄, 執行 configure.bat
5. 在與第 4 步同一目錄下的 cmd 中, 執行 configure-aprutil.bat
1)和2)兩個步驟中選擇一個執行就行了
1) 此時, 如果環境變數中沒有 sed 程式, 則會出現:
此時應該:
手動修改 apr-util\include\apu.hw 裡的內容
#define APU_HAVE_APR_ICONV 1改為
#define APU_HAVE_APR_ICONV 0
2) 如果系統可以找到 sed 程式, 那麼會出現:
6. 開啟 apache-log4cxx-0.10.0\projects 下的 log4cxx.dsw
開啟工程會提示轉換:
7. 將 log4cxx 設定為啟動項.
8. 開始編譯. 快捷鍵 F7.
9.
1) 出現
'4>D:\log4cxx-src\apache-log4cxx-0.10.0\src\main\include\log4cxx/spi/loggingevent.h(155): error C2252: 只能在名稱空間範圍內顯式例項化模板' 錯誤.
a) 雙擊 "輸出" 視窗中的錯誤行, 此時會在 "程式碼視窗" 中出現錯誤的位置.
b) 選擇 LOG4CXX_LIST_DEF, 按鍵盤 F12, 此時會跳轉到該巨集的定義
c) 將
#define LOG4CXX_LIST_DEF(N, T) \
template class LOG4CXX_EXPORT std::allocator<T>; \
template class LOG4CXX_EXPORT std::vector<T>; \
typedef std::vector<T> N替換為:
#define LOG4CXX_LIST_DEF(N, T) \
typedef std::vector<T> N
2) 出現
'2>network_io\unix\multicast.c(137): error C2079: “mip”使用未定義的 struct“group_source_req"' 等錯誤.
雙擊第一行出錯輸出, 將 136 和 148 行的 #if MCAST_JOIN_SOURCE_GROUP 註釋, 替換為 #if defined (group_source_req)
3) 出現
'4>..\src\main\cpp\stringhelper.cpp(64): error C2039: “insert_iterator”: 不是“std”的成員' 等錯誤.
在該 .cpp 中(stringhelper.cpp) 加入標頭檔案 <iterator>:
4) 出現
'無法解析的外部符號 xxx' 等錯誤.
將 apr, aprutil, xml 新增至 log4cxx 的引用中.
10. 選擇重新生成解決方案. 否則可能會造成 '無法開啟 apr_iconv.h' 錯誤. 個人懷疑是第 5 步中, configure-aprutil.bat 指令碼中的 sed 命令沒有正確更新檔案的時間所致.
編譯成功後:
下面介紹其使用方法:
1.優先順序
Logger的語法:
log4j.rootLogger = [ level ] , appenderName, appenderName, … 其中,level 是日誌記錄的優先順序,分為OFF、FATAL、ERROR、WARN、INFO、DEBUG、ALL或者自定義的級別。Log4j建議只使用四個級別,優先順序從高到低分別是ERROR、WARN、INFO、DEBUG。通過在這裡定義的級別,可以控制到應用程式中相應級別的日誌資訊的開關。比如這裡定義了INFO級別,則應用程式中所有DEBUG級別的日誌資訊將不被列印出來。 appenderName名字任意,用來標示日誌資訊輸出到哪裡,可以同時指定多個。
2.輸出目的地
Appender的語法:
log4j.appender.appenderName = fully.qualified.name.of.appender.class log4j.appender.appenderName.option1 = value1 … log4j.appender.appenderName.option = valueN 其中,Log4j提供的appender有以下幾種: org.apache.log4j.ConsoleAppender 控制檯 org.apache.log4j.FileAppender 檔案 org.apache.log4j.DailyRollingFileAppender 每天產生一個日誌檔案 org.apache.log4j.RollingFileAppender 檔案大小到達指定尺寸的時候產生一個新的檔案 org.apache.log4j.WriterAppender 將日誌資訊以流格式傳送到任意指定的地方
3.輸出格式
log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class log4j.appender.appenderName.layout.option1 = value1 … log4j.appender.appenderName.layout.option = valueN Log4j提供的Layout有以下幾種: org.apache.log4j.HTMLLayout 以HTML表格形式佈局 org.apache.log4j.PatternLayout 可以靈活地指定佈局模式 org.apache.log4j.SimpleLayout 包含日誌資訊的級別和資訊字串 org.apache.log4j.TTCCLayout 包含日誌產生的時間、執行緒、類別等等資訊 Log4j採用類似C語言中的printf函式的列印格式格式化日誌資訊,列印引數如下: %m 輸出程式碼中指定的訊息 %p 輸出優先順序,即DEBUG,INFO,WARN,ERROR,FATAL %r 輸出自應用啟動到輸出該log資訊耗費的毫秒數 %c 輸出所屬的類目,通常就是所在類的全名 %t 輸出產生該日誌事件的執行緒名 %n 輸出一個回車換行符,Windows平臺為“/r/n”,Unix平臺為“/n” %d 輸出日誌時間點的日期或時間,預設格式為ISO8601,也可以在其後指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},輸出2008年11月14日 15:16:17,890 %l 輸出日誌事件的發生位置,包括類目名、發生的執行緒,以及在程式碼中的行數。
以下只講怎樣使用。
一、建測試工程:testlog4cxx,直接選控制檯應用程式, 在配置屬性頁中,選C/C++,常規,在附加包含目錄中加入“./”;選連結器,常規,在附加庫目錄中加入"./",點選中常規下面的輸入,在附加依賴項中加入“log4cxx/log4cxx.lib”
二、在測試工程目錄下加入log4cxx檔案包
三、加入include標頭檔案:
#include <log4cxx/logger.h>
#include <log4cxx/propertyconfigurator.h>
四、定義配置檔案log4cxx.properties的位置
log4cxx::PropertyConfigurator::configure("log4cxx.properties");
此處的作用為定義配置檔案的位置,如果想讓程式在D盤根目錄下載入配置檔案,可以如下定義:
log4cxx::PropertyConfigurator::configure("D:\\log4cxx.properties");
五、定義不同的列印級別
此在配置檔案中log4cxx.properties將會有所定義。假設有這樣的場景,你必須使用別人提供一個DLL用來獲取介面,此DLL中使用了log4cxx日誌,然後你又想在自己的工程中同樣引入log4cxx日誌,為了區分你的日誌和別人的日誌,你該如何做呢,假設別人使用的為log4j.rootLogger頂級介面。(預設情況下,所有的日誌都會在頂級介面對應的日誌檔案中進行記錄)
這樣一個設定可使得頂級介面與子介面分離:
# 遮蔽gather的Appender繼承
log4j.additivity.gather = false
以下配置假設引用的DLL庫使用了log4cxx的根介面,並且輸出兩種級別的檔案,一種為error,一種為debug
# 設定root logger為DEBUG級別,使用了兩個Appender log4j.rootLogger=DEBUG, COM, ERR
# 設定sample logger為DEBUG級別 log4j.logger.sample = DEBUG, sample
# 遮蔽sample的Appender繼承 log4j.additivity.sample = false
log4j.logger.sample.error = WARN, sample.error #log4j.additivity.sample.error = false
log4j.appender.COM=org.apache.log4j.RollingFileAppender log4j.appender.COM.File=../log/dll_output.log log4j.appender.COM.Append=false log4j.appender.COM.MaxFileSize=3MB log4j.appender.COM.MaxBackupIndex=10 log4j.appender.COM.ImmediateFlush=true log4j.appender.COM.layout=org.apache.log4j.PatternLayout log4j.appender.COM.layout.ConversionPattern=%d %-5p %m%n
log4j.appender.ERR=org.apache.log4j.RollingFileAppender log4j.appender.ERR.File=../log/dll_error.log log4j.appender.ERR.Append=false log4j.appender.ERR.Threshold=ERROR log4j.appender.ERR.MaxFileSize=3MB log4j.appender.ERR.MaxBackupIndex=10 log4j.appender.ERR.layout=org.apache.log4j.PatternLayout log4j.appender.ERR.layout.ConversionPattern=%d %-5p %m%n
log4j.appender.sample=org.apache.log4j.RollingFileAppender log4j.appender.sample.File=../log/sample_output.log log4j.appender.sample.Append=false log4j.appender.sample.MaxFileSize=10MB log4j.appender.sample.MaxBackupIndex=10 log4j.appender.sample.ImmediateFlush=true log4j.appender.sample.layout=org.apache.log4j.PatternLayout log4j.appender.sample.layout.ConversionPattern=%d %-5p %m%n
log4j.appender.sample.error=org.apache.log4j.RollingFileAppender log4j.appender.sample.error.File=../log/sample_error.log log4j.appender.sample.error.Append=false log4j.appender.sample.error.MaxFileSize=3MB log4j.appender.sample.error.MaxBackupIndex=10 log4j.appender.sample.error.ImmediateFlush=true log4j.appender.sample.error.layout=org.apache.log4j.PatternLayout log4j.appender.sample.error.layout.ConversionPattern=%d %-5p %m%n
六、後臺列印的日誌與控制檯顯示的級別不同(一般控制檯列印比較慢,顯示的儘可能少,後臺則儘可能詳盡)
使用log4j.appender.ca.Threshold=WARN可達到控制檯顯示與後臺列印不相同,如下一個配置檔案設定:
# 設定root logger為DEBUG級別,使用了兩個Appender log4j.rootLogger=DEBUG, server, ca
# 設定kline logger為DEBUG級別 log4j.logger.kline = DEBUG, kline, ca
# 遮蔽kline的Appender繼承 log4j.additivity.kline = false
log4j.logger.kline.error = WARN, kline.error, ca #log4j.additivity.kline.error = false
log4j.appender.server=org.apache.log4j.RollingFileAppender log4j.appender.server.File=../log/server_output.log log4j.appender.server.Append=false log4j.appender.server.MaxFileSize=3MB log4j.appender.server.MaxBackupIndex=10 log4j.appender.server.ImmediateFlush=true log4j.appender.server.layout=org.apache.log4j.PatternLayout log4j.appender.server.layout.ConversionPattern=%d %-5p %m%n
log4j.appender.server.error=org.apache.log4j.RollingFileAppender log4j.appender.server.error.File=../log/server_error.log log4j.appender.server.error.Append=false log4j.appender.server.error.Threshold=ERROR log4j.appender.server.error.MaxFileSize=3MB log4j.appender.server.error.MaxBackupIndex=10 log4j.appender.server.error.layout=org.apache.log4j.PatternLayout log4j.appender.server.error.layout.ConversionPattern=%d %-5p %m%n
log4j.appender.kline=org.apache.log4j.RollingFileAppender log4j.appender.kline.File=../log/kline_output.log log4j.appender.kline.Append=false log4j.appender.kline.MaxFileSize=10MB log4j.appender.kline.MaxBackupIndex=10 log4j.appender.kline.ImmediateFlush=true log4j.appender.kline.layout=org.apache.log4j.PatternLayout log4j.appender.kline.layout.ConversionPattern=%d %-5p %m%n
log4j.appender.kline.error=org.apache.log4j.RollingFileAppender log4j.appender.kline.error.File=../log/kline_error.log log4j.appender.kline.error.Append=false log4j.appender.kline.error.MaxFileSize=3MB log4j.appender.kline.error.MaxBackupIndex=10 log4j.appender.kline.error.ImmediateFlush=true log4j.appender.kline.error.layout=org.apache.log4j.PatternLayout log4j.appender.kline.error.layout.ConversionPattern=%d %-5p %m%n
#對Appender ca進行設定: # 這是一個控制檯型別的Appender # 輸出格式(layout)為PatternLayout log4j.appender.ca=org.apache.log4j.ConsoleAppender log4j.appender.ca.Threshold=WARN log4j.appender.ca.layout=org.apache.log4j.PatternLayout log4j.appender.ca.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %.16c - %m%n
七、列印日誌的方法
LOG4CXX_INFO(log4cxx::Logger::getLogger("hello"), "你好,log4cxx!");
以上列印方法可行,不過寫起來比較麻煩,建議進行一番包裝。
如下程式碼所示:
/******************************************************************************
author : luhx
purpose : 日誌系統,以巨集log_XXX開頭,封裝log4cxx日誌。
*******************************************************************************/
#pragma once
#include "log4cxx/logger.h"
#include "log4cxx/propertyconfigurator.h"
/*
日誌類
*/
#include<string>
using namespace std;
class CServerlog;
extern CServerlog* g_serverlog;
#define log_debug(pszFormat, ...) g_serverlog->WriteFormatDebugLog( pszFormat, __VA_ARGS__)
#define log_info(pszFormat, ...) g_serverlog->WriteFormatInfoLog( pszFormat, __VA_ARGS__)
#define log_warn(pszFormat, ...) g_serverlog->WriteFormatWarnLog( pszFormat, __VA_ARGS__)
#define log_error(pszFormat, ...) g_serverlog->WriteFormatErrorLog( pszFormat, __VA_ARGS__)
class CServerlog
{
public:
CServerlog();
~CServerlog();
void WriteFormatDebugLog(char* lpszFormat, ...);
void WriteFormatInfoLog(char* lpszFormat, ...);
void WriteFormatWarnLog(char* lpszFormat, ...);
void WriteFormatErrorLog(char* lpszFormat, ...);
protected:
log4cxx::LoggerPtr infologger;
log4cxx::LoggerPtr errorlogger;
};
#include <Windows.h>
#include <tchar.h>
#include <stdio.h>
#include "serverlog.h"
CServerlog* g_serverlog = new CServerlog();
CServerlog::CServerlog()
{
log4cxx::PropertyConfigurator::configure("log4cxx.properties");
infologger = (log4cxx::Logger::getLogger("server"));
errorlogger = (log4cxx::Logger::getLogger("server.error"));
}
CServerlog::~CServerlog()
{
}
void CServerlog::WriteFormatDebugLog(char* lpszFormat, ...)
{
va_list args;
va_start(args, lpszFormat);
CHAR szBuffer[1024];
vsprintf(szBuffer, lpszFormat, args);
va_end(args);
infologger->debug(szBuffer);
}
void CServerlog::WriteFormatInfoLog(char* lpszFormat, ...)
{
va_list args;
va_start(args, lpszFormat);
CHAR szBuffer[1024];
vsprintf(szBuffer, lpszFormat, args);
va_end(args);
infologger->info(szBuffer);
}
void CServerlog::WriteFormatWarnLog(char* lpszFormat, ...)
{
va_list args;
va_start(args, lpszFormat);
CHAR szBuffer[1024];
vsprintf(szBuffer, lpszFormat, args);
va_end(args);
errorlogger->warn(szBuffer);
}
void CServerlog::WriteFormatErrorLog(char* lpszFormat, ...)
{
va_list args;
va_start(args, lpszFormat);
CHAR szBuffer[1024];
vsprintf(szBuffer, lpszFormat, args);
va_end(args);
errorlogger->error(szBuffer);
}
另外,發現列印出的日誌在某些機器上不支援中文, 中文顯示為亂碼,通過在配置檔案中增加如下配置項即可解決:
log4j.appender.sample.encoding=UTF-8,這裡,sample即為你的日誌類.
前面已經說完了怎樣使用log4cxx進行日誌記錄,今天發現問題稍有點複雜。原因是系統中用到的一個dll已經使用了log4cxx。我們在開發的過程中也想使用log4cxx,但不想與DLL中的日誌寫到同一個檔案中,問題就來了,怎樣區別列印到不同的檔案中呢,DLL中採用的為應該為getRootLogger的方式。
這個網址中的文章很好,解決方案主要參考此文:http://www.open-open.com/doc/view/4bf2bd4f517c4044b1f7d4e2d22eccaf
log4cxx主要是由三部分組成:loggers, appenders和layouts.這三個主要組成部分,協同協作能夠使我們根據實際的需要進行日誌的輸出,它們規定了日誌資訊的型別和級別,控制應用程式執行時的日誌資訊組成方式以及日誌記錄的輸出方式。
Logger是Log4cxx的核心,Logger具有繼承關係的層次結構,最頂層為RootLogger,每個Logger都有一個級別(Level),每個Logger可以附加多個Appender.Appender代表了日誌輸出的目標,對於每一個Appender可以通過Layout進行格式配置。
Logger命名:保持Logger與其所在的類的名稱一致的方法是目前所知的最好的命名策略。
一個關於Logger繼承的例子:
#設定root logger 為DEBUG級別,使用了ca, fa兩個Appender
log4j.rootLogger = DEBUG, ca, fa
#設定list logger
log4j.logger.list = DEBUG, listApp
#設定list.voice logger
log4j.logger.list.voice= INFO, listVoice, listVoiceBak
list是list.voice的父親Logger, list.voice是list的兒子Logger. rootLogger是根Logger, 在此例中list和list.voice兩個Logger又都繼承了rootLogger,不過只是繼承了Appenders,Level並沒有繼。(此解有待驗證)
還是看配置檔案:log4cxx.properties
# 設定root logger為DEBUG級別,使用了ca和fa兩個Appender
log4j.rootLogger=DEBUG, fa, ca
# 設定.listApp logger,遮蔽logger-list的LEVEL繼承
log4j.logger.listApp=DEBUG, listApp
# 設定.listApp2 logger,遮蔽logger-list的LEVEL繼承
log4j.logger.listApp2=DEBUG, listApp2
#遮蔽listApp的Appender繼承
log4j.additivity.listApp=false
#遮蔽listApp2的Appender繼承
log4j.additivity.listApp2=false
#對Appender fa進行設定:# 這是一個檔案型別的Appender,
log4j.appender.fa=org.apache.log4j.FileAppender
log4j.appender.fa.ImmediateFlush=true
# 其輸出檔案(File)為 Runlog.log,
log4j.appender.fa.File=Runlog.log
log4j.appender.fa.layout=org.apache.log4j.PatternLayout
# 輸出方式(Append)為覆蓋方式,
log4j.appender.fa.Append=false
# 輸出格式(layout)為PatternLayout
log4j.appender.fa.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %.16c - %m%n
#對Appender ca進行設定:
# 這是一個控制檯型別的Appender
log4j.appender.ca=org.apache.log4j.ConsoleAppender
# 輸出格式(layout)為PatternLayout
log4j.appender.ca.layout=org.apache.log4j.PatternLayout
log4j.appender.ca.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %.16c - %m%n
#對Appender listApp進行設定# 這是一個檔案型別的Appender,
log4j.appender.listApp=org.apache.log4j.FileAppender
#立即寫入日誌檔案
log4j.appender.listApp.ImmediateFlush=true
# 其輸出檔案(File)為 listApp.log
log4j.appender.listApp.File=listApp.log
# 輸出方式(Append)為覆蓋方式,
log4j.appender.listApp.Append=false
log4j.appender.listApp.layout=org.apache.log4j.PatternLayout
log4j.appender.listApp.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %.16c - %m%n
#對Appender listApp2進行設定
log4j.appender.listApp2=org.apache.log4j.FileAppender
log4j.appender.listApp2.ImmediateFlush=true
log4j.appender.listApp2.File=listApp2.log
log4j.appender.listApp2.Append=false
log4j.appender.listApp2.layout=org.apache.log4j.PatternLayout
log4j.appender.listApp2.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %.16c - %m%n
以下為實現程式碼:
// testlog4cxx.cpp : 定義控制檯應用程式的入口點。
//
#include "stdafx.h"
#include <iostream>
using namespace std;
#include <log4cxx/logger.h>
#include <log4cxx/propertyconfigurator.h>
using namespace log4cxx;
using namespace log4cxx::helpers;
int _tmain(int argc, _TCHAR* argv[])
{
PropertyConfigurator::configure("log4cxx.properties");
LOG4CXX_INFO(log4cxx::Logger::getLogger("list5"), "list,log4cxx!");
LOG4CXX_INFO(log4cxx::Logger::getLogger("list5"), "list,mylog4cxx!");
LOG4CXX_INFO(log4cxx::Logger::getLogger("listApp"), "listApp,log4cxx!");
LOG4CXX_INFO(log4cxx::Logger::getLogger("listApp2"), "listApp2, hello world!");
return 0;
}
將log4cxx.properties與生成後的testlog4cxx.exe放置同一目錄,執行後發現目錄下生成三個檔案,分別為:他們的檔名及內容分別為
Runlog.log:
2012-04-06 21:13:56 INFO list5 - list,log4cxx!
2012-04-06 21:13:56 INFO list5 - list,mylog4cxx!
listApp.log:
2012-04-06 21:13:56 INFO listApp - listApp,log4cxx!
2012-04-06 21:13:56 INFO listApp - listApp TimeRsp.nErrno: 123
listApp2.log:
2012-04-06 21:13:56 INFO listApp2 - listApp2, hello world!
由此可以看出,所有Logger預設繼承於rootLogger的Appender,一般情況下(Level級別允許)總會在Runlog.log中打出,若想有所例外,需用配置log4j.additivity.AppenderName = false將預設繼承去除。
相關文章
- Mysql安裝過程中CMAKE編譯出錯MySql編譯
- Linux上安裝GCC編譯器過程(轉)LinuxGC編譯
- 編譯器的工作過程和原理編譯
- 原始碼編譯MySQL Cluster7.2.15安裝過程原始碼編譯MySql
- 編譯器的編譯基本過程編譯
- 編譯過程編譯
- GCC編譯和連結過程GC編譯
- STLPort的編譯和安裝 (轉)編譯
- 原始碼編譯安裝MySQL5.6.12詳細過程原始碼編譯MySql
- iOS編譯過程的原理和應用iOS編譯
- Javac編譯過程Java編譯
- 編譯核心過程編譯
- 交叉編譯Qt/E和Qtopia過程編譯QT
- 編譯器的工作過程編譯
- EVC編譯TCPMP的過程編譯TCP
- 尤拉系統初體驗與編譯安裝FFmpeg的過程記錄編譯
- pycharm中安裝和使用sqlite過程詳解PyCharmSQLite
- ORM:ODB安裝使用過程ORM
- CMM編譯器和C編譯器過程呼叫實現的比較編譯
- protobuf 編譯工具安裝與使用編譯
- nginx 編譯安裝與配置使用Nginx編譯
- 編譯連結過程編譯
- 編譯過程簡介編譯
- C++ 編譯過程C++編譯
- PHP的編譯安裝PHP編譯
- JavaScript的預編譯過程分析JavaScript編譯
- 編譯C++ 程式的過程編譯C++
- rpm安裝的php 通過編譯增加模組PHP編譯
- Java程式碼編譯和執行的整個過程Java編譯
- Centos下Ambari2.7.5的編譯和安裝CentOS編譯
- webpack的安裝過程Web
- ios底層 編譯過程iOS編譯
- .NET 程式碼編譯過程編譯
- glade 編譯過程 (轉)編譯
- 編譯安裝zabbix編譯
- apache編譯安裝Apache編譯
- 編譯安裝nmon編譯
- swoole 編譯安裝編譯