文章目录
- 1. 导入依赖
- 2. aop拦截器
- 3. logback配置
- 4. 测试类
- 5. 关键点
- 6. 效果图
1. 导入依赖
<!-- AOP --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency><!-- 数据序列化--><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.78</version></dependency>
2. aop拦截器
package com.course.server.config;import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.support.spring.PropertyPreFilters;
import com.course.server.util.UuidUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
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.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Field;@Aspect
@Component
public class LogAspect {private final static Logger LOG = LoggerFactory.getLogger(LogAspect.class);/** 定义一个切点 */@Pointcut("execution(public * com.course.*.controller..*Controller.*(..))")public void controllerPointcut() {}@Before("controllerPointcut()")public void doBefore(JoinPoint joinPoint) throws Throwable {// 日志编号MDC.put("UUID", UuidUtil.getShortUuid());// 开始打印请求日志ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getRequest();Signature signature = joinPoint.getSignature();String name = signature.getName();// 打印业务操作String nameCn = "";if (name.contains("list") || name.contains("query")) {nameCn = "查询";} else if (name.contains("save")) {nameCn = "保存";} else if (name.contains("delete")) {nameCn = "删除";} else {nameCn = "操作";}// 使用反射,获取业务名称Class clazz = signature.getDeclaringType();Field field;String businessName = "";try {field = clazz.getField("BUSINESS_NAME");if (!StringUtils.isEmpty(field)) {businessName = (String) field.get(clazz);}} catch (NoSuchFieldException e) {LOG.error("未获取到业务名称");} catch (SecurityException e) {LOG.error("获取业务名称失败", e);}// 打印请求信息LOG.info("------------- 【{}】{}开始 -------------", businessName, nameCn);LOG.info("请求地址: {} {}", request.getRequestURL().toString(), request.getMethod());LOG.info("类名方法: {}.{}", signature.getDeclaringTypeName(), name);LOG.info("远程地址: {}", request.getRemoteAddr());// 打印请求参数Object[] args = joinPoint.getArgs();Object[] arguments = new Object[args.length];for (int i = 0; i < args.length; i++) {if (args[i] instanceof ServletRequest|| args[i] instanceof ServletResponse|| args[i] instanceof MultipartFile) {continue;}arguments[i] = args[i];}// 排除字段,敏感字段或太长的字段不显示String[] excludeProperties = {"shard"};PropertyPreFilters filters = new PropertyPreFilters();PropertyPreFilters.MySimplePropertyPreFilter excludefilter = filters.addFilter();excludefilter.addExcludes(excludeProperties);LOG.info("请求参数: {}", JSONObject.toJSONString(arguments, excludefilter)); // 为空的会不打印,但是像图片等长字段也会打印}@Around("controllerPointcut()")public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {long startTime = System.currentTimeMillis();Object result = proceedingJoinPoint.proceed();// 排除字段,敏感字段或太长的字段不显示String[] excludeProperties = {"password", "shard"};PropertyPreFilters filters = new PropertyPreFilters();PropertyPreFilters.MySimplePropertyPreFilter excludefilter = filters.addFilter();excludefilter.addExcludes(excludeProperties);LOG.info("返回结果: {}", JSONObject.toJSONString(result, excludefilter));LOG.info("------------- 结束 耗时:{} ms -------------", System.currentTimeMillis() - startTime);return result;}}
3. logback配置
logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration><!-- 修改一下路径--><property name="PATH" value="/log/imooc/course/business"></property><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder>
<!-- <Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight(%-5level) %blue(%-50logger{50}:%-4line) %green(%-8X{UUID}) %msg%n</Pattern>--><Pattern>%d{ss.SSS} %highlight(%-5level) %blue(%-30logger{30}:%-4line) %green(%-8X{UUID}) %msg%n</Pattern></encoder></appender><appender name="TRACE_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${PATH}/trace.log</file><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><FileNamePattern>${PATH}/trace.%d{yyyy-MM-dd}.%i.log</FileNamePattern><timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"><maxFileSize>10MB</maxFileSize></timeBasedFileNamingAndTriggeringPolicy></rollingPolicy><layout><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %-50logger{50}:%-4line %green(%-8X{UUID}) %msg%n</pattern></layout></appender><appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>${PATH}/error.log</file><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><FileNamePattern>${PATH}/error.%d{yyyy-MM-dd}.%i.log</FileNamePattern><timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"><maxFileSize>10MB</maxFileSize></timeBasedFileNamingAndTriggeringPolicy></rollingPolicy><layout><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %-50logger{50}:%-4line %green(%-8X{UUID}) %msg%n</pattern></layout><filter class="ch.qos.logback.classic.filter.LevelFilter"><level>ERROR</level><onMatch>ACCEPT</onMatch><onMismatch>DENY</onMismatch></filter></appender><root level="ERROR"><appender-ref ref="ERROR_FILE" /></root><root level="TRACE"><appender-ref ref="TRACE_FILE" /></root><root level="INFO"><appender-ref ref="STDOUT" /></root>
</configuration>
4. 测试类
package com.course.business.controller.admin;import com.course.server.dto.ChapterDto;
import com.course.server.dto.PageDto;
import com.course.server.dto.ResponseDto;
import com.course.server.service.ChapterService;
import com.course.server.util.ValidatorUtil;
import org.springframework.web.bind.annotation.*;import javax.annotation.Resource;@RestController
@RequestMapping("/admin/chapter")
public class ChapterController {// 业务标识public static final String BUSINESS_NAME = "大章";@Resourceprivate ChapterService chapterService;//http://127.0.0.1:9002/business/admin/chapter/list@PostMapping("/list")public ResponseDto list(@RequestBody PageDto pageDto) {ResponseDto responseDto = new ResponseDto();chapterService.list(pageDto);responseDto.setContent(pageDto);return responseDto;}@PostMapping("/save")public ResponseDto save(@RequestBody ChapterDto chapterDto) {// 保存校验ValidatorUtil.require(chapterDto.getName(), "名称");ValidatorUtil.require(chapterDto.getCourseId(), "课程ID");ValidatorUtil.length(chapterDto.getCourseId(), "课程ID", 1, 8);ResponseDto responseDto = new ResponseDto();chapterService.save(chapterDto);responseDto.setContent(chapterDto);return responseDto;}@DeleteMapping("/delete/{id}")public ResponseDto delete(@PathVariable String id) {ResponseDto responseDto = new ResponseDto();chapterService.delete(id);return responseDto;}
}
5. 关键点
LogAspect中日志key “UUID”要和logback.xml中的获取的key UUID要一致,UUID生成规则自定义
6. 效果图