美文网首页码农的世界Java 杂谈Springboot
spring boot错误日志统一写数据库处理

spring boot错误日志统一写数据库处理

作者: jessehua | 来源:发表于2019-04-25 12:40 被阅读3次

    首先,应用日志直接写入数据库(关系型、NoSQL)的话,会极大地影响应用的性能和并发能力。本人做过压测实验,并发数到达一定量后,业务接口没受到什么影响,反倒是应用日志由于生产速度过快,导致日志数据大量堆积,无法写入数据库,成为应用的瓶颈。互联网软件行业对性能、并发要求比较高,通常使用的日志收集系统架构有如下几种: ElasticSearch + Logstash + Kibana(ELK)、ElasticSearch + Filebeat + Kibana(EFK)、Kafka + ELK、 Kafka + EFK。每个应用服务器都要安装agent客户端从日志文件中收集日志,ElasticSearch做存储,Kibana做展示。

    但是,传统软件行业很多对性能、并发性要求并不高,很多软件项目可能只有一个管理后台,如果硬上互联网那一套日志收集系统,无疑会增加项目的部署和维护难度。这种情况下,应用info级别的日志可以在项目中定义一个AOP切面异步写入数据库。本文主要介绍错误日志的统一存储。

    在spring boot项目中,默认使用的是slf4j + logback日志框架。只需实现logback的Appender接口,自定义一个错误日志处理类即可对错误日志进行统一存储。

    先上效果图:


    错误日志列表 错误日志详情页

    错误日志数据库表设计


    错误日志数据库表结构

    添加错误日志实体类

    public class ErrorLogPO {
    
        private Integer logId;
    
        private String className;
    
        private String methodName;
    
        private String exceptionName;
    
        private String errMsg;
    
        private String stackTrace;
    
        private Date createTime;
    
        public Integer getLogId() {
            return logId;
        }
    
        public void setLogId(Integer logId) {
            this.logId = logId;
        }
    
        public String getClassName() {
            return className;
        }
    
        public void setClassName(String className) {
            this.className = className;
        }
    
        public String getMethodName() {
            return methodName;
        }
    
        public void setMethodName(String methodName) {
            this.methodName = methodName;
        }
    
        public String getExceptionName() {
            return exceptionName;
        }
    
        public void setExceptionName(String exceptionName) {
            this.exceptionName = exceptionName;
        }
    
        public String getErrMsg() {
            return errMsg;
        }
    
        public void setErrMsg(String errMsg) {
            this.errMsg = errMsg;
        }
    
        public String getStackTrace() {
            return stackTrace;
        }
    
        public void setStackTrace(String stackTrace) {
            this.stackTrace = stackTrace;
        }
    
        public Date getCreateTime() {
            return createTime;
        }
    
        public void setCreateTime(Date createTime) {
            this.createTime = 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);
        }
    }
    

    相关文章

      网友评论

        本文标题:spring boot错误日志统一写数据库处理

        本文链接:https://www.haomeiwen.com/subject/dkwagqtx.html