聊一聊日志系统 Logback

291 阅读3分钟
  • logback无缝衔接SLF4J(也是Ceki Gülcü的作品,日志门面)
  • 项目中需要包含对slf4j-api-*.jar的依赖
  • 项目中需要包含对logback-core.jarlogback-classic.jar的依赖 使用方法还是千篇一律的写法
package chapters.introduction;
// 这里并没有引入logback相关文件,在记录日志时也是只使用SLF4j提供的接口
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HelloWorld1 {
    public static void main(String[] args) {
        // 这句话会根据classpath下的引用查找SLF4j的具体实现类,可参照上一篇
        Logger logger = LoggerFactory.getLogger("chapters.introduction.HelloWorld1");
        logger.debug("Hello world.");
    }
}

由三大组件构成

  • logback-core:核心组件,为其他两个提供支撑
  • logback-classic:扩展Logback-core,而且是日志功能的真实实现,可理解为log4j的进阶版本
  • logback-access:可结合logback-core用于http容器的日志功能,如tomcat,不常用

由三大主类构成

  • Logger:在logback-classic中实现,日志记录器
  • Appender :在logback-core中实现,打印到什么地方
  • Layout.:在logback-core中实现,设置打印格式

Logger介绍

获取Logger

通过LoggerFactory.getLogger(A)获取Logger,如果A相同,Logger始终是同一个对象

// 那么:aLogger和bLogger引用完全相同的Logger对象。
Logger aLogger = LoggerFactory.getLogger("com.foo");
Logger bLogger = LoggerFactory.getLogger("com.foo"); 

日志记录等级及关系: TRACE < DEBUG < INFO < WARN < ERROR

日志记录等级有2个用处:

  • 在代码逻辑中输出日志时,需要明确等级,如logger.info("打印普通日志"),明确了等级=info
  • 日志全局配置中需要指定真实打印日志的等级,如logging.level=warn,只有大于>warn的日志等级才会真实被打印,上面的info不会被打印

Logger的继承关系

为什么会存在继承关系? 比如在使用logger的时候并没有配置level,怎么办? 这时候就是根据继承关系向上查找,直到找到后生效。 如果都没有设置怎么办? 不会发生这种情况,因为根记录器提供一个默认的level,并设为debug,也就是保底的配置。 如何继承?看一个示例:

//ROOT_LOGGER_NAME固定为ROOT,是所有日志记录器的根(Root)
Logger rootLogger = LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
Logger faLogger = LoggerFactory.getLogger("com.foo");
Logger chLogger = LoggerFactory.getLogger("com.foo.bar");

只要缺省了某一个配置,就会沿着继承关系逐级向上查找,直到找到为止!

  • Level的继承关系:如果自身设置了level,则使用自身的生效;否则向上查找;
  • Appender的继承关系:默认是累加的,即自身和父级的都会生效(这种累加会一直向上查找,直到遇到附加flag=false的为止),当然可以设置flag=false禁用累加方式,Appender比较复杂,见下表:
Logger设置Appender附加Flag最终输出目标备注
rootA1不适用A1根日志记录器不需要设置Flag
xA-x1, A-x2trueA1, A-x1, A-x2其中A1是继承了root
x.ynonetrueA1, A-x1, A-x2继承了root和x
x.y.zA-xyz1trueA1, A-x1, A-x2, A-xyz1继承了root、x和x.y
securityA-secfalseA-sec不继承父级
security.accessnonetrueA-sec只继承security,因为security设置了false,所以不再继续继承祖先

Appender

一个Logger可以被打印到多个Appender指定的位置,如同时打印到文件或console。

Layout

用来控制打印到Appender的输出格式,比如%-4relative [%thread] %-5level %logger{32} - %msg%n输出以下格式:

176 [main] DEBUG manual.architecture.HelloWorld2 - Hello world.

推荐写法

通常,记录日志的写法如下:

logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));

无论该日志是否打印,日志内容中的+都会执行解析,增加了开销,所以更推荐以下写法:

Object entry = new SomeObject(); 
//只有需要打印`debug`级别的日志时,才会解析日志内容,平时无开销
logger.debug("The entry is {}.", entry);