Logback 构建在三个主要的类上:Logger,Appender 和 Layouts。这三个不同类型的组件一起作用能够让开发者根据消息的类型以及日志的级别来打印日志。
Logger类作为 logback-classic 模块的一部分。Appender与Layouts接口作为 logback-core 的一部分。作为一个通用的模块,logback-core 没有 logger 的概念。
我们可以使用Logger,根据开发人员设定的标准,对日志进行分类。在 logback-classic 中,分类是 logger 的一部分,每一个 logger 都依附在LoggerContext上,它负责产生 logger,并且通过一个树状的层级结构来进行管理。
一个 Logger 被当作为一个实体,它们的命名是大小写敏感的,并且遵循一下规则:
" logger1.logger2.logger3 ":logger1是logger2的父logger,是logger3的祖先logger。logger2是logger3的父logger。
root logger 作为 logger 层次结构的最高层。它是一个特殊的 logger,因为它是每一个层次结构的一部分。每一个 logger 都可以通过它的名字去获取。
获取root logger:
Logger rootLogger = LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME)
所有其它的 logger 通过org.clf4j.LoggerFactory 类的静态方法getLogger去获取,这个方法需要传入一个 logger 的名字。
获取普通Logger:
//通过类名获取,logger名即为该类名,推荐
private static Logger logger = LoggerFactory.getLogger(TestLogback01.class);
//通过logger名获取
private static Logger logger1 = LoggerFactory.getLogger("myLogger");
如果logger名称相同,得到的是同一个logger实例。logback 不会严格限制 logger 的命名,你完全可以根据自己的喜好来。但是,根据类的全限定名来对 logger 进行命名,是目前最好的方式,没有之一。
logger中的方法:
@Test
public void test(){
logger.trace("trace....");
logger.info("info....");
logger.debug("debug....");
logger.warn("warn....");
logger.error("error....");
}
Appenter:
有选择的启用或者禁用日志的输出只是 logger 的一部分功能。logback 允许日志在多个地方进行输出。站在 logback 的角度来说,输出目的地叫做 appender。appender 包括console、file、remote socket server、MySQL、PostgreSQL、Oracle 或者其它的数据库、JMS、remote UNIX Syslog daemons 中。
一个 logger 可以有多个 appender。
logger 通过 addAppender 方法来新增一个 appender。对于给定的 logger,每一个允许输出的日志都会被转发到该 logger 的所有 appender 中去。换句话说,appender 从 logger 的层级结构中去继承叠加性。叠加规则:
Layout
用户既想自定义日志的输出地,也想自定义日志的输出格式。通过给 appender 添加一个 layout 可以做到。layout 的作用是将日志格式化,而 appender 的作用是将格式化后的日志输出到指定的目的地。PatternLayout 能够根据用户指定的格式来格式化日志,类似于 C 语言的 printf 函数。示例:
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{5} - %msg%n
4、参数化日志
考虑到 logback-classic 实现了 SLF4J 的 Logger 接口,一些打印方法可以接收多个传参。这些打印方法的变体主要是为了提高性能以及减少对代码可读性的影响。
对于一些 Logger 如下输出日志:
logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
会产生构建消息参数的成本,是因为需要将整数转为字符串,然后再将字符串拼接起来。
为了避免构建参数带来的损耗,可以在日志记录之前做一个判断,如下:
if(logger.isDebugEnabled()) {
logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
}
在这种情况下,如果 logger没有开启 debug 模式,不会有构建参数带来的性能损耗。换句话说,如果 logger 在 debug 级别,将会有两次性能的损耗,一次是判断是否启用了 debug 模式,一次是打印 debug 日志。在实际应用当中,这种性能上的损耗是可以忽略不计的,因为它所花费的时间小于打印一条日志的时间的 1%。
考虑到 logback-classic 实现了 SLF4J 的 Logger 接口,一些打印方法可以接收多个传参。这些打印方法的变体主要是为了提高性能以及减少对代码可读性的影响。
对于一些 Logger 如下输出日志:
logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
会产生构建消息参数的成本,是因为需要将整数转为字符串,然后再将字符串拼接起来。但是我们是不需要关心 debug 信息是否被记录(强行曲解作者的意思)。
为了避免构建参数带来的损耗,可以在日志记录之前做一个判断,如下:
if(logger.isDebugEnabled()) {
logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
}
在这种情况下,如果 logger没有开启 debug 模式,不会有构建参数带来的性能损耗。换句话说,如果 logger 在 debug 级别,将会有两次性能的损耗,一次是判断是否启用了 debug 模式,一次是打印 debug 日志。在实际应用当中,这种性能上的损耗是可以忽略不计的,因为它所花费的时间小于打印一条日志的时间的 1%。
更好的选择
有一种更好的方式去格式化日志信息。假设 entry 是一个 Object 对象:
Object entry = new SomeObject();
logger.debug("The entry is {}", entry);
只有在需要打印 debug 信息的时候,才会去格式化日志信息,将 '{}' 替换成 entry 的字符串形式。也就是说在这种情况下,如果禁止了日志的打印,也不会有构建参数上的性能消耗。
下面两行输出的结果是一样的,但是一旦禁止日志打印,第二个变量的性能至少比第一个变量好上 30 倍。
logger.debug("The new entry is " + entry + ".");
logger.debug("The new entry is {}", entry);
使用两个参数的例子如下:
logger.debug("The new entry is {}, It replaces {}.", entry, oldEntry);
如果需要使用三个或三个以上的参数,可以采用如下的形式:
Object[] paramArray = {newVal, below, above};
logger.debug("Value {} was inserted between {} and {}.", paramArray);
5、一个可用的logback配置文件示例
该配置文件将项目中的所有允许输出的日志,通过指定的格式,输出到到指定的文件中去:
E:/log/testFile2.log
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{5} - %msg%n
true
false