前言
之前在《从零学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
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); } 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
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
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
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上进行查看(请求时间,请求内容,响应内容,响应时间)
总结
- 通过日志收集系统可以对系统进行监控
- 有助于排错
- 可以看到每个接口的处理时间,是我们对系统进行优化一个重要参考指标
- 会持续将生产项目中进行优化同步到本项目中并进行输出