SpringBoot系列-- SpringBoot整合SLF4j+log4j2+aop记录web请求

1. 项目环境

  • IDEA 2020.1.4
  • Maven 3.6
  • JDK 1.8
  • SpringBoot 2.x

项目文件在GitHub(欢迎star⭐):
https://github.com/Gang-bb/Gangbb-SpringBoot

如有疑问或是建议,欢迎评论区留言或者QQ:949526365

2. 选型思考

image-20210122145522388

Java 中比较常用的日志框架

  • log4j(Log for Java):Apache 的一个开源项目,七种日志级别:OFF、FATAL、ERROR、WARN、INFO、DEBUG、TRACE
  • logback:是一个很成熟的日志框架,其实 logBack 和 log4j 出自一个人之手,这个人就是 Ceki Gülcü。logback 比 log4j 大约快 10 倍、消耗更少的内存,迁移成本也很低,自动压缩日志、支持多样化配置、不需要重启就可以恢复 I/O 异常等优势
  • log4j2:作者认为,log4j2已经不仅仅是 log4j 的一个升级版本了,而是从头到尾被重写的,这可以认为这其实就是完全不同的两个框架

Spring Boot 默认使用 logback,但相比较而言,log4j2 在性能上面会更好。SpringBoot 高版本都不再支持 log4j,而是支持 log4j2。log4j2,在使用方面与 log4j 基本上没什么区别,比较大的区别是 log4j2 不再支持 properties 配置文件,支持 xml、json 格式的文件。

Java 简易日志门面(Simple Logging Facade for Java,缩写 SLF4J),它并不是真正的日志框架,他是对所有日志框架制定的一种规范、标准、接口,并不是一个框架的具体的实现,因为接口并不能独立使用,需要和具体的日志框架实现配合使用。可以在软件部署的时候决定要使用的 Logging 框架,目前主要支援的有 Java logging API、log4j 及 logback 等框架。

接口用于定制规范,可以有多个实现,使用时是面向接口的(导入的包都是 slf4j 的包而不是具体某个日志框架中的包),即直接和接口交互,不直接使用实现,所以可以任意的更换实现而不用更改代码中的日志相关代码。

比如:slf4j 定义了一套日志接口,项目中使用的日志框架是logback,开发中调用的所有接口都是 slf4j 的,不直接使用 logback,调用是 自己的工程调用 slf4j 的接口,slf4j 的接口去调用 logback 的实现,可以看到整个过程应用程序并没有直接使用 logback,当项目需要更换更加优秀的日志框架时(如log4j2)只需要引入 log4j2 的 jar 和 Llg4j2 对应的配置文件即可,完全不用更改 Java 代码中的日志相关的代码 logger.info(“xxx”),也不用修改日志相关的类的导入的包( import org.slf4j.Logger; import org.slf4j.LoggerFactory;)

总结:使用日志接口便于更换为其他日志框架。

PS:以上为参考文章截取

阿里巴巴Java开发手册中对于日志的要求:

image-20210122145848032

毕竟大厂沉淀的开发建议,还是很有借鉴意义的!

综上分析,决定使用 日志门面:SLF4J 日志实现:log4j2

3. 配置log4j2

3.1 引入Maven依赖

        <!--日志相关-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
        </dependency>

此时要排除默认自带的log组件

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <!--以下是排除的组件-->
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
</dependency>

3.2 配置文件

配置文件的路径:src/main/resources/log4j2.xml

image-20210122150730327

配置文件内容:

<?xml version="1.0" encoding="UTF-8"?>
<!--
    6个优先级从高到低依次为:OFF、FATAL、ERROR、WARN、INFO、DEBUG、TRACE、 ALL。
    如果设置优先级为WARN,那么OFF、FATAL、ERROR、WARN 4个级别的log能正常输出
    设置为OFF 表示不记录log4j2本身的日志,
 -->
<!-- status:用来指定log4j本身的打印日志级别,monitorInterval:指定log4j自动重新配置的监测间隔时间 -->
<Configuration status="fatal">
  <Properties>
    <Property name="baseDir" value="${sys:user.home}/logs/Gangbb-Springboot-Log"/>
  </Properties>

  <Appenders>
    <Console name="Console" target="SYSTEM_OUT">
      <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) -->
      <ThresholdFilter level="info" onMatch="ACCEPT"
        onMismatch="DENY"/>
      <PatternLayout
        pattern="[%d{MM:dd HH:mm:ss.SSS}] [%level] [%logger{36}] - %msg%n"/>
    </Console>

    <!--debug级别日志文件输出-->
    <RollingFile name="debug_appender" fileName="${baseDir}/debug.log"
      filePattern="${baseDir}/debug_%i.log.%d{yyyy-MM-dd}">
      <!-- 过滤器 -->
      <Filters>
        <!-- 限制日志级别在debug及以上在info以下 -->
        <ThresholdFilter level="debug"/>
        <ThresholdFilter level="info" onMatch="DENY" onMismatch="NEUTRAL"/>
      </Filters>
      <!-- 日志格式 -->
      <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
      <!-- 策略 -->
      <Policies>
        <!-- 每隔一天转存 -->
        <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
        <!-- 文件大小 -->
        <SizeBasedTriggeringPolicy size="100 MB"/>
      </Policies>
      <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
      <DefaultRolloverStrategy max="15"/>
    </RollingFile>

    <!-- info级别日志文件输出 -->
    <RollingFile name="info_appender" fileName="${baseDir}/info.log"
      filePattern="${baseDir}/info_%i.log.%d{yyyy-MM-dd}">
      <!-- 过滤器 -->
      <Filters>
        <!-- 限制日志级别在info及以上在error以下 -->
        <ThresholdFilter level="info"/>
        <ThresholdFilter level="error" onMatch="DENY" onMismatch="NEUTRAL"/>
      </Filters>
      <!-- 日志格式 -->
      <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
      <!-- 策略 -->
      <Policies>
        <!-- 每隔一天转存 -->
        <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
        <!-- 文件大小 -->
        <SizeBasedTriggeringPolicy size="100 MB"/>
      </Policies>
      <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
      <DefaultRolloverStrategy max="15"/>
    </RollingFile>

    <!-- error级别日志文件输出 -->
    <RollingFile name="error_appender" fileName="${baseDir}/error.log"
      filePattern="${baseDir}/error_%i.log.%d{yyyy-MM-dd}">
      <!-- 过滤器 -->
      <Filters>
        <!-- 限制日志级别在error及以上 -->
        <ThresholdFilter level="error"/>
      </Filters>
      <!-- 日志格式 -->
      <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/>
      <Policies>
        <!-- 每隔一天转存 -->
        <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
        <!-- 文件大小 -->
        <SizeBasedTriggeringPolicy size="100 MB"/>
      </Policies>
      <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件开始覆盖-->
      <DefaultRolloverStrategy max="15"/>
    </RollingFile>
  </Appenders>
  <!--Logger节点用来单独指定日志的形式,比如要为指定包下的class指定不同的日志级别等。-->
  <!--然后定义loggers,只有定义了logger并引入的appender,appender才会生效-->
  <Loggers>
    <!--过滤掉spring和mybatis的一些无用的DEBUG信息-->
    <logger name="org.mybatis" level="info" additivity="false">
      <AppenderRef ref="Console"/>
    </logger>
    <!--监控系统信息-->
    <!--若是additivity设为false,则 子Logger 只会在自己的appender里输出,而不会在 父Logger 的appender里输出。-->
    <Logger name="org.springframework" level="info" additivity="false">
      <AppenderRef ref="Console"/>
    </Logger>

    <Root level="debug">
      <AppenderRef ref="Console"/>
      <AppenderRef ref="debug_appender"/>
      <AppenderRef ref="info_appender"/>
      <AppenderRef ref="error_appender"/>
    </Root>

  </Loggers>
</Configuration>

配置好后重启下项目看看文件:

image-20210123094420457

成功生成!

4. AOP统一打印Web请求和返回信息

4.1 引入AOP切面依赖

        <!--AOP统一打印和返回信息-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency> 
        <!-- 解析 UserAgent信息 -->
        <dependency>
            <groupId>eu.bitwalker</groupId>
            <artifactId>UserAgentUtils</artifactId>
            <version>1.21</version>
        </dependency>

4.2 定义打印请求和返回信息过滤器

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import eu.bitwalker.useragentutils.OperatingSystem;
import eu.bitwalker.useragentutils.UserAgent;
import eu.bitwalker.useragentutils.Version;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;


/**
 * @author : Gangbb
 * @ClassName : WebLogAspect
 * @Description : 网络请求和响应过滤器
 * @Date : 2021/1/11 22:11
 */
@Aspect
@Component
public class WebLogAspect {
    /**
     * 定义一个log用于打印
     */
    private final Logger log = LoggerFactory.getLogger(this.getClass());
	
    /**
     * 切入点
     */
    @Pointcut("execution(public * com.gangbb.gangbbspringbootlog.controller.*.*(..)))")
    public void weblog(){

    }

    @Before("weblog()")
    public void doBefore(JoinPoint joinPoint){
        //收到请求,记录请求内容
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        //获取当前请求
        HttpServletRequest request = attributes.getRequest();
        //获取浏览器信息
        String header = request.getHeader("User-Agent");
        //转成UserAgent对象
        UserAgent browser = UserAgent.parseUserAgentString(header);
        //获取浏览器版本号
        Version version = browser.getBrowser().getVersion(request.getHeader("User-Agent"));
        //获取系统信息
        OperatingSystem os = browser.getOperatingSystem();

        //记录真正的内容
        log.info("URL : " + request.getRequestURL().toString());
        log.info("HTTP_METHOD :" + request.getMethod());
        log.info("IP : " + request.getRemoteAddr());
        log.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "."
                + joinPoint.getSignature().getName());
        log.info("ARGS : " + Arrays.toString(joinPoint.getArgs()));
        if(browser.getBrowser()!= null && version != null){
            log.info("BROWSER/VERSION : " + browser.getBrowser().getName() + "/" + version.getVersion());
        }
        if(os != null){
            log.info("OS_NAME : " + os.getName());
        }


    }
    //返回时拦截
    @AfterReturning(returning = "res", pointcut = "weblog()")
    public void doAfterReturning(Object res) throws JsonProcessingException {
        //处理完请求,返回内容
        //new ObjectMapper()用于对象转json
        log.info("RESPONSE : " + new ObjectMapper().writeValueAsString(res));
    }
}

4.3 请求测试查看打印信息

image-20210123095149337 image-20210123095350148

5. 参考文章

https://blog.youkuaiyun.com/weixin_34162228/article/details/88809217

https://blog.youkuaiyun.com/chengqiuming/article/details/105692373

https://www.cnblogs.com/windpoplar/p/12006641.html

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值