Springboot接口项目利用AOP记录日志
Posted 画笔灬
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Springboot接口项目利用AOP记录日志相关的知识,希望对你有一定的参考价值。
一、 背景
一直想给项目构建一个统一的日志收集系统,先迈出第一步,构建一个日志收集类,用AOP实现无侵入日志收集
二、 环境
1.此随笔内容基于spring boot项目
2.数据库为mysql 5.7.9版本
3.jdk 版本为1.8
三、 说明
此版采用数据库存储,之后考虑使用elasticsearch等工具存储
四、 内容
1、构建日志采集实体类:BaseLogMessage
public class BaseLogMessage { private String serverIP; private String appName; private String method; private String type; private String userCode; private String uri; private String operationName; private String operationStatus; private long startTime; private Object parameter; private Object result; private int SpendTime; // 此处省略get、set }
2、构建一个配置文件读取类,用于读取配置文件中的系统名称:SystemPropetiesUtil
@Configuration public class SystemPropetiesUtil { @Value("${spring.application.name}") private String sysName;//系统名称
// 此处省略get、set
}
3、新建一个AOP类,在控制器方法上作为切点,执行日志收集: LogAspect
@Aspect @Component public class LogAspect { @Autowired private SystemPropetiesUtil systemPropetiesUtil; //定义切点方法 @Pointcut("execution(public * cq..campus.prevented.controller.*.*(..))") public void controllerLog() { } public static final Logger LOGGER = LoggerFactory.getLogger(LogAspect.class); @Around("controllerLog()") public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable { long startTime = System.currentTimeMillis(); //获取当前请求对象 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); //记录请求信息 BaseLogMessage baseLogMessage = new BaseLogMessage(); //1.获取到所有的参数值的数组 Object[] args = joinPoint.getArgs(); Signature signature = joinPoint.getSignature(); MethodSignature methodSignature = (MethodSignature) signature; //2.获取到方法的所有参数名称的字符串数组 String[] parameterNames = methodSignature.getParameterNames(); Object result = joinPoint.proceed(); Method method = methodSignature.getMethod(); if (method.isAnnotationPresent(ApiOperation.class)) { ApiOperation apiOperation = method.getAnnotation(ApiOperation.class); baseLogMessage.setOperationName(apiOperation.value()); } long endTime = System.currentTimeMillis(); String urlStr = request.getRequestURL().toString(); baseLogMessage.setUri(urlStr); baseLogMessage.setType("操作日志"); baseLogMessage.setServerIP(getRemoteIP(request)); baseLogMessage.setMethod(request.getMethod()); baseLogMessage.setAppName(systemPropetiesUtil.getSysName()); baseLogMessage.setResult(result); baseLogMessage.setParameter(getParameter(method, joinPoint.getArgs())); baseLogMessage.setSpendTime((int) (endTime - startTime)); baseLogMessage.setStartTime(endTime); LOGGER.info("{}", JsonUtils.objectToJson(baseLogMessage)); // 数据库存储操作 return result; } /** * 异常返回通知,用于拦截异常日志信息 连接点抛出异常后执行 * * @param joinPoint 切入点 * @param e 异常信息 */ @AfterThrowing(pointcut = "controllerLog()", throwing = "e") public void saveExceptionLog(JoinPoint joinPoint, Throwable e) { long startTime = System.currentTimeMillis(); if(null==kafkaClient){ kafkaClient = KafkaProducerClient.getInstance(systemPropetiesUtil.getKafkaHost()); // redisClient= RedisClient.getInstance(systemPropetiesUtil.getReidsHost(), Integer.parseInt(systemPropetiesUtil.getRedisPort()), ""); } // 获取RequestAttributes RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); // 从获取RequestAttributes中获取HttpServletRequest的信息 HttpServletRequest request = (HttpServletRequest) requestAttributes .resolveReference(RequestAttributes.REFERENCE_REQUEST); String urlStr = request.getRequestURL().toString(); BaseLogMessage baseLogMessage = new BaseLogMessage(); Signature signature = joinPoint.getSignature(); MethodSignature methodSignature = (MethodSignature) signature; Method method = methodSignature.getMethod(); StringBuffer strbuff = new StringBuffer(); for (StackTraceElement stet : elements) { strbuff.append(stet + " "); } String message = exceptionName + ":" + exceptionMessage + strbuff.toString(); try { Object[] args = joinPoint.getArgs(); String[] parameterNames = methodSignature.getParameterNames(); long endTime = System.currentTimeMillis(); baseLogMessage.setUri(urlStr); baseLogMessage.setType("异常日志"); baseLogMessage.setServerIP(getRemoteIP(request)); baseLogMessage.setMethod(request.getMethod()); baseLogMessage.setAppName(systemPropetiesUtil.getSysName()); baseLogMessage.setResult(message); baseLogMessage.setParameter(getParameter(method, joinPoint.getArgs())); baseLogMessage.setSpendTime((int) (endTime - startTime)); baseLogMessage.setStartTime(endTime); LOGGER.info("{}", JsonUtils.objectToJson(baseLogMessage)); // 数据库存储操作 } catch (Exception e2) { e2.printStackTrace(); } } /** * 根据方法和传入的参数获取请求参数 */ private Object getParameter(Method method, Object[] args) { List<Object> argList = new ArrayList<>(); Parameter[] parameters = method.getParameters(); for (int i = 0; i < parameters.length; i++) { RequestBody requestBody = parameters[i].getAnnotation(RequestBody.class); if (requestBody != null) { argList.add(args[i]); } RequestParam requestParam = parameters[i].getAnnotation(RequestParam.class); if (requestParam != null) { Map<String, Object> map = new HashMap<>(); String key = parameters[i].getName(); if (!StringUtils.isEmpty(requestParam.value())) { key = requestParam.value(); } map.put(key, args[i]); argList.add(map); } } if (argList.size() == 0) { return null; } else if (argList.size() == 1) { return argList.get(0); } else { return argList; } } /** * 获取请求ip */ public static String getRemoteIP(HttpServletRequest request) { String ip =null; if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { 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.getRemoteAddr(); } if (ip != null) { //对于通过多个代理的情况,最后IP为客户端真实IP,多个IP按照‘,‘分割 int position = ip.indexOf(","); if (position > 0) { ip = ip.substring(0, position); } } return ip; } }
五、 问题
1、如果方法正常执行,不进入AOP类,请检查AOP的切点是否书写正确。
日常记录,留待查阅~
以上是关于Springboot接口项目利用AOP记录日志的主要内容,如果未能解决你的问题,请参考以下文章
Spring Boot AOP 扫盲,实现接口访问的统一日志记录
springboot系列(二十一):基于AOP实现自定义注解且记录接口日志|超级超级详细,建议收藏