MyBatis控制特定SQL日志级别

前述

生产过程中往往需要将重要的SQL输出到日志中以方便查看系统运行情况,但是当大数据量文件入库时的SQL信息会异常多,多到一两天时间就能把日志服务器撑爆,而且大量日志输出会影响服务器性能。但是由于架构设计及开发规范等因素限制,我们的入库SQL和查询等在一个namespace(xml)下,所以想通过logback配置文件修改的方式也不可行,无奈,只能翻mybatis源码来寻找出路。


Mybatis日志输出的原理我这里不展开讲了,他本身支持很多日志框架,当然,大名鼎鼎的slf4j也在其中,我们的框架就使用的slf4j+logback实现。mybatis中每一条SQL语句(namespace+id对应唯一一条SQL)都有其自己对应的logger对象,每个logger对象又有自己的日志层级,因此拿出该对象并修改其日志层级应该可以成事。
二话不说上代码
spring加载完成后找到配置文件中的SQLID,取出他们的logger对象,使用反射修改日志层级(mybatis没有开放这块的功能,或者说我没有找到)


工具类(path:…${moudle_name}\src\main\java\com**\util\logback\LogLevelChangeUtil.java)

package util.logback;

import ch.qos.logback.classic.Level;
import com.dcits.galaxy.core.spring.SpringApplicationContext;
import org.apache.ibatis.logging.slf4j.Slf4jImpl;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextStartedEvent;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.Enumeration;
import java.util.Properties;

/**
 * @author 
 * @Title: FilterSqlService
 * @Description 用于处理sql对应的日志级别
 * @date 
 */
@Service
public class LogLevelChangeUtil implements ApplicationListener {
    private static final Logger logger = LoggerFactory.getLogger(LogLevelChangeUtil.class);
    private Properties nolog = new Properties();
    private Configuration configuration;

    /**
     * 容器初始化完成后修改nosql配置文件配置的sql语句日志级别
     * @param contextStartedEvent
     */
    @Override
    public void onApplicationEvent(ContextStartedEvent contextStartedEvent) {
        logger.info("Start set SQL log level!");
        DefaultSqlSessionFactory sqlSessionFactory = (DefaultSqlSessionFactory)SpringApplicationContext.getContext().getBean("sqlSessionFactory");
        configuration = sqlSessionFactory.getConfiguration();
        InputStream nologProperties = ClassLoader.getSystemResourceAsStream("META-INF/spring/nosql.properties");
        try {
            nolog.load(nologProperties);
            Enumeration propertyNames = nolog.propertyNames();
            while(propertyNames.hasMoreElements()){
                String sqlId = (String)propertyNames.nextElement();
                String logLevel = nolog.getProperty(sqlId);
                if(logger.isInfoEnabled()){
                    logger.info("Will set SQL:[{}] LOGLEVEL to [{}]",sqlId,logLevel);
                }
                changeStatementLogLevel(configuration, sqlId,logLevel);
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 通过SQlmappedStatement全名查找并修改其对应的日志对象级别为关闭
     * @param configuration
     * @param name
     */
    public void changeStatementLogLevel(Configuration configuration, String name,String logLevel) {
        MappedStatement mappedStatement = null;
        try {
            mappedStatement = configuration.getMappedStatement(name);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if(mappedStatement==null){
            logger.warn("SQL:--->[{}] is not exist!",name);
            return;
        }
        Slf4jImpl slf4jLog = (Slf4jImpl)mappedStatement.getStatementLog();
        try {
            Field log = slf4jLog.getClass().getDeclaredField("log");
            log.setAccessible(true);
            Object slfLocation =log.get(slf4jLog);
            Field logger = slfLocation.getClass().getDeclaredField("logger");
            logger.setAccessible(true);
            Object loggerObj = logger.get(slfLocation);
            Field effectiveLevelInt = loggerObj.getClass().getDeclaredField("effectiveLevelInt");
            effectiveLevelInt.setAccessible(true);
            int level = getLevelInt(logLevel);
            effectiveLevelInt.setInt(loggerObj , level);
            LogLevelChangeUtil.logger.info("SQL log level setting successful!");
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    private int getLevelInt(String logLevel) {
        int level = 10000;
        if("off".compareToIgnoreCase(logLevel)==0){
            level = Level.OFF_INT;
        }else if("error".compareToIgnoreCase(logLevel)==0){
            level=Level.ERROR_INT;
        }else if("warn".compareToIgnoreCase(logLevel)==0){
            level=Level.WARN_INT;
        }else if("trace".compareToIgnoreCase(logLevel)==0){
            level=Level.TRACE_INT;
        }
        return level;
    }
}

配置文件(path:…${moudle_name}\src\main\resources\META-INF\spring\nosql.properties)

配置文件内容
***.***.***.***.**Dao(1).insertMap=off
***.***.***.***.**Dao(2).insertMap=off
***.***.***.***.**Dao(3).insertMap=off

注意

该方式在spring容器初始化完成后只修改一次,若程序运行过程中修改logback.xml文件,并且logback的scan配置为"true",则会在logback重新扫描配置文件后失效,需要支持logback的重新加载并且保持以上修改生效的话可以设计通过其他方式调用以上工具类。

你可能感兴趣的:(logback,mybatis)