• 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);
        }
    }
    

    添加到数据库暂不展示

  • 相关阅读:
    shell 函数
    使用Alpine镜像构建镜像
    macos修改vmware Fusion的NAT网络
    K8s Pod与宿主机时区不同步
    nginx热升级
    awk分析web日志
    k8s 新建用户远程连接集群和context切换
    查询出口公网ip
    微服务之服务网格 Istio
    Systemd 、systemctl进程管理工具
  • 原文地址:https://www.cnblogs.com/npeng/p/16008881.html
Copyright © 2020-2023  润新知