前言
之前在《从零学ELK系列(十):SpringBoot项目接入ELK升级版(超详细图文教程)》中演示了SpringBoot项目接入ELK请求记录及优化,本次针对于未知异常通过拦截进行记录;
优化前:
- 系统发生异常没有记录异常信息
优化后:
- 记录本次请求的异常信息
目录
- 从零学ELK系列(一):为什么要跟我学从零学ELK系列
- 从零学ELK系列(二):VMware安装Centos(超详细图文教程)
- 从零学ELK系列(三):Centos安装Docker(超详细图文教程)
- 从零学ELK系列(四):Docker安装Elasticsearch(超详细图文教程)
- 从零学ELK系列(五):Docker安装Kibana(超详细图文教程)
- 从零学ELK系列(六):Docker安装Logstash(超详细图文教程)
- 从零学ELK系列(七):Centos安装Filebeat(超详细图文教程)
- 从零学ELK系列(八):SpringBoot项目接入ELK(超详细图文教程)
- 从零学ELK系列(九):Nginx接入ELK(超详细图文教程)
- 从零学ELK系列(十):SpringBoot项目接入ELK升级版(超详细图文教程)
- 从零学ELK系列(十一):SpringBoot项目接入ELK超级版(超详细图文教程)
架构图&时序图
- 架构图
- 程序写入日志时序图
- ELK收集日志及Kibina查询日志时序图
代码实现
-
完整代码(GitHub,欢迎大家Star,Fork,Watch)
https://github.com/dangnianchuntian/springboot
-
主要代码展示
- ExceptionLogAspect
package com.zhanghan.zhelkboot.aop; import com.zhanghan.zhelkboot.util.FileBeatLogUtil; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import java.util.Arrays; @Aspect @Order(1) @Component public class ExceptionLogAspect { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Pointcut("execution(* com.zhanghan.zhelkboot..*.*(..))") public void methodPointCut() { } @AfterThrowing(throwing = "ex", pointcut = "methodPointCut()") public void throwss(JoinPoint joinPoint, Exception ex) { try { String methodArgs = Arrays.toString(joinPoint.getArgs()); FileBeatLogUtil.writeExceptionLog(joinPoint.getSignature().toString(), methodArgs, ex.getMessage()); } catch (Exception e) { logger.error("ExceptionLogAspect;writeExceptionLog;Exception:{}", e.getMessage()); } } }
- RecordExcepitonLogServiceImpl
package com.zhanghan.zhelkboot.service.impl; import com.zhanghan.zhelkboot.controller.request.RecordExceptionLogRequest; import com.zhanghan.zhelkboot.service.RecordExcepitonLogService; import com.zhanghan.zhelkboot.util.wrapper.WrapMapper; import org.springframework.stereotype.Service; import java.util.HashMap; import java.util.Map; @Service public class RecordExcepitonLogServiceImpl implements RecordExcepitonLogService { @Override public Object recordExcepitonLog(RecordExceptionLogRequest recordExceptionLogRequest) { Integer divisor = recordExceptionLogRequest.getDivisor(); int consult = 2 / divisor; Map<String, Object> map = new HashMap(); map.put("consult", consult); return WrapMapper.ok(map); } }
- FileBeatLogUtil
package com.zhanghan.zhelkboot.util; import com.alibaba.fastjson.JSON; import com.zhanghan.zhelkboot.util.wrapper.Wrapper; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.MDC; import org.springframework.util.StringUtils; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.text.SimpleDateFormat; import java.util.*; public class FileBeatLogUtil { public static void writeRequestInfo(HttpServletRequest request, String applicationName, String reqName, String requestParams) { String requestURI = request.getRequestURI(); //获取requestHeader Enumeration<String> requestHeaderNames = request.getHeaderNames(); Map<String, Object> reuestHeaderMap = new HashMap<>(); while (requestHeaderNames.hasMoreElements()) { String name = requestHeaderNames.nextElement(); String value = request.getHeaders(name).nextElement(); reuestHeaderMap.put(name, value); } String requestHeader = ""; if (null != reuestHeaderMap && reuestHeaderMap.size() > 0) { requestHeader = JSON.toJSONString(reuestHeaderMap); } //防止MDC值空指针,所有入参不为null applicationName = StringUtils.isEmpty(applicationName) ? "" : applicationName; requestURI = StringUtils.isEmpty(requestURI) ? "" : requestURI; reqName = StringUtils.isEmpty(reqName) ? "" : reqName; requestParams = "null".equals(requestParams) ? "" : requestParams; //MDC值为ES键值对JSON信息 MDC.put("applicationName", applicationName); MDC.put("requestTime", getStringTodayTime()); MDC.put("requestURI", requestURI); MDC.put("requestHeader", requestHeader); MDC.put("sourceName", reqName); MDC.put("requestParams", requestParams); MDC.put("exceptionCount", "0"); } public static void writeExceptionLog(String exceptionMethodName, String exceptionMethodArgs, String exceptionMessage) { MDC.put("exceptionCount", "1"); exceptionMessage = String.format("MethodName:%s;Args:%s;Exception:%s", exceptionMethodName, exceptionMethodArgs, exceptionMessage); //MDC值为ES键值对JSON信息 MDC.put("exceptionMessage", exceptionMessage); } public static void writeResponseLog(Object o, Logger log, HttpServletResponse response) { //取responseHeader内容 Map<String, Object> responseHeaderMap = new HashMap<>(); Collection<String> headerNames = response.getHeaderNames(); headerNames.forEach(name -> { responseHeaderMap.put(name, response.getHeader(name)); }); String strResponseHeader = ""; if (null != responseHeaderMap && responseHeaderMap.size() > 0) { strResponseHeader = JSON.toJSONString(responseHeaderMap); } //获取response内容 String responseCode = ""; String responseMsg = ""; String responseBody = ""; Wrapper wrapper; if (null != o) { wrapper = (Wrapper) o; if (null != wrapper) { responseCode = String.valueOf(wrapper.getCode()); responseMsg = wrapper.getMessage(); Object result = wrapper.getResult(); if (null != result) { responseBody = result.toString(); } } } //MDC值为ES键值对JSON信息 MDC.put("responseHeader", strResponseHeader); MDC.put("responseCode", responseCode); MDC.put("responseMsg", responseMsg); MDC.put("responseBody", responseBody); MDC.put("responseTime", getStringTodayTime()); Map<String, String> copyOfContextMap = MDC.getCopyOfContextMap(); String reqInfoJsonStr = JSON.toJSONString(copyOfContextMap); log.info(reqInfoJsonStr); } public static String getParams(JoinPoint joinPoint) { Object[] argValues = joinPoint.getArgs(); String[] argNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames(); LinkedHashMap<String, Object> linkedHashMap = new LinkedHashMap<>(); if (argNames != null && argNames.length > 0) { for (int i = 0; i < argNames.length; i++) { String thisArgName = argNames[i]; String thisArgValue = argValues[i].toString(); linkedHashMap.put(thisArgName, thisArgValue); } } return JSON.toJSONString(linkedHashMap); } public static String getStringTodayTime() { Date todat_date = new Date(); //将日期格式化 SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); //转换成字符串格式 return simpleDateFormat.format(todat_date); } }
测试
-
在进行请求
-
无异常请求
-
有异常请求
-
-
在Kibina上进行查看
-
发现日志中新加的参数有警告
-
解决警告(设置中重新更新索引)
-
查看正常日志(新加参数已无异常)
-
查看异常日志
-
根据条件查看有异常请求
-
总结
- 可以精准统计出系统中有多少请求出现异常,以及异常类方法,请求参数,异常信息
- 会持续将生产项目中进行优化同步到本项目中并进行输出