| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156 |
- package com.loan.system.domain.entity;
- import com.fasterxml.jackson.databind.JsonNode;
- import com.fasterxml.jackson.databind.ObjectMapper;
- import lombok.*;
- import org.apache.commons.io.output.ByteArrayOutputStream;
- import org.apache.commons.lang3.exception.ExceptionUtils;
- import javax.persistence.*;
- import javax.validation.constraints.NotNull;
- import javax.validation.constraints.Size;
- import java.io.IOException;
- import java.time.Instant;
- import java.time.LocalDateTime;
- import java.time.format.DateTimeFormatter;
- import java.util.Base64;
- import java.util.zip.GZIPOutputStream;
- @Entity
- @Table(name = "exception_log")
- @Data
- @NoArgsConstructor
- @AllArgsConstructor
- @Builder
- public class ExceptionLog {
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- @Column(name = "id", nullable = false)
- private Long id;
- @Size(max = 120)
- @Column(name = "exception_type", length = 120)
- private String exceptionType;
- @Size(max = 500)
- @Column(name = "message", length = 500)
- private String message;
- @Size(max = 200)
- @Column(name = "summary", length = 200)
- private String summary;
- @Size(max = 200)
- @Column(name = "cause", length = 200)
- private String cause;
- @Lob
- @Column(name = "stack_trace")
- private String stackTrace;
- @Size(max = 255)
- @Column(name = "url")
- private String url;
- @Column(name = "user_id")
- private Long userId;
- @Size(max = 45)
- @Column(name = "ip", length = 45)
- private String ip;
- @Size(max = 300)
- @Column(name = "user_agent", length = 300)
- private String userAgent;
- @Size(max = 10)
- @Column(name = "method", length = 10)
- private String method;
- @Lob
- @Column(name = "params")
- private String params;
- @Column(name = "is_resolve")
- private Boolean isResolve;
- @NotNull
- @Column(name = "create_time", nullable = false)
- private String createTime;
- private static final ObjectMapper MAPPER = new ObjectMapper();
- /** 快速构建:只传异常 + 请求上下文 */
- public static ExceptionLog build(Exception e, String url, String method,
- String ip, String userAgent, Long userId,
- Object paramsObj) {
- Throwable root = ExceptionUtils.getRootCause(e);
- if (root == null) root = e;
- return ExceptionLog.builder()
- .exceptionType(e.getClass().getSimpleName())
- .message(trim(e.getMessage(), 500))
- .summary(buildSummary(e))
- .cause(root.getClass().getSimpleName())
- .stackTrace(compressTrace(e, 50))
- .url(trim(url, 255))
- .method(trim(method, 10))
- .ip(trim(ip, 45))
- .userAgent(trim(userAgent, 300))
- .userId(userId == null ? 0L : userId)
- .params(toJson(paramsObj))
- .createTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
- .build();
- }
- /** 摘要:类名 + 第一行堆栈 */
- private static String buildSummary(Exception e) {
- StringBuilder sb = new StringBuilder(e.getClass().getSimpleName());
- if (e.getMessage() != null) {
- sb.append(": ").append(trim(e.getMessage(), 120));
- }
- if (e.getStackTrace().length > 0) {
- StackTraceElement first = e.getStackTrace()[0];
- sb.append(" at ")
- .append(first.getClassName())
- .append(":")
- .append(first.getLineNumber());
- }
- return trim(sb.toString(), 200);
- }
- /** 截取堆栈前 max 行并 GZIP + Base64 */
- private static String compressTrace(Exception e, int max) {
- String raw = ExceptionUtils.getStackTrace(e);
- String[] lines = raw.split("\n");
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < Math.min(max, lines.length); i++) {
- sb.append(lines[i]).append('\n');
- }
- try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
- GZIPOutputStream gzip = new GZIPOutputStream(bos)) {
- gzip.write(sb.toString().getBytes("UTF-8"));
- gzip.finish();
- return Base64.getEncoder().encodeToString(bos.toByteArray());
- } catch (IOException ignored) {
- return "";
- }
- }
- private static String toJson(Object obj) {
- if (obj == null) return null;
- try {
- JsonNode node = MAPPER.valueToTree(obj);
- // TODO 这里可继续做脱敏
- return MAPPER.writeValueAsString(node);
- } catch (Exception ignore) {
- return null;
- }
- }
- private static String trim(String str, int max) {
- if (str == null) return "";
- return str.length() > max ? str.substring(0, max) : str;
- }
- }
|