前言
以前在《從零學ELK系列(十):SpringBoot項目接入ELK升級版(超詳細圖文教程)》中演示了SpringBoot項目接入ELK請求記錄及優化,本次針對於未知異常經過攔截進行記錄;java
優化前:git
- 系統發生異常沒有記錄異常信息
優化後:github
- 記錄本次請求的異常信息
目錄
- 從零學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)spring
-
主要代碼展現springboot
- ExceptionLogAspect
/* * Copyright (c) 2020. zhanghan_java@163.com All Rights Reserved. * 項目名稱:SpringBoot項目接入ELK * 類名稱:ExceptionLogAspect.java * 建立人:張晗 * 聯繫方式:zhanghan_java@163.com * 開源地址: https://github.com/dangnianchuntian/springboot * 博客地址: https://zhanghan.blog.csdn.net */ 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
/* * Copyright (c) 2020. zhanghan_java@163.com All Rights Reserved. * 項目名稱:SpringBoot項目接入ELK * 類名稱:RecordExcepitonLogServiceImpl.java * 建立人:張晗 * 聯繫方式:zhanghan_java@163.com * 開源地址: https://github.com/dangnianchuntian/springboot * 博客地址: https://zhanghan.blog.csdn.net */ 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
/* * Copyright (c) 2020. zhanghan_java@163.com All Rights Reserved. * 項目名稱:SpringBoot項目接入ELK * 類名稱:FileBeatLogUtil.java * 建立人:張晗 * 聯繫方式:zhanghan_java@163.com * 開源地址: https://github.com/dangnianchuntian/springboot * 博客地址: https://zhanghan.blog.csdn.net */ 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); } /** * 獲取請求參數,處理爲json字符串 * * @param joinPoint * @return */ 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); } }
測試
-
在進行請求架構
-
無異常請求
app -
有異常請求
ide
-
-
在Kibina上進行查看測試
-
發現日誌中新加的參數有警告
-
解決警告(設置中從新更新索引)
-
查看正常日誌(新加參數已無異常)
-
查看異常日誌
-
根據條件查看有異常請求
-
總結
- 能夠精準統計出系統中有多少請求出現異常,以及異常類方法,請求參數,異常信息
- 會持續將生產項目中進行優化同步到本項目中並進行輸出