準備工作
開啟http://logging.apache.org/log4j/,點選左側Download,我下載的是Apache Log4j 2 binary (zip),目前是2.0.2版本。解壓後有30幾個jar包,大部分是跟相容性及移植性相關的可選元件,我們要用的是:
log4j-api-2.0.2.jar
log4j-core-2.0.2.jar
第一個示例程式
log4j2.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <Configuration status="warn"> 3 <Appenders> 4 <Console name="Console" target="SYSTEM_OUT"> 5 <PatternLayout pattern="%m%n" /> 6 </Console> 7 </Appenders> 8 <Loggers> 9 <Root level="INFO"> 10 <AppenderRef ref="Console" /> 11 </Root> 12 </Loggers> 13 </Configuration>
解釋一下Configuration後面的status,這個用於設定log4j2自身內部的資訊輸出,可以不設定,當設定成trace時,你會看到log4j2內部各種詳細輸出。
log4j2配置檔案可以使用XML或JSON,似乎 不再支援properties檔案了。預設的檔名也有所不同,log4j2.xml,不再是log4j.xml。
log4j2.xml可以放在任意的地方,只要你最後把它放到了classpath裡,上面的專案中新建一個resources目錄用於放置log4j2.xml,如果在未加入classpath時嘗試執行時會報如下錯誤:
ERROR StatusLogger No log4j2 configuration file found. Using default configuration: logging only errors to the console.
以Eclipse環境為例,可以在Run—Run Configurations對應專案的Classpath中選擇User Entries,點選Advanced,選擇Add Folder,把resources資料夾新增進來。
Log4jTest.java
1 package mh.sample; 2 3 import org.apache.logging.log4j.LogManager; 4 import org.apache.logging.log4j.Logger; 5 6 public class Log4jTest { 7 static Logger log = LogManager.getLogger( Log4jTest.class.getName()); 8 9 public static void main(String[] args) { 10 log.info("This is an info log."); 11 log.warn("This is a warn log."); 12 } 13 }
需要注意的是,log4j2中要用LogManager.get logger,而不是以前的Logger.getLogger
執行此類,可以看到控制檯輸出兩條日誌資訊:
This is an info log.
This is a warn log.
通過將配置檔案中的level改為”WARN”,可以看到只有一條日誌了:
This is a warn log.
關於level的事就這麼簡單,不多解釋了。
關於pattern,詳見:http://logging.apache.org/log4j/2.x/manual/layouts.html#PatternLayout
比如常用的是
[%-5p] %d %c - %m%n
如果使用此pattern,輸出將類似如下:
[INFO ] 2014-08-29 16:18:15,066 mh.sample.Log4jTest - This is an info log.
[WARN ] 2014-08-29 16:18:15,069 mh.sample.Log4jTest - This is a warn log.
5表示自動將level級別補空格至5個字元
第二個示例程式
Log4j2.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <Configuration status="warn"> 3 <Appenders> 4 <Console name="Console" target="SYSTEM_OUT"> 5 <PatternLayout pattern="[%-5p] %d %c - %m%n" /> 6 </Console> 7 <File name="File" fileName="dist/my.log"> 8 <PatternLayout pattern="%m%n" /> 9 </File> 10 </Appenders> 11 12 <Loggers> 13 <Logger name="mh.sample2.Log4jTest2" level="INFO"> 14 <AppenderRef ref="File" /> 15 </Logger> 16 <Root level="INFO"> 17 <AppenderRef ref="Console" /> 18 </Root> 19 </Loggers> 20 </Configuration>
在配置檔案中我新加了一個Appender,
<File name="File" fileName="dist/my.log">
<PatternLayout pattern="%m%n" />
</File>
另外,新加了一個Logger,
<Logger name="mh.sample2.Log4jTest2" level="INFO">
<AppenderRef ref="File" />
</Logger>
Log4jTest.java
public class Log4jTest { static Logger log = LogManager.getLogger( Log4jTest.class.getName()); public static void main(String[] args) { log.info("This is an info log."); log.warn("This is a warn log."); // Log4jTest2 lt2 = new Log4jTest2(); lt2.printLog(); } }
Log4jTest2.java
1 public class Log4jTest2 { 2 3 static Logger log = LogManager.getLogger( Log4jTest2.class.getName()); 4 5 public void printLog(){ 6 log.info("This is an info log from Log4jTest2."); 7 log.warn("This is a warn log from Log4jTest2."); 8 } 9 }
執行起來,控制檯的輸出是,
[INFO ] 2014-08-29 17:16:39,899 mh.sample.Log4jTest - This is an info log.
[WARN ] 2014-08-29 17:16:39,901 mh.sample.Log4jTest - This is a warn log.
[INFO ] 2014-08-29 17:16:39,903 mh.sample2.Log4jTest2 - This is an info log from Log4jTest2.
[WARN ] 2014-08-29 17:16:39,904 mh.sample2.Log4jTest2 - This a is warn log from Log4jTest2.
專案下dist/my.log檔案中的內容是,
This is an info log from Log4jTest2.
This is a warn log from Log4jTest2.
可以看到,Root總是輸出所有。而新新增的logger負責把Log4jTest2中的日誌寫到指定檔案。這個特性有利於在比較大的專案中對日誌進行分類。
需要注意的一點是,log4j存在一個“反直覺”的行為,嘗試把Root的level改為“WARN”,重新執行,控制檯的輸出是:
[WARN ] 2014-08-29 17:24:06,788 mh.sample.Log4jTest - This is a warn log.
[INFO ] 2014-08-29 17:24:06,793 mh.sample2.Log4jTest2 - This is an info log from Log4jTest2.
[WARN ] 2014-08-29 17:24:06,794 mh.sample2.Log4jTest2 - This is a warn log from Log4jTest2.
Log4jTest2中的info級別的log還是被輸出了,也就是,只要某個logger接受了該log請求,那為作為父親的Root就會跟著接受此log請求,而不再考慮它的level
順帶介紹additivity屬性,
<Logger name="mh.sample2.Log4jTest2" level="INFO" additivity="false">
作了上面的修改,再執行程式,控制檯的輸出:
[WARN ] 2014-08-29 17:31:17,388 mh.sample.Log4jTest - This is a warn log.
所以additivity實際上中止了log請求向上級傳播,這導致了Log4jTest2中的日誌被Logger name="mh.sample2.Log4jTest2" “截留”。
常用Appender之RollingFile
Log4j2.xml
<Configuration status="warn"> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="[%-5p] %d %c - %m%n" /> </Console> <File name="File" fileName="dist/my.log"> <PatternLayout pattern="%m%n" /> </File> <RollingFile name="RollingFile" fileName="dist/my2.log" filePattern="dist/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz"> <PatternLayout pattern="[%-5p] %d %c - %m%n" /> <Policies> <TimeBasedTriggeringPolicy /> <SizeBasedTriggeringPolicy size="25 KB" /> </Policies> <DefaultRolloverStrategy max="20"/> </RollingFile> </Appenders> <Loggers> <Logger name="mh.sample2.Log4jTest2" level="INFO" additivity="false"> <AppenderRef ref="File" /> <AppenderRef ref="RollingFile" /> </Logger> <Root level="WARN"> <AppenderRef ref="Console" /> </Root> </Loggers> </Configuration>
上述RollingFile的配置將把日誌內容追加到dist/my2.log檔案中,每當大小達到設定的25KB,就會按filePattern的規則備份到dist目錄下的子目錄中,由於當前是2014年9月3號,該子目錄名稱是2014-09,裡面的檔案則是app-09-03-2014-1.log.gz,app-09-03-2014-2.log.gz,app-09-03-2014-3.log.gz,……,從這還可以看出,log4j2自動按你配置的檔名進行gzip壓縮。
DefaultRolloverStrategy屬性如不設定,則預設為最多同一資料夾下7個檔案,這裡設定了20.
常用Appender之Syslog
log4j2中對syslog的簡單配置,這裡就不重複展示log4j2.xml了:
<Syslog name="SYSLOG" host="localhost" port="514" protocol="UDP" facility="LOCAL3"/>
host是指你將要把日誌寫到的目標機器,可以是ip(本地ip或遠端ip,遠端ip在實際專案中很常見,有專門的日誌伺服器來儲存日誌),也可以使用主機名,如果是本地,還可以使用localhost或127.0.0.1
Port指定埠,預設514,參見/etc/rsyslog.conf(以Fedora系統為例,下同)。protocol指定傳輸協議,這裡是UDP,facility是可選項,後面可以看到用法,具體關於facility的規則可以點選:http://logging.apache.org/log4j/2.x/manual/appenders.html#SyslogAppender
Syslog及Syslog-ng相關配置(Fedora)
在執行程式之前,需要修改
/etc/rsyslog.conf
把這兩行前的#去掉,即取消註釋:
#$ModLoad imudp
#$UDPServerRun 514
這裡啟用udp監聽,514是預設監聽埠,重啟syslog:
service syslog restart
大部分日誌會預設寫到/var/log/messages中,如果不想寫到這個檔案裡,可以按下面修改,這樣local3的日誌就會寫到app.log中。這裡的local3即 log4j2.xml中facility的配置。
*.info;mail.none;authpriv.none;cron.none;local3.none /var/log/messages
新增一行
local3.* /var/log/app.log
除了使用自帶的syslog,我們也可以使用syslog的替代品,比如syslog-ng,這對於log4j2.xml配置沒有影響。
安裝:
yum install syslog-ng
啟動:
service syslog-ng start
其配置檔案為
/etc/syslog-ng/syslog-ng.conf
啟動前把source一節中這一行取消註釋即可:
#udp(ip(0.0.0.0) port(514));
這個埠會和syslog衝突,可以使用別的埠比如50014,同時修改log4j2.xml中的port屬性。另外提一下,使用非預設埠,要求log4j版本在1.2.15或以上。
syslog-ng本身也可以設定把日誌送到遠端機器上,
在源機器上的syslog-ng.conf中新增:
destination d_remote1 { udp(153.65.171.73 port(514));};
這表示源機器上的syslog-ng會把接收到的日誌送到遠端主機153.65.171.73的514埠上,只要保證153.65.171.73上的syslog-ng正常執行並監聽對應的埠即可。
送書了,送書了,關注公眾號“程式設計師雜書館”,送出O'Reilly《Spark快速大資料分析》紙質書(亦有一批PDF分享)! —— 2018年12月