ExceptionLog.java 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. package com.loan.system.domain.entity;
  2. import com.fasterxml.jackson.databind.JsonNode;
  3. import com.fasterxml.jackson.databind.ObjectMapper;
  4. import lombok.*;
  5. import org.apache.commons.io.output.ByteArrayOutputStream;
  6. import org.apache.commons.lang3.exception.ExceptionUtils;
  7. import javax.persistence.*;
  8. import javax.validation.constraints.NotNull;
  9. import javax.validation.constraints.Size;
  10. import java.io.IOException;
  11. import java.time.Instant;
  12. import java.time.LocalDateTime;
  13. import java.time.format.DateTimeFormatter;
  14. import java.util.Base64;
  15. import java.util.zip.GZIPOutputStream;
  16. @Entity
  17. @Table(name = "exception_log")
  18. @Data
  19. @NoArgsConstructor
  20. @AllArgsConstructor
  21. @Builder
  22. public class ExceptionLog {
  23. @Id
  24. @GeneratedValue(strategy = GenerationType.IDENTITY)
  25. @Column(name = "id", nullable = false)
  26. private Long id;
  27. @Size(max = 120)
  28. @Column(name = "exception_type", length = 120)
  29. private String exceptionType;
  30. @Size(max = 500)
  31. @Column(name = "message", length = 500)
  32. private String message;
  33. @Size(max = 200)
  34. @Column(name = "summary", length = 200)
  35. private String summary;
  36. @Size(max = 200)
  37. @Column(name = "cause", length = 200)
  38. private String cause;
  39. @Lob
  40. @Column(name = "stack_trace")
  41. private String stackTrace;
  42. @Size(max = 255)
  43. @Column(name = "url")
  44. private String url;
  45. @Column(name = "user_id")
  46. private Long userId;
  47. @Size(max = 45)
  48. @Column(name = "ip", length = 45)
  49. private String ip;
  50. @Size(max = 300)
  51. @Column(name = "user_agent", length = 300)
  52. private String userAgent;
  53. @Size(max = 10)
  54. @Column(name = "method", length = 10)
  55. private String method;
  56. @Lob
  57. @Column(name = "params")
  58. private String params;
  59. @Column(name = "is_resolve")
  60. private Boolean isResolve;
  61. @NotNull
  62. @Column(name = "create_time", nullable = false)
  63. private String createTime;
  64. private static final ObjectMapper MAPPER = new ObjectMapper();
  65. /** 快速构建:只传异常 + 请求上下文 */
  66. public static ExceptionLog build(Exception e, String url, String method,
  67. String ip, String userAgent, Long userId,
  68. Object paramsObj) {
  69. Throwable root = ExceptionUtils.getRootCause(e);
  70. if (root == null) root = e;
  71. return ExceptionLog.builder()
  72. .exceptionType(e.getClass().getSimpleName())
  73. .message(trim(e.getMessage(), 500))
  74. .summary(buildSummary(e))
  75. .cause(root.getClass().getSimpleName())
  76. .stackTrace(compressTrace(e, 50))
  77. .url(trim(url, 255))
  78. .method(trim(method, 10))
  79. .ip(trim(ip, 45))
  80. .userAgent(trim(userAgent, 300))
  81. .userId(userId == null ? 0L : userId)
  82. .params(toJson(paramsObj))
  83. .createTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
  84. .build();
  85. }
  86. /** 摘要:类名 + 第一行堆栈 */
  87. private static String buildSummary(Exception e) {
  88. StringBuilder sb = new StringBuilder(e.getClass().getSimpleName());
  89. if (e.getMessage() != null) {
  90. sb.append(": ").append(trim(e.getMessage(), 120));
  91. }
  92. if (e.getStackTrace().length > 0) {
  93. StackTraceElement first = e.getStackTrace()[0];
  94. sb.append(" at ")
  95. .append(first.getClassName())
  96. .append(":")
  97. .append(first.getLineNumber());
  98. }
  99. return trim(sb.toString(), 200);
  100. }
  101. /** 截取堆栈前 max 行并 GZIP + Base64 */
  102. private static String compressTrace(Exception e, int max) {
  103. String raw = ExceptionUtils.getStackTrace(e);
  104. String[] lines = raw.split("\n");
  105. StringBuilder sb = new StringBuilder();
  106. for (int i = 0; i < Math.min(max, lines.length); i++) {
  107. sb.append(lines[i]).append('\n');
  108. }
  109. try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
  110. GZIPOutputStream gzip = new GZIPOutputStream(bos)) {
  111. gzip.write(sb.toString().getBytes("UTF-8"));
  112. gzip.finish();
  113. return Base64.getEncoder().encodeToString(bos.toByteArray());
  114. } catch (IOException ignored) {
  115. return "";
  116. }
  117. }
  118. private static String toJson(Object obj) {
  119. if (obj == null) return null;
  120. try {
  121. JsonNode node = MAPPER.valueToTree(obj);
  122. // TODO 这里可继续做脱敏
  123. return MAPPER.writeValueAsString(node);
  124. } catch (Exception ignore) {
  125. return null;
  126. }
  127. }
  128. private static String trim(String str, int max) {
  129. if (str == null) return "";
  130. return str.length() > max ? str.substring(0, max) : str;
  131. }
  132. }