java Logback日志

一、前言

本文以一个简单的项目为例,一步步展示logback的同步和异步配置方法,并且配置的日志要求满足阿里巴巴Java开发手册-日志规约 ,因为对于线上服务,日志对于排查问题有至关重要的作用,规范的日志格式配合shell脚本可以快速定位问题。

最开始使用Java日志系统,最大的疑惑就是分不清楚log4jslf4jlogback等日志库之间的关系,不过网上有不少文章介绍这部分相关知识,比如理解Java日志体系Java日志框架那些事儿混乱的 Java 日志体系,可以作为提前阅读。

二、配置同步日志

2.1 日志要求

首先项目的整体结构如下图所示:

[图片上传失败...(image-7f2add-1667555293301)]

一共有两个packageutilhttp,下面分别有两个类Util.javaHttp.java,我们的日志要求是:

  • http目录下的文件产生的日志全部记录到:~/logs/${appname}/http.log,级别:INFO
  • util目录下的文件产生的日志全部记录到:~/logs/${appname}/util.log,级别:DEBUG
  • 其余日志文件均记录到:~/logs/${appname}/${appname}.log,级别:INFO
  • 要求所有的日志均至少保存15天
  • 如果保存15天,内容太大可能造成磁盘风险,则最大保存10GB的日志。

2.2 添加POM

使用logback+slf4j的组合,需要依赖的pom如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.test</groupId>
    <artifactId>logback-test</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.10</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.3</version>
        </dependency>
    </dependencies>
</project>

2.3 添加logback.xml

logback查找配置的顺序如下所示:

  • 在系统配置文件System Properties中寻找是否有logback.configurationFile对应的value
  • 在classpath下寻找是否有logback.groovy(即logback支持groovy与xml两种配置方式)
  • 在classpath下寻找是否有logback-test.xml
  • 在classpath下寻找是否有logback.xml

resources目录下添加logback.xml,目前内容为空,如下所示:

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

</configuration>

[图片上传失败...(image-5b5332-1667555293301)]

2.4 配置appender

appender用来配置日志的文件名、日志的写入策略,滚动策略等,我们按照2.1的要求配置appender如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <property name="APP_NAME" value="logbacktest" />
    <property name="LOG_NAME" value="${user.home}/logs/${APP_NAME}/${APP_NAME}.log" />

    <appender name="APP_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!--指定日志文件名称-->
        <file>${LOG_NAME}</file>
        <encoder>
            <!--指定日志内容格式-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
            <charset>utf8</charset>
        </encoder>
        <rollingPolicy
                class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${LOG_NAME}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <!--日志最大保存15天-->
            <maxHistory>15</maxHistory>
            <!--日志最大的文件大小-->
            <maxFileSize>100MB</maxFileSize>
            <!--日志最大保存10GB-->
            <totalSizeCap>10GB</totalSizeCap>
        </rollingPolicy>
    </appender>

</configuration>

property可以用来定义变量,我们定义APP_NAMElogbacktest,后面可以用${APP_NAME}来使用这个变量,其它的配置见注释。

2.5 配置root

上面我们定义了appender,定义了日志文件名,日志写入策略,但是现在还有一个问题就是:哪个路径下的日志写入上面定义的appender?是Main.java下的,还是Util.java下的呢?

当然,logback有配置专门去配置路径,这里我们先配置root,即:默认表示所有路径。我们在2.1中的要求是其余日志文件均记录到:~/logs/${appname}/${appname}.log,级别:INFO,我们目前可以通过配置root来满足这个需求,如下:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <property name="APP_NAME" value="logbacktest" />
    <property name="LOG_NAME" value="${user.home}/logs/${APP_NAME}/${APP_NAME}.log" />

    <appender name="APP_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
        ...
    </appender>

    <root level="INFO">
        <!--ref表示具体的appender name-->
        <appender-ref ref="APP_LOG" />
    </root>

</configuration>

这样配置之后,所有的日志都会以root的level,即INFO去使用APP_LOG这个appender打印。

2.6 打印日志

编辑Util.java如下所示,打印5个级别的日志:

package com.test.util;

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

/**
 * @author bodong.ybd
 * @date 2019/6/17
 */
public class Util {
    private static final Logger log = LoggerFactory.getLogger(Util.class);

    public static void loginfo() {
        log.trace("trace");
        log.debug("debug");
        log.info("info");
        log.warn("warn");
        log.error("error");
    }
}

编辑Main.java文件内容如下所示:

package com.test;

import com.test.util.Util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author bodong.ybd
 * @date 2019/6/17
 */
public class Main {
    private static final Logger log = LoggerFactory.getLogger(Main.class);

    private static void loginfo() {
        log.trace("trace");
        log.debug("debug");
        log.info("info");
        log.warn("warn");
        log.error("error");
    }

    public static void main(String[] args) {
        loginfo();
        Util.loginfo();
    }
}

运行Main.java,打印日志,效果如下:

➜  ~ ls ~/logs/logbacktest
logbacktest.log
➜  ~ cat ~/logs/logbacktest/logbacktest.log
2019-06-19 19:33:26 [main] INFO  com.test.Main - info
2019-06-19 19:33:26 [main] WARN  com.test.Main - warn
2019-06-19 19:33:26 [main] ERROR com.test.Main - error
2019-06-19 19:33:26 [main] INFO  com.test.util.Util - info
2019-06-19 19:33:26 [main] WARN  com.test.util.Util - warn
2019-06-19 19:33:26 [main] ERROR com.test.util.Util - error

可以看到产生了一个日志文件logbacktest.log,并且Main.javaUtil.java的日志都打印在了这个文件中,日志级别是root配置的INFO,但是这样并不满足要求,因为2.1要求:util目录下的文件产生的日志全部记录到:~/logs/${appname}/util.log,级别:DEBUG,这个问题需要配置logger来解决。

2.7 配置logger

logger用来设置某个包或者类具体日志的打印级别,下面我们把Util.java配置拆出去。

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <property name="APP_NAME" value="logbacktest" />
    <property name="LOG_NAME" value="${user.home}/logs/${APP_NAME}/${APP_NAME}.log" />
    <property name="UTIL_NAME" value="${user.home}/logs/${APP_NAME}/util.log" />

    <appender name="APP_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
            ...
            </appender>

    <appender name="UTIL_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!--指定日志文件名称-->
        <file>${UTIL_NAME}</file>
        <encoder>
            <!--指定日志内容格式-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
            <charset>utf8</charset>
        </encoder>
        <rollingPolicy
                class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${UTIL_NAME}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <!--日志最大保存15天-->
            <maxHistory>15</maxHistory>
            <!--日志最大的文件大小-->
            <maxFileSize>100MB</maxFileSize>
            <!--日志最大保存10GB-->
            <totalSizeCap>10GB</totalSizeCap>
        </rollingPolicy>
    </appender>

    <!--com.test.util目录下的文件产生的日志全部记录到util.log-->
    <!--默认的日志级别是DEBUG-->
    <!--additivity=false表示如果能匹配到这条规则就不用往上继续查找到root节点去-->
    <logger name="com.test.util" level="DEBUG" additivity="false" >
        <appender-ref ref="UTIL_LOG"/>
    </logger>

    <root level="INFO">
        <!--ref表示具体的appender-->
        <appender-ref ref="APP_LOG" />
    </root>

</configuration>

这样配置完成后就可以把Util.javaMain.java的日志分开了,效果如下所示:

➜  ~ ls ~/logs/logbacktest
logbacktest.log util.log
➜  ~ cat ~/logs/logbacktest/logbacktest.log
2019-06-19 19:42:51 [main] INFO  com.test.Main - info
2019-06-19 19:42:51 [main] WARN  com.test.Main - warn
2019-06-19 19:42:51 [main] ERROR com.test.Main - error
➜  ~ cat ~/logs/logbacktest/util.log
2019-06-19 19:42:51 [main] DEBUG com.test.util.Util - debug
2019-06-19 19:42:51 [main] INFO  com.test.util.Util - info
2019-06-19 19:42:51 [main] WARN  com.test.util.Util - warn
2019-06-19 19:42:51 [main] ERROR com.test.util.Util - error

好了,看到这里,详细Http.java相关的日志你肯定也会配置了,试试看。

三、配置异步日志

[图片上传失败...(image-9641cc-1667555293301)]

图片链接:https://logging.apache.org/log4j/2.x/manual/async.html

使用logback配置异步日志可以使用appender:ch.qos.logback.classic.AsyncAppender,将上面的Util.java的log配置为异步如下所示:

<appender name ="ASYNC_UTIL_LOG" class="ch.qos.logback.classic.AsyncAppender">
        <discardingThreshold>0</discardingThreshold>
        <queueSize>512</queueSize>
        <neverBlock>true</neverBlock>
        <appender-ref ref="UTIL_LOG"/>
    </appender>

    <logger name="com.test.util" level="DEBUG" additivity="false" >
        <appender-ref ref="ASYNC_UTIL_LOG"/>
    </logger>

discardingThreshold:默认情况下,当阻塞队列剩余20%的容量时,它将丢弃级别TRACE,DEBUG和INFO的事件,仅保留级别WARN和ERROR的事件。要保留所有事件,请将discardingThreshold设置为0。
queueSize:阻塞队列的最大容量。默认情况下,queueSize设置为256。
neverBlock:如果为false(默认值),appender将阻塞在添加队列的接口处。设置为true,appender将删除消息,不会阻止您的应用程序。
这几个参数更加详细的解释见: https://logback.qos.ch/manual/appenders.html

logback还有另一种异步日志配置方式,即使用disruptor,可参考这里配置。

四、总结

本文以一个例子说明了logback+slf4配置同步和异步日志的方法,使用异步日志除了可以提高程序性能之外,还可以防止部分磁盘IO Hang导致的问题,水平有限,如有不足,请指出。

[完]

原文链接:https://zhuanlan.zhihu.com/p/70595392

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

推荐阅读更多精彩内容