Spring AOP实现统一日志输出

前端之家收集整理的这篇文章主要介绍了Spring AOP实现统一日志输出前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

目的:

统一日志输出格式

思路:

1、针对不同的调用场景定义不同的注解,目前想的是接口层和服务层。

2、我设想的接口层和服务层的区别在于:

  (1)接口层可以打印客户端IP,而服务层不需要

  (2)接口层的异常需要统一处理并返回,而服务层的异常只需要向上抛出即可

3、就像Spring中的@Controller、@Service、@Repository注解那样,虽然作用是一样的,但是不同的注解用在不同的地方显得很清晰,层次感一下就出来了

4、AOP去拦截特定注解的方法调用

5、为了简化使用者的操作,采用Spring Boot自动配置

1. 注解定义

@H_404_49@package com.cjs.example.annotation; import java.lang.annotation.ElementType; java.lang.annotation.Retention; java.lang.annotation.RetentionPolicy; java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface SystemControllerLog { String description() default ""; boolean async() default false; }
@H_404_49@ SystemServiceLog { String description() ; }

2. 定义一个类包含所有需要输出的字段

@H_404_49@ com.cjs.example.service; lombok.Data; java.io.Serializable; @Data public class SystemLogStrategy implements Serializable { private boolean async; private String threadId; String location; String description; String className; String methodName; String arguments; String result; Long elapsedTime; public String format() { return "线程ID: {},注解位置: {},方法描述: {},目标类名: {},目标方法: {},调用参数: {},返回结果: {},花费时间: {}"; } Object[] args() { return new Object[]{this.threadId,this.location,1)">this.description,1)">this.className,1)">this.methodName,1)">this.arguments,1)">this.result,1)">this.elapsedTime}; } }

3. 定义切面

@H_404_49@ com.cjs.example.aspect; com.alibaba.fastjson.JSON; com.cjs.example.annotation.SystemControllerLog; com.cjs.example.annotation.SystemRpcLog; com.cjs.example.annotation.SystemServiceLog; com.cjs.example.enums.AnnotationTypeEnum; com.cjs.example.service.SystemLogStrategy; com.cjs.example.util.JsonUtil; com.cjs.example.util.ThreadUtil; org.aspectj.lang.ProceedingJoinPoint; org.aspectj.lang.Signature; org.aspectj.lang.annotation.Around; org.aspectj.lang.annotation.Aspect; org.aspectj.lang.annotation.Pointcut; org.slf4j.Logger; org.slf4j.LoggerFactory; java.lang.reflect.Method; @Aspect class SystemLogAspect { static final Logger LOG = LoggerFactory.getLogger(SystemLogAspect.); ); @Pointcut("execution(* com.ourhours..*(..)) && !execution(* com.ourhours.logging..*(..))") void pointcut() { } @Around("pointcut()" Object doInvoke(ProceedingJoinPoint pjp) { long start = System.currentTimeMillis(); Object result = null; try { result = pjp.proceed(); } catch (Throwable throwable) { throwable.printStackTrace(); LOG.error(throwable.getMessage(),throwable); throw new RuntimeException(throwable); } finally { long end = System.currentTimeMillis(); long elapsedTime = end - start; printLog(pjp,result,elapsedTime); } return result; } /** * 打印日志 * @param pjp 连接点 * result 方法调用返回结果 * elapsedTime 方法调用花费时间 */ void printLog(ProceedingJoinPoint pjp,Object result,1)">long elapsedTime) { SystemLogStrategy strategy = getFocus(pjp); if (null != strategy) { strategy.setThreadId(ThreadUtil.getThreadId()); strategy.setResult(JsonUtil.toJSONString(result)); strategy.setElapsedTime(elapsedTime); if (strategy.isAsync()) { new Thread(()->LOG.info(strategy.format(),strategy.args())).start(); }else { LOG.info(strategy.format(),strategy.args()); } } } * 获取注解 SystemLogStrategy getFocus(ProceedingJoinPoint pjp) { Signature signature = pjp.getSignature(); String className = signature.getDeclaringTypeName(); String methodName = signature.getName(); Object[] args = pjp.getArgs(); String targetClassName = pjp.getTarget().getClass().getName(); { Class<?> clazz = Class.forName(targetClassName); Method[] methods = clazz.getMethods(); for (Method method : methods) { (methodName.equals(method.getName())) { if (args.length == method.getParameterCount()) { SystemLogStrategy strategy = SystemLogStrategy(); strategy.setClassName(className); strategy.setMethodName(methodName); SystemControllerLog systemControllerLog = method.getAnnotation(SystemControllerLog.); systemControllerLog) { strategy.setArguments(JsonUtil.toJSONString(args)); strategy.setDescription(systemControllerLog.description()); strategy.setAsync(systemControllerLog.async()); strategy.setLocation(AnnotationTypeEnum.CONTROLLER.getName()); strategy; } SystemServiceLog systemServiceLog = method.getAnnotation(SystemServiceLog. systemServiceLog) { strategy.setArguments(JsonUtil.toJSONString(args)); strategy.setDescription(systemServiceLog.description()); strategy.setAsync(systemServiceLog.async()); strategy.setLocation(AnnotationTypeEnum.SERVICE.getName()); strategy; } ; } } } } (ClassNotFoundException e) { LOG.error(e.getMessage(),e); } ; } }

4. 配置

PS:

这里也可以用组件扫描,执行在Aspect上加@Component注解即可,但是这样的话有个问题。

就是,如果你的这个Aspect所在包不是Spring Boot启动类所在的包或者子包下就需要指定@ComponentScan,因为Spring Boot默认只扫描和启动类同一级或者下一级包。

 

@H_404_49@ com.cjs.example.config; com.cjs.example.aspect.SystemLogAspect; org.springframework.boot.autoconfigure.AutoConfigureOrder; org.springframework.boot.autoconfigure.condition.ConditionalOnClass; org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; org.springframework.context.annotation.Bean; org.springframework.context.annotation.Configuration; org.springframework.context.annotation.EnableAspectJAutoProxy; @Configuration @AutoConfigureOrder(2147483647) @EnableAspectJAutoProxy(proxyTargetClass = true) @ConditionalOnClass(SystemLogAspect.) @ConditionalOnMissingBean(SystemLogAspect.) SystemLogAutoConfiguration { @Bean SystemLogAspect systemLogAspect() { SystemLogAspect(); } }

 

5. 自动配置(resources/Meta-INF/spring.factories)

@H_404_49@org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.ourhours.logging.config.SystemLogAutoConfiguration

6. 其它工具类

 

@H_301_413@6.1. 获取客户端IP
@H_404_49@ com.cjs.example.util; org.springframework.web.context.request.RequestContextHolder; org.springframework.web.context.request.ServletRequestAttributes; javax.servlet.http.HttpServletRequest; HttpContextUtils { static HttpServletRequest getHttpServletRequest() { ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); servletRequestAttributes.getRequest(); } String getIpAddress() { HttpServletRequest request = getHttpServletRequest(); String ip = request.getHeader("X-Forwarded-For"); if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { .equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } .equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_CLIENT_IP".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_X_FORWARDED_FOR".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } }else if (ip != null && ip.length() > 15) { String[] ips = ip.split(","); for (int index = 0; index < ips.length; index++) { String strIp = (String) ips[index]; if (!("unknown".equalsIgnoreCase(strIp))) { ip = strIp; break; } } } ip; } }
@H_301_413@6.2. 格式化成JSON字符串
@H_404_49@ com.alibaba.fastjson.serializer.SerializerFeature; JsonUtil { String toJSONString(Object object) { JSON.toJSONString(object,SerializerFeature.DisableCircularReferenceDetect); } }
@H_301_413@6.3. 存取线程ID
@H_404_49@ java.util.UUID; ThreadUtil { final ThreadLocal<String> threadLocal = new ThreadLocal<>(); String getThreadId() { String threadId = threadLocal.get(); null == threadId) { threadId = UUID.randomUUID().toString(); threadLocal.set(threadId); } threadId; } }

7. 同时还提供静态方法

@H_404_49@ com.cjs.example; Log { static Logger LOGGER = SingletonHolder{ static Log instance = Log(); } Log(){} static Log getInstance(Class<?> clazz){ LOGGER = LoggerFactory.getLogger(clazz); SingletonHolder.instance; } info(String description,Object args,Object result) { LOGGER.info("线程ID: {},返回结果: {}",ThreadUtil.getThreadId(),description,JsonUtil.toJSONString(args),JsonUtil.toJSONString(result)); } error(String description,Throwable t) { LOGGER.error("线程ID: {},JsonUtil.toJSONString(result),t); } }

8. pom.xml

@H_404_49@<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> modelVersion>4.0.0</> groupId>com.cjs.exampleartifactId>cjs-loggingversion>0.0.1-SNAPSHOTpackaging>jarnameparent> >org.springframework.boot>spring-boot-starter-parent>2.0.2.RELEASErelativePath/> <!-- lookup parent from repository --> propertiesproject.build.sourceEncoding>UTF-8project.reporting.outputEncodingjava.version>1.8aspectj.version>1.8.13servlet.versionslf4j.version>1.7.25fastjson.version>1.2.47dependenciesdependency> >spring-boot-autoconfigureoptional>true>org.springframework>spring-web>javax.servlet>javax.servlet-api>${servlet.version}scope>provided>org.aspectj>aspectjweaver>${aspectj.version}>org.slf4j>slf4j-api>${slf4j.version}>com.alibaba>fastjson>${fastjson.version}buildpluginsplugin> >org.apache.maven.plugins>maven-compiler-plugin>3.7.0configuration> sourcetargetencoding>UTF8>maven-source-pluginexecutionsexecution> id>attach-sourcesgoals> goal> project>

8. 工程结构

 

猜你在找的Spring相关文章