spring boot錯誤日志統一寫數據庫處理


首先,應用日志直接寫入數據庫(關系型、NoSQL)的話,會極大地影響應用的性能和並發能力。本人做過壓測實驗,並發數到達一定量后,業務接口沒受到什么影響,反倒是應用日志由於生產速度過快,導致日志數據大量堆積,無法寫入數據庫,成為應用的瓶頸。互聯網軟件行業對性能、並發要求比較高,通常使用的日志收集系統架構有如下幾種: ElasticSearch + Logstash + Kibana(ELK)、ElasticSearch + Filebeat + Kibana(EFK)、Kafka + ELK、 Kafka + EFK。每個應用服務器都要安裝agent客戶端從日志文件中收集日志,ElasticSearch做存儲,Kibana做展示。

但是,傳統軟件行業很多對性能、並發性要求並不高,很多軟件項目可能只有一個管理后台,如果硬上互聯網那一套日志收集系統,無疑會增加項目的部署和維護難度。這種情況下,應用info級別的日志可以在項目中定義一個AOP切面異步寫入數據庫。本文主要介紹錯誤日志的統一存儲。

在spring boot項目中,默認使用的是slf4j + logback日志框架。只需實現logback的Appender接口,自定義一個錯誤日志處理類即可對錯誤日志進行統一存儲。

錯誤日志數據庫表設計

添加錯誤日志實體類

@Data
public class ErrorLogPO {

    private Integer logId;
    private String className;
    private String methodName;
    private String exceptionName;
    private String errMsg;
    private String stackTrace;
    private Date createTime;
}

添加錯誤日志寫數據庫自定義Appender類

@Component
public class DbErrorLogAppender extends UnsynchronizedAppenderBase<ILoggingEvent> {

    /**
     * 錯誤日志數據庫增刪改查服務
     */
    @Autowired
    private ILogService logService;

    /**
     * DbErrorLogAppender初始化
     */
    @PostConstruct
    public void init() {
        LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();

        ThresholdFilter filter = new ThresholdFilter();
        filter.setLevel("ERROR");
        filter.setContext(context);
        filter.start();
        this.addFilter(filter);
        this.setContext(context);

        context.getLogger("ROOT").addAppender(DbErrorLogAppender.this);

        super.start();
    }

    /**
     * 錯誤日志拼裝成實體類,寫入數據庫
     */
    @Override
    protected void append(ILoggingEvent loggingEvent) {
        IThrowableProxy tp = loggingEvent.getThrowableProxy();

        // ErrorLogPO數據表實體類
        ErrorLogPO errorLog = new ErrorLogPO();
        errorLog.setErrMsg(loggingEvent.getMessage());
        errorLog.setCreateTime(new Date(loggingEvent.getTimeStamp()));

        if (loggingEvent.getCallerData() != null && loggingEvent.getCallerData().length > 0) {
            StackTraceElement element = loggingEvent.getCallerData()[0];
            errorLog.setClassName(element.getClassName());
            errorLog.setMethodName(element.getMethodName());
        }

        if (tp != null) {
            errorLog.setExceptionName(tp.getClassName());
            errorLog.setStackTrace(getStackTraceMsg(tp));
        }

        try {
            // 錯誤日志實體類寫入數據庫
            logService.addErrorLog(errorLog);
        } catch (Exception ex) {
            this.addError("上報錯誤日志失敗:" + ex.getMessage());
        }
    }

    /**
     * 拼裝堆棧跟蹤信息
     */
    private String getStackTraceMsg(IThrowableProxy tp) {
        StringBuilder buf = new StringBuilder();

        if (tp != null) {
            while (tp != null) {
                this.renderStackTrace(buf, tp);
                tp = tp.getCause();
            }
        }

        return buf.toString();
    }

    /**
     * 堆棧跟蹤信息拼裝成html字符串
     */
    private void renderStackTrace(StringBuilder sbuf, IThrowableProxy tp) {
        this.printFirstLine(sbuf, tp);
        int commonFrames = tp.getCommonFrames();
        StackTraceElementProxy[] stepArray = tp.getStackTraceElementProxyArray();

        for (int i = 0; i < stepArray.length - commonFrames; ++i) {
            StackTraceElementProxy step = stepArray[i];
            sbuf.append("<br />&nbsp;&nbsp;&nbsp;&nbsp;");
            sbuf.append(Transform.escapeTags(step.toString()));
            sbuf.append(CoreConstants.LINE_SEPARATOR);
        }

        if (commonFrames > 0) {
            sbuf.append("<br />&nbsp;&nbsp;&nbsp;&nbsp;");
            sbuf.append("\t... ").append(commonFrames).append(" common frames omitted").append(CoreConstants.LINE_SEPARATOR);
        }

    }

    /**
     * 拼裝堆棧跟蹤信息第一行
     */
    public void printFirstLine(StringBuilder sb, IThrowableProxy tp) {
        int commonFrames = tp.getCommonFrames();
        if (commonFrames > 0) {
            sb.append("<br />").append("Caused by: ");
        }

        sb.append(tp.getClassName()).append(": ").append(Transform.escapeTags(tp.getMessage()));
        sb.append(CoreConstants.LINE_SEPARATOR);
    }
}

添加到數據庫暫不展示


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM