From dd5461d44a1f1ecb0bd76320b516e7db77ebb278 Mon Sep 17 00:00:00 2001 From: wt Date: Thu, 19 Dec 2019 09:14:26 +0800 Subject: [PATCH] mybatis logger --- docs/Mybatis/Mybatis-log.md | 250 ++++++++++++++++++++++++++++++++++++ 1 file changed, 250 insertions(+) create mode 100644 docs/Mybatis/Mybatis-log.md diff --git a/docs/Mybatis/Mybatis-log.md b/docs/Mybatis/Mybatis-log.md new file mode 100644 index 0000000..335ed49 --- /dev/null +++ b/docs/Mybatis/Mybatis-log.md @@ -0,0 +1,250 @@ +# mybatis 日志源码 +- Author: [HuiFer](https://github.com/huifer) +- Description: 该文介绍 mybatis 日志相关源码 +## 核心类 +- `org.apache.ibatis.logging.Log` +- `org.apache.ibatis.logging.LogFactory` +- 多个日志实现 + - `org.apache.ibatis.logging.log4j2.Log4j2Impl` + - `org.apache.ibatis.logging.slf4j.Slf4jLocationAwareLoggerImpl` + - ... + +## 源码流程 +- mybatis 提供了一个日志接口,内容如下. +```java +/** + * mybatis 的日志接口,提供日志级别 + *
    + *
  1. error
  2. + *
  3. debug
  4. + *
  5. trace
  6. + *
  7. warn
  8. + *
+ *

通过自己定义的接口来实现各大日志框架的内容达到高可用

+ * @author Clinton Begin + */ +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); +} +``` +- 有了日志接口必然有实现类, mybatis 有`log4j2` 、 `slf4j` 等日志的相关实现 , 下面是`Slf4jImpl`的代码,其他代码也是一样的模式进行初始化就不再重复贴代码了. +```java +public class Slf4jImpl implements Log { + + private Log log; + + /** + * 创建日志实例 + * @param clazz + */ + public Slf4jImpl(String clazz) { + Logger logger = LoggerFactory.getLogger(clazz); + + if (logger instanceof LocationAwareLogger) { + try { + // check for slf4j >= 1.6 method signature + logger.getClass().getMethod("log", Marker.class, String.class, int.class, String.class, Object[].class, Throwable.class); + log = new Slf4jLocationAwareLoggerImpl((LocationAwareLogger) logger); + return; + } catch (SecurityException | NoSuchMethodException e) { + // fail-back to Slf4jLoggerImpl + } + } + + // Logger is not LocationAwareLogger or slf4j version < 1.6 + log = new Slf4jLoggerImpl(logger); + } + + @Override + public boolean isDebugEnabled() { + return log.isDebugEnabled(); + } + + @Override + public boolean isTraceEnabled() { + return log.isTraceEnabled(); + } + + @Override + public void error(String s, Throwable e) { + log.error(s, e); + } + + @Override + public void error(String s) { + log.error(s); + } + + @Override + public void debug(String s) { + log.debug(s); + } + + @Override + public void trace(String s) { + log.trace(s); + } + + @Override + public void warn(String s) { + log.warn(s); + } + +} + +``` +- 通过上述方法来达到统一接口多个实现,这个在开发中也经常使用.多日志的实现方法有了还缺一个创建方法,创建方法由`org.apache.ibatis.logging.LogFactory`提供 +```java + +/** + *

日志工厂,实现内容:

+ *
    + *
  1. org.slf4j.Logger 日志框架 slf4j
  2. + *
  3. org.apache.commons.logging.Log 日志框架 apache
  4. + *
  5. org.apache.logging.log4j.Logger 日志框架 log4j2
  6. + *
  7. org.apache.log4j.Logger 日志框架 log4j
  8. + *
  9. java.util.logging.Logger 日志框架,JDK的logger
  10. + * + *
+ * @author Clinton Begin + * @author Eduardo Macarron + */ +public final class LogFactory { + + /** + * Marker to be used by logging implementations that support markers. + */ + public static final String MARKER = "MYBATIS"; + + private static Constructor logConstructor; + + /** + * 日志的实现类的具体选择 + */ + static { + // slf4j 日志 + tryImplementation(LogFactory::useSlf4jLogging); + // apache 日志 + tryImplementation(LogFactory::useCommonsLogging); + // log4j2 日志 + tryImplementation(LogFactory::useLog4J2Logging); + // log4 日志 + tryImplementation(LogFactory::useLog4JLogging); + // JDK 日志 + tryImplementation(LogFactory::useJdkLogging); + // 空 日志 + tryImplementation(LogFactory::useNoLogging); + } + + /** + * 私有化构造方法,这是一个单例 + */ + private LogFactory() { + // disable construction + } + + public static Log getLog(Class aClass) { + return getLog(aClass.getName()); + } + + public static Log getLog(String logger) { + try { + return logConstructor.newInstance(logger); + } catch (Throwable t) { + throw new LogException("Error creating logger for logger " + logger + ". Cause: " + t, t); + } + } + + public static synchronized void useCustomLogging(Class clazz) { + setImplementation(clazz); + } + + public static synchronized void useSlf4jLogging() { + setImplementation(org.apache.ibatis.logging.slf4j.Slf4jImpl.class); + } + + public static synchronized void useCommonsLogging() { + setImplementation(org.apache.ibatis.logging.commons.JakartaCommonsLoggingImpl.class); + } + + public static synchronized void useLog4JLogging() { + setImplementation(org.apache.ibatis.logging.log4j.Log4jImpl.class); + } + + public static synchronized void useLog4J2Logging() { + setImplementation(org.apache.ibatis.logging.log4j2.Log4j2Impl.class); + } + + public static synchronized void useJdkLogging() { + setImplementation(org.apache.ibatis.logging.jdk14.Jdk14LoggingImpl.class); + } + + public static synchronized void useStdOutLogging() { + setImplementation(org.apache.ibatis.logging.stdout.StdOutImpl.class); + } + + public static synchronized void useNoLogging() { + setImplementation(org.apache.ibatis.logging.nologging.NoLoggingImpl.class); + } + + /** + * 选择具体的日志实现 + */ + private static void tryImplementation(Runnable runnable) { + if (logConstructor == null) { + try { + // run()? 似乎违背了代码的语义, 看静态方法.静态方法多行同类型的操作我认为是一个多线程 + runnable.run(); + } catch (Throwable t) { + // ignore + } + } + } + + /** + * 选择具体的日志实现 + */ + private static void setImplementation(Class implClass) { + try { + Constructor 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); + } + } + +} + +``` + +- `LogFactory`是一个单例对象,对外公开`getLog`方法在使用时直接`private static final Log log = LogFactory.getLog(CglibProxyFactory.class);`即可 + +- 在 `org.apache.ibatis.session.Configuration` 中可以看到下面这些注册方法 +```java + // 日志实现类 + typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class); + typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class); + typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class); + typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class); + typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class); + typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class); + typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class); + +```