隨着通用日誌組件轉入 Slf4j,logback 也變成了默認的日誌實現,像 log4j 同樣,logback.xml 中也能夠使用系統屬性或環境變量,如 ${catalina.home}。在 log4j.properties 中,若是變量在系統屬性和環境變量中找不到的話默認爲 "" 空字符串,而到了 logback.xml 中若是某個變量找不到默認就是 "變量名_IS_UNDEFINED" 了,這樣就比較奇怪了。html
那如何在沒有配置 catalina.home 系統屬性或環境變量時設置一個默認值呢,例如,沒有 catalina.home 時取值爲 ".",這時值日誌文件的路徑就是java
./logs/unmi-%d{yyyy-MM-dd}.loggit
了,也就是生成中當前目錄下的 logs 子目錄中,這樣算是很友好的方式。下面就來分析怎麼一步步找到答案的,沒耐心或是隻求結果的話,直接滾屏到下面就行。github
咱們的問題是,對於下面那樣的 logback.xml 配置:app
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
<
configuration
>
<
appender
name
=
"stdout"
class
=
"ch.qos.logback.core.ConsoleAppender"
>
<
encoder
charset
=
"UTF-8"
>
<
pattern
>%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</
pattern
>
</
encoder
>
</
appender
>
<
appender
name
=
"RollingFile"
class
=
"ch.qos.logback.core.rolling.RollingFileAppender"
>
<
rollingPolicy
class
=
"ch.qos.logback.core.rolling.TimeBasedRollingPolicy"
>
<
fileNamePattern
>${catalina.home}/logs/unmi-%d{yyyy-MM-dd}.log</
fileNamePattern
>
<
maxHistory
>10</
maxHistory
>
</
rollingPolicy
>
<
encoder
>
<
pattern
>%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</
pattern
>
</
encoder
>
</
appender
>
<
root
level
=
"DEBUG"
>
<
appender-ref
ref
=
"stdout"
/>
<
appender-ref
ref
=
"RollingFile"
/>
</
root
>
</
configuration
>
|
由於測試時直接在 Eclipse 或控制檯下執行,在未定義 catalina.home 的狀況下,一運行就會在當前目錄下生成 catalina.home_IS_UNDEFINED 目錄,其中有 logs 子目錄,再纔是 unmi-2014-05-19.log 這個日誌文件。學習
搜索了網上關於默認值的解決方案,有說在 logback.xml 中寫條件表達式,例如:測試
1
2
3
4
5
6
7
8
|
<
if
condition
=
"property('catalina.home')==null"
>
<
then
>
<
fileNamePattern
>logs/unmi-%d{yyyy-MM-dd}.log</
fileNamePattern
>
</
then
>
<
else
>
<
fileNamePattern
>${catalina.home}/logs/unmi-%d{yyyy-MM-dd}.log</
fileNamePattern
>
</
else
>
</
if
>
|
參考: http://logback.qos.ch/manual/configuration.html#conditional 和 http://stackoverflow.com/questions/15911303/how-can-i-configure-logback-conditionally-based-on-context-name。ui
上面的 if-then-else 是有問題的 The FileNamePattern option must be set before using TimeBasedRollingPolicy,須要把 if 的覆蓋範圍擴大,不過試了也不怎麼爽。spa
回到 IS_UNDEFINED, 它老是有來頭的,搜索源代碼可找到它出如今 https://github.com/qos-ch/logback/blob/master/logback-core/src/main/java/ch/qos/logback/core/util/OptionHelper.java#L103:日誌
final static String _IS_UNDEFINED = "_IS_UNDEFINED";
還有一處未被使用,這樣就有突破口了,再跟蹤代碼,找到 logback 是怎麼取到相對應變量值的 https://github.com/qos-ch/logback/blob/master/logback-core/src/main/java/ch/qos/logback/core/subst/NodeToStringTransformer.java#L102,handleVariable 方法代碼片段:
01
02
03
04
05
06
07
08
09
10
11
12
|
if
(n.defaultPart ==
null
) {
stringBuilder.append(key + CoreConstants.UNDEFINED_PROPERTY_SUFFIX);
cycleCheckStack.pop();
return
;
}
Node defaultPart = (Node) n.defaultPart;
StringBuilder defaultPartBuffer =
new
StringBuilder();
compileNode(defaultPart, defaultPartBuffer, cycleCheckStack);
cycleCheckStack.pop();
String defaultVal = defaultPartBuffer.toString();
stringBuilder.append(defaultVal);
|
注:從這個類的 lookupKey(String key) 方法中能夠知道 logback 從四個地方取變量值,分別是 propertyContainer0, propertyContrainer1, System, Evn,前兩不知道不要緊,後兩個咱們明白先從 System 屬性,沒有再從環境變量中取。
上面咱們會想到 Node 除了有 payload 還有一個 defaultPart 用了設定默認值部分。至於默認值怎麼設置,猜想試是 ${catalina.home:abc},沒用,不過別急,logback 的測試用例告訴了咱們表達式的寫法。
見 https://github.com/qos-ch/logback/blob/master/logback-core/src/test/java/ch/qos/logback/core/util/OptionHelperTest.java#L99, 看到這樣寫代默認值的表達式
${v2:-toto} //格式是 ${變量名:-默認值},光有冒號還不夠,再加條短線後面纔是默認值
因此學習它後,前面的日誌文件名配置部分就寫成
1
2
|
<!-- 若是沒有定義 catalina.home 系統屬性或環境變量時,生成的日誌文件在 ./logs 目錄下 -->
<
fileNamePattern
>${catalina.home:-.}/logs/unmi-%d{yyyy-MM-dd}.log</
fileNamePattern
>
|
完整的 logback.xml 內容以下:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
<?
xml
version
=
"1.0"
encoding
=
"UTF-8"
?>
<
configuration
>
<
appender
name
=
"stdout"
class
=
"ch.qos.logback.core.ConsoleAppender"
>
<
encoder
charset
=
"UTF-8"
>
<
pattern
>%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</
pattern
>
</
encoder
>
</
appender
>
<
appender
name
=
"RollingFile"
class
=
"ch.qos.logback.core.rolling.RollingFileAppender"
>
<
rollingPolicy
class
=
"ch.qos.logback.core.rolling.TimeBasedRollingPolicy"
>
<
fileNamePattern
>${catalina.home:-.}/logs/unmi-%d{yyyy-MM-dd}.log</
fileNamePattern
>
<
maxHistory
>10</
maxHistory
>
</
rollingPolicy
>
<
encoder
>
<
pattern
>%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</
pattern
>
</
encoder
>
</
appender
>
<
root
level
=
"DEBUG"
>
<
appender-ref
ref
=
"stdout"
/>
<
appender-ref
ref
=
"RollingFile"
/>
</
root
>
</
configuration
>
|
多看看這 OptionHelperTest 測試用例,還能發現默認值也能用變量,如
${B:-${A}}