随着通用日志组件转入 Slf4j,logback 也变成了默认的日志实现,像 log4j 一样,logback.xml 中也可以使用系统属性或环境变量,如 ${catalina.home}。在 log4j.properties 中,如果变量在系统属性和环境变量中找不到的话默认为 "" 空字符串,而到了 logback.xml 中如果某个变量找不到默认就是 "变量名_IS_UNDEFINED" 了,这样就比较奇怪了。

那如何在没有配置 catalina.home 系统属性或环境变量时设置一个默认值呢,例如,没有 catalina.home  时取值为 ".",这时值日志文件的路径就是

./logs/unmi-%d{yyyy-MM-dd}.log

了,也就是生成中当前目录下的 logs 子目录中,这样算是很友好的方式。下面就来分析怎么一步步找到答案的,没耐心或是只求结果的话,直接滚屏到下面就行。

我们的问题是,对于下面那样的 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>

因为测试时直接在 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

上面的 if-then-else 是有问题的 The FileNamePattern option must be set before using TimeBasedRollingPolicy,需要把 if 的覆盖范围扩大,不过试了也不怎么爽。

回到  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}}

Logback.xml 给变量指定默认值的更多相关文章

  1. JavaScript 中对象解构时指定默认值

    待解构字段为原始值 正常情况下, const obj = { a: 1, b: 2, }; const { a, b } = obj; console.log(a, b); // 1 2 当被解构字段 ...

  2. Java中boolean型变量的默认值问题

    1.首先分析Java中的三种不同变量的区别,如下表所示   概念 默认值 其他 类变量 也叫静态变量,是类中独立于方法之外的变量 用static 修饰 有默认初始值,系统自动初始化. 如boolean ...

  3. Java变量的默认值和初始化

    Java变量的默认值和初始化 学习自 <Thinking In Java> 技术小黑屋-为什么局部变量需要显式设置初始化值 变量的默认值 注意只有成员变量才有默认值,而局部变量必须要赋初值 ...

  4. Android——问题解决之adb not responding;adb不是内部或外部命令;path变量的默认值为多少

    adb not responding 恩,这是出现的问题.我们开始来解决它吧! 出现这种问题大多是因为adb端口被占用导致这个问题,所以只要找到占用端口号程序,结束即可!就是这么简单(adb运行端口号 ...

  5. 【oracle】关于创建表时用default指定默认值的坑

    刚开始学create table的时候没注意,学到后面发现可以指定默认值.于是写了如下语句: 当我查询的时候发现,查出来的结果是这样的.. 很纳闷有没有,我明明指定默认值了呀,为什么创建出来的表还是空 ...

  6. php函数指定默认值的方法

    发布:JB02   来源:脚本学堂     [大 中 小] 本文介绍下,在php编程中,指定函数的默认值的方法,分享二个例子,供大家学习参考下.本文转自:http://www.jbxue.com/ar ...

  7. stdmap 用 at() 取值,如果 key 不存在,不好意思,程序崩溃。QMap 用 value()取值,如果 key 不存在,不会崩溃,你还可以指定默认值

    我觉得 Qt6 最应该升级的是容器类 stdmap 在遍历的时候,同时获取 key 与 value 非常方便: for(auto& var:map){    qDebug()<<v ...

  8. SASS 中变量的默认值

    SASS 中定义的变量,后设置的值会覆盖旧的值. $color: red; $color: blue; .btn { color: $color; } 编译后为: .btn { color: blue ...

  9. Java类成员变量的默认值

    1.布尔型(boolean)变量默认值为false,byte.short.int.long为0,字符型为'\u0000'(空字符),浮点型(float double)为0.0,引用类型(String) ...

随机推荐

  1. PCI9054 DMA设置流程

    1.设置方式寄存器:设置DMA通道的传输方式,寄存器DMAMODE0或者DMAMODE1的位9:0-表示块传输,1-表示散/聚传输: 2.设置PCI地址寄存器:设置PCI总线侧的地址空间. 3.设置L ...

  2. R学习笔记(4): 使用外部数据

    来源于:R学习笔记(4): 使用外部数据 博客:心内求法 鉴于内存的非持久性和容量限制,一个有效的数据处理工具必须能够使用外部数据:能够从外部获取大量的数据,也能够将处理结果保存.R中提供了一系列的函 ...

  3. PHP解码unicode编码中文字符代码

    function replace_unicode_escape_sequence($match) { return mb_convert_encoding(pack('H*', $match[1]), ...

  4. [Err] 1136 - Column count doesn't match value count at row 1

    1 错误描述 [Err] 1136 - Column count doesn't match value count at row 1 Procedure execution failed 1136 ...

  5. Unhandled event loop exception GC overhead limit exceeded

    1.错误描述 java.lang.OutOfMemoryError: GC overhead limit exceeded at java.util.zip.ZipFile.<init>( ...

  6. javaWeb之邮箱发送(邮箱客户端配置)

    这里使用的是本机的邮箱服务器  , 代码执行条件: 1.·邮箱服务器  , 下载地址   密码   s4xn 邮箱服务器配置: 1):安装 2):打开服务器 红色部分是默认账号,不用处理 3)系统设置 ...

  7. 芝麻HTTP: Python爬虫入门之Urllib库的高级用法

    1.设置Headers 有些网站不会同意程序直接用上面的方式进行访问,如果识别有问题,那么站点根本不会响应,所以为了完全模拟浏览器的工作,我们需要设置一些Headers 的属性. 首先,打开我们的浏览 ...

  8. 自定义方法实现ArrayList排序

    package cn.edu.nwpu.java; import java.util.ArrayList; import java.util.Collection; import com.sun.xm ...

  9. Spring Security验证流程剖析及自定义验证方法

    Spring Security的本质 Spring Security本质上是一连串的Filter, 然后又以一个独立的Filter的形式插入到Filter Chain里,其名为FilterChainP ...

  10. pat1051-1060

    1051 自己写的非常麻烦 http://blog.csdn.net/biaobiaoqi/article/details/9338397 的算法比较好,我的就贴下吧,主要对入栈出栈不够理解 #inc ...