老板下了死命令,要把日志系统切换到Logback

Log4j 介绍过了,SLF4J 也介绍过了,那接下来,你懂的,Logback 就要隆重地登场了,毕竟它哥仨有一个爹,那就是巨佬 Ceki Gulcu。

就在昨天,老板听我说完 Logback 有多牛逼之后,彻底动心了,对我下了死命令,“这么好的日志系统,你还不赶紧点,把它切换到咱的项目当中!”

我们项目之前用的 Log4j,在我看来,已经足够用了,毕竟是小公司,性能上的要求没那么苛刻。

01、Logback 强在哪

1)非常自然地实现了 SLF4J,不需要像 Log4j 和 JUL 那样加一个适配层。

2)Spring Boot 的默认日志框架使用的是 Logback。一旦某款工具库成为了默认选项,那就说明这款工具已经超过了其他竞品。

注意看下图(证据找到了,来自 Spring Boot 官网):

也可以通过源码的形式看得到:

3)支持自动重新加载配置文件,不需要另外创建扫描线程来监视。

4)既然是巨佬的新作,那必然在性能上有了很大的提升,不然呢?

02、Logback 使用示例

第一步,在 pom.xml 文件中添加 Logback 的依赖:

<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.3</version>
</dependency>

Maven 会自动导入另外两个依赖:

logback-core 是 Logback 的核心,logback-classic 是 SLF4J 的实现。

第二步,来个最简单的测试用例:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author 微信搜「沉默王二」,回复关键字 PDF
 */
public class Test {
    static Logger logger = LoggerFactory.getLogger(Test.class);
    public static void main(String[] args) {
        logger.debug("logback");
    }
}

Logger 和 LoggerFactory 都来自 SLF4J,所以如果项目是从 Log4j + SLF4J 切换到 Logback 的话,此时的代码是零改动的。

运行 Test 类,可以在控制台看到以下信息:

12:04:20.149 [main] DEBUG com.itwanger.Test - logback

在没有配置文件的情况下,一切都是默认的,Logback 的日志信息会输出到控制台。可以通过 StatusPrinter 来打印 Logback 的内部信息:

LoggerContext lc = (LoggerContext)LoggerFactory.getILoggerFactory();
StatusPrinter.print(lc);

在 main 方法中添加以上代码后,再次运行 Test 类,可以在控制台看到以下信息:

12:59:22.314 [main] DEBUG com.itwanger.Test - logback
12:59:22,261 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback-test.xml]
12:59:22,262 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.groovy]
12:59:22,262 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.xml]
12:59:22,268 |-INFO in ch.qos.logback.classic.BasicConfigurator@5e853265 - Setting up default configuration.

也就是说,Logback 会在 classpath 路径下先寻找 logback-test.xml 文件,没有找到的话,寻找 logback.groovy 文件,还没有的话,寻找 logback.xml 文件,都找不到的话,就输出到控制台。

一般来说,我们会在本地环境中配置 logback-test.xml,在生产环境下配置 logback.xml。

第三步,在 resource 目录下增加 logback-test.xml 文件,内容如下所示:

<configuration debug="true">
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} %relative [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="debug">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>

Logback 的配置文件非常灵活,最基本的结构为 <configuration> 元素,包含 0 或多个 <appender> 元素,其后跟 0 或多个 <logger> 元素,其后再跟最多只能存在一个的 <root> 元素。

1)配置 appender,也就是配置日志的输出目的地,通过 name 属性指定名字,通过 class 属性指定目的地:

  • ch.qos.logback.core.ConsoleAppender:输出到控制台。
  • ch.qos.logback.core.FileAppender:输出到文件。
  • ch.qos.logback.core.rolling.RollingFileAppender:文件大小超过阈值时产生一个新文件。

除了输出到本地,还可以通过 SocketAppender 和 SSLSocketAppender 输出到远程设备,通过 SMTPAppender 输出到邮件。甚至可以通过 DBAppender 输出到数据库中。

encoder 负责把日志信息转换成字节数组,并且把字节数组写到输出流。

pattern 用来指定日志的输出格式:

  • %d:输出的时间格式。
  • %thread:日志的线程名。
  • %-5level:日志的输出级别,填充到 5 个字符。比如说 info 只有 4 个字符,就填充一个空格,这样日志信息就对齐了。

反例(没有指定 -5 的情况):

  • %logger{length}:logger 的名称,length 用来缩短名称。没有指定表示完整输出;0 表示只输出 logger 最右边点号之后的字符串;其他数字表示输出小数点最后边点号之前的字符数量。
  • %msg:日志的具体信息。
  • %n:换行符。
  • %relative:输出从程序启动到创建日志记录的时间,单位为毫秒。

2)配置 root,它只支持一个属性——level,值可以为:TRACE、DEBUG、INFO、WARN、ERROR、ALL、OFF。

appender-ref 用来指定具体的 appender。

3)查看内部状态信息

可以在代码中通过 StatusPrinter 来打印 Logback 内部状态信息,也可以通过在 configuration 上开启 debug 来打印内部状态信息。

重新运行 Test 类,可以在控制台看到以下信息:

13:54:54,718 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Found resource [logback-test.xml] at [file:/Users/maweiqing/Documents/GitHub/JavaPointNew/codes/logbackDemo/target/classes/logback-test.xml]
13:54:54,826 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [ch.qos.logback.core.ConsoleAppender]
13:54:54,828 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [STDOUT]
13:54:54,833 |-INFO in ch.qos.logback.core.joran.action.NestedComplexPropertyIA - Assuming default type [ch.qos.logback.classic.encoder.PatternLayoutEncoder] for [encoder] property
13:54:54,850 |-INFO in ch.qos.logback.classic.joran.action.RootLoggerAction - Setting level of ROOT logger to DEBUG
13:54:54,850 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [STDOUT] to Logger[ROOT]
13:54:54,850 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - End of configuration.
13:54:54,851 |-INFO in ch.qos.logback.classic.joran.JoranConfigurator@f8c1ddd - Registering current configuration as safe fallback point
13:54:54.853 [main] DEBUG com.itwanger.Test - logback

4)自动重载配置

之前提到 Logback 很强的一个功能就是支持自动重载配置,那想要启用这个功能也非常简单,只需要在 configuration 元素上添加 scan=true 即可。

<configuration scan="true">
    ...
</configuration>

默认情况下,扫描的时间间隔是一分钟一次。如果想要调整时间间隔,可以通过 scanPeriod 属性进行调整,单位可以是毫秒(milliseconds)、秒(seconds)、分钟(minutes)或者小时(hours)。

下面这个示例指定的时间间隔是 30 秒:

<configuration scan="true" scanPeriod="30 seconds"
   ...
</configuration>

注意:如果指定了时间间隔,没有指定时间单位,默认的时间单位为毫秒。

当设置 scan=true 后,Logback 会起一个 ReconfigureOnChangeTask 的任务来监视配置文件的变化。

03、把 log4j.properties 转成 logback-test.xml

如果你的项目以前用的 Log4j,那么可以通过下面这个网址把 log4j.properties 转成 logback-test.xml:

http://logback.qos.ch/translator/

把之前 log4j.properties 的内容拷贝一份:

### 设置###
log4j.rootLogger = debug,stdout,D,E

### 输出信息到控制台 ###
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n

### 输出DEBUG 级别以上的日志到=debug.log ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = debug.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG 
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

### 输出ERROR 级别以上的日志到=error.log ###
log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File =error.log 
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR 
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss}  [ %t:%r ] - [ %p ]  %m%n

粘贴到该网址的文本域:

点击「Translate」,可以得到以下内容:

<?xml version="1.0" encoding="UTF-8"?>

<!-- For assistance related to logback-translator or configuration  -->
<!-- files in general, please contact the logback user mailing list -->
<!-- at http://www.qos.ch/mailman/listinfo/logback-user             -->
<!--                                                                -->
<!-- For professional support please see                            -->
<!--    http://www.qos.ch/shop/products/professionalSupport         -->
<!--                                                                -->
<configuration>
  <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
    <Target>System.out</Target>
    <encoder>
      <pattern>[%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n</pattern>
    </encoder>
  </appender>
  <appender name="D" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <!--See http://logback.qos.ch/manual/appenders.html#RollingFileAppender-->
    <!--and http://logback.qos.ch/manual/appenders.html#TimeBasedRollingPolicy-->
    <!--for further documentation-->
    <Append>true</Append>
    <File>debug.log</File>
    <encoder>
      <pattern>%d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n</pattern>
    </encoder>
    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
      <level>DEBUG</level>
    </filter>
  </appender>
  <appender name="E" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <!--See http://logback.qos.ch/manual/appenders.html#RollingFileAppender-->
    <!--and http://logback.qos.ch/manual/appenders.html#TimeBasedRollingPolicy-->
    <!--for further documentation-->
    <File>error.log</File>
    <Append>true</Append>
    <encoder>
      <pattern>%d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n</pattern>
    </encoder>
    <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
      <level>ERROR</level>
    </filter>
  </appender>
  <root level="debug">
    <appender-ref ref="stdout"/>
    <appender-ref ref="D"/>
    <appender-ref ref="E"/>
  </root>
</configuration>

可以确认一下内容,发现三个 appender 都在。

但是呢,转换后的文件并不能直接使用,需要稍微做一些调整,因为:

第一,日志的格式化有细微的不同,Logback 中没有 %l

第二,RollingFileAppender 需要指定 RollingPolicy 和 TriggeringPolicy,前者负责日志的滚动功能,后者负责日志滚动的时机。如果 RollingPolicy 也实现了 TriggeringPolicy 接口,那么只需要设置 RollingPolicy 就好了。

TimeBasedRollingPolicy 和 SizeAndTimeBasedRollingPolicy 是两种最常用的滚动策略。

TimeBasedRollingPolicy 同时实现了 RollingPolicy 与 TriggeringPolicy 接口,因此使用 TimeBasedRollingPolicy 的时候就可以不指定 TriggeringPolicy。

TimeBasedRollingPolicy 可以指定以下属性:

  • fileNamePattern,用来定义文件的名字(必选项)。它的值应该由文件名加上一个 %d 的占位符。%d 应该包含 java.text.SimpleDateFormat 中规定的日期格式,缺省是 yyyy-MM-dd。滚动周期是通过 fileNamePattern 推断出来的。

  • maxHistory,最多保留多少数量的日志文件(可选项),将会通过异步的方式删除旧的文件。比如,你指定按月滚动,指定 maxHistory = 6,那么 6 个月内的日志文件将会保留,超过 6 个月的将会被删除。

  • totalSizeCap,所有日志文件的大小(可选项)。超出这个大小时,旧的日志文件将会被异步删除。需要配合 maxHistory 属性一起使用,并且是第二条件。

来看下面这个 RollingFileAppender 配置:

<appender name="D" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>debug.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <!--            按天滚动 -->
        <fileNamePattern>debug.%d{yyyy-MM-dd}.log</fileNamePattern>
        <!--            保存 30 天的历史记录,最大大小为 30GB -->
        <maxHistory>30</maxHistory>
        <totalSizeCap>3GB</totalSizeCap>
    </rollingPolicy>
    <encoder>
        <pattern>%relative [%thread] %level %logger{35} - %msg%n</pattern>
    </encoder>
</appender>

基于按天滚动的文件策略,最多保留 30 天,最大大小为 30G。

SizeAndTimeBasedRollingPolicy 比 TimeBasedRollingPolicy 多了一个日志文件大小设定的属性:maxFileSize,其他完全一样。

基于我们对 RollingPolicy 的了解,可以把 logback-test.xml 的内容调整为以下内容:

<configuration>
    <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
        <Target>System.out</Target>
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>
<appender name="D" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <Append>true</Append>
    <File>debug.log</File>
    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <!--            按天轮转 -->
        <fileNamePattern>debug.%d{yyyy-MM-dd}.log</fileNamePattern>
        <!--            保存 30 天的历史记录,最大大小为 30GB -->
        <maxHistory>30</maxHistory>
        <totalSizeCap>3GB</totalSizeCap>
    </rollingPolicy>
    <encoder>
        <pattern>%relative [%thread] %-5level %logger{35} - %msg%n</pattern>
    </encoder>
</appender>
    <appender name="E" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <File>error.log</File>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--            按天轮转 -->
            <fileNamePattern>error.%d{yyyy-MM-dd}.log</fileNamePattern>
            <!--            保存 30 天的历史记录,最大大小为 30GB -->
            <maxHistory>30</maxHistory>
            <totalSizeCap>3GB</totalSizeCap>
        </rollingPolicy>
        <encoder>
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>ERROR</level>
        </filter>
    </appender>
    <root level="debug">
        <appender-ref ref="stdout"/>
        <appender-ref ref="D"/>
        <appender-ref ref="E"/>
    </root>
</configuration>

修改 Test 类的内容:

public class Test {
    static Logger logger = LoggerFactory.getLogger(Test.class);
    public static void main(String[] args) {
        logger.debug("logback");
        logger.error("logback");
    }
}

运行后,可以在 target 目录下看到两个文件:debug.log 和 errror.log。

到此为止,项目已经从 Log4j 切换到 Logback 了,过程非常的丝滑顺畅,嘿嘿。

04、Logback 手册

Logback 的官网上是有一份手册的,非常详细,足足 200 多页,只不过是英文版的。小伙伴们可以看完我这篇文章入门实操的 Logback 教程后,到下面的地址看官方手册。

http://logback.qos.ch/manual/index.html

如果英文阅读能力有限的话,可以到 GitHub 上查看雷锋翻译的中文版:

https://github.com/itwanger/logback-chinese-manual

当然了,还有一部分小伙伴喜欢看离线版的 PDF,我已经整理好了:

链接:https://pan.baidu.com/s/16FrbwycYUUIfKknlLhRKYA 密码:bptl

实话实话吧,白嫖的感觉就是舒服,赶紧去下载吧!日常求个三连,谢谢你勤劳的手指,嘿嘿。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343