当前位置:   article > 正文

springboot整合logback,自定义日志格式,输入es(elk集成)_springboot日志输出到es

springboot日志输出到es

记录一次,springboot集成logback,自定义日志格式,输入到es

1.涉及依赖版本

  1. <!--springboot版本-->
  2. <parent>
  3. <groupId>org.springframework.boot</groupId>
  4. <artifactId>spring-boot-starter-parent</artifactId>
  5. <version>2.1.10.RELEASE</version>
  6. <relativePath/>
  7. </parent>
  8. <!--logstash-->
  9. <dependency>
  10. <groupId>net.logstash.logback</groupId>
  11. <artifactId>logstash-logback-encoder</artifactId>
  12. <version>5.3</version>
  13. </dependency>

注意事项:springboot版本使用2.1,不要使用最新版本的logstash依赖,过高版本,会出现启动报错,测试过5.3以及4.11版本,均可以启动正常,项目使用5.3

2.logback-spring.xml配置文件

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <configuration debug="false">
  3. <!--自定义日志输出参数-->
  4. <!--<conversionRule conversionWord="msg" converterClass="com.herenit.pharmacycloud.log.LogMessageConverter" />-->
  5. <!-- 控制台输出 -->
  6. <springProperty scope="context" name="logstash-server" source="spring.logstash.server"/>
  7. <springProperty scope="context" name="logstash-port" source="spring.logstash.port"/>
  8. <springProperty scope="context" name="logstash-service-id" source="spring.logstash.serviceId"/>
  9. <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
  10. <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
  11. <level>info</level>
  12. </filter>
  13. <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder" charset="utf8">
  14. <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
  15. <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight(%-5level) --- [%15.15(%thread)] %cyan(%-40.40(%logger{40})) : %msg%n</pattern>
  16. </encoder>
  17. </appender>
  18. <!-- 按照每天生成日志文件 -->
  19. <!-- <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
  20. <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
  21. &lt;!&ndash;日志文件输出的文件名&ndash;&gt;
  22. <FileNamePattern>${LOG_HOME}/TestWeb.log.%d{yyyy-MM-dd}.log</FileNamePattern>
  23. &lt;!&ndash;日志文件保留天数&ndash;&gt;
  24. <MaxHistory>30</MaxHistory>
  25. </rollingPolicy>
  26. <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
  27. &lt;!&ndash;格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符&ndash;&gt;
  28. <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
  29. </encoder>
  30. &lt;!&ndash;日志文件最大的大小&ndash;&gt;
  31. <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
  32. <MaxFileSize>10MB</MaxFileSize>
  33. </triggeringPolicy>
  34. </appender>-->
  35. <!--输出到logstash的appender-->
  36. <appender name="LOGSTASH" class="net.logstash.logback.appender.LogstashTcpSocketAppender">
  37. <param name="Encoding" value="UTF-8"/>
  38. <remoteHost>${logstash-server}</remoteHost>
  39. <port>${logstash-port}</port>
  40. <encoder charset="UTF-8" class="net.logstash.logback.encoder.LogstashEncoder" >
  41. <customFields>{"serviceId":"${logstash-service-id}"}</customFields>
  42. <includeMdcKeyName>startTime</includeMdcKeyName>
  43. <includeMdcKeyName>endTime</includeMdcKeyName>
  44. <includeMdcKeyName>requestRawJson</includeMdcKeyName>
  45. <includeMdcKeyName>responseRawJson</includeMdcKeyName>
  46. <includeMdcKeyName>responseTime</includeMdcKeyName>
  47. <includeMdcKeyName>url</includeMdcKeyName>
  48. <includeMdcKeyName>method</includeMdcKeyName>
  49. <includeMdcKeyName>path</includeMdcKeyName>
  50. </encoder>
  51. </appender>
  52. <!--配置异步日志输出-->
  53. <appender name="LOGSTASH-ASYN" class="ch.qos.logback.classic.AsyncAppender">
  54. <appender-ref ref="LOGSTASH"/>
  55. </appender>
  56. <!-- 日志输出级别 -->
  57. <root level="INFO">
  58. <appender-ref ref="STDOUT" />
  59. <appender-ref ref="LOGSTASH-ASYN" />
  60. <!--<appender-ref ref="FILE" />-->
  61. </root>
  62. </configuration>

application.yml

  1. spring:
  2. #应用名称
  3. application:
  4. name: test
  5. #日志配置
  6. logstash:
  7. server: 127.0.0.1
  8. port: 5506
  9. serviceId: service-test-dev

说明:serviceId值,用于es创建对应的项目索引库,具体配置参考下方(logstash对应配置文件)

  1. input{
  2. tcp{
  3. mode => "server"
  4. host => "127.0.0.1"
  5. port => 5506
  6. codec => json_lines
  7. }
  8. }
  9. output{
  10. elasticsearch{
  11. hosts =>["127.0.0.1:9200"]
  12. index=>"%{serviceId}-%{+YYYY.MM.dd}.log"
  13. }
  14. }

3.统一日志拦截(自定义日志输出字段)

  1. @Aspect
  2. @Order(1)
  3. @Component
  4. public class WebLogAspect {
  5. private Logger logger = LoggerFactory.getLogger(this.getClass());
  6. ThreadLocal<LogTemplate> logJsonModel = new ThreadLocal<>();
  7. //定义项目请求拦截路径,一般是controller层
  8. @Pointcut("execution(public * com.pp.test.facade..*.*(..))")
  9. public void webLog() {
  10. }
  11. @Before("webLog()")
  12. public void doBefore(JoinPoint joinPoint) throws Throwable {
  13. //封装请求入参日志对象
  14. LogTemplate logTemplate = new LogTemplate();
  15. logTemplate.setStartTime(LocalDateTime.now());
  16. logTemplate.setResponseTime(System.currentTimeMillis());
  17. // 接收到请求,记录请求内容
  18. ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
  19. HttpServletRequest request = attributes.getRequest();
  20. // 记录下请求内容
  21. logTemplate.setUrl(request.getRequestURL().toString());
  22. //处理文件上传日志记录
  23. Object[] args = joinPoint.getArgs();
  24. Stream<?> stream = ArrayUtils.isEmpty(args) ? Stream.empty() : Arrays.stream(args);
  25. List<Object> logArgs = stream
  26. .filter(arg -> (!(arg instanceof HttpServletRequest) && !(arg instanceof HttpServletResponse)))
  27. .collect(Collectors.toList());
  28. //过滤后序列化无异常
  29. String methodName = joinPoint.getSignature().getName();
  30. logTemplate.setRequestRawJson(StringEscapeUtils.unescapeJavaScript(JSONObject.toJSONString(JSON.toJSONString(logArgs))));
  31. logTemplate.setMethod(methodName);
  32. logTemplate.setPath(joinPoint.getSignature().getDeclaringTypeName());
  33. logJsonModel.set(logTemplate);
  34. }
  35. /**
  36. * @Description:记录响应请求正常日志
  37. * @Param: [ret]
  38. * @Return: void
  39. */
  40. @AfterReturning(returning = "ret", pointcut = "webLog()")
  41. public void doAfterReturning(JoinPoint joinPoint,Object ret) throws Throwable {
  42. //过滤后序列化无异常
  43. String methodName = joinPoint.getSignature().getName();
  44. logJsonModel.get().setResponseRawJson(StringEscapeUtils.unescapeJavaScript(JSONObject.toJSONString(ret)));
  45. logJsonModel.get().setResponseTime(System.currentTimeMillis() - logJsonModel.get().getResponseTime());
  46. logJsonModel.get().setEndTime(LocalDateTime.now());
  47. addLogData();
  48. logger.info(logJsonModel.get().toString());
  49. }
  50. //自定义日志需要输入的字段
  51. private void addLogData() {
  52. MDC.put("startTime", logJsonModel.get().getStartTime().format(DateTimeFormatter.ofPattern(DateTimeUtils.DATE_TIME)));
  53. MDC.put("endTime", logJsonModel.get().getEndTime().format(DateTimeFormatter.ofPattern(DateTimeUtils.DATE_TIME)));
  54. MDC.put("requestRawJson", logJsonModel.get().getRequestRawJson());
  55. MDC.put("responseRawJson",logJsonModel.get().getResponseRawJson());
  56. MDC.put("responseTime", Long.toString(logJsonModel.get().getResponseTime()));
  57. MDC.put("url", logJsonModel.get().getUrl());
  58. MDC.put("method", logJsonModel.get().getMethod());
  59. MDC.put("path", logJsonModel.get().getPath());
  60. }
  61. /**
  62. * @Description:记录响应请求异常日志
  63. * @Param: [ex]
  64. * @Return: void
  65. */
  66. @AfterThrowing(throwing="ex",pointcut = "webLog()")
  67. public void afterThrowing(Exception ex) {
  68. String errorCode = "";
  69. String msg = "";
  70. if (ex instanceof BusinessException) {
  71. BusinessException be = (BusinessException)ex;
  72. errorCode = be.getCode();
  73. msg = be.getMessage();
  74. } else if (ex instanceof MethodArgumentNotValidException) {
  75. MethodArgumentNotValidException be = (MethodArgumentNotValidException)ex;
  76. msg = be.getBindingResult().getAllErrors().stream()
  77. .map(DefaultMessageSourceResolvable::getDefaultMessage)
  78. .reduce((m1, m2) -> m1 + ";" + m2)
  79. .orElse("未获取到错误信息");
  80. errorCode = RespCode.FAILED.getCode();
  81. }else {
  82. errorCode = RespCode.FAILED.getCode();
  83. msg = ex.getMessage();
  84. }
  85. // logger.error("异常code:"+errorCode+",异常信息:"+msg, e);
  86. JSONObject repJson=new JSONObject();
  87. repJson.put("errorCode",errorCode);
  88. repJson.put("errorMsg",msg);
  89. logJsonModel.get().setResponseRawJson(StringEscapeUtils.unescapeJavaScript(JSONObject.toJSONString(repJson)));
  90. logJsonModel.get().setResponseTime(System.currentTimeMillis() - logJsonModel.get().getResponseTime());
  91. logJsonModel.get().setEndTime(LocalDateTime.now());
  92. logJsonModel.get().setMessage(msg);
  93. addLogData();
  94. // logger.error("异常code:"+errorCode+",异常信息:"+msg, ex);
  95. logger.info(logJsonModel.get().toString());
  96. }
  97. }

4.问题以及总结

针对自定义字段输入到日志格式中,想过重写相关的底层代码,也测试过,但是结果都不是本人想要的效果,

net.logstash.logback.encoder.LogstashEncoder,通过重写改实现类,但是改类提供更改的方法有限,

希望有大佬,针对这个问题提供宝贵经验。

另外还存在一个问题,springboot启动的所有日志,也会一条一条输入到es中,这个就很烦,目前使用比较笨的方法,

写了一个拦截器,过滤springboot项目的无关的启动日志;

也希望针对这个问题,大佬们提供有效可行的建议

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/936276
推荐阅读
相关标签
  

闽ICP备14008679号