logback 简介

Ceki Gülcü在Java日志领域世界知名。他创造了Log4J ,这个最早的Java日志框架即便在JRE内置日志功能的竞争下仍然非常流行。随后他又着手实现SLF4J 这个“简单的日志前端接口(Façade)”来替代Jakarta Commons-Logging 。
LOGBack,一个“可靠、通用、快速而又灵活的Java日志框架”。

一、日志 API

1 【强制】各应用中不可直接使用日志系统(Log4j、Logback)中的API,而应依赖使用日志框架 SLF4J 中的 API,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。
Lombok Slf4j 注解方式

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class Fooooo {
  public void barrrr() {
    log.info("hello world ...");
  }
}

二、日志输出

1.【强制】通用格式为:“DESC. [key1={}, key2=‘{}’, key3={}]”,例如:

log.info("execute one query request. [request='{}', response='{}'", json, queryResponseJson);
log.warn("try to re-query for incomplete query. [sql='{}']", sql, e);
  • “DESC” 为行为描述,一句有区分度的话,以“.”结尾。目的:作为 “Anchor” 及锚点,能够用来定位一类问题,它需要有辨识度,需要能通过 GREP 简单地过滤到。
  • “[key1={}, key2=‘{}’, key3={}]” 为案发现场上下文,其中的 key 的命名遵循Java变量命名规范,而如果 value 为字符串,建议加上单引号。
  • 必须使用占位符"{}"代替字符串拼接。

2.【强制】异常信息应该包括两类信息:案发现场信息和异常堆栈信息。如果不处理,那么通过关键字 throws 往上抛出。

log.error("execute one query request. [request='{}', response='{}', elapse={}, cacheResponse='{}']", json, queryResponseJson, elapse, queryContext.getCacheResponse(), e);

3.【推荐】尽量用英文来描述日志错误信息,如果日志中的错误信息用英文描述不清楚的话使用中文描述即可,否则容易产生歧义。

三、日志配置

1.【强制】避免重复打印日志,浪费磁盘空间,务必在配置文件中设置 additivity=false。
2.【强制】注意日志使用级别,ERROR 级别只记录系统逻辑出错、异常或者重要的错误信息。
ERROR:
当程序发生不可预期的错误(按照设计不应该发生)时。
或者不可恢复的错误(启动时初始化失败,直接退出)时。
WARN:
当程序发生可预期或者不影响主要功能的时候时使用。
INFO:
用于追踪程序的正常执行。
典型的在 Web 模块中,任何一处 INFO 日志的输出在一次 Web Request 中被调用的次数应该是 “常量级” 别的,且大部分情况下这个常量应该是 1。
DEBUG:
调试日志,DEBUG 日志打印的量没有限制,但需要注意不要影响执行性能。

四、日志性能

【推荐】禁止在循环体内、被频繁调用的方法、回调方法内,打 log 要小心。如无必要,一律禁止。
【推荐】对于 trace/debug 级别的日志输出,必须进行日志级别的判断,才可输出日志。如无性能损耗的生成日志内容,此条规范可选。

import lombok.extern.slf4j.Slf4j;

// 如果判断为真,那么可以输出 trace 和 debug 级别的日志
if (log.isDebugEnabled()) {
  log.debug("desc log. [id={}, name='{}']", id, getName());
}

五、栈信息打印

try {

} catch (Exception e){
  	log.error("dsfs", e); // 会打印栈信息到 log 文件。而不是标准输出
  	e.printStackTrace();	// 会打印到标准输出,不推荐这种方法
}

六、logback-spring.xml

在工程src目录下建立logback.xml
注:
1.logback首先会试着查找logback.groovy文件;
2.当没有找到时,继续试着查找logback-test.xml文件;
3.当没有找到时,继续试着查找logback.xml文件;
4.如果仍然没有找到,则使用默认配置(打印到控制台)。
自定义日志配置
根据不同的日志系统,你可以按如下规则组织配置文件名,就能被正确加载:
Logback:logback-spring.xml, logback-spring.groovy, logback.xml, logback.groovy
Log4j:log4j-spring.properties, log4j-spring.xml, log4j.properties, log4j.xml
Log4j2:log4j2-spring.xml, log4j2.xml
JDK (Java Util Logging):logging.properties
Spring Boot官方推荐优先使用带有-spring的文件名作为你的日志配置(如使用logback-spring.xml,而不是logback.xml),命名为logback-spring.xml的日志配置文件,spring boot可以为它添加一些spring boot特有的配置项。
默认的命名规则,并且放在 src/main/resources 下面即可
如果你即想完全掌控日志配置,但又不想用logback.xml作为Logback配置的名字,application.yml可以通过logging.config属性指定自定义的名字:

logging.config=classpath:logging-config.xml

logback配置样例

<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
    <springProperty scop="context" name="server" source="spring.application.name" defaultValue="log"/>
    <springProperty scop="context" name="logstash.destination" source="logstash.destination" />
    <property name="log.path" value="log" />
    <property name="log.maxHistory" value="60" />
    <property name="log.infoTotalSizeCap" value="10GB" />
    <property name="log.errorTotalSizeCap" value="5GB" />
    <property name="log.sqlTotalSizeCap" value="10GB" />
    <property name="log.maxFileSize" value="100MB" /><!--KBMBGB-->
    <property name="log.colorPattern" value="%magenta(%d{yyyy-MM-dd HH:mm:ss.SSS}) %yellow(${server} [%thread]) %highlight(%-5level) %red([%X{tl}]) %green(%logger) - %msg%n"/>
    <property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} ${server} [%thread] %-5level [%X{tl}] %logger{36} - %msg%n"/>

    <!--输出到控制台-->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>${log.colorPattern}</pattern>
        </encoder>
    </appender>

    <!-- info日志文件输出  -->
    <appender name="infoFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <prudent>false</prudent>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <!--
        maxHistory的保存时长与fileNamePattern设置有关,如果保存格式为yyyyMMddHHmm,那maxHistory的时间单位就是分钟,如果保存格式为yyyyMMdd,那maxHistory的时间单位就是天。
        通常在过渡期间执行归档删除。但是,某些应用程序的生存时间可能不足以触发翻转。由此可见,对于这样短暂的应用程序,归档删除可能永远不会有执行的机会。
        通过将 cleanHistoryOnStart 设置为 true,将在附加程序启动时执行归档删除。
        TimeBasedRollingPolicy支持自动文件压缩。如果 fileNamePattern 选项的值以.gz 或 .zip 结尾,则启用此功能。
        各种技术原因,文件轮转不是由时钟驱动的,而是取决于日志记录事件的到达。
        例如,在 200238 日,假设 fileNamePattern 设置为yyyy-MM-dd (每日翻转),则文件轮转发生在午夜后第一个事件到达时,比如在00:23:47秒来了一个记录日志的实践,
        那么实际上文件将在在 20023900:23:47轮转,而非20023900:00:00-->
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!--
            %d:表示日志的后缀,精确到天,即一天一个日志文件。如:auto-2022-01-01.log。
            %i:表示如果这一天中文件大小超过了100MB,将重新记录一个文件。如:auto-2022-01-01.0.log。
            -->
            <fileNamePattern>${log.path}/info/info-%d{yyyy-MM-dd}.%i.zip</fileNamePattern>
            <!--日志文件保留天数-->
            <maxHistory>${log.maxHistory}</maxHistory>
            <!-- 所有存档文件的总大小。当超过总大小上限时,最早的 Files 将被异步删除。 -->
            <totalSizeCap>${log.infoTotalSizeCap}</totalSizeCap>
            <!-- 每天日志归档路径以及格式  %i是索引 超过50MB 日志文件索引会以0开始-->
            <maxFileSize>${log.maxFileSize}</maxFileSize>
        </rollingPolicy>
        <encoder>
            <pattern>${log.pattern}</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    <!-- 异常日志文件输出  -->
    <appender name="errorFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <prudent>false</prudent>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>ERROR</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${log.path}/error/error-%d{yyyy-MM-dd}.%i.zip</fileNamePattern>
            <maxHistory>${log.maxHistory}</maxHistory>
            <totalSizeCap>${log.errorTotalSizeCap}</totalSizeCap>
            <maxFileSize>${log.maxFileSize}</maxFileSize>
        </rollingPolicy>
        <encoder>
            <pattern>${log.pattern}</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    <!--sql日志文件  使用mybatis时,sql语句debug下才会打印-->
    <appender name="sqlFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <prudent>false</prudent>
        <filter class="ch.qos.logback.classic.filter.LevelFilter">
            <level>DEBUG</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <fileNamePattern>${log.path}/sql/sql-%d{yyyy-MM-dd}.%i.zip</fileNamePattern>
            <maxHistory>${log.maxHistory}</maxHistory>
            <totalSizeCap>${log.sqlTotalSizeCap}</totalSizeCap>
            <maxFileSize>${log.maxFileSize}</maxFileSize>
        </rollingPolicy>
        <encoder>
            <pattern>${log.pattern}</pattern>
            <charset>UTF-8</charset>
        </encoder>
    </appender>

    <!--输出到logstash的appender-->
    <appender name="logstash" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
        <!--ThresholdFilter 过滤掉低于指定临界值的事件  过滤掉TRACEDEBUG级别的日志-->
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFO</level>
        </filter>
        <!--可以访问的logstash日志收集端口-->
        <destination>${logstash.destination}</destination>
        <encoder charset="UTF-8" class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
            <providers>
                <timestamp>
                    <timeZone>Asia/Shanghai</timeZone>
                </timestamp>
                <!--自定义日志输出格式-->
                <pattern>
                    <pattern>
                        {
                        "server": "${server}",
                        "level": "%level",
                        "traceId": "%X{tl}",
                        "thread": "%thread",
                        "class": "%logger",
                        "message": "%message",
                        "stack_trace": "%exception{15}"
                        }
                    </pattern>
                </pattern>
            </providers>
        </encoder>
    </appender>

    <!-- 文件 异步日志(async) -->
    <appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender"
              immediateFlush="false" neverBlock="true">
        <!-- 不丢失日志.默认的,如果队列的80%已满,则会丢弃TRACTDEBUGINFO级别的日志 -->
        <discardingThreshold>0</discardingThreshold>
        <!-- 更改默认的队列的深度,该值会影响性能.默认值为256 -->
        <queueSize>2048</queueSize>
        <neverBlock>true</neverBlock>
        <!-- 添加附加的appender,最多只能添加一个 -->
        <appender-ref ref="logstash" />
    </appender>

    <!-- Level: FATAL 0  ERROR 3  WARN 4  INFO 6  DEBUG 7 -->
    <root level="INFO">
        <appender-ref ref="console" />
    </root>

    <!-- SIT+开发环境. 多个使用逗号隔开. -->
    <springProfile name="test,dev">
        <logger name="com.***" level="DEBUG" additivity="false">
            <appender-ref ref="console" />
            <appender-ref  ref="ASYNC"/>
        </logger>
    </springProfile>
    <!-- UAT+生产环境. -->
    <springProfile name="uat,prod">
        <logger name="com.***" level="DEBUG" additivity="false">
            <appender-ref ref="errorFile" />
            <appender-ref ref="sqlFile" />
            <appender-ref ref="infoFile" />
            <appender-ref  ref="ASYNC"/>
        </logger>
    </springProfile>
</configuration>
Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐