讓System.out.println回家種田,換句話說,就是該幹嘛幹嘛去。
您可能在想: System.out.println幾乎在每個Java程式裡都有那麼幾行,如何讓他老人家回家種田呢? 我們怎麼能少了這麼重要的革命同志呢?
doodoofish這裡要說的是"該幹嘛幹嘛去",不是System.out.println管的就別讓他管。想想,我們用System.out.println (文中稱SOP,不是Service Oriented Programming,是System.out.println) 主要在三種情況下:
一、輸出文本到stdout,作為輸出結果;
二、顯示調試資訊,用來Debug;
三、顯示資訊,讓管理員觀賞
第一種情況,沒得說,是SOP的老本行,不服不行。讓他就繼續幹吧。
第二、三種情況,就不該SOP管,讓他管也管不好。如果我調試用SOP來顯示資訊,一旦哪天我決定不要顯示這些煩人的東西了,怎麼辦? 好辦啊,一個檔案一個檔案找,一行一行找,把SOP給Comment起來不就完了,多方便啊。可是哪天我又要顯示這些調試資訊了,又怎麼辦? 第二天我又不要了,下個月我又要了... ... 我們程式員閑得慌嗎? 可有人就是認為我們很閑,你的老闆可能就是一個。我的朋友5bug是個天才,他說有辦法,就是每個SOP前加一個 if 來判斷是否顯示。天才啊!
我比較笨,比郭靖還笨。我就沒想到這個法子,一直沒想出來,直到上10個星期,看到一篇介紹Log4J的文章。一開始看不懂 (我比較笨) ,倒過來看一遍總算懂了。
說起來又是一個類庫,在http://logging.apache.org/site/binindex.cgi這裡下載。然後就把那個log4j1.2.8.jar從"dist/lib"目錄下copy到我的項目LogTest的"lib"目錄下 (真煩,copy檔案是很煩人的操作,需要一定的技術)。然後,我就寫了個簡單的class (複雜的我不會寫) 來調用log4j的東東。
import
org.apache.log4j.*;
public
class LogTest {
static Logger logger = Logger.getLogger(LogTest.class.getName());
public static void main(String[] args) {
logger.debug("Debug ...");
logger.info("Info ...");
logger.warn("Warn ...");
logger.error("Error ...");
}
}
沒什麼新鮮,就是個Logger。那"static Logger logger = Logger.getLogger(LogTest.class.getName());" 就是建立一個屬於LogTest類的Logger對象,建立時要告知Logger你當前的Class是什麼,所以就有了"LogTest.class.getName()"這奇怪的東東。
logger.debug就是輸出debug的資訊
logger.info就是輸出提示資訊
logger.warn就是顯示警告資訊
logger.error就是顯示錯誤資訊
log是什麼? Log是個很深奧的古希臘名詞,不貶義,也不褒義。說簡單了,就是"日誌",就是你要寫什麼就寫什麼,要寫哪裡就是哪裡。不同與SOP,log4j可以讓你把要寫的東西分成4級 (其實有更多級,你可以自己定義,比如叫: 三級++),這些層級叫優先權 (Priority),這裡四個優先權就是debug, info, warn, 和error,優先權從低到高,log4j能讓你控制顯示哪些優先權的資訊。log4j也讓你要寫哪就哪,可以是螢幕,可以是一個檔案,甚至是一個Email,一個XML,一個Socket,等等。這些控制都在一個小檔案裡,叫log4j.properties (其實你可以用其它名字,這裡是預設名)。這個檔案在工程目錄下。以下是log4j.properties的內容:
#### Use one appender to log to console
log4j.rootCategory=DEBUG, stdout
#### Appender writes to console
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p (%F:%L) - %m%n
就是說我要顯示所有優先權等於和高於Debug的資訊,所以上面那個Class會顯示Debug ...,Info ...,Warn ...,Error ...所有資訊。
"stdout"表示我定義了一個輸出端,叫stdout (隨便什麼名字都好)。
下面的三行說,這個stdout輸出端其實是標準輸出Console,也就是螢幕。輸出的格式是Pattern。轉換方式是%5p (%F:%L) - %m%n,即前五格用來顯示優先權,再顯示當前的檔案名稱,加當前的行數。最後是logger.debug()或logger.info()或logger.warn()或logger.error()裡的資訊。%n表示斷行符號空行。
運行程式,最後輸出的是:
DEBUG (LogTest.java:9) - Debug ...
INFO (LogTest.java:10) - Info ...
WARN (LogTest.java:11) - Warn ...
ERROR (LogTest.java:12) - Error ...
搞了半天,最後就輸出這麼個東西? 對啊。有人就說了,"還不如System.out.println呢"。不過你看看,你做了些什麼。
一、下載了log4j,copy類庫到工程的lib目錄
二、寫一個log4j.properties檔案 (4行)
三、在Class裡加了"static Logger logger = Logger.getLogger(LogTest.class.getName());"一句話
四、用logger.xxx來輸出資訊。
完了。你得到的若干好處是:
一、在log4j.properties檔案裡把"log4j.rootCategory=DEBUG, stdout"改寫成"log4j.rootCategory=OFF, stdout",這樣所有的log資訊都不會顯示了;
二、在log4j.properties檔案裡把"log4j.rootCategory=DEBUG, stdout"改寫成"log4j.rootCategory=INFO, stdout",這樣只顯示INFO, WARN, ERROR的log資訊,而DEBUG資訊不會被顯示;
三、在log4j.properties檔案裡把"log4j.rootCategory=DEBUG, stdout"改寫成"log4j.rootCategory=DEBUG, stdout, R",再加上下面三句話:
log4j.appender.R=org.apache.log4j.RollingFileAppender
log4j.appender.R.File=log.txt
log4j.appender.R.MaxFileSize=100KB
log4j.appender.R.MaxBackupIndex=1
log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%d{yyyy MMM dd HH:mm:ss} %-5p %c - %m%n
這樣,你的log資訊不光顯示在螢幕上,而且將被儲存在一個叫"log.txt"的檔案裡,檔案最大為100KB。如果檔案大小超過100KB,檔案會被備份成"log.txt.1",新的"log.txt"繼續記錄log資訊。
你可以改變log4j.properties,而不需重新編譯就可以控制log資訊是否顯示、log資訊的輸出端類型、輸出方式、輸出格式,等等。你上面的四步工作帶來這麼多好處,我還要System.out.println來顯示log資訊嗎? 不需要了。
以下是我LogTest項目的目錄結構:
build.xml檔案的內容:
<project
name="Log4j Test" default="build" basedir=".">
<property name="app.home" value="${basedir}" />
<property name="app.src" value="${app.home}/src" />
<property name="app.bin" value="${app.home}/bin" />
<property name="app.lib" value="${app.home}/lib" />
<path id="classpath">
<fileset dir="${app.lib}">
<include name="**/*.jar" />
</fileset>
<path location="${app.home}"/>
<path location="${app.bin}"/>
</path>
<target name="init">
<mkdir dir="${app.bin}"/>
</target>
<target name="build" depends="init" description="compile the source " >
<javac srcdir="${app.src}" destdir="${app.bin}">
<classpath refid="classpath"/>
</javac>
</target>
<target name="run" description="run">
<java classname="LogTest" dir="${app.bin}" fork="true">
<classpath refid="classpath"/>
</java>
</target>
<target name="clean" description="clean up" >
<delete dir="${app.bin}" />
</target>
</project>
總共四個檔案,一個用來測試的LogTest類,一個build.xml ANT檔案,一個log4j.properties配製檔案,一個log4j-1.2.8.jar類庫。
log4j的功能有很多,doodoofish這裡就不詳細介紹了,以下是參考文獻:
Don't Use System.out.println! Use Log4j by Vipan Singla
Build Flexible Logs With log4j by Vikram Goyal
log4j by Ashley J.S Mills, University Of Birmingham
Add logging to your Java Applications by Kevin Brown
How does the Java logging API stack up against log4j? by Kevin Brown
OpenSymphony Logging Primer
log4j FAQ at jGuru