logback日志框架

1. logback简介

目前比较常用的java日志框架包括:logback,log4j,log4j2,JUL。

log4j2和log4j是两套完全不同的框架,并且不兼容;logback是在log4j的基础上重新开发的一套日志框架,是完全实现slf4j接口API(因为开发logback和开发slf4j的是同一个人),是springboot默认推荐的日志框架。

logback分成三个模块:

logback-core, logback-classic和logback-access。

1)logback-core是其他两个模块的共同依赖模块

2)logback-classic原生实现了slf4j接口API

3)logback-access模块与Tomcat, Jetty等servlet容器集成,以提供http访问日志的功能。

我们还可以基于logback-core实现构建自己的模块

2. 代码演示

2.1 pom依赖

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

2.2 代码

logback中日志级别分成5级:

  TRACE < DEBUG < INFO < WARN < ERROR

默认情况下,trace级别的日志不会输出

public class LoggingTest01 {
    //Logger是单例的,有则取,没有则创建
    //name可以随便写,一般使用当前类的全路径名
    private static final Logger logger = LoggerFactory.getLogger(LoggingTest01.class);

    public static void main(String[] args) {
        logger.trace("hello world! I am trace level");
        logger.debug("hello world! I am debug level");
        logger.info("hello world! I am info level");
        logger.warn("hello world! I am warn level");
        logger.error("hello world! I am error level");
    }
}

16:59:39.391 [main] DEBUG com.john.logging.LoggingTest01 - hello world! I am debug level
16:59:39.402 [main] INFO com.john.logging.LoggingTest01 - hello world! I am info level
16:59:39.402 [main] WARN com.john.logging.LoggingTest01 - hello world! I am warn level
16:59:39.402 [main] ERROR com.john.logging.LoggingTest01 - hello world! I am error level

2.3 记录器介绍

在logback中,所有的记录器都是缓存在一个map中的,而且有层级关系,最顶层的是root,root记录器是自动生成的,不需要创建。

层级关系由name决定,层级关系主要是方便记录器属性的管理,子记录器在没有配置相关属性时,会继承父记录器的对应属性。例如,记录器D会继承记录器A的属性,记录器A会继承根记录器的属性。

而且,日志的输出默认是有叠加性的,例如,如果使用记录器D打印一条日志,那么默认情况下,该日志还会被记录器A和跟记录器输出,也就是一共会打印3次。当然,该属性可以被关闭。在进行日志搜集到ELK时,可以在跟记录器设置一个appender,将日志传输出去。

如果我们按如下方式生成一个记录器:

private static final Logger logger = LoggerFactory.getLogger("org.example.App");
private static final Logger logger2 = LoggerFactory.getLogger("org.example.App2");

 那么会对应生成所有如下名称的记录器:

2.4 记录器的属性

slf4j接口定义的记录器属性只有name,但是loagback的记录器有三个属性:name, level和addtivity.

1)name属性:记录器的唯一标识,也就类似于map的key

2)level属性(可选):记录器的级别,允许的级别:TRACE < DEBUG < INFO < WARN < ERROR

   对应的方法:setLevel, getLevel, getEffectiveLevel

3)addtivity属性(可选):是否允许叠加打印日志

2.4.1 level测试

(1)设置level后,会影响当前记录器和当前记录器的所有未设置level属性的子记录器;

(2)设置level后,小于该level的日志将不会被打印。

package com.john.logging;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import org.slf4j.LoggerFactory;

public class LoggingTest01 {

    private static final Logger rootLogger = (Logger)LoggerFactory.getLogger("root");
    private static final Logger logger = (Logger)LoggerFactory.getLogger(LoggingTest01.class);

    public static void main(String[] args) {
        System.out.println("默认root日志级别:" + rootLogger.getEffectiveLevel());
        System.out.println("默认test日志级别:" + logger.getEffectiveLevel());
        logger.setLevel(Level.TRACE);
        System.out.println("修改后root的日志级别:" + rootLogger.getEffectiveLevel());
        System.out.println("修改后test的日志级别:" + logger.getEffectiveLevel());
    }
}

默认root日志级别:DEBUG
默认test日志级别:DEBUG
修改后root的日志级别:DEBUG
修改后test的日志级别:TRACE

2.4.2 addtivity测试

    <logger name="com.aa" level="DEBUG">
        <appender-ref ref="myAppender"/>
    </logger>

    <logger name="com" level="DEBUG">
        <appender-ref ref="myAppender"/>
    </logger>

    <root level = "DEBUG" additivity="false">
        <appender-ref ref="myAppender" />
    </root>

additivity的逻辑是这样的:

1)使用当前的记录器(例如com.aa)进行打印,打印完毕之后,判断当前记录器的addivity属性;

2)如果当前addivity的属性为false,那么当前记录器的父记录器不再打印;否则调用父记录器进行打印;

3)父附加器执行1)和2)...

具体表现如下:

1)在com.aa记录器上加addtivity属性为false,com.aa记录器打印一次;

2)在com记录器上加addtivity属性为false,那么com.aa记录器和com记录器各打印一次;

3)如果在root记录器上加addtivity属性为false,相当于没有加(因为root记录器没有父记录器了),那么com.aa记录器、com记录器和root记录器各打印一次;

4)在root和com记录器上都加,那么com.aa记录器和com记录器各打印一次。

2.5 使用配置文件配置记录器属性

1)使用logger标签创建记录器,level属性指定级别

2)使用root标签创建跟记录器,level属性指定级别

3)root标签等于<logger name="root">

<configuration xmlns="http://ch.qos.logback/xml/ns/logback"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://ch.qos.logback/xml/ns/logback
               https://raw.githubusercontent.com/enricopulatzo/logback-XSD/master/src/main/xsd/logback.xsd">

    <!--使用logger创建记录器-->
    <logger name = "com.aa" level = "WARN" >
    </logger>

    <!--设置根记录器的级别为INFO, root标签等于<logger name="root">标签-->
    <root level="INFO">
    </root>
</configuration>

2.6 附加器Appender

记录器会将输出日志的任务交给附加器来完成,不同的附加器会将日志输出到不同的地方,比如控制器附加器,文件附加器,网络附加器等。

常用的附加器如下:

控制台附加器:ConsoleAppender

文件附加器:FileAppender

滚动文件附加器:RollingFileAppender

1)附加器的pattern标签

pattern由文字文本和转换说明符组成。我们可以在其中自由插入任何文字文本,每个转换说明符都以百分,后跟可选的格式修饰符、转换字和大括号之间的可选参数。转换字控制要转换的数据字段,例如记录器名称、级别、日期或者线程名称。格式修饰符控制字段宽度,填充以及对齐方式。

pattern的全量信息参考logback-classic的PatternLayout类

配置参考 

<configuration xmlns="http://ch.qos.logback/xml/ns/logback"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://ch.qos.logback/xml/ns/logback
               https://raw.githubusercontent.com/enricopulatzo/logback-XSD/master/src/main/xsd/logback.xsd">
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">

      <!--  编码器,也就是日志输出时,使用什么样的格式进行输出-->
        <encoder>
            <pattern>%d{HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <appender name="FILE" class="ch.qos.logback.core.FileAppender">

        <!--文件输出到哪个文件中,文件的全路径名或者相对项目根路径的相对路径名-->
        <file>d:\logs\myApp.log</file>

        <!--是否是追加形式,如果为false,每次重新打开文件写日志时会覆盖之前的内容-->
        <append>true</append>
        <encoder>
            <pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n</pattern>
        </encoder>
    </appender>


    <!--使用logger创建记录器-->
    <logger name = "com.aa" level = "WARN" >
        <appender-ref ref="FILE" />
        <appender-ref ref="STDOUT" />
    </logger>

</configuration>

 2)滚动文件附加器

基于时间的滚动策略:TimeBasedRollingPolicy

    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>myApp.log</file>
        <encoder>
            <pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n</pattern>
        </encoder>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--不要使用文件名不允许的字符,按分钟分文件夹-->
            <fileNamePattern>%d{yyyy-MM-dd HH-mm/ss}.log</fileNamePattern>
            <!--<fileNamePattern>%d{yyyy-MM-dd HH-mm-ss}.log</fileNamePattern>-->
            <!--最多保留多少个文件-->
            <maxHistory>3</maxHistory>
            <!--所有文件的大小不能超过5GB-->
            <totalSizeCap>5GB</totalSizeCap>
        </rollingPolicy>
    </appender>

基于大小和时间的滚动策略:SizeAndTimeBasedRollingPolicy

<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>myApp.log</file>
        <encoder>
            <pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n</pattern>
        </encoder>
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            <!--每天归档一次,每个文件的后缀递增,按天归档,每天序号重新更新-->
            <fileNamePattern>%d{yyyy-MM-dd}.%i.log</fileNamePattern>
            <!--<fileNamePattern>%d{yyyy-MM-dd HH-mm-ss}.log</fileNamePattern>-->
            <!--最多保留多少个归档文件,如果按天归档,那么最多保留三天的日志-->
            <maxHistory>3</maxHistory>
            <!--所有文件的大小不能超过5GB-->
            <totalSizeCap>5GB</totalSizeCap>
            <!--每个文件的大小不能超过1KB-->
            <maxFileSize>1KB</maxFileSize>
        </rollingPolicy>
    </appender>

2.7 自定义附加器

public class MyAppender<E> extends UnsynchronizedAppenderBase<E> {

    private Encoder encoder;

    protected void append(E e) {
        ILoggingEvent event = (ILoggingEvent) e;

        byte[] bytes = encoder.encode(event);
        try {
            String msg = new String(bytes, "utf-8");
            System.out.println(msg);
        } catch (UnsupportedEncodingException ex) {
            ex.printStackTrace();
        }
    }

    public Encoder getEncoder() {
        return encoder;
    }

    public void setEncoder(Encoder encoder) {
        this.encoder = encoder;
    }
}
    <appender name="myAppender" class="com.john.logging.MyAppender">
        <encoder>
            <pattern>%date %msg%n</pattern>
        </encoder>
    </appender>

    <!--使用logger创建记录器-->
    <logger name="com.aa" level="WARN">
        <appender-ref ref="myAppender"/>
    </logger>

2.8 附加器过滤器

过滤器是附加器的一个组件,它用于决定附加器是否输出日志。一个附加器可以包含一个或者多个过滤器。每个过滤器都会返回一个枚举值,可选的值:DENY,NEUTRAL, ACCEPT。

附加器根据过滤器返回值判断是否输出日志,过滤器按照从上至下的顺序一次进行判断:

1)如果是DENY,那么不再继续判断该过滤器以下的其他过滤器,直接不输出日志;

      如果是ACCEPT,也不再继续判断该过滤器以下的其他过滤器,直接输出日志;

2)如果是NEUTRAL,那么说明该过滤器不进行是否输出日志判断,而是交给下一个过滤器进行判断;但如果没有下一个过滤器,则输出日志。

常用的过滤器:

1)LevelFilter: 级别过滤器,级别相等才会打印日志

2)ThresholdFilter: 阈值过滤器,级别大于等于才会打印日志

3)EvaluatorFilter:评估者过滤器

4)JaninoEventEvaluator

5)TurboFilter:涡轮过滤器

6)DulicateMessageFilter:重复消息过滤器

<configuration xmlns="http://ch.qos.logback/xml/ns/logback"
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://ch.qos.logback/xml/ns/logback
               https://raw.githubusercontent.com/enricopulatzo/logback-XSD/master/src/main/xsd/logback.xsd">

    <appender name="myAppender" class="com.john.logging.MyAppender">
        <encoder>
            <pattern>%date %msg%n</pattern>
        </encoder>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFO</level>
            <onMatch>ACCEPT</onMatch>
            <onMismatch>DENY</onMismatch>
        </filter>
    </appender>

    <!--使用logger创建记录器-->
    <logger name="com.aa" level="error">
        <appender-ref ref="myAppender"/>
    </logger>

</configuration>

过滤器与logger标签的level属性:

日志先由logger标签的level属性进行过滤,如果有日志要输出,那么再由各个附加器的过滤器进行过滤输出。

自定义过滤器:

案例1:

public class MyFilter extends Filter<ILoggingEvent> {

    private Level level;

    public FilterReply decide(ILoggingEvent event) {
        if(event.getLevel().equals(level)) {
            return FilterReply.ACCEPT;
        }
        return FilterReply.DENY;
    }

    public void setLevel(Level level) {

        this.level = level;
    }
}

案例2:

    <appender name="myAppender" class="com.john.logging.MyAppender">
        <encoder>
            <pattern>%date %msg%n</pattern>
        </encoder>
        <filter class="com.john.logging.MyFilter">
            <level>INFO</level>
            <keyword>successful</keyword>
        </filter>
    </appender>
public class MyFilter extends Filter<ILoggingEvent> {
    private String keyword;

    public FilterReply decide(ILoggingEvent event) {
        if (event.getMessage().contains(keyword)) {
            return FilterReply.ACCEPT;
        }
        return FilterReply.DENY;
    }

    public void setKeyword(String keyword) {
        this.keyword = keyword;
    }
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值