日志是程式員居家旅行必備,哦不對,是定位問題,修複bug,甚至是驗證應用是否正常的必備利器。甚至很多時候,我們做一次部署僅僅是為了加一行log。雖然現在有各種各樣的問題診斷工具,但是在定位線上問題時,我們很多時候還是希望有列印良好的log。
列印良好的log很重要,但是知道我們需要的log在哪裡也很重要。因為各種各樣的原因,我們經常會将log打到不同的檔案中,這樣就導緻了出問題找幾個日志檔案的情況。
這不,預設情況下tomcat就會生成幾個日志檔案:catalina.out, catalina.{yyyy-mm-dd}.log, localhost.{yyyy-mm-dd}.log。(嗯,這裡說的是預設情況下,這些都是可以配置修改的)。
這幾個不同的日志檔案裡的内容也不盡相同,查問題也要看不同的日志檔案,如果沒找到檔案,甚至都無法了解真正的問題是什麼。
我們先來看看這幾個日志都是怎麼産生的,然後來了解一下什麼樣子的東西會出現在哪個日志檔案。
catalina.out
catalina.out其實是tomcat的标準輸出(stdout)和标準出錯(stderr),這是在tomcat的啟動腳本裡指定的,如果沒有修改的話stdout和stderr會重定向到這裡。是以我們在應用裡使用system.out列印的東西都會到這裡來。另外,如果我們在應用裡使用其他的日志架構,配置了向console輸出的,則也會在這裡出現。比如以logback為例,如果配置ch.qos.logback.core.consoleappender則會輸出到catalina.out裡。
cataliana.{yyyy-mm-dd}.log和localhost.{yyyy-mm-dd}.log
這兩個日志都是通過logging.properties配置的(預設情況下,啟動腳本裡指定了java.util.logging.config.file和java.util.logging.manager兩個變量)。一個典型的logging.properties可能如下所示:
handlers = 1catalina.org.apache.juli.filehandler, 2localhost.org.apache.juli.filehandler, java.util.logging.consolehandler
.handlers = 1catalina.org.apache.juli.filehandler, java.util.logging.consolehandler
1catalina.org.apache.juli.filehandler.level = info # 這裡推薦改為 off,關閉這個日志記錄。
1catalina.org.apache.juli.filehandler.directory = ${catalina.base}/logs # 推薦注釋
1catalina.org.apache.juli.filehandler.prefix = catalina. # 推薦注釋
2localhost.org.apache.juli.filehandler.level = fine
2localhost.org.apache.juli.filehandler.directory = ${catalina.base}/logs
2localhost.org.apache.juli.filehandler.prefix = localhost.
java.util.logging.consolehandler.level = info
java.util.logging.consolehandler.formatter = java.util.logging.simpleformatter
org.apache.catalina.core.containerbase.[catalina].[localhost].level = info
org.apache.catalina.core.containerbase.[catalina].[localhost].handlers = 2localhost.org.apache.juli.filehandler
這個檔案大緻的意思是,root輸出到catalina和console。而這裡的catalina按照配置對應的是catalina.{yyyy-mm-dd}.log,這裡的console最終會輸出到catalina.out。這就是我們看到catalina.{yyyy-mm-dd}.log和catalina.out的日志很多都是一樣的原因。
配置檔案中還有一個localhost,所有logname或parent logname為org.apache.catalina.core.containerbase.[catalina].[localhost]的都會輸出到localhost.{yyyy-mm-dd}.log檔案。而這個logname又代表着什麼呢?在tomcat中有一個server.xml的配置檔案,其中有這麼一個片段:
<engine name="catalina" defaulthost="localhost">
<host name="localhost" appbase="webapps"
unpackwars="false" autodeploy="false">
</host>
</engine>
我們可以這麼簡單的了解: 一個tomcat程序對應着一個engine,一個engine下可以有多個host(virtual host),一個host裡可以有多個context,比如我們常常将應用部署在root下還是webapps裡其他目錄,這個就是context。
這其中engine對應着tomcat裡的standardengine類,host對應着standardhost類,而context對應着standardcontext。這幾個類都是從containerbase派生。這些類裡打的一些跟應用代碼相關的日志都是使用containerbase裡的getlogger,而這個這個logger的logger name就是: org.apache.catalina.core.containerbase.[current container name].[current container name]...
而我們一個webapp裡listener, filter, servlet的初始化就是在standardcontext裡進行的,比如root裡有一個listener初始化出異常了,列印日志則logger name是org.apache.catalina.core.containerbase.[catalina].[localhost].[/]。這其中catalina和localhost是上面xml片段裡的engine和host的name,而[/]是root對應的standardcontext的name。是以listener, filter, servlet初始化時的日志是需要看localhost.{yyyy-mm-dd}.log這個日志的。比如現在我們使用spring,spring的初始化我們往往是使用spring提供的一個listener進行的,而如果spring初始化時因為某個bean初始化失敗,導緻整個應用沒有啟動,這個時候的異常日志是輸出到localhost中的,而不是cataina.out中。是以有的時候我們應用無法啟動了,然後找catalina.out日志,但最後也沒有定位根本原因是什麼,就是因為我們找的日志不對。但有的時候catalina.out裡也有我們想要的日志,那是因為我們的應用或使用的一些元件自己捕獲了異常,然後将其列印了,這個時候如果恰好這些日志被我們配置成輸出到console,則這些日志也會在catalina.out裡出現了。
總結
那麼總結起來,catalina.out即标準輸出和标準出錯,所有輸出到這兩個位置的都會進入catalina.out,這裡包含tomcat運作自己輸出的日志以及應用裡向console輸出的日志。catalina.{yyyy-mm-dd}.log是tomcat自己運作的一些日志,這些日志還會輸出到catalina.out,但是應用向console輸出的日志不會輸出到catalina.{yyyy-mm-dd}.log。localhost.{yyyy-mm-dd}.log主要是應用初始化(listener, filter, servlet)未處理的異常最後被tomcat捕獲而輸出的日志,而這些未處理異常最終會導緻應用無法啟動。
最後想想,這裡分幾個日志檔案其實不利于問題查找,為啥不幹脆都輸出到catalina.out裡呢?我想tomcat作為通用容器本身,可能考慮到engine下有多個host,每個host的日志還是要輸出到不同的檔案。而實際中我們往往是單容器,單host,甚至是隻有一個root的context。是以對于這種情況,我覺得是可以将所有日志都輸出到catalina.out友善查問題,特别是那些還不知道初始化失敗應該去看localhost日志的同學。嗯,可以和運維商量一下。