前言
之前在《從零學ELK系列(八):SpringBoot專案接入ELK(超詳細圖文教程)》中演示了SpringBoot專案接入ELK,后來專案中對這部分進行了優化,之前博文中也有讀者問到,將優化整理成博文和大家共享;
優化前:
- 一次請求記錄兩條日志(request一條,response一條),通過UUID傳連起來
優化后:
- 一次請求記錄一條日志(request資訊與response資訊都在一起)
目錄
-
從零學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收集日志及Kibina查詢日志時序圖

代碼實作
-
完整代碼(GitHub,歡迎大家Star,Fork,Watch)
https://github.com/dangnianchuntian/springboot
-
主要代碼展示
- FileBeatLogUtil
/* * Copyright (c) 2020. [email protected] All Rights Reserved. * 專案名稱:SpringBoot專案接入ELK * 類名稱:FileBeatLogUtil.java * 創建人:張晗 * 聯系方式:[email protected] * 開源地址: 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 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 = org.springframework.util.StringUtils.isEmpty(applicationName) ? "" : applicationName; requestURI = org.springframework.util.StringUtils.isEmpty(requestURI) ? "" : requestURI; reqName = org.springframework.util.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); } 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(); responseBody = wrapper.getResult().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); } }- RequestLogAspectConf
/* * Copyright (c) 2020. [email protected] All Rights Reserved. * 專案名稱:SpringBoot專案接入ELK * 類名稱:RequestLogAspectConf.java * 創建人:張晗 * 聯系方式:[email protected] * 開源地址: 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.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.annotation.Order; import org.springframework.core.env.Environment; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; @Aspect @Order(0) @Component public class RequestLogAspectConf { @Autowired private HttpServletRequest request; @Autowired private Environment env; private final Logger logger = LoggerFactory.getLogger(this.getClass()); /** * 范圍切點方法 */ @Pointcut("execution(* com.zhanghan.zhelkboot.controller..*.*(..))") public void methodPointCut() { } @Before("methodPointCut()") void doBefore(JoinPoint joinPoint) { authLogic(joinPoint); } private void authLogic(JoinPoint joinPoint) { try { String applicationName = env.getProperty("spring.application.name"); //獲取當前http請求 String reqName = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName(); String requestParams = FileBeatLogUtil.getParams(joinPoint); FileBeatLogUtil.writeRequestInfo(request, applicationName, reqName, requestParams); } catch (Exception e) { logger.error("authLogic;Exception:{}", e.getMessage()); } } }- ResponseLogAdvice
/* * Copyright (c) 2020. [email protected] All Rights Reserved. * 專案名稱:SpringBoot專案接入ELK * 類名稱:ResponseLogAdvice.java * 創建人:張晗 * 聯系方式:[email protected] * 開源地址: https://github.com/dangnianchuntian/springboot * 博客地址: https://zhanghan.blog.csdn.net */ package com.zhanghan.zhelkboot.aop; import com.zhanghan.zhelkboot.util.FileBeatLogUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.MethodParameter; import org.springframework.http.MediaType; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; import javax.servlet.http.HttpServletResponse; @ControllerAdvice public class ResponseLogAdvice implements ResponseBodyAdvice { @Autowired private HttpServletResponse response; private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Override public boolean supports(MethodParameter methodParameter, Class aClass) { return true; } @Override public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) { try { if (o != null) { Logger log = LoggerFactory.getLogger("logstashInfo"); FileBeatLogUtil.writeResponseLog(o, log, response); } } catch (Exception e) { logger.error("beforeBodyWrite;Exception:{}", e.getMessage()); } return o; } }- LombokController
/* * Copyright (c) 2020. [email protected] All Rights Reserved. * 專案名稱:SpringBoot專案接入ELK * 類名稱:LombokController.java * 創建人:張晗 * 聯系方式:[email protected] * 開源地址: https://github.com/dangnianchuntian/springboot * 博客地址: https://zhanghan.blog.csdn.net */ package com.zhanghan.zhelkboot.controller; import com.zhanghan.zhelkboot.controller.request.LombokRequest; import com.zhanghan.zhelkboot.util.wrapper.WrapMapper; import com.zhanghan.zhelkboot.util.wrapper.Wrapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.Map; @RestController public class LombokController { private static Logger logger = LoggerFactory.getLogger(LombokController.class); @RequestMapping(value = "/lombok", method = RequestMethod.POST) public Wrapper lombok(@RequestBody LombokRequest lombokRequest) { logger.info("lombok param {}", lombokRequest.toString()); Map<String, Object> map = new HashMap(); map.put("intLombok", lombokRequest.getIntLombok()); map.put("strLombok", lombokRequest.getStrLombok()); map.put("boleanLombok", lombokRequest.getBoleanLombok()); map.put("personLombok", lombokRequest.getPersonLombok()); return WrapMapper.ok(map); } }
測驗
-
在服務器上部署并進行請求

-
在Kibina上進行查看(請求時間,請求內容,回應內容,回應時間)

總結
- 通過日志收集系統可以對系統進行監控
- 有助于排錯
- 可以看到每個介面的處理時間,是我們對系統進行優化一個重要參考指標
- 會持續將生產專案中進行優化同步到本專案中并進行輸出
CSDN認證博客專家
分布式
Spring
Redis
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/2847.html
標籤:其他
上一篇:Java基礎之檔案操作(IO流)
