項目背景
1 因為項目需要,需要對客戶端的一個控件做訪問日志記錄。用以統計頁面過去七天的平均數量。項目中一直使用Log4j1,這次升級使用Log4j2。準備將用戶的登錄id和設備id單獨記錄在一個日志文件中,每天生成一份文件,保留七天。
2 項目中使用Jetty作為web容器,Jetty將統一打印日志。但是這次的特殊統計,需要單獨設置一個logger對象。輸出到單獨的日志文件。
第一部分: 原有的日志流程總結
1. log4j的配置方法
線上的日志應該是jetty打印的,log4j采用了SYSTEM_OUT的輸出,沒有指定。但是配置了具體的日志的格式和方法:
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="[%date{yyyy-MM-dd HH:mm:ss.SSS}][%thread][%level][%class][%line]:%message%n"/>
</Console>
</Appenders>
說明:
- Console 的target是SYSTEM_OUT是輸出到統一的輸出流,沒有指定日志文件
2. jetty的日志配置方法
jetty的啟動配置文件xml在 jetty.xml
<New id="ServerLog" class="java.io.PrintStream">
<Arg>
<New class="org.eclipse.jetty.util.RolloverFileOutputStream">
<Arg>
<SystemProperty name="jetty.logs" default="./logs" />/<SystemProperty name="jetty.appkey" default="jetty" />.log.yyyy_mm_dd
</Arg>
<Arg type="boolean">true</Arg>
<Arg type="int">10</Arg>
<Arg>
<Call class="java.util.TimeZone" name="getTimeZone">
<Arg>GMT+8</Arg>
</Call>
</Arg>
<Arg type="string">yyyy-MM-dd</Arg>
<Arg type="string"></Arg>
<Get id="ServerLogName" name="datedFilename" />
</New>
</Arg>
</New>
<Call class="org.eclipse.jetty.util.log.Log" name="info">
<Arg>Redirecting stderr/stdout to <Ref id="ServerLogName"/></Arg>
</Call>
<Call class="java.lang.System" name="setErr">
<Arg>
<Ref id="ServerLog"/>
</Arg>
</Call>
<Call class="java.lang.System" name="setOut">
<Arg>
<Ref id="ServerLog"/>
</Arg>
</Call>
同時具體的參數配置在 boot.init
和 mms啟動文件之中: 文件配置之中:
JVM_ARGS="-server -Dfile.encoding=UTF-8 -Dapp.key=apollo-item -Dsun.jnu.encoding=UTF-8 -Djava.io.tmpdir=/tmp -Djava.net.preferIPv6Addresses=false -Duser.timezone=GMT+08 -Djava.util.prefs.systemRoot=/home/sankuai/.java -XX:-LoopUnswitching -XX:-OmitStackTraceInFastThrow"
在mms的啟動文件之中的代碼。說明(mms文件時 線上的jetty啟動配置文件)
EXEC_JAVA=$EXEC_JAVA" -Djetty.appkey=$MODULE -Djetty.context=$CONTEXT -Djetty.logs=$LOG_ROOT"
LOG_ROOT=/opt/logs/mobile
日志打印流程說明
配置文件log4j.xml 中的
<Console name="Console" target="SYSTEM_OUT">
表示 log4j2將日志配置到System.out輸入到控制到輸出流。Jetty中對于所有的控制臺輸出流統一進行處理,有一個
ServerLog
的配置部分代表將所有的
控制臺輸出流統一輸出到文件之中,使用到了org.eclipse.jetty.util.RolloverFileOutputStream
類,這個類的定義式:
RolloverFileOutputStream This output stream puts content in a file that is rolled over every 24 hours.
代表將輸出流放到一個每24小時產生一個新的文件的日志之中。然后具體的文件輸出地址與日志打印的格式都在
<Arg>
標簽中進行了詳細的配置。相應的參數從jetty的啟動命令中獲取。語句<SystemProperty name="jetty.logs" default="./logs" />/<SystemProperty name="jetty.appkey" default="jetty" />.log.yyyy_mm_dd
代表了日志的輸出文件地址。
第二部分 - 新的日志配置
1 項目背景:
原有的項目中所有的日志都是統一到一個日志文件中,但是產品臨時需要對一個功能進行統計分析,需要進行單獨的日志處理。這時就需要設計一個新的日志對象并增加一個新的日志輸出流,從其他的統一日志中進行區分,以用于單獨的統計與分析功能。具體的配置過程如下ji:
2 具體配置
配置appender(日志輸出源)
#配置一個新的appender(輸出流 - 直接打到文件輸出流)
<RollingFile name="Daijia" fileName="/opt/logs/mobile/apolloitem/daijia"
filePattern="'.'yyyy-MM" Append="true" >
<PatternLayout pattern="%-5p:%d:%c{1} [%x] - %m%n"/>
<Policies>
<SizeBasedTriggeringPolicy size="16 MB"/>
</Policies>
<DefaultRolloverStrategy fileIndex="min" max="16"/>
</RollingFile>
說明
- RollingFile代表新增加一個appender(輸出源),日志名稱是Daijia, fileName代表的則是 日志文件的輸出地點,這里采用了絕對路徑,也可以采用相對路徑。
配置一個日志對象
<logger name="com.meituan.apollo.item.filter.util.VListResultModelFilterUtil" level="info" additivity="false">
<appender-ref ref="Daijia" />
</logger>
在Root里面配置新的AppendRef
配置根日志
<Root level="INFO">
<AppenderRef ref="Console"/>
#if($environment == 'online')
<AppenderRef ref="Sentry"/>
#end
# <AppenderRef ref="Daijia"/>
</Root>
說明
- Root里面都代表日志的根日志,所有其他的日志都集成來自Root,如果把一個Appender放到<Root>的根日志配置下面,則所有的log對象都會輸出到這些源頭。我們想單獨記錄記錄E代駕的日志,所以就不把代駕的輸出源放到根日志下面了.
3 配置log4j中應該注意的地方(或者說遇到的坑)
1 log4j遷移的問題
1)問題
2015-11-02 17:38:02,742 ERROR Appenders contains an invalid element or attribute "appender"
2015-11-02 17:38:02,746 ERROR Unable to locate appender Daijia for logger
2015-11-02 17:38:02,746 ERROR Unable to locate appender Daijia for logger com.meituan.apollo.item.controller.IndexControlle
2)原因與解決辦法
說appender
這個標簽在log4j.xml中是違法的,第一次出現這個問題的時候很納悶,因為這是從網上復制拷貝的配置語法。但是仔細深入發現之后,問題在于<appender>標簽是屬于log4j 1的版本,升級到log4j2之后,不再支持原有<appender>
標簽。
log4j1的console輸出源
<appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
<layout class="org.apache.log4j.PatternLayout">
<param name="ConversionPattern" value="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/>
</layout>
</appender>
遷移到log4j2之后的語法配置就應該是;
<Console name="STDOUT" target="SYSTEM_OUT">
<PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/>
</Console>
同樣對于所有的輸出流式日志文件的<appender>
標簽同樣是這樣的配置語法。具體的log4j2遷移參考官網:Migrating from Log4j 1.x
2 RollingFile必須加上參數大小的配置
1)問題
2015-11-02 18:28:06,742 ERROR A TriggeringPolicy must be provided
2015-11-02 18:28:06,743 ERROR Null object returned for RollingFile in Appenders.
2015-11-02 18:28:06,747 ERROR Unable to locate appender Daijia for logger com.meituan.apollo.item.filter.util.VListResultModelFilterUtil
2015-11-02 18:28:06,748 ERROR Unable to locate appender Daijia for logger
2)原因與解決辦法
出現這個問題是因為采用了<RollingFile>
是因為,RollingFile的輸出流代表指定輸入到指定參數的文件之中,然后根據的指定的策略重新覆蓋日志文件。所以<RollingFile>
必須配置 TriggeringPolicy(觸發策略) 和 RolloverStrategy(覆蓋策略)這兩個參數,否則出錯。這里采取的策略是根據日志大小進行重新覆蓋SizeBasedTriggeringPolicy
.并采用了默認的覆蓋行為DefaultRolloverStrategy
進行重新覆蓋。
詳細的<RollingFile>
的輸出流配置可以參考:log4j2中Appender的配置
參考資料鏈接:
- Log4j官網: http://logging.apache.org/log4j/2.x/