将异常Exception堆栈信息转换成字符串智能版

// 基本版

package cn.com.utils;

import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;

public class ExceptionMsgUtils {

    /**
     * getExceptionInfo
     * @param e
     * @return result限制长度30000,MySQL text字段长65535,防止存不进去
     */
    public static String getExceptionInfo(Exception e) {
        StringWriter sw = null;
        PrintWriter pw = null;
        try {
            sw = new StringWriter();
            pw = new PrintWriter(sw);
            e.printStackTrace(pw);
            String result = sw.toString();
            if(StringUtils.isNotBlank(result) && result.length() > 30000) {
                result = result.substring(0,30000);
            }
            return result;
        } finally {
            if (null != sw) {
                try {
                    sw.close();
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
            if (null != pw) {
                pw.close();
            }
        }
    }
}



// 更加智能的升级版本

package cn.bevis;

import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class ExceptionAdvUtil {

    /**
     * 项目基础包
     */
    private static final String PROJECT_BASE_PACKAGE = "cn.bevis";

    /**
     * 将异常堆栈转换为字符串,智能过滤保留关键信息
     * <p>
     * 策略:
     * 1. 前5行完整保留(包含异常类型、消息和关键调用位置)
     * 2. 5行后只保留项目相关代码
     * 3. 总长度不超过limit限制
     * 4. 支持嵌套异常(Caused by)的关键信息提取
     *
     * @param e     异常对象
     * @param limit 最大字符数限制
     * @return 过滤后的异常堆栈字符串
     */
    public static String stacktraceToString(Throwable e, int limit) {
        try {
            if (e == null || limit <= 0) {
                return StrUtil.EMPTY;
            }

            String str = ExceptionUtil.stacktraceToString(e, 30000);
            if (StrUtil.isBlank(str)) {
                return StrUtil.EMPTY;
            }

            StringBuilder result = new StringBuilder(limit);
            String[] lines = str.split(StrUtil.LF);
            int lineCount = 0;
            boolean inCausedBySection = false;
            int causedByLineCount = 0;

            for (String line : lines) {
                // 检查是否超过限制长度
                if (result.length() >= limit) {
                    break;
                }

                // 检测是否进入Caused by段落
                if (line.trim().startsWith("Caused by:")) {
                    inCausedBySection = true;
                    causedByLineCount = 0;
                }

                // 判断是否应该保留该行
                boolean shouldKeep = false;

                if (!inCausedBySection) {
                    // 主异常:前5行全部保留
                    if (lineCount < 5) {
                        shouldKeep = true;
                    } else {
                        // 5行之后,只保留项目相关代码行
                        shouldKeep = isProjectRelevant(line);
                    }
                } else {
                    // Caused by段落:每个cause的前3行保留(异常类型+消息+关键位置)
                    if (causedByLineCount < 3) {
                        shouldKeep = true;
                    } else {
                        // 之后只保留项目相关代码
                        shouldKeep = isProjectRelevant(line);
                    }
                    causedByLineCount++;
                }

                if (shouldKeep) {
                    appendLine(result, line, limit);
                }

                lineCount++;
            }

            return result.toString();
        } catch (Throwable exception) {
            log.warn("ExceptionAdvUtil.stacktraceToString 执行失败!", exception);
            return ExceptionUtil.stacktraceToString(e, limit);
        }
    }

    /**
     * 安全地追加一行到结果中,确保不超过限制
     *
     * @param result 结果缓冲区
     * @param line   要追加的行
     * @param limit  最大长度限制
     */
    private static void appendLine(StringBuilder result, String line, int limit) {
        if (StrUtil.isBlank(line)) {
            return;
        }

        int neededLength = line.length() + 1;
        if (result.length() + neededLength <= limit) {
            result.append(line).append(StrUtil.LF);
        } else {
            int remaining = limit - result.length();
            if (remaining > 0) {
                result.append(line.substring(0, remaining)).append(StrUtil.LF);
            }
        }
    }

    /**
     * 判断该行是否与项目相关
     *
     * @param line 堆栈行
     * @return 如果是项目相关代码返回true
     */
    private static boolean isProjectRelevant(String line) {
        return StrUtil.isNotBlank(line) && line.contains(PROJECT_BASE_PACKAGE);
    }

}

 

posted @ 2019-10-12 10:07  Bevis  阅读(810)  评论(0)    收藏  举报