原文地址:http://blog.csdn.net/lujinhong2/article/details/47662819
JAVA日誌系統 一slf4j 一常用樣本 二其它樣本 三一些注意事項 二log4j 一使用java向rsyslog發送日誌 基本使用方法 不使用設定檔 三logging 四使用slf4jlog4j2向rsyslog發送日誌 一rsyslog配置 1在etcrsyslogd中建立stormconf 2修改etcrsyslogconf 二java程式中輸出日誌 1添加依賴 2準備設定檔 3輸出日誌
java有大量的架構用於日誌輸出,常見的包括slf4j, log4j, logback, logging等. 一、slf4j
slf4j只是一個門面(facet),它不包含具體的實現,而是將一些log4j,java.logging等實現封裝成統一的介面。
commons-logging和slf4j都是日誌的介面,供使用者使用,而沒有提供實現。
log4j,logback等等才是日誌的真正實現。
當我們調用介面時,介面的工廠會自動尋找恰當的實現,返回一個實現的執行個體給我服務。這些過程都是透明化的,使用者不需要進行任何操作。
這裡有個小故事,當年Apache說服 log4j以及其他的日誌來按照commons-logging的標準編寫,但是由於commons-logging的類載入有點問題,實現起來也不友 好,因此log4j的作者就創作了slf4j,也因此而與commons-logging兩分天下。至於到底使用哪個,由使用者來決定吧。
這樣,slf4j出現了,它通過簡單的實現就能找到符合自己介面的實作類別,如果不是滿足自己標準的日誌,可以通過一些中間實現比如上面的slf4j-log4j12.jar來進行適配。
好,言歸正傳,如何使用slf4j。 (一)常用樣本
1、在pom.xml中添加以下內容
<dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.0</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>1.7.0</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency>
2、建立log4j.properties檔案,詳細配置見http://www.cnblogs.com/lujinhong2/p/4637219.html
注意檔案必須在config目錄下(對於maven項目,log4j.properties位於src/main/resources下面)
### set log levels ###log4j.rootLogger = debug , stdout , D , E### 輸出到控制台 ###log4j.appender.stdout = org.apache.log4j.ConsoleAppenderlog4j.appender.stdout.Target = System.outlog4j.appender.stdout.layout = org.apache.log4j.PatternLayoutlog4j.appender.stdout.layout.ConversionPattern = %d{ABSOLUTE} %5p %c{1}:%L - %m%n### 輸出到記錄檔 ###log4j.appender.D = org.apache.log4j.DailyRollingFileAppenderlog4j.appender.D.File = logs/log.loglog4j.appender.D.Append = true## 輸出DEBUG層級以上的日誌log4j.appender.D.Threshold = DEBUG log4j.appender.D.layout = org.apache.log4j.PatternLayoutlog4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n### 儲存異常資訊到單獨檔案 ###log4j.appender.E = org.apache.log4j.DailyRollingFileAppender ## 異常記錄檔名log4j.appender.E.File = ./error.loglog4j.appender.E.Append = true## 只輸出ERROR層級以上的日誌!!!log4j.appender.E.Threshold = ERROR log4j.appender.E.layout = org.apache.log4j.PatternLayoutlog4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %l:%c:%t:%r ] - [ %p ] %m%n
3、在java檔案中調用
package org.lujinhong.javademo.slf4jdemo;import java.io.File;import java.io.IOException;import org.slf4j.Logger;import org.slf4j.LoggerFactory;/** * @author jinhong-lu * @date 2015年7月9日 下午4:34:49 * @Description: */public class Slf4jDemo { private static final Logger LOG = LoggerFactory.getLogger(Slf4jDemo.class); public static void main(String[] args) { // 使用此變數會組建檔案成功 String fileName = "1.txt"; // 使用此變數會組建檔案失敗 // String fileName = "/tt/1.txt"; try { new File(fileName).createNewFile(); LOG.info("create file " + fileName + "!"); } catch (IOException e) { e.printStackTrace(); LOG.error("create file " + fileName + " fail!!!!" + e.getMessage()); } }}(二)其它樣本
首先,項目中必須要包括slf4j-api.jar,此外,還應該包括slf4j為具體實現所提供的適配器(如slf4j-log4j12.jar),以及那個具體實現的jar包(如log4j-1.**.jar)。
我們以以下代碼為例:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Slf4jDemo {
private static final Logger LOG = LoggerFactory.getLogger(Slf4jDemo.class);
public static void main(String[] args) { LOG.error("Error Message!"); LOG.warn("Warn Message!"); LOG.info("Info Message!"); LOG.debug("Debug Message!"); LOG.trace("Trace Message!");}
}
由於所使用的具體實現不同,日誌輸出也有不同的結果。這也反應了通過使用slf4j,使得可以方便的替換日誌系統。
1、Slf4j-simple
slf4j內建的一個簡單實現,可用於小項目中,但無法配置記錄層級等。
官方文檔中的描述為:Binding for Simple implementation, which outputs all events to System.err. Only messages of level INFO and higher are printed. This binding may be useful in the context of small applications.
在項目的build_path中加入slf4j-1.6.6.jar與slf4j-simple-1.6.6.jar。
輸出結果如下:
2 [main] ERROR Slf4jDemo - Error Message!2 [main] WARN Slf4jDemo - Warn Message!2 [main] INFO Slf4jDemo - Info Message!
2、slf4j-jdk
使用jkd內建的日誌系統,在項目的build_path中加入slf4j-1.6.6.jar與slf4j-jdk14-1.6.6.jar。
輸出結果如下:
二月 16, 2015 11:09:36 下午 Slf4jDemo main嚴重: Error Message!二月 16, 2015 11:09:36 下午 Slf4jDemo main警告: Warn Message!二月 16, 2015 11:09:36 下午 Slf4jDemo main資訊: Info Message!
3、slf4j-log4j
log4j是目前用得最多的日誌系統,它更適用於大型項目。
在項目的build_path中加入slf4j-1.6.6.jar與slf4j-log4j-1.6.6.jar,以及log4j的具體實現,如log4j-1.2.16.jar。
輸出結果如下:
log4j:WARN No appenders could be found for logger (Slf4jDemo).log4j:WARN Please initialize the log4j system properly.log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
可以通過設定檔配置輸出日誌的層級。 (三)一些注意事項
1、注意build_path中不能有多個日誌實現,否則會導致slf4j不知道該使用哪個實現,從而出現以下錯誤
SLF4J: Class path contains multiple SLF4J bindings.SLF4J: Found binding in [jar:file:/Users/liaoliuqing/99_Project/1_myCodes/5_JavaEEDemo/lib/slf4j-log4j12-1.6.6.jar!/org/slf4j/impl/StaticLoggerBinder.class]SLF4J: Found binding in [jar:file:/Users/liaoliuqing/99_Project/1_myCodes/5_JavaEEDemo/lib/slf4j-jdk14-1.6.6.jar!/org/slf4j/impl/StaticLoggerBinder.class]SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.SLF4J: Actual binding is of type [org.slf4j.impl.Log4jLoggerFactory]log4j:WARN No appenders could be found for logger (Slf4jDemo).log4j:WARN Please initialize the log4j system properly.log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.
3、有個很大的坑:
由於很多項目都會使用log4j作為日誌架構,而在一個項目的引用包中的log4j設定檔有可能將自己定義的log4j.properties檔案覆蓋。
如果發現自己的log4j設定檔不生效,則使用:
java -Dlog4j.debug 主類
來檢查目前載入了哪個設定檔,然後將其刪除即可。
另外,即使使用了
PropertyConfigurator.configure(“log4j2.properties”);
指定設定檔,也有可能被第三方jar包使用同樣語句覆蓋了。 二、log4j
log4j有2個版本,1.x和2.x,2.x較為完善,但是目前大家都習慣了用1.x,而且很多開源項目都使用了1.x,所以一般使用1.x就可以了。
關於2.x的使用,請參考http://www.cnblogs.com/lujinhong2/p/4637295.html
1.x要注意版本的問題,一般使用1.2.17配合slfj的1.7.12版本即可,只要不相差太多都沒問題。 (一)使用java向rsyslog發送日誌 基本使用方法
1、開通rsyslog遠程UDP訪問
vi /etc/rsyslog.conf
將下面兩段前面的#號去掉
#$ModLoad imudp #$UDPServerRun 514
如果原來沒有話則加上這2行
2,建立存放log的檔案地址
vi /etc/rsyslog.conf
加入以下兩段
local2.info /var/log/login_info.log local2.debug /var/log/login_debug.log
3,開通防火牆
將UDP連接埠514對外開放出來
4,重啟rsyslog
service rsyslog restart
5,log4j的配置
將日誌同時輸出到syslog和console
log4j.rootLogger =ALL,systemOut,SYSLOGlog4j.appender.systemOut = org.apache.log4j.ConsoleAppender log4j.appender.systemOut.layout = org.apache.log4j.PatternLayout log4j.appender.systemOut.layout.ConversionPattern = [%-5p][%-22d{yyyy/MM/dd HH:mm:ssS}][%l]%n%m%n log4j.appender.systemOut.Threshold = DEBUG log4j.appender.systemOut.ImmediateFlush = TRUE log4j.appender.systemOut.Target = System.out log4j.appender.SYSLOG=org.apache.log4j.net.SyslogAppender log4j.appender.SYSLOG.syslogHost=192.168.172.114log4j.appender.syslog.Threshold=DEBUG log4j.appender.SYSLOG.layout=org.apache.log4j.PatternLayout log4j.appender.SYSLOG.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n log4j.appender.SYSLOG.Header=truelog4j.appender.SYSLOG.Facility=local2
6、寫java代碼
package com.lujinhong.demo.log4j;import org.apache.log4j.Logger;public class Log4jDemo { private static Logger LOG = Logger.getLogger(Log4jDemo. class ); public static void main(String[] args) { //System.setProperty("LOGDIR","/Users/liaoliuqing/Downloads/"); LOG.info("lujinhong !!INFO MESSAGE!!"); LOG.error("ERROR MESSAGE!!!!"); LOG.debug("DEBUG MESSAGE!"); LOG.warn("WARN MESSAGE!!"); new MyJavaClass().adder(2, 3); }}
7,運行應用程式,可以在/var/log下看到不同層級的日誌資訊,如login_info.log和login_debug.log 不使用設定檔
有時候,log4j的設定檔會互相覆蓋,真的很煩,因此可以將配置寫到代碼中去
1、定義一個單例類,將相關配置寫入:
package com.lujinhong.demo.log4j;import org.apache.log4j.Level;import org.apache.log4j.Logger;import org.apache.log4j.PatternLayout;import org.apache.log4j.net.SyslogAppender;public class MySysLogger { //記錄層級 private static Level level = Level.INFO; private static Logger LOG = null; public static synchronized Logger getInstance() { if (LOG == null) { new MySysLogger(); } return LOG; } private MySysLogger() { LOG = Logger.getRootLogger(); LOG.setLevel(level); SyslogAppender appender = new SyslogAppender(); appender.setName("ma30"); appender.setSyslogHost("192.168.172.98"); appender.setLayout(new PatternLayout( "%r [%t] %-5p %c%x: - %m%n")); appender.setHeader(true); appender.setFacility("local7"); //appender.setWriter(new PrintWriter(System.out)); //如果是檔案是RollingFileAppender:setWriter(new PrintWriter(new File("F:/test/_debug.log"))); LOG.addAppender(appender); }}
2、在需要日誌輸出的類中添加以下語句
private static Logger LOG = MySysLogger.getInstance();
然後就可以使用LOG變數進行日誌輸出了
吐槽幾句,log4j的坑啊….
(1)CLASSPATH中不能有多個log4j的版本本,否則有有奇形怪狀的NoSuchMethod, NoSuchFiled, NoClassDefineFound等異常。明明是太多了,還告訴你沒有
(2)與slf4j的搭建,必須版本一致,如slf4j-1.7.2對應log4j-1.2.17
(3)設定檔啊,如果你引用的第三方包有log4j.properties,而又沒有提供給你編輯,那恭喜你,慢慢調吧。把log4j的配置寫入代碼吧,不要用設定檔了
(4) 如果你打算只使用log4j,而不使用slf4j作封裝,切記classpath中只能有log4j,不能有log4j-over-slf4j-1.7.12.jar這種包,不然會出現各種各樣的錯誤,如:
NoSuchMethod, NoSuchFiled, NoClassDefineFound, IncompatibleClassChangeError: Implementing class 三、logging
直接一個樣本
(1)在你的代碼中增加一個類,代碼見後面。一般情況下只需要修改輸出日誌的檔案名稱就可以了。
(2)在任何需要輸出日誌的類中添加一個變數,private static Logger LOG = MyLogger.getInstance();
然後就可以使用LOG變數來做日誌輸出了。
說明一下:
1、storm從0.9.1後使用的日誌架構是slf4j+logbakc,取代了原先的log4j。我們嘗試使用log4j作日誌架構,發現log4j.properties被storm某個jar包裡面的配置覆蓋了,導致自己定義的log4j.properties怎麼都不生效,然後嘗試logback也有類似問題,定義的cluster.xml沒有給出worker進程的日誌配置。
2、使用java.util.logging設定檔也可以,便在分布式系統中的配置載入比較複雜,而且java.util.logging很坑,很容易寫錯,比如每一行的最後都不能有空格等。
package com.netease.sytopology.util;
import java.io.IOException;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
/**
* @author lujinhong
* @date 2015年7月25日 下午3:19:56
* @Description:
*/
public class MyLogger {
//檔案路徑及檔案名稱
private static String logFile = “/home/Hadoop/storm/logs/userlog/ma30_filter.log”;
//記錄層級
private static Level level = Level.INFO;
//每個記錄檔的大小,單位為M。
private static int logFileSize = 100;
//儲存記錄檔的資料
private static int logFileCount = 10;
//logger的名稱
private static String logName = “com.lujinhong.demo”;
private static Logger LOG = null;
public static synchronized Logger getInstance() {
if (LOG == null) {
new MyLogger();
}
return LOG;
}
private MyLogger() {
LOG = Logger.getLogger(logName);
FileHandler fileHandler = null;
try {
fileHandler = new FileHandler(logFile,logFileSize*1024*1024,logFileCount,true);
} catch (SecurityException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
fileHandler.setLevel(level);
fileHandler.setFormatter(new SimpleFormatter());
LOG.removeHandler(new ConsoleHandler());
LOG.addHandler(fileHandler);
}
} 四、使用slf4j+log4j2向rsyslog發送日誌 (一)rsyslog配置 1、在/etc/rsyslog.d中建立storm.conf
內容如下:
$WorkDirectory /home/data/log/$MaxMessageSize 64k$ModLoad imudp$UDPServerRun 514$template stromtemplate, "/home/data/log/storm_%STRUCTURED-DATA:R,ERE,1:.*category=\"(.+)\".*programname.*--end%_%$YEAR%%$MONTH%%$DAY%.log"$template stromformat, "[%TIMESTAMP:1:10:date-rfc3339% %TIMESTAMP:12:19:date-rfc3339%] %syslogseverity-text:::uppercase% %STRUCTURED-DATA:R,ERE,2:.*category=\"(.+)\".*programname=\"(.+)\".*--end% %msg%\n"$EscapeControlCharactersOnReceive off$FileOwner hadoop$FileGroup hadoop$FileCreateMode 0644$DirCreateMode 0755$Umask 0022local7.* -?stromtemplate;stromformatlocal7.* ~
2、修改/etc/rsyslog.conf
修改以下3行:
*.*;auth,authpriv.none;cron.none,local7.none -/var/log/syslogkern.*,local7.noe -/var/log/kern.log*.=info;*.=notice;*.=warn;\ auth,authpriv.none;\ cron,daemon.none;\ mail,news.none,local7.none -/var/log/messages
聲明local7的日誌不會發送到syslog, kern.log,message。否則日誌量太大會撐爆。
此時rsyslog就已經配置完成,它會將發送至local7的日誌解釋後放到/home/data/log中。 (二)java程式中輸出日誌 1、添加依賴
以maven為例,添加以下內容:
<dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-api</artifactId> <version>2.1</version></dependency><dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.1</version></dependency><dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>2.1</version></dependency><dependency> <groupId>org.slf4j</groupId> <artifactId>log4j-over-slf4j</artifactId> <version>1.6.6</version></dependency>
其它編譯方式則添加相應的jar包。 2、準備設定檔
在resources目錄下建立log4j2.xml,內容如下:
<?xml version="1.0" encoding="UTF-8"?><Configuration status="warn" name="test" packages=""> <Appenders> <Syslog name="test" format="RFC5424" host="192.168.1.100" port="514" protocol="UDP" appName="storm" includeMDC="true" mdcId="mdc" facility="LOCAL7" enterpriseNumber="18060" newLine="true" messageId="Audit" id="App"> <LoggerFields> <KeyValuePair key="category" value="%c"/> <KeyValuePair key="programname" value="%C"/> </LoggerFields> </Syslog> </Appenders> <Loggers> <Logger name="test" level="INFO"> <AppenderRef ref="test"/> </Logger> <Root level="INFO"> <AppenderRef ref="test"/> </Root> </Loggers></Configuration>
3、輸出日誌
(1)先擷取Logger對象
public static Logger LOG = LoggerFactory.getLogger("mytest");
其中參數建議為拓撲名稱。
(2)然後你就可以輕鬆的輸出日誌了
logger.error("INFO ljh_test again!");logger.info("INFO ljh_test message info");logger.debug("INFO ljh_test message debug");logger.warn("INFO ljh_test message warn");
完整代碼如下:https://github.com/lujinhong/log4j2demo
package com.lujinhong.demo.log4j2;import org.slf4j.Logger;import org.slf4j.LoggerFactory;public class Log4j2RsyslogDemo { public static Logger logger = LoggerFactory.getLogger("mytest"); public static void main(String[] args) { System.out.println("Hello World2!"); logger.error("INFO ljh_test again!"); logger.info("INFO ljh_test message info"); logger.debug("INFO ljh_test message debug"); logger.warn("INFO ljh_test message warn"); }}