Mybatis日志源码分析

Mybatis的日志分析

在使用Mybatis的时候,可以看到控制台的日志输出。有很多的日志框架,那么Mybatis如何与这些日志框架进行整合呢?优先级又如何确定?日志信息如何优雅的输出?

1、统一日志标准

使用接口进行标准统一。

public interface Log {

  boolean isDebugEnabled();

  boolean isTraceEnabled();

  void error(String s, Throwable e);

  void error(String s);

  void debug(String s);

  void trace(String s);

  void warn(String s);
}

2、采用适配器模式进行适配

每个日志的实现都提供对应的适配器,以log4j为例:
将第三方的日志接口转换为Mybatis规定的统一日志接口

public class Log4jImpl implements Log {

  private static final String FQCN = Log4jImpl.class.getName();
  // 待适配的对象
  private final Logger log;

  public Log4jImpl(String clazz) {
    log = Logger.getLogger(clazz);
  }

  @Override
  public boolean isDebugEnabled() {
    return log.isDebugEnabled();
  }

  @Override
  public boolean isTraceEnabled() {
    return log.isTraceEnabled();
  }

  @Override
  public void error(String s, Throwable e) {
    log.log(FQCN, Level.ERROR, s, e);
  }

  @Override
  public void error(String s) {
    log.log(FQCN, Level.ERROR, s, null);
  }

  @Override
  public void debug(String s) {
    log.log(FQCN, Level.DEBUG, s, null);
  }

  @Override
  public void trace(String s) {
    log.log(FQCN, Level.TRACE, s, null);
  }

  @Override
  public void warn(String s) {
    log.log(FQCN, Level.WARN, s, null);
  }
}

3、按优先级进行加载,加载到之后,后序的不再加载

按顺序加载:

static {
    tryImplementation(LogFactory::useSlf4jLogging);
    tryImplementation(LogFactory::useCommonsLogging);
    tryImplementation(LogFactory::useLog4J2Logging);
    tryImplementation(LogFactory::useLog4JLogging);
    tryImplementation(LogFactory::useJdkLogging);
    tryImplementation(LogFactory::useNoLogging);
  }

工厂类中保存对应的日志的Construct对象

private static void setImplementation(Class<? extends Log> implClass) {
    try {
      Constructor<? extends Log> candidate = implClass.getConstructor(String.class);
      Log log = candidate.newInstance(LogFactory.class.getName());
      if (log.isDebugEnabled()) {
        log.debug("Logging initialized using '" + implClass + "' adapter.");
      }
      logConstructor = candidate;
    } catch (Throwable t) {
      throw new LogException("Error setting Log implementation.  Cause: " + t, t);
    }
  }

4、动态代理模式进行优雅输出

为了将业务与日志进行解耦,采取动态代理模式,以ConnectionLogger为例:

   
    public Object invoke(Object proxy, Method method, Object[] params)
          throws Throwable {
        // 从Object类中继承过来的方法不进行增强
        try {
          if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, params);
          }
          /**
           *  PreparedStatment:  connection.prepareStatement(sql);
           *  connection.prepareCall(sql);
           */
          if ("prepareStatement".equals(method.getName()) || "prepareCall".equals(method.getName())) {
            if (isDebugEnabled()) {
                // 日志输出
              debug(" Preparing: " + removeExtraWhitespace((String) params[0]), true);
            }
            PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
            stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);
            return stmt;
          } else if ("createStatement".equals(method.getName())) {
            Statement stmt = (Statement) method.invoke(connection, params);
            stmt = StatementLogger.newInstance(stmt, statementLog, queryStack);
            return stmt;
          } else {
            return method.invoke(connection, params);
          }
        } catch (Throwable t) {
          throw ExceptionUtil.unwrapThrowable(t);
        }
      }


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章