SpringBoot中基于AOP打印接口日志和全局异常处理

新建SpringBoot项目

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class LogApplication {

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

引入web、aop和log4j2依赖包

dependencies {
    compile('org.springframework.boot:spring-boot-starter-web')
    compile('org.springframework.boot:spring-boot-starter-aop')
    compile('org.springframework.boot:spring-boot-starter-log4j2')
}

新增log4j2配置文件

<?xml version="1.0" encoding="UTF-8"?>

<!-- t01Synchronizedon't forget to set system property
-DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
     to make all loggers asynchronous. -->

<!-- status="OFF",可以去掉,它的含义为是否记录log4j2本身的event信息,默认是OFF -->
<configuration status="OFF">
    <!-- 定义下面的引用名 -->
    <Properties>
        <!-- <property name="basePath">系统日志位置</property> -->
        <property name="basePath">/web/demo/log</property>
        <!-- 暂时业务都输出到同一个日志文件 -->
        <property name="server">${basePath}/web-log</property>
    </Properties>
    <!--先定义所有的appender -->
    <appenders>
        <!--输出控制台的配置 -->
        <Console name="Console" target="SYSTEM_OUT">
            <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) -->
            <ThresholdFilter level="trace" onMatch="ACCEPT" onMismatch="DENY"/>
            <!--输出日志的格式 -->
            <PatternLayout
                    pattern="%d{HH:mm:ss,SSS} [%t01Synchronized] %X{X-B3-TraceId} %X{X-B3-SpanId} %X{X-B3-ParentSpanId} %-5level %logger{36} %L %M - %msg%n"/>
        </Console>

        <!--打印出所有的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档 -->
        <!-- 按月生成归档日志,可以使用 filePattern="logs/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}aspect.log" -->
        <RollingRandomAccessFile name="RollingRandomAccessFile"
                                 filename="${server}.log" filePattern="${server}-%d{yyyy-MM-dd}.log"
                                 append="true">
            <ThresholdFilter level="trace" onMatch="ACCEPT" onMismatch="DENY"/>
            <PatternLayout>
                <Pattern>[%d{yyyy-MM-dd HH:mm:ss,SSS}] [%t01Synchronized] [%X{X-B3-TraceId}] [%X{X-B3-SpanId}] [%X{X-B3-ParentSpanId}] [%-5level] [%class{36}] [%L] [%M] [%msg%xEx]%n</Pattern>
            </PatternLayout>
            <Policies>
                <SizeBasedTriggeringPolicy size="100 MB"/>
                <TimeBasedTriggeringPolicy interval="1"/>
            </Policies>
            <DefaultRolloverStrategy max="2"/>
        </RollingRandomAccessFile>
    </appenders>
    <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效 -->
    <loggers>
        <root level="info">
            <appender-ref ref="RollingRandomAccessFile"/>
            <appender-ref ref="Console"/>
        </root>
    </loggers>
</configuration>

自定义切入点

import java.lang.annotation.*;

/**
 * @Author: vhtk
 * @Description:
 * @Date: 2020/6/22
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WriteLog {

}

自定义切面

import com.alibaba.fastjson.JSONObject;
import org.aspectj.lang.JoinPoint;
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.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import javax.servlet.ServletRequest;
import java.lang.reflect.Method;

/**
 * @Author: vhtk
 * @Description:
 * @Date: 2020/6/22
 */
@Component
@Aspect
@Profile("default")
public class WriteLogHandler {

    @Pointcut(value = "@annotation(com.example.log.aspect.WriteLog) || execution(* com.example.log..*Controller.*(..))")
    public void pointCut() {

    }

    /**
     * 日志
     */
    private static Logger logger = LoggerFactory.getLogger(WriteLogHandler.class);

    /**
     * 字符数量
     */
    private static final int LOG_SIZE = 2048;


    /**
     * 切入点声明
     */
    private static String LINE_SPLIT = "\n|\t\t\t";


    /**
     * 方法切入点(输出方法执行概要日志)
     *
     * @param joinPoint 连接点
     * @return 原方法返回值
     * @throws Throwable 异常
     */
    @Around(value = "pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        StringBuilder sb = new StringBuilder();
        printStart(sb);
        sb.append(LINE_SPLIT);
        sb.append("执行方法:");
        buildMethodInfo(joinPoint, sb);
        sb.append(LINE_SPLIT);

        try {
            Object retVal = joinPoint.proceed();
            String json = JSONObject.toJSONString(retVal);
            if (!StringUtils.isEmpty(json) && json.length() >= LOG_SIZE) {
                json = json.substring(0, LOG_SIZE);
            }
            sb.append("返回值:").append(json);
            return retVal;
        } catch (Throwable ex) {
            sb.append("发生异常:").append(ex.getMessage());
            throw ex;
        } finally {
            //调用
            sb.append(LINE_SPLIT);
            sb.append("耗时(ms):");
            //计算方法耗时
            sb.append(System.currentTimeMillis() - startTime);
            printEnd(sb);
            logger.info(sb.toString());
        }
    }

    private void buildMethodInfo(JoinPoint joinPoint, StringBuilder stringBuilder) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        //方法
        stringBuilder.append(method.toGenericString());
        stringBuilder.append(LINE_SPLIT);
        stringBuilder.append("参数依次为:");
        for (int i = 0; i < joinPoint.getArgs().length; i++) {

            stringBuilder.append(LINE_SPLIT);
            stringBuilder.append("\t\t");
            stringBuilder.append(i);
            stringBuilder.append(":");
            Object argument = joinPoint.getArgs()[i];
            if (argument instanceof ServletRequest) {
                stringBuilder.append(argument);
            } else {
                stringBuilder.append(JSONObject.toJSON(argument));
            }
        }
    }

    private void printStart(StringBuilder stringBuilder) {
        stringBuilder.append("\n+-----------------");
    }

    private void printEnd(StringBuilder stringBuilder) {
        stringBuilder.append("\n\\__________________");
    }
}

全局异常处理器

package com.example.log.aspect;

import com.example.log.common.BusinessException;
import com.example.log.common.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

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


/**
 * @Author: vhtk
 * @Description:
 * @Date: 2020/6/22
 */
@ControllerAdvice
public class ControllerExceptionHandler {
    private final Logger logger = LoggerFactory.getLogger(getClass());

    @ExceptionHandler(Throwable.class)
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public Object handleException(Throwable e, HttpServletRequest req) {
        logger.error("Catch business exception: {}", e.getMessage(), e);
        if (e instanceof MethodArgumentNotValidException) {
            MethodArgumentNotValidException bind = (MethodArgumentNotValidException) e;
            List<ObjectError> allErrors = bind.getBindingResult().getAllErrors();
            StringBuffer message = new StringBuffer();
            allErrors.forEach(error -> message.append(error.getDefaultMessage()).append(";"));
            return Result.fail("请求参数错误" + req.getRequestURL().toString(), message.toString());
        } else if (e instanceof BusinessException) {
            BusinessException businessException = (BusinessException) e;
            return Result.info(businessException.getCode(), "请求发生异常" + req.getRequestURL().toString(), businessException.getMessage());
        } else {
            return Result.fail("请求发生异常" + req.getRequestURL().toString(), e.getMessage());
        }
    }
}

测试代码

import com.example.log.aspect.WriteLog;
import com.example.log.dto.TestDto;
import com.example.log.service.TestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
 * @Author: vhtk
 * @Description:
 * @Date: 2020/6/22
 */
@RestController
public class TestController {

    @Autowired
    private TestService testService;

    @GetMapping("/get")
    public Integer testGet(@RequestParam Integer id){
        testService.testGet(id);
        return id;
    }

    @PostMapping("/post")
    @WriteLog
    public void testPost(@RequestBody TestDto testDto){
        testService.testPost(testDto);
    }

    @PutMapping("/put")
    public void testPut(@RequestBody TestDto testDto){
        testService.testPut(testDto);
    }

    @DeleteMapping("/delete")
    public void testDelete(@RequestParam Integer id){
        testService.testDelete(id);
    }
}

运行效果

接口正常返回
在这里插入图片描述
接口异常
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值