logback设置开发环境日志级别
生产环境日志级别一般都是warn和error,但在开发环境中,warn级别太高了,我们可能需要debug级别来观察日志输出。这时就会改动logback.xml,然后有的小伙伴一不小心就提交到代码库了,和其他小伙伴冲突甚至上线了。
上面说的是多环境公用同一个logback.xml的问题,当然可以用多个profile来区分开发和生产环境。如果一个模块很多人开发的话,不同的人关注不同的日志,如我关心sql日志,其他人可能关心spring日志或者业务日志、监控日志,这样还是要改本地的logback.xml。
其实还可以通过另一种方式不改变logback.xml来实现不同开发人员对应不同的日志输出,即先读取本地配置的日志级别,读取不到就用原来的级别,本地配置的级别可以在系统环境变量中设置。logback是支持这种方式的,语法${你的环境变量:-默认值}
,如
<logger name="jdbc.sqltiming" level="${logback.sql.level:-warn}" additivity="false">
<appender-ref ref="stdout"/>
</logger>
线上配置的是warn级别,如果本地配置了logback.sql.level=debug,那么日志级别就是debug,否则就是原来的值warn。注意warn前面是:-
。
配置参考:https://www.cloudesire.com/configure-logback-log-level-via-environment-variables/
源码解析:
在创建jdbc.sqltiming这个Logger的时候,读取level属性,在OptionHelper类中
/**
* See http://logback.qos.ch/manual/configuration.html#variableSubstitution
*/
public static String substVars(String input, PropertyContainer pc0, PropertyContainer pc1) {
try {
return NodeToStringTransformer.substituteVariable(input, pc0, pc1);
} catch (ScanException e) {
throw new IllegalArgumentException("Failed to parse input [" + input + "]", e);
}
}
先对input语法解析成Tokenizer,${logback.sql.level:-warn}会被解析成2部分:变量logback.sql.level和默认值warn,在NodeToStringTransformer类中:
private String lookupKey(String key) {
String value = propertyContainer0.getProperty(key);
if (value != null)
return value;
if (propertyContainer1 != null) {
value = propertyContainer1.getProperty(key);
if (value != null)
return value;
}
value = OptionHelper.getSystemProperty(key, null);
if (value != null)
return value;
value = OptionHelper.getEnv(key);
if (value != null) {
return value;
}
return null;
}
会优先在logback.xml中进行变量查找,然后在SystemProperty中查找,最后到系统环境变量中查找。
看完代码就明白了吧,对于propertyContainer0和propertyContainer1有什么区别不太了解,Property和Env的区别见https://www.jianshu.com/p/ac99ce832d6b
logback其他知识点
- logger层级关系,像包名目录关系一样,上级包是下级包的父类,如com.java.lang的上级是com.java,再网上是com,再往上是root。
- 如果当前类没有定义logger,通过Logger.get(Class)也能得到logger对象,日志等级(effectiveLevel)是自己的或者最近的上级的。
- 每个logger都可以定义自己的appender,同时也会找上级的appender,会往自己的+所有上级的appender里写日志(即使日志等级小于上级的effectiveLevel也会写),设置additivity=false可以设置不找上级的。因此设置了additivity=false就必须要设置自己的appender。 additivity只是找appender时有用,找到了就可以写,不用管上级的level。
- 如果你要写的日志等级低于logger对象的effectiveLevel,则不输出日志,即使你写的日志等级可能高于root logger等级也不写。
- com和com.xxx都没设置appender,那么他们都没有appedner,都会找到root的appender,appender不累加,即com.xxx找到的也只有root appender,其上级com是没有appender的。
public static final int OFF_INT = Integer.MAX_VALUE;
public static final int ERROR_INT = 40000;
public static final int WARN_INT = 30000;
public static final int INFO_INT = 20000;
public static final int DEBUG_INT = 10000;
public static final int TRACE_INT = 5000;
public static final int ALL_INT = Integer.MIN_VALUE;