1 创建springboot项目
(ps:本文不做详细介绍,可以阅读另一篇博客:https://www.cnblogs.com/liyhbk/p/13572989.html)
1.1 添加pom依赖
<dependencies>
<!--Spring Boot Web 基础环境-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--Spring Boot 测试环境-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.xnx3.util/xnx3-util -->
<dependency>
<groupId>com.xnx3.util</groupId>
<artifactId>xnx3-util</artifactId>
<version>1.0.0</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
<!--aop-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.1.7.RELEASE</version>
</dependency>
<!--hutool-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>4.5.5</version>
</dependency>
<!--接口文档-->
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<version>1.5.21</version>
</dependency>
</dependencies>
1.2 配置application.yml文件
# 配置端口 server: port: 8084 spring: # 配置数据源 datasource: url: jdbc:mysql://localhost:3306/db1?useSSL=false&serverTimezone=UTC username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver mybatis: # 配置mapper.xml 文件所在的路径 mapper-locations: classpath:mapper/*.xml # 配置映射类所在的路径 type-aliases-package: com.liyh.entity # 开启驼峰映射 configuration: map-underscore-to-camel-case: true #打印sql logging: level: com.liyh.mapper: debug
1.3 创建logback.xml文件
<?xml version="1.0" encoding="UTF-8"?> <configuration debug="false"> <!-- 彩色日志依赖的渲染类 --> <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/> <conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/> <conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/> <!-- 彩色日志格式 --> <property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/> <!-- Console 输出设置 --> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>${CONSOLE_LOG_PATTERN}</pattern> <charset>utf8</charset> </encoder> </appender> <!-- 日志输出级别 --> <root level="INFO"> <appender-ref ref="STDOUT"/> </root> </configuration>
2 配置AOP拦截器
2.1 spring-aop注解拦截顺序
2.1.1 正常运行:
2.1.2 程序报错:
2.2 创建控制器切面类 LogAspectj
package com.liyh.log; import com.liyh.entity.ExceptionLog; import com.liyh.entity.SqlLog; import com.liyh.entity.UserLog; import com.liyh.service.LogService; import com.liyh.utils.SqlUtils; import org.apache.ibatis.session.SqlSessionFactory; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.*; import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; 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 javax.servlet.http.HttpServletRequest; import java.lang.reflect.Method; /** * 控制器切面 * @Author: liyh * @Date: 2020/9/17 14:08 */ @Aspect @Component public class LogAspectj { @Autowired private LogService logService; @Autowired SqlSessionFactory sqlSessionFactory; private static Logger logger = LoggerFactory.getLogger(LogAspectj.class); /** * @Pointcut : 创建一个切点,方便同一方法的复用。 * value属性就是AspectJ表达式, */ @Pointcut("execution(* com.liyh.controller.*.*(..))") //@Pointcut("@annotation(com.liyh.log.LogAnno)") public void userLog() { } @Pointcut("execution(* com.liyh.mapper.*.*(..))") public void sqlLog() { } @Pointcut("execution(* com.liyh.controller.*.*(..))") public void exceptionLog() { } //前置通知 //指定该方法是前置通知,并指定切入点 @Before("userLog()") public void userLog(JoinPoint pj) { try { UserLog userLog = new UserLog(); HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); String method = request.getMethod(); Signature signature = pj.getSignature(); MethodSignature methodSignature = (MethodSignature) signature; Method targetMethod = methodSignature.getMethod(); if ("POST".equals(method) || "GET".equals(method)) { String ipAddress = getIpAddress(request); String requestId = (String) request.getAttribute("requestId"); // 根据请求参数或请求头判断是否有“requestId”,有则使用,无则创建 if (StringUtils.isEmpty(requestId)) { requestId = "req_" + System.currentTimeMillis(); request.setAttribute("requestId", requestId); } userLog.setRequestId(requestId); //请求id userLog.setMethodName(targetMethod.getName()); //方法名 userLog.setMethodClass(signature.getDeclaringTypeName()); //方法所在的类名 userLog.setRequestUrl(request.getRequestURL().toString());//请求URI userLog.setRemoteIp(ipAddress); //操作IP地址 System.out.println("userLog = " + userLog); // logService.saveUserLog(userLog); } } catch (Throwable throwable) { throwable.printStackTrace(); } } //环绕通知 @Around("sqlLog()") public Object sqlLog(ProceedingJoinPoint pj) throws Throwable { // 发送异步日志事件 long start = System.currentTimeMillis(); SqlLog sqlLog = new SqlLog(); HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); Signature signature = pj.getSignature(); MethodSignature methodSignature = (MethodSignature) signature; Method targetMethod = methodSignature.getMethod(); String ipAddress = getIpAddress(request); String requestId = (String) request.getAttribute("requestId"); // 根据请求参数或请求头判断是否有“requestId”,有则使用,无则创建 if (StringUtils.isEmpty(requestId)) { requestId = "req_" + System.currentTimeMillis(); request.setAttribute("requestId", requestId); } //执行方法 Object object = pj.proceed(); //获取sql String sql = SqlUtils.getMybatisSql(pj, sqlSessionFactory); //执行时长(毫秒) long loadTime = System.currentTimeMillis() - start; sqlLog.setRequestId(requestId); //请求id sqlLog.setMethodName(targetMethod.getName()); //方法名 sqlLog.setMethodClass(signature.getDeclaringTypeName()); //方法所在的类名 sqlLog.setRequestUrl(request.getRequestURL().toString());//请求URI sqlLog.setRemoteIp(ipAddress); //操作IP地址 sqlLog.setSql(sql);//sql sqlLog.setLoadTime(loadTime);//执行时间 System.out.println("sqlLog = " + sqlLog); // logService.saveSqlLog(sqlLog); return object; } //异常通知 用于拦截异常日志 @AfterThrowing(pointcut = "exceptionLog()", throwing = "e") public void exceptionLog(JoinPoint pj, Throwable e) { try { ExceptionLog exceptionLog = new ExceptionLog(); HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); Signature signature = pj.getSignature(); MethodSignature methodSignature = (MethodSignature) signature; Method targetMethod = methodSignature.getMethod(); String ipAddress = getIpAddress(request); String requestId = (String) request.getAttribute("requestId"); // 根据请求参数或请求头判断是否有“requestId”,有则使用,无则创建 if (StringUtils.isEmpty(requestId)) { requestId ="req_" + System.currentTimeMillis(); request.setAttribute("requestId", requestId); } exceptionLog.setRequestId(requestId); //请求id exceptionLog.setMethodName(targetMethod.getName()); //方法名 exceptionLog.setMethodClass(signature.getDeclaringTypeName()); //方法所在的类名 exceptionLog.setRequestUrl(request.getRequestURL().toString());//请求URI exceptionLog.setMessage(e.getMessage()); //异常信息 exceptionLog.setRemoteIp(ipAddress); //操作IP地址 System.out.println("exceptionLog = " + exceptionLog); // logService.saveExceptionLog(exceptionLog); } catch (Exception ex) { ex.printStackTrace(); } } /** * 获取IP地址的方法 * @param request 传一个request对象下来 * @return */ public String getIpAddress(HttpServletRequest request) { String ip = request.getHeader("x-forwarded-for"); if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_CLIENT_IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_X_FORWARDED_FOR"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } return ip; } }
2.3 定义注解类 LogAnno
package com.liyh.log; import org.springframework.core.annotation.AliasFor; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @Author: liyh * @Date: 2020/9/17 17:12 */ @Target({ElementType.METHOD,ElementType.TYPE}) //作用于方法 使用在类,接口 @Retention(RetentionPolicy.RUNTIME) //运行时有效 public @interface LogAnno { @AliasFor("value") String[] operating() default {}; @AliasFor("operating") String[] value() default {}; }
2.4 添加json工具类和获取sql工具类
package com.liyh.utils; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.JavaType; import com.fasterxml.jackson.databind.ObjectMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.StringUtils; import java.io.IOException; /** * jsonUtil工具类 * @Author: liyh * @Date: 2020/9/17 17:12 */ public class JsonUtil { private static final Logger LOGGER = LoggerFactory.getLogger(JsonUtil.class); private static ObjectMapper mapper = new ObjectMapper(); /** * 对象转Json格式字符串 * @param obj 对象 * @return Json格式字符串 */ public static <T> String obj2String(T obj) { if (obj == null) { return null; } try { return obj instanceof String ? (String) obj : mapper.writeValueAsString(obj); } catch (JsonProcessingException e) { LOGGER.warn("Parse Object to String error : {}", e.getMessage()); return null; } } /** * 对象转Json格式字符串(格式化的Json字符串) * @param obj 对象 * @return 美化的Json格式字符串 */ public static <T> String obj2StringPretty(T obj) { if (obj == null) { return null; } try { return obj instanceof String ? (String) obj : mapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj); } catch (JsonProcessingException e) { LOGGER.warn("Parse Object to String error : {}", e.getMessage()); return null; } } /** * 字符串转换为自定义对象 * @param str 要转换的字符串 * @param clazz 自定义对象的class对象 * @return 自定义对象 */ public static <T> T jsonToObj(String str, Class<T> clazz){ if(StringUtils.isEmpty(str) || clazz == null){ return null; } try { return clazz.equals(String.class) ? (T) str : mapper.readValue(str, clazz); } catch (Exception e) { LOGGER.warn("Parse String to Object error : {}", e.getMessage()); return null; } } /** * 集合对象与Json字符串之间的转换 * @param str 要转换的字符串 * @param typeReference 集合类型如List<Object> * @param <T> * @return */ public static <T> T jsonToObj(String str, TypeReference<T> typeReference) { if (StringUtils.isEmpty(str) || typeReference == null) { return null; } try { return (T) (typeReference.getType().equals(String.class) ? str : mapper.readValue(str, typeReference)); } catch (IOException e) { LOGGER.warn("Parse String to Object error", e); return null; } } /** * 集合对象与Json字符串之间的转换 * @param str 要转换的字符串 * @param collectionClazz 集合类型 * @param elementClazzes 自定义对象的class对象 * @param <T> * @return */ public static <T> T string2Obj(String str, Class<?> collectionClazz, Class<?>... elementClazzes) { JavaType javaType = mapper.getTypeFactory().constructParametricType(collectionClazz, elementClazzes); try { return mapper.readValue(str, javaType); } catch (IOException e) { LOGGER.warn("Parse String to Object error : {}" + e.getMessage()); return null; } } }