JAVA日志系统
JAVA日志系統(tǒng)
@(JAVA)[java, 大數(shù)據(jù)]
- JAVA日志系統(tǒng)
- 一slf4j
- 一最簡(jiǎn)單示例
- 二常用示例
- 三其它示例
- 四一些注意事項(xiàng)
- 二log4j
- 一使用java向rsyslog發(fā)送日志
- 基本使用方法
- 不使用配置文件
- 一使用java向rsyslog發(fā)送日志
- 三logging
- 四使用slf4jlog4j2向rsyslog發(fā)送日志
- 一rsyslog配置
- 1在etcrsyslogd中新建stormconf
- 2修改etcrsyslogconf
- 二java程序中輸出日志
- 1添加依賴
- 2準(zhǔn)備配置文件
- 3輸出日志
- 一rsyslog配置
java有大量的框架用于日志輸出,常見的包括slf4j, log4j, logback, logging等.
一、slf4j
slf4j只是一個(gè)門面(facet),它不包含具體的實(shí)現(xiàn),而是將一些log4j,java.logging等實(shí)現(xiàn)包裝成統(tǒng)一的接口。commons-logging和slf4j都是日志的接口,供用戶使用,而沒(méi)有提供實(shí)現(xiàn)!
log4j,logback等等才是日志的真正實(shí)現(xiàn)。
當(dāng)我們調(diào)用接口時(shí),接口的工廠會(huì)自動(dòng)尋找恰當(dāng)?shù)膶?shí)現(xiàn),返回一個(gè)實(shí)現(xiàn)的實(shí)例給我服務(wù)。這些過(guò)程都是透明化的,用戶不需要進(jìn)行任何操作!
這里有個(gè)小故事,當(dāng)年Apache說(shuō)服 log4j以及其他的日志來(lái)按照commons-logging的標(biāo)準(zhǔn)編寫,但是由于commons-logging的類加載有點(diǎn)問(wèn)題,實(shí)現(xiàn)起來(lái)也不友 好,因此log4j的作者就創(chuàng)作了slf4j,也因此而與commons-logging兩分天下。至于到底使用哪個(gè),由用戶來(lái)決定吧。
這樣,slf4j出現(xiàn)了,它通過(guò)簡(jiǎn)單的實(shí)現(xiàn)就能找到符合自己接口的實(shí)現(xiàn)類,如果不是滿足自己標(biāo)準(zhǔn)的日志,可以通過(guò)一些中間實(shí)現(xiàn)比如上面的slf4j-log4j12.jar來(lái)進(jìn)行適配。
好,言歸正傳,如何使用slf4j?
(一)最簡(jiǎn)單示例
最簡(jiǎn)單的情況是slf4j-simple提供的簡(jiǎn)單實(shí)現(xiàn),功能非常簡(jiǎn)單的,但使用非常方便。
1、pom.xml添加依賴
2、使用slf4j
private static Logger LOG = LoggerFactory.getLogger(RedisSlotCRC16Demo.class); LOG.info("Mean time to calculate 1000 slot is: " + getIntervalInMill(beginTime));(二)常用示例
1、在pom.xml中添加以下內(nèi)容
<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、創(chuàng)建log4j.properties文件,詳細(xì)配置見http://www.cnblogs.com/lujinhong2/p/4637219.html
注意文件必須在config目錄下(對(duì)于maven項(xiàng)目,log4j.properties位于src/main/resources下面)
### set log levels ### log4j.rootLogger = debug , stdout , D , E### 輸出到控制臺(tái) ### log4j.appender.stdout = org.apache.log4j.ConsoleAppender log4j.appender.stdout.Target = System.out log4j.appender.stdout.layout = org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern = %d{ABSOLUTE} %5p %c{1}:%L - %m%n### 輸出到日志文件 ### log4j.appender.D = org.apache.log4j.DailyRollingFileAppender log4j.appender.D.File = logs/log.log log4j.appender.D.Append = true ## 輸出DEBUG級(jí)別以上的日志 log4j.appender.D.Threshold = DEBUG log4j.appender.D.layout = org.apache.log4j.PatternLayout log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n### 保存異常信息到單獨(dú)文件 ### log4j.appender.E = org.apache.log4j.DailyRollingFileAppender## 異常日志文件名 log4j.appender.E.File = ./error.log log4j.appender.E.Append = true ## 只輸出ERROR級(jí)別以上的日志!!! log4j.appender.E.Threshold = ERROR log4j.appender.E.layout = org.apache.log4j.PatternLayout log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %l:%c:%t:%r ] - [ %p ] %m%n3、在java文件中調(diào)用
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) {// 使用此變量會(huì)生成文件成功String fileName = "1.txt";// 使用此變量會(huì)生成文件失敗// 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());}}}(三)其它示例
首先,項(xiàng)目中必須要包括slf4j-api.jar,此外,還應(yīng)該包括slf4j為具體實(shí)現(xiàn)所提供的適配器(如slf4j-log4j12.jar),以及那個(gè)具體實(shí)現(xiàn)的jar包(如log4j-1.**.jar)。
我們以以下代碼為例:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Slf4jDemo {
private static final Logger LOG = LoggerFactory.getLogger(Slf4jDemo.class);
}
由于所使用的具體實(shí)現(xiàn)不同,日志輸出也有不同的結(jié)果。這也反應(yīng)了通過(guò)使用slf4j,使得可以方便的替換日志系統(tǒng)。
1、Slf4j-simple
slf4j自帶的一個(gè)簡(jiǎn)單實(shí)現(xiàn),可用于小項(xiàng)目中,但無(wú)法配置日志級(jí)別等。
官方文檔中的描述為: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.
在項(xiàng)目的build_path中加入slf4j-1.6.6.jar與slf4j-simple-1.6.6.jar。
輸出結(jié)果如下:
2 [main] ERROR Slf4jDemo - Error Message! 2 [main] WARN Slf4jDemo - Warn Message! 2 [main] INFO Slf4jDemo - Info Message!2、slf4j-jdk
使用jkd自帶的日志系統(tǒng),在項(xiàng)目的build_path中加入slf4j-1.6.6.jar與slf4j-jdk14-1.6.6.jar。
輸出結(jié)果如下:
二月 16, 2015 11:09:36 下午 Slf4jDemo main 嚴(yán)重: Error Message! 二月 16, 2015 11:09:36 下午 Slf4jDemo main 警告: Warn Message! 二月 16, 2015 11:09:36 下午 Slf4jDemo main 信息: Info Message!3、slf4j-log4j
log4j是目前用得最多的日志系統(tǒng),它更適用于大型項(xiàng)目。
在項(xiàng)目的build_path中加入slf4j-1.6.6.jar與slf4j-log4j-1.6.6.jar,以及l(fā)og4j的具體實(shí)現(xiàn),如log4j-1.2.16.jar。
輸出結(jié)果如下:
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.可以通過(guò)配置文件配置輸出日志的級(jí)別。
(四)一些注意事項(xiàng)
1、注意build_path中不能有多個(gè)日志實(shí)現(xiàn),否則會(huì)導(dǎo)致slf4j不知道該使用哪個(gè)實(shí)現(xiàn)(各個(gè)系統(tǒng)加載classpath的順序會(huì)有差異的),從而出現(xiàn)以下錯(cuò)誤
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、有個(gè)很大的坑:
由于很多項(xiàng)目都會(huì)使用log4j作為日志框架,而在一個(gè)項(xiàng)目的引用包中的log4j配置文件有可能將自己定義的log4j.properties文件覆蓋。
如果發(fā)現(xiàn)自己的log4j配置文件不生效,則使用:
java -Dlog4j.debug 主類
來(lái)檢查目前加載了哪個(gè)配置文件,然后將其刪除即可。
另外,即使使用了
PropertyConfigurator.configure(“l(fā)og4j2.properties”);
指定配置文件,也有可能被第三方j(luò)ar包使用同樣語(yǔ)句覆蓋了。
二、log4j
log4j有2個(gè)版本,1.x和2.x,2.x較為完善,但是目前大家都習(xí)慣了用1.x,而且很多開源項(xiàng)目都使用了1.x,所以一般使用1.x就可以了。
關(guān)于2.x的使用,請(qǐng)參考http://www.cnblogs.com/lujinhong2/p/4637295.html
1.x要注意版本的問(wèn)題,一般使用1.2.17配合slfj的1.7.12版本即可,只要不相差太多都沒(méi)問(wèn)題。
(一)使用java向rsyslog發(fā)送日志
基本使用方法
1、開通rsyslog遠(yuǎn)程UDP訪問(wèn)
vi /etc/rsyslog.conf
將下面兩段前面的#號(hào)去掉
#$ModLoad imudp #$UDPServerRun 514如果原來(lái)沒(méi)有話則加上這2行
2,建立存放log的文件地址
vi /etc/rsyslog.conf
加入以下兩段
3,開通防火墻
將UDP端口514對(duì)外開放出來(lái)
4,重啟rsyslog
service rsyslog restart
5,log4j的配置
將日志同時(shí)輸出到syslog和console
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,運(yùn)行應(yīng)用程序,可以在/var/log下看到不同級(jí)別的日志信息,如login_info.log和login_debug.log
不使用配置文件
有時(shí)候,log4j的配置文件會(huì)互相覆蓋,真的很煩,因此可以將配置寫到代碼中去
1、定義一個(gè)單例類,將相關(guān)配置寫入:
2、在需要日志輸出的類中添加以下語(yǔ)句
private static Logger LOG = MySysLogger.getInstance();然后就可以使用LOG變量進(jìn)行日志輸出了
吐槽幾句,log4j的坑啊….
(1)CLASSPATH中不能有多個(gè)log4j的版本本,否則有有奇形怪狀的NoSuchMethod, NoSuchFiled, NoClassDefineFound等異常。明明是太多了,還告訴你沒(méi)有
(2)與slf4j的搭建,必須版本一致,如slf4j-1.7.2對(duì)應(yīng)log4j-1.2.17
(3)配置文件啊,如果你引用的第三方包有l(wèi)og4j.properties,而又沒(méi)有提供給你編輯,那恭喜你,慢慢調(diào)吧。把log4j的配置寫入代碼吧,不要用配置文件了
(4) 如果你打算只使用log4j,而不使用slf4j作包裝,切記classpath中只能有l(wèi)og4j,不能有l(wèi)og4j-over-slf4j-1.7.12.jar這種包,不然會(huì)出現(xiàn)各種各樣的錯(cuò)誤,如:
NoSuchMethod, NoSuchFiled, NoClassDefineFound, IncompatibleClassChangeError: Implementing class
三、logging
直接一個(gè)示例
(1)在你的代碼中增加一個(gè)類,代碼見后面。一般情況下只需要修改輸出日志的文件名稱就可以了。
(2)在任何需要輸出日志的類中添加一個(gè)變量,private static Logger LOG = MyLogger.getInstance();
然后就可以使用LOG變量來(lái)做日志輸出了。
說(shuō)明一下:
1、storm從0.9.1后使用的日志架構(gòu)是slf4j+logbakc,取代了原先的log4j。我們嘗試使用log4j作日志框架,發(fā)現(xiàn)log4j.properties被storm某個(gè)jar包里面的配置覆蓋了,導(dǎo)致自己定義的log4j.properties怎么都不生效,然后嘗試logback也有類似問(wèn)題,定義的cluster.xml沒(méi)有給出worker進(jìn)程的日志配置。
2、使用java.util.logging配置文件也可以,便在分布式系統(tǒng)中的配置加載比較復(fù)雜,而且java.util.logging很坑,很容易寫錯(cuò),比如每一行的最后都不能有空格等。
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”;
//日志級(jí)別
private static Level level = Level.INFO;
//每個(gè)日志文件的大小,單位為M。
private static int logFileSize = 100;
//保存日志文件的數(shù)據(jù)
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發(fā)送日志
(一)rsyslog配置
1、在/etc/rsyslog.d中新建storm.conf
內(nèi)容如下:
$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;stromformat local7.* ~2、修改/etc/rsyslog.conf
修改以下3行:
*.*;auth,authpriv.none;cron.none,local7.none -/var/log/syslog kern.*,local7.noe -/var/log/kern.log *.=info;*.=notice;*.=warn;\auth,authpriv.none;\cron,daemon.none;\mail,news.none,local7.none -/var/log/messages聲明local7的日志不會(huì)發(fā)送到syslog, kern.log,message。否則日志量太大會(huì)撐爆。
此時(shí)rsyslog就已經(jīng)配置完成,它會(huì)將發(fā)送至local7的日志解釋后放到/home/data/log中。
(二)java程序中輸出日志
1、添加依賴
以maven為例,添加以下內(nèi)容:
<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>其它編譯方式則添加相應(yīng)的jar包。
2、準(zhǔn)備配置文件
在resources目錄下創(chuàng)建log4j2.xml,內(nèi)容如下:
<?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對(duì)象
public static Logger LOG = LoggerFactory.getLogger("mytest");其中參數(shù)建議為拓?fù)涿Q。
(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");} }總結(jié)
- 上一篇: spark之1:快速入门
- 下一篇: Windows10+GPU版 pytor