• LogBack入数据库重写


    项目需要:将info以及error的日志信息写入到数据库中;同时所有的日志都要写入到日志文件中。

    可以封装一下,在基类的logError/logInfo中调用了log.error()以及log.info之后在调用一次LoggerDBService进行写入;但是这样就意味着"不美",日志还需要调用两次;而且因为早期设计问题,并不是所有的日志都采用基类的logError/logInfo。

    看了一下logback源码,分析了一下其机制,于是决定采用重写DBAppender并结合AsyncAppender进行异步调用的方式进行实现。对于日志类操作,如果写入数据库这种比较消耗资源和时间的事情进行同步,很不值,于是才决定通过异步方式进行处理。

    AsyncAppender(AA)是一个独立的Appender,放置到它下面的appender(通过appender-ref属性进行设定)也就不需要在放置到root节点下面,因为AsyncAppender设计的逻辑就是:在root下面引用该Appender,然后通过AA进行调度此appender进行日志输出。

        <appender name="asyncLog" class="ch.qos.logback.classic.AsyncAppender">

            <discardingThreshold>0</discardingThreshold>

            <queueSize>10000</queueSize>

            <appender-ref ref="dbLog"/>

        </appender>

        <root level="debug">

            <appender-ref ref="stdout" />

            <appender-ref ref="txtLog" />

            <appender-ref ref="asyncLog"/>

        </root>

    至于调度的逻辑,首先要明白一个概念: LoggingEvent(日志事件),任何一次logback的输出动作,都是一个LogEvent,logEvent里面包括了很多信息,包括要写入的内容,级别等等一系列信息。AA的调度逻辑就是将每次的输出动作放到内置的BlockingQueue中;然后再从BlockingQueue中取出来交给关联的Appender进行处理。LogEvent和Appender是无关的,前者是内容;后者是处理内容。

    ILoggingEvent是每次传入append方法的入参。

    重写的DB继承自UnsynchronizedAppenderBase<ILoggingEvent>,重写了append方法,里面使用自己的DBManager来进行数据库处理;我没有继承DBAppenderBase,是因为里面固化了一些内容,很多都是不需要的;而且采用它,就必须要指定数据库的连接字符串,用户名密码,这意味着同样的数据库要在两个地方进行配置:应用级别的配置文件以及logback的配置文件。于是我索性就直接继承了UnsynchronizedAppenderBase<ILoggingEvent>,没有重用DBAppender相关内容。

    public class TransportDBLoggerAppender extends UnsynchronizedAppenderBase<ILoggingEvent> {

     

        @Override

        public void append(ILoggingEvent eventObject) {

            try {

                String content = eventObject.getFormattedMessage();

                System.out.println("content内容是: " + content);

                Map<String, String> map = new HashMap<String, String>();

                map.put("LOG_LEVEL", eventObject.getLevel().levelStr);

                map.put("CONTENT", content.replace("'", "''"));

                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

                map.put("CREATE_DATE", sdf.format(new Date()));

                // 拼接SQL语句,然后执行

                … …

            } catch (Throwable sqle) {

                String errorMsg = CommonUtil.getTrace(sqle);

                System.out.println(errorMsg);

            }

        }

    }

    在配置文件中,还需要指定过滤级别,因为只需要info和error需要写入到数据库中;在过来级别logback提供了两种方式来进行处理,分别是LevelFilter以及ThresholderFilter,前者只能指定过来特定的级别的操作(ACCEPT,NEUTRAL,DENY),后者则是过滤指定级别,之下的将会被拒绝(DENY)。

        <appender name="dbLog" class="test.MyDBLoggerAppender">

            <filter class="ch.qos.logback.classic.filter.ThresholdFilter">

                <level>INFO</level>

            </filter>

        </appender>

    调用测试代码,发现并没有走入库逻辑,后来才发现原来是因为测试代码走完后,整个应用退出,于是logback也退出了;换言之,放置到Queue里面的内容根本就没有被处理,logback的线程也就消亡了。于是尝试让测试线程阻塞10秒钟,至此,才看到入库的动作以及数据。

            Logger logger = LoggerFactory.getLogger(this.getClass());

            logger.info("test transDbLogger INFO");

            logger.error("test transDbLogger ERROR");

            logger.debug("test transDbLogger DEBUG");

            System.out.println("OK, complete!");

            try {

                Thread.sleep(10000);

            } catch (InterruptedException e) {

                // TODO Auto-generated catch block

                e.printStackTrace();

            }

     

     

  • 相关阅读:
    mysql替代like模糊查询的方法
    8个超实用的jQuery插件应用
    判断登陆设备是否为手机
    SQL tp3.2 批量更新 saveAll
    SQL-批量插入和批量更新
    防止手机端底部导航被搜索框顶起
    php COM
    thinkphp3.2 where 条件查询 复查的查询语句
    Form表单提交,js验证
    jupyter notebook 使用cmd命令窗口打开
  • 原文地址:https://www.cnblogs.com/xiashiwendao/p/5932718.html
Copyright © 2020-2023  润新知