SpringBoot整合logback实现日志输出

19 篇文章 0 订阅
15 篇文章 0 订阅

SpringBoot整合logback实现日志输出

本文介绍了SpringBoot整合logback实现日志输出的配置流程,主要包括:

1、使用logback-spring.xml配置logback

2、使用webFilter拦截WEB请求

3、使用自定义的拦截器拦截其他形式触发的交易

1、增加配置文件

1.1、增加logback-spring.xml配置

在resources目录下,新建一个xml配置文件,内容如下:

  • logback-spring.xml
<?xml version="1.0" encoding="UTF-8" ?>
<configuration scan="false">
    <!-- use application.properties -->
    <springProperty name="APP_NAME" source="spring.application.name"/>

    <property name="log.dir" value="${user.home}/log/" />
    <timestamp key="ymd" datePattern="yyyy-MM-dd"/>
    <timestamp key="byMillionSecond" datePattern="HH_mm_ss.SSS"/>
    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{0} [%X{traceId}] - %msg%n</pattern>
        </encoder>
    </appender>
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">

            <!--日志文件输出的文件名-->
            <FileNamePattern>${log.dir}/${APP_NAME}.%d{yyyy-MM-dd}.%i.log</FileNamePattern>
            <!--日志文件保留天数-->
            <MaxHistory>30</MaxHistory>
            <!-- 每个日志文件的大小上线,超过这个上线之后就产生新的文件 -->
            <maxFileSize>20MB</maxFileSize>
            <totalSizeCap>300GB</totalSizeCap>
        </rollingPolicy>
        <append>true</append>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{0} [%X{traceId}] - %msg%n</pattern>
            <charset class="java.nio.charset.Charset">UTF-8</charset>
        </encoder>
    </appender>

    <!-- 日志输出级别 -->
    <root level="INFO">
        <!-- 控制台 生产去除-->
        <appender-ref ref="STDOUT"/>
        <!-- 交易及异常日志-->
        <appender-ref ref="FILE"/>
    </root>
    <!-- 增加MyBatis的日志输出功能,如果别的模块有需要打印的,需要copy下面的代码,并将name改为对应dao存放的完整包路径即可 -->
    <logger name="com.iambest.druid.dao" level="debug" />
</configuration>

1.2、配置application.properties

在application.properties文件中增加一行配置:

  • application.properties
spring.application.name=druid_study

1.3、logback-spring.xml配置讲解

这里我们定义了一个变量,APP_NAME,值取的是application.properties中配置的spring.application.name这个键

<springProperty name="APP_NAME" source="spring.application.name"/>

这里定义了日志的存放目录,使用用户主目录下的log目录,兼容不同的操作系统,支持linux和win以及mac os

<property name="log.dir" value="${user.home}/log/" />

这里定义了控制台输出的格式,其中我们定义了一个自定义的参数traceId,这个后续我们会讲解

	<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{0} [%X{traceId}] - %msg%n</pattern>
        </encoder>
    </appender>

这里定义了打印到文件中的输出格式,同样的也加入了一个自定义的参数traceId

    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">

            <!--日志文件输出的文件名-->
            <FileNamePattern>${log.dir}/${APP_NAME}.%d{yyyy-MM-dd}.%i.log</FileNamePattern>
            <!--日志文件保留天数-->
            <MaxHistory>30</MaxHistory>
            <!-- 每个日志文件的大小上线,超过这个上线之后就产生新的文件 -->
            <maxFileSize>20MB</maxFileSize>
            <totalSizeCap>300GB</totalSizeCap>
        </rollingPolicy>
        <append>true</append>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{0} [%X{traceId}] - %msg%n</pattern>
            <charset class="java.nio.charset.Charset">UTF-8</charset>
        </encoder>
    </appender>

这里定义日志的输出级别,以及使用那些配置;注意,控制台的输出需要在生产环境去除掉

    <!-- 日志输出级别 -->
    <root level="INFO">
        <!-- 控制台 生产去除-->
        <appender-ref ref="STDOUT"/>
        <!-- 交易及异常日志-->
        <appender-ref ref="FILE"/>
    </root>

这里定义了MyBatis的日志输出配置,需要将我们的dao所在的包定义进去;如果有多个,则需要新建一个logger

    <!-- 增加MyBatis的日志输出功能,如果别的模块有需要打印的,需要copy下面的代码,并将name改为对应dao存放的完整包路径即可 -->
    <logger name="com.iambest.druid.dao" level="debug" />

2、编写自定义拦截器

在第一节中,我们的配置文件里面增加了一个自定义的参数traceId,我们用来追踪一笔交易,方便我们进行检索;交易的触发有几种方式,我们这里拦截常用的两种方式:一种是web互联网发起的交易,另一种则是系统定时或者异步交易发起的;

对于异步或者定时触发的交易,我们拦截使用了Action结尾的交易;当然你也可以自定义其他的方式,都是可以支持的。

2.1、增加Web拦截器

定义一个Web拦截器,使用MDC.put(),增加自定义的日志参数

2.1.1、编写Filter

编写LogMdcFilter

拦截/*下的请求,对所有的请求都增加一个自定义的参数traceId

  • LogMdcFilter
package com.iambest.druid.filter;

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

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
import java.util.UUID;

/**
 * @author zhang_wei
 * @version 1.0.0
 * @Classname LogMdcFilter
 * @Date 2021/2/27 23:31
 * @Created by zhang_wei
 * @since 1.0.0
 */
@WebFilter(urlPatterns = "/*", filterName = "logMdcFilter")
public class LogMdcFilter implements Filter {

    private static final String TRACE_ID = "traceId";

    private static final Logger logger = LoggerFactory.getLogger(LogMdcFilter.class);


    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        logger.info("LogMdcFilter...");
        insertMdc();
        try {
            filterChain.doFilter(servletRequest, servletResponse);
        }finally {
            MDC.remove(TRACE_ID);
        }

    }

    @Override
    public void destroy() {

    }

    /**
     * 增加日志的序号
     */
    private void insertMdc(){
        UUID uuid = UUID.randomUUID();
        MDC.put(TRACE_ID, uuid.toString());
    }
}
2.1.2、将Filter加到Spring容器中

我们定义了Filter之后,需要将Filter加入到Spring的容器中才能生效;有两种方法:

1、在Filter上增加@Service或者@Component注解

2、在Application主类上增加@ServletComponentScan注解

我这里使用@ServletComponentScan注解

  • DruidApplication
@ServletComponentScan(basePackages = {"com.iambest.druid.filter"})
@SpringBootApplication
public class DruidApplication {

    public static void main(String[] args) {
        SpringApplication.run(DruidApplication.class, args);
    }
}

2.2、 自定义异步触发交易的拦截器

1、我们需要引入切面的包,在pom.xml中进行引入

2、指定包下所有以Action结尾命名的类,都进行拦截并处理

  • pom.xml
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.13</version>
</dependency>
  • LogMdcAspect
package com.iambest.druid.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;

import java.util.UUID;

/**
 *
 * 切面日志
 *
 * @author zhang_wei
 * @version 1.0.0
 * @Classname LogMdcAspect
 * @Date 2021/2/28 9:41
 * @Created by zhang_wei
 * @since 1.0.0
 */
@Aspect
@Component
public class LogMdcAspect {

    private static final String TRACE_ID = "traceId";

    @Pointcut("within(com.iambest.druid..*Action*) &&execution(public * *(..))")
    public void logPointCut(){

    }

    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        MDC.put(TRACE_ID, UUID.randomUUID().toString());
        Object result = point.proceed();
        MDC.remove(TRACE_ID);
        return result;
    }

}

3、测试

分两部分进行测试

1、测试Web接入的交易

2、测试一个其他方式触发的交易,

3.1、编写Controller

我们编写一个Controller,拦截getAll请求,查询数据库中的数据,并通过日志输出结果

  • StudInfoController
/**
 * @author zhang_wei
 * @version 1.0.0
 * @Classname StudInfoController
 * @Date 2021/2/26 11:07
 * @Created by zhang_wei
 * @since 1.0.0
 */
@Controller
public class StudInfoController {

    Logger logger = LoggerFactory.getLogger(StudInfoController.class);

    @Autowired
    StudentInfoService studentInfoService;

    @RequestMapping("/getAll")
    @ResponseBody
    public List<StudentInfo> getAllStudentInfos() {
        List<StudentInfo> result = studentInfoService.findAll();
        logger.info("result={}", result);
        return result;
    }
}

3.2、编写一个RunnerImpl及Action类

我们这里编写一个ApplicationRunnerImpl类,实现ApplicationRunner接口,实现在容器启动市执行一些代码操作

这里我们吊起一个线程类,在这个TimerRunnerThread线程类中新启动一个Executor线程来模拟定时触发交易

  • ApplicationRunnerImpl
package com.iambest.druid.runner;

import com.iambest.druid.thread.TimerRunnerThread;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

/**
 * @author zhang_wei
 * @version 1.0.0
 * @Classname ApplicationRunnerImpl
 * @Date 2021/3/1 21:04
 * @Created by zhang_wei
 * @since 1.0.0
 */
@Component
public class ApplicationRunnerImpl implements ApplicationRunner {

    @Autowired
    TimerRunnerThread thread;

    @Override
    public void run(ApplicationArguments applicationArguments) throws Exception {
        thread.start();
    }
}
  • TimerRunnerThread
package com.iambest.druid.thread;

import com.iambest.druid.action.TimerRunnerAction;
import com.iambest.druid.util.SpringContextUtil;
import org.springframework.stereotype.Service;

/**
 * @author zhang_wei
 * @version 1.0.0
 * @Classname TimerRunnerThread
 * @Date 2021/3/5 14:08
 * @Created by zhang_wei
 * @since 1.0.0
 */
@Service
public class TimerRunnerThread extends Thread{

    @Override
    public void run() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        Executor executor = new Executor();
        executor.start();
    }

    class Executor extends Thread {
        @Override
        public void run() {
            int i = 0;
            while (i < 5){
                TimerRunnerAction action = SpringContextUtil.getBean(TimerRunnerAction.class);
                action.execute();
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {

                }
                i++ ;
            }
        }
    }
}
  • TimerRunnerAction
package com.iambest.druid.action;

import com.iambest.druid.entity.StudentInfo;
import com.iambest.druid.service.StudentInfoService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;

/**
 * @author zhang_wei
 * @version 1.0.0
 * @Classname TimerRunnerAction
 * @Date 2021/3/5 14:30
 * @Created by zhang_wei
 * @since 1.0.0
 */
@Component
public class TimerRunnerAction {

    static final Logger logger = LoggerFactory.getLogger(TimerRunnerAction.class);

    @Autowired
    StudentInfoService studentInfoService;

    public void execute() {
        logger.info("TimerRunnerAction={}", getClass());
        List<StudentInfo> result = studentInfoService.findAll();
        logger.info("result={}", result);
    }
}

3.3、启动服务进行测试

我们启动服务,查看控制台的输出,如下:

  • 验证模拟定时触发的输出

在这里插入图片描述

  • 验证web入口的日志输出

在这里插入图片描述

  • 查看文件

在我的用户目录下的log目录,

存在一个按照我们配置生成的文件,查看其中的日志输出,也是正常的,也正确的打印出我们自定义的参数

如下:

在这里插入图片描述

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jack_David

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值