Java 日志框架之 Logback 学习攻略

Java 日志框架之 Logback 学习攻略

  • 1. 日志框架知多少?
    • 1.1 门面日志分类
    • 1.2 日志实现分类
  • 2. 日志框架安装篇
    • 2.1 SLF4J 与Logging 集成
    • 2.2 SLF4J 与Log4J 集成
    • 2.3 SLF4J 和 Logback 集成
    • 2.4 Spring Boot 和Logback 集成
  • 3. Logback XML 配置篇
    • 3.1 推荐配置
    • 3.2 配置含义讲解
      • 3.2.1 配置 configuration根节点
      • 3.2.2 配置监听器
      • 3.2.3 配置钩子
    • 3.3 调用方式
  • 4. Logback Java 配置篇
    • 4.1 自定义线程打印Logger
    • 4.2 调用方式
  • 5. 参考资料

1. 日志框架知多少?

  • 日志框架浩瀚如海,种类众多,但是大致可分为门面日志(接口日志)和实现类日志(接口实现类)
  • 门面日志中最为出名的有Common Logging ,SLF4J
  • 日志实现框架最出名的有Log4J, 后来为了改进升级,有两个著名的日志实现替代框架Log4j2 以及Logback.
  • Log4j2 和 Logback 的抉择,Spring Boot 默认使用的也是Logback日志。
  • 推荐优先选logback

1.1 门面日志分类

  • Common Logging

Apache Commons Logging(JCL) 提供了一个Log接口,旨在实现轻量级和独立的其他日志工具包的抽象

  • SLF4J

Simple Logging Facade for Java(SLF4J)用作各种日志框架(例如java.util.logging,logback,log4j)的简单外观或抽象,允许最终用户在部署时插入所需的日志记录框架。

1.2 日志实现分类

  • j.u.l (java.util.logging)

JDK1.4 之后自带的

  • Log4J

2015年8月5日,测井服务项目管理委员会宣布Log4j 1.x已达到使用寿命,建议用户使用Log4j 1升级到Apache Log4j 2

  • Apache Log4j 2

Apache Log4j 2是对Log4j的升级,它比其前身Log4j 1.x提供了重大改进,并提供了Logback中可用的许多改进,同时修复了Logback架构中的一些固有问题.

  • LogBack
  • Logback旨在作为流行的log4j项目的后续版本
  • 目前,logback分为三个模块:logback-core,logback-classic和logback-access
    • logback-core模块为其他两个模块奠定了基础。
    • logback-classic模块可以被同化为log4j的显着改进版本。此外,logback-classic本身实现了SLF4J API,因此您可以在logback和其他日志框架(如log4j或java.util.logging(JUL))之间来回切换。
    • logback-access模块​​与Servlet容器(如Tomcat和Jetty)集成,以提供HTTP访问日志功能。请注意,您可以在logback-core之上轻松构建自己的模块

Simple Logging Facade for Java(SLF4J) 可以作为以上四种日志的日志门面。

SLF4J支持各种日志框架,绑定概念图如下:
Java 日志框架之 Logback 学习攻略_第1张图片

2. 日志框架安装篇

2.1 SLF4J 与Logging 集成

如果想使用SLF4J 和java.util.logging 集成,那么只需要添加如下依赖即可

<dependency> 
  <groupId>org.slf4jgroupId>
  <artifactId>slf4j-jdk14artifactId>
  <version>1.7.26version>
dependency>

注意:

  • 如上配置除了会在classpath 添加slf4j-jdk14-1.7.26.jar
  • 由于Maven传递依赖的特性,也会自动引入slf4j-api-1.7.26.jar
  • 如果手动再次添加了这个依赖,当然也不会有问题,只要需要注意兼容性和版本冲突问题

2.2 SLF4J 与Log4J 集成

SLF4J 和 log4j 集成只需要添加如下依赖即可

<dependency> 
  <groupId>org.slf4jgroupId>
  <artifactId>slf4j-log4j12artifactId>
  <version>1.7.26version>
dependency>

注意:

  • 如上配置classpath除了会加入slf4j-log4j12-1.7.26.jar
  • 由于Maven传递依赖的特性,还会引入slf4j-api-1.7.26.jarlog4j-1.2.17.jar
  • 如果手动再次添加了这俩,当然也不会有问题,只要需要注意兼容性和版本冲突问题

2.3 SLF4J 和 Logback 集成

SLF4J 和Logback集成只需要添加一个依赖即可

<dependency> 
  <groupId>ch.qos.logbackgroupId>
  <artifactId>logback-classicartifactId>
  <version>1.2.3version>
dependency>

注意:

  • 如上配置classpath 除了会添加logback-classic-1.2.3.jar
  • 由于Maven传递依赖的特性,也会自动引入slf4j-api-1.7.26.jarlogback-core-1.2.3.jar
  • 如果手动再次添加了这俩,当然也不会有问题,只要需要注意兼容性和版本冲突问题

2.4 Spring Boot 和Logback 集成

Spring Boot 项目和Logback 集成
如果是Spring Boot 项目和Logback 集成,则只需要添加如下依赖即可

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-loggingartifactId>
dependency>

注意:

  • 但是实际开发的时候,你可能会发现我们往往不需要手动添加spring-boot-starter-logging 依赖就可以直接使用。
  • 那是因为我们一般默认添加了如下依赖:
 <dependency> 
      <groupId>org.springframework.bootgroupId> 
      <artifactId>spring-boot-starter-testartifactId> 
      <scope>testscope>  
  dependency>
  • 由于Maven依赖传递性,使用的spring-boot-starter-test 传递依赖了
<dependency>
     <groupId>org.springframework.bootgroupId> 
     <artifactId>spring-bootartifactId> 
     <version>2.1.6.RELEASEversion> 
     <scope>compilescope>  
 dependency>
  • 而这个依赖传递依赖了
<dependency> 
     <groupId>org.springframework.bootgroupId> 
     <artifactId>spring-boot-starter-loggingartifactId>
     <version>2.1.6.RELEASEversion> 
     <scope>compilescope>  
dependency> 
  • 最后这个依赖又传递了如下三个依赖:
   
    <dependency>
      <groupId>ch.qos.logbackgroupId>
      <artifactId>logback-classicartifactId>
      <version>1.2.3version>
      <scope>compilescope>
    dependency>
     
    <dependency>
      <groupId>org.apache.logging.log4jgroupId>
      <artifactId>log4j-to-slf4jartifactId>
      <version>2.11.2version>
      <scope>compilescope>
    dependency>
     
    <dependency>
      <groupId>org.slf4jgroupId>
      <artifactId>jul-to-slf4jartifactId>
      <version>1.7.26version>
      <scope>compilescope>
    dependency>   

3. Logback XML 配置篇

Logback 查找XML配置顺序

  • 首先classpath 路径下寻找 logback-test.xml
  • 如果没有找到,寻找logback.groovy
  • 如果也没找到,寻找logback.xml
  • 如果还没找到加载默认配置

3.1 推荐配置

<configuration debug="true" scan="true" scanPeriod="30 seconds" packagingData="false">

    

    
    
    <property scope="context" name="APP_NAME" value="myApp"/>
    <property scope="context" name="LOG_FILE_PATH" value="/opt/applog/${APP_NAME}/log" />

    
    
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">

        
        <encoder>
            
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight(%-5level) --- %cyan(%logger{36}) --- %c:%L:%n%m%npattern>
        encoder>
    appender>

    
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        
        <file>${LOG_FILE_PATH}/${APP_NAME}.logfile>
        
        <append>trueappend>
        
        <immediateFlush>trueimmediateFlush>
        <encoder>
            
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level --- %logger{36} --- %c:%L:%n%m%npattern>
        encoder>
        
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
            
            <fileNamePattern>${LOG_FILE_PATH}/%d{yyyy-MM-dd,aux}/${APP_NAME}-%d{yyyy-MM-dd,UTC}.%i.logfileNamePattern>
            
            <maxFileSize>100MBmaxFileSize>
            <maxHistory>30maxHistory>
            <totalSizeCap>20GBtotalSizeCap>
            
            <cleanHistoryOnStart>falsecleanHistoryOnStart>
            
            
            
            
        rollingPolicy>
    appender>

    
    <root level="INFO">
        <appender-ref ref="STDOUT" />
        <appender-ref ref="FILE" />
    root>

    
    <logger name="com.xingyun" level="INFO" additivity="true"/>
    
    <logger name="com.xingyun.dao" level="DEBUG" additivity="true"/>
configuration>

3.2 配置含义讲解

接下来讲讲这些配置的含义。

3.2.1 配置 configuration根节点

这个节点有四个属性可以配置:

  • debug="true"
  • 1.配置文件被找到
  • 2.配置文件是格式良好的XML
    如果找到配置文件但格式不正确,则logback将检测错误情况并自动在控制台上打印其内部状态
    Java代码测试打印方法如下:
      // 假设已经添加了 SLF4J 和Logback 依赖
     LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
     // 打印Logback 状态信息
     StatusPrinter.print(lc); 
  • scan=“true”: Logback-classic可以扫描其配置文件中的更改,并在配置文件更改时自动重新配置。
  • scanPeriod=“30 seconds”:
    默认情况下,将每分钟扫描一次配置文件以进行更改,这里我们改成了30秒检测一次。 我们可以通过设置元素的scanPeriod属性来指定不同的扫描周期。 可以以milliseconds, seconds, minutes 或者 hours为单位指定值
  • packagingData=“true”: 从1.1.4版开始,默认情况下禁用打包数据,一般不要打开.

3.2.2 配置监听器

上面的configuration节点配置debug=true 等价于如下方式配置控制台监听,两者任选其一即可

    <configuration scan="true" scanPeriod="30 seconds" packagingData="false">
        <statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />    
    configuration>

3.2.3 配置钩子

  • 在独立的Java应用程序中,向配置文件添加指令是确保在JVM退出之前允许完成任何正在进行的压缩任务的简单方法。
    在Web服务器中的应用程序中,将自动安装webShutdownHook,使指令非常冗余且不必要。

3.3 调用方式

  • 调用方式:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 功能: Logback 日志使用测试
 * 作者: 星云
 */
@RestController
public class LoggerController {

    private static final Logger LOGGER=LoggerFactory.getLogger(LoggerController.class);

    @GetMapping("/test1.do")
    public String test1(){
        for (int i = 0; i <1000 ; i++) {
            LOGGER.info("Current message From main Thread");
        }
        return "success";
    }
}
  • 如果使用了lomback 插件,那么可以省略成如下所示:
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 功能: Logback 日志使用测试
 * 作者: 星云
 */
@Slf4j
@RestController
public class LoggerController {
    
    @GetMapping("/test1.do")
    public String test1(){
        for (int i = 0; i <1000 ; i++) {
            log.info("Current message From main Thread");
        }
        return "success";
    }
}

注意: 如上操作后,日志会输出到控制台,并且输出到 /opt/applog/myApp/log/myApp.log
如果日志日期超过一天或单个文件超过100MB,那么就归档到

  • /opt/applog/myApp/log/2019-08-20/myApp-2019-08-20.0.log
  • /opt/applog/myApp/log/2019-08-20/myApp-2019-08-20.1.log
  • /opt/applog/myApp/log/2019-08-20/myApp-2019-08-20.2.log
  • /opt/applog/myApp/log/2019-08-20/myApp-2019-08-20.3.log
  • /opt/applog/myApp/log/2019-08-20/myApp-2019-08-20.4.log
  • /opt/applog/myApp/log/2019-08-20/myApp-2019-08-20.5.log

4. Logback Java 配置篇

在实际项目中可能会有这样一种需求,需要根据线程分隔到不同的日志文件夹。

项目中原来使用的log4j , 如今我想升级为logback

那么如何操作呢?

4.1 自定义线程打印Logger

Java 日志框架之 Logback 学习攻略_第2张图片
LogbackThreadLogger.java内容如下:

import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy;
import ch.qos.logback.core.util.FileSize;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.nio.charset.Charset;

/**
 * 线程日志工具类
 */
public class LogbackThreadLogger extends RollingFileAppender{

    /**
     * 禁用构造方法
     * */
    private LogbackThreadLogger(){}

    // App Name
    public final static String APP_NAME="myApp";

    //通用配置
    //日志存放路径
    public final static String LOG_FILE_BASE_PATH="/opt/applog/"+APP_NAME+"/log/";
    //配置日志输出格式
    public static final String fileLogLayout="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level --- %logger{36} --- %c:%L:%n%m%n";
    //配置日志编码
    public static final String charSetName="UTF-8";
    //是否自动追加
    public static final Boolean append=true;
    //是否立即刷新
    public static final Boolean immediateFlush=true;
    //单个文件最大值
    public static final String maxFileSize="100MB";
    //单个文件最大值
    public static final  FileSize maxFileSizeValue=FileSize.valueOf(maxFileSize);
    //保存30天
    public static final Integer maxHistory=30;
    //日志保存最大容量
    public static final String totalSizeCap="20GB";
    //日志保存最大容量
    public static final  FileSize totalSizeCapValue=FileSize.valueOf(totalSizeCap);
    //启动清理
    public static final Boolean cleanHistoryOnStart=false;

    /**
     * 打印线程日志
     * @param folderName
     * @param className
     * @return
     */
    @SuppressWarnings("unused")
    public static Logger getLogger(String folderName,String className){

        //创建日志实例对象
        Logger logger = (Logger) LoggerFactory.getLogger(className);
        //获取日志上下文
        LoggerContext loggerContext = logger.getLoggerContext();

        //创建文件追加器
        RollingFileAppender rollingFileAppender = new RollingFileAppender();
        //配置上下文
        rollingFileAppender.setContext(loggerContext);
        //配置文本追加 如果设置为false则覆盖
        rollingFileAppender.setAppend(append);
        //配置立即追加
        rollingFileAppender.setImmediateFlush(immediateFlush);
        //配置日志路径和名称 opt/applog/log/spring/log/test/myThread.log
        rollingFileAppender.setFile(LOG_FILE_BASE_PATH+folderName+ File.separator+className+".log");
        //配置输出格式和输出字符集
        rollingFileAppender.setEncoder(configPatternLayoutEncoder(loggerContext));
        //配置滚动策略 opt/applog/log/spring/log/2019-08-20/test/myThread.log
        rollingFileAppender.setRollingPolicy(configSizeAndTimeBasedRollingPolicy(loggerContext,rollingFileAppender,folderName,className));
        //启动文件追加器
        rollingFileAppender.start();
        //如果为false,线程日志不会重复追加到主日志文件中,如果为 true,则会输出到主日志和线程文件夹中
        logger.setAdditive(false);
        //添加日志追加器
        logger.addAppender(rollingFileAppender);
        return logger;
    }


    /**
     * 日志输出格式和字符集
     * @param loggerContext
     * @return
     */
    private static PatternLayoutEncoder configPatternLayoutEncoder(LoggerContext loggerContext){

        PatternLayoutEncoder patternLayoutEncoder = new PatternLayoutEncoder();
        //配置日志上下文
        patternLayoutEncoder.setContext(loggerContext);
        //配置日志输出格式 layout
        patternLayoutEncoder.setPattern(fileLogLayout);
        //配置日志输出字符集
        patternLayoutEncoder.setCharset(Charset.forName(charSetName));
        //启动encoder
        patternLayoutEncoder.start();

        return patternLayoutEncoder;
    }

    /**
     * 日志滚动策略
     * @param loggerContext
     * @param rollingFileAppender
     * @param folderName
     * @param className
     * @return
     */
    private static SizeAndTimeBasedRollingPolicy configSizeAndTimeBasedRollingPolicy(LoggerContext loggerContext,RollingFileAppender rollingFileAppender,String folderName,String className){
        //配置大小和日期时间分割策略
        SizeAndTimeBasedRollingPolicy sizeAndTimeBasedRollingPolicy = new SizeAndTimeBasedRollingPolicy<>();
        //配置上下文
        sizeAndTimeBasedRollingPolicy.setContext(loggerContext);
        //配置日志追加器
        sizeAndTimeBasedRollingPolicy.setParent(rollingFileAppender);
        //配置日志滚动策略规则 /opt/applog/myApp/log/2019-08-20/test/myThread.log
        sizeAndTimeBasedRollingPolicy.setFileNamePattern(LOG_FILE_BASE_PATH+"%d{yyyy-MM-dd,aux}"+File.separator+folderName+File.separator+className+"-%d{yyyy-MM-dd,UTC}.%i.log");
        //设置单个文件大小
        sizeAndTimeBasedRollingPolicy.setMaxFileSize(maxFileSizeValue);
        //保存多少天
        sizeAndTimeBasedRollingPolicy.setMaxHistory(maxHistory);
        //磁盘最大日志大小
        sizeAndTimeBasedRollingPolicy.setTotalSizeCap(totalSizeCapValue);
        //启动时候清理日志
        sizeAndTimeBasedRollingPolicy.setCleanHistoryOnStart(cleanHistoryOnStart);
        //开启滚动策略
        sizeAndTimeBasedRollingPolicy.start();
        return sizeAndTimeBasedRollingPolicy;
    }
}

4.2 调用方式

import org.slf4j.Logger;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 功能: Logback 日志使用测试
 * 作者: 星云
 */
@RestController
public class LoggerController {

    
    @GetMapping("/test1.do")
    public String test1(){

        Logger logger= LogbackThreadLogger.getLogger("test","myThread");
        
        for (int i = 0; i <1000 ; i++) {
            logger.info("current Message From myThread");
        }
        return "success";
    }
}

注意: 如上操作后,日志会输出到控制台,并且输出到 /opt/applog/myApp/log/test/myThread.log
如果日志日期超过一天或单个文件超过100MB,那么就归档到

  • /opt/applog/myApp/log/2019-08-20/test/myThread-2019-08-20.0.log
  • /opt/applog/myApp/log/2019-08-20/test/myThread-2019-08-20.1.log
  • /opt/applog/myApp/log/2019-08-20/test/myThread-2019-08-20.2.log
  • /opt/applog/myApp/log/2019-08-20/test/myThread-2019-08-20.2.log
  • /opt/applog/myApp/log/2019-08-20/test/myThread-2019-08-20.2.log
  • /opt/applog/myApp/log/2019-08-20/test/myThread-2019-08-20.2.log

合并测试:

import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 功能: Logback 日志使用测试
 * 作者: 星云
 */
@Slf4j
@RestController
public class LoggerController {
    @GetMapping("/test1.do")
    public String test1(){

        Logger logger= LogbackThreadLogger.getLogger("test","myThread");

        for (int i = 0; i <6 ; i++) {
            logger.info("current Message From myThread");
            log.info("current Message From Main Thread");
        }
        return "success";
    }
}

最终测试效果如下所示:
Java 日志框架之 Logback 学习攻略_第3张图片
2019-08-20 文件夹内容如下:
Java 日志框架之 Logback 学习攻略_第4张图片
test 文件夹内容如下:
Java 日志框架之 Logback 学习攻略_第5张图片

5. 参考资料

  • logback 官方手册
  • 为什么阿里巴巴禁止工程师直接使用日志系统(Log4j、Logback)中的 API
  • logback自定义logger的java代码

你可能感兴趣的:(日志框架)