slf4j与logback
slf4j是日志门面,通过它调用日志实现类
logback为日志实现类
通常用法
Logger logger = LoggerFactory.getLogger(A.class);
logger.info("msg");
就从此为入口,进行源码分析
// 根据名称从工厂获取日志实现类
public static Logger getLogger(String name) {
ILoggerFactory iLoggerFactory = getILoggerFactory();
return iLoggerFactory.getLogger(name);
}
// 获取日志工厂,INITIALIZATION_STATE初始值为0
public static ILoggerFactory getILoggerFactory() {
if (INITIALIZATION_STATE == 0) {
Class var0 = LoggerFactory.class;
synchronized(LoggerFactory.class) {
if (INITIALIZATION_STATE == 0) {
INITIALIZATION_STATE = 1;
performInitialization();
}
}
}
switch(INITIALIZATION_STATE) {
case 1:
return SUBST_FACTORY;
case 2:
throw new IllegalStateException("");
case 3:
return StaticLoggerBinder.getSingleton().getLoggerFactory();
case 4:
return NOP_FALLBACK_FACTORY;
default:
throw new IllegalStateException("");
}
}
// 执行实例化
private static final void performInitialization() {
bind();
if (INITIALIZATION_STATE == 3) {
versionSanityCheck();
}
}
// 绑定日志实现
private static final void bind() {
String msg;
try {
Set<URL> staticLoggerBinderPathSet = null;
if (!isAndroid()) {
staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
}
StaticLoggerBinder.getSingleton();
INITIALIZATION_STATE = 3;
} catch (NoClassDefFoundError var2) {
INITIALIZATION_STATE = 4;
} catch (NoSuchMethodError var3) {
} catch (Exception var4) {
}
}
// 查找org/slf4j/impl/StaticLoggerBinder.class实现类
String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class"
static Set<URL> findPossibleStaticLoggerBinderPathSet() {
LinkedHashSet staticLoggerBinderPathSet = new LinkedHashSet();
try {
ClassLoader loggerFactoryClassLoader = LoggerFactory.class.getClassLoader();
Enumeration paths;
if (loggerFactoryClassLoader == null) {
paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);
} else {
paths = loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH);
}
while(paths.hasMoreElements()) {
URL path = (URL)paths.nextElement();
staticLoggerBinderPathSet.add(path);
}
} catch (IOException var4) {
}
return staticLoggerBinderPathSet;
}
// 初始化
StaticLoggerBinder.getSingleton();
static {
SINGLETON.init();
}
void init() {
(new ContextInitializer(this.defaultLoggerContext)).autoConfig();
this.contextSelectorBinder.init(this.defaultLoggerContext, KEY);
this.initialized = true;
}
// 自动配置
public void autoConfig() throws JoranException {
StatusListenerConfigHelper.installIfAsked(this.loggerContext);
URL url = this.findURLOfDefaultConfigurationFile(true);
if (url != null) {
this.configureByResource(url);
} else {
Configurator c = (Configurator)EnvUtil.loadFromServiceLoader(Configurator.class);
if (c != null) {
try {
c.setContext(this.loggerContext);
c.configure(this.loggerContext);
} catch (Exception var4) {
throw new LogbackException("");
}
} else {
BasicConfigurator basicConfigurator = new BasicConfigurator();
basicConfigurator.setContext(this.loggerContext);
basicConfigurator.configure(this.loggerContext);
}
}
}
// 查找logback.xml配置文件
public URL findURLOfDefaultConfigurationFile(boolean updateStatus) {
ClassLoader myClassLoader = Loader.getClassLoaderOfObject(this);
URL url = this.findConfigFileURLFromSystemProperties(myClassLoader, updateStatus);
if (url != null) {
return url;
} else {
url = this.getResource("logback-test.xml", myClassLoader, updateStatus);
if (url != null) {
return url;
} else {
url = this.getResource("logback.groovy", myClassLoader, updateStatus);
return url != null ? url : this.getResource("logback.xml", myClassLoader, updateStatus);
}
}
}
// 返回日志工厂实现类,进而获取Logger实例输出日志
// 到这里绑定结束,INITIALIZATION_STATE = 3
// 根据条件判断,获取绑定的工厂实现类
StaticLoggerBinder.getSingleton().getLoggerFactory();
public ILoggerFactory getLoggerFactory() {
if (!this.initialized) {
return this.defaultLoggerContext;
} else if (this.contextSelectorBinder.getContextSelector() == null) {
throw new IllegalStateException("contextSelector cannot be null. See also http://logback.qos.ch/codes.html#null_CS");
} else {
return this.contextSelectorBinder.getContextSelector().getLoggerContext();
}
}
总结:每个slf4j日志实现类都存在
org/slf4j/impl/StaticLoggerBinder.class
里面持有工厂实现类的单例,比如logback中的defaultLoggerContext,如果存在多个实现类,由JVM根据类加载次序,决定加载哪一个,然后再进行日志工厂的配置,比如
(new ContextInitializer(this.defaultLoggerContext)).autoConfig();
再回到开头
Logger logger = LoggerFactory.getLogger(A.class);
返回的就是logback日志工厂的实例
mybatis日志模块
org.apache.ibatis.logging.LogFactory
// 按序加载日志实现类
static {
tryImplementation(new Runnable() {
public void run() {
LogFactory.useSlf4jLogging();
}
});
tryImplementation(new Runnable() {
public void run() {
LogFactory.useCommonsLogging();
}
});
tryImplementation(new Runnable() {
public void run() {
LogFactory.useLog4J2Logging();
}
});
tryImplementation(new Runnable() {
public void run() {
LogFactory.useLog4JLogging();
}
});
tryImplementation(new Runnable() {
public void run() {
LogFactory.useJdkLogging();
}
});
tryImplementation(new Runnable() {
public void run() {
LogFactory.useNoLogging();
}
});
}
private static void tryImplementation(Runnable runnable) {
if (logConstructor == null) {
try {
runnable.run();
} catch (Throwable var2) {
}
}
}
以slf4j为例
public static synchronized void useSlf4jLogging() {
setImplementation(Slf4jImpl.class);
}
private static void setImplementation(Class<? extends Log> implClass) {
try {
Constructor<? extends Log> candidate = implClass.getConstructor(String.class);
Log log = (Log)candidate.newInstance(LogFactory.class.getName());
if (log.isDebugEnabled()) {
log.debug("Logging initialized using '" + implClass + "' adapter.");
}
logConstructor = candidate;
} catch (Throwable var3) {
throw new LogException("Error setting Log implementation. Cause: " + var3, var3);
}
}
// 创建slf4j日志对象Logger,并适配到mybatis日志对象Log
public Slf4jImpl(String clazz) {
Logger logger = LoggerFactory.getLogger(clazz);
if (logger instanceof LocationAwareLogger) {
try {
logger.getClass().getMethod("log", Marker.class, String.class, Integer.TYPE, String.class, Object[].class, Throwable.class);
this.log = new Slf4jLocationAwareLoggerImpl((LocationAwareLogger)logger);
return;
} catch (SecurityException var4) {
} catch (NoSuchMethodException var5) {
}
}
this.log = new Slf4jLoggerImpl(logger);
}
// 创建Slf4jImpl对象,这样mybatis的Log就完成了到slf4j的适配
public static Log getLog(String logger) {
try {
return (Log)logConstructor.newInstance(logger);
} catch (Throwable var2) {
throw new LogException("Error creating logger for logger " + logger + ". Cause: " + var2, var2);
}
}
总结:mybatis日志模块其实就是对各日志实现类适配
mybatis日志输出
mybatis通过动态代理,拦截Connection,Statement,PrepareStatement,ResultSet对象进行日志输出,关键代码:
SqlSession session = new SqlSession();
session.selectList(statement);
// 1 DefaultSqlSession
public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {
MappedStatement ms = this.configuration.getMappedStatement(statement);
return this.executor.query(ms, this.wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);
}
// 2 BaseExecutor
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
BoundSql boundSql = ms.getBoundSql(parameter);
CacheKey key = this.createCacheKey(ms, parameter, rowBounds, boundSql);
return this.query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
// 3 SimpleExecutor
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
List var9;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this.wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = this.prepareStatement(handler, ms.getStatementLog());
var9 = handler.query(stmt, resultHandler);
} finally {
this.closeStatement(stmt);
}
return var9;
}
// 4 SimpleExecutor
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Connection connection = this.getConnection(statementLog);
Statement stmt = handler.prepare(connection, this.transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
// 5 BaseExecutor
protected Connection getConnection(Log statementLog) throws SQLException {
Connection connection = this.transaction.getConnection();
return statementLog.isDebugEnabled() ? ConnectionLogger.newInstance(connection, statementLog, this.queryStack) : connection;
}
通过一条执行链路跟踪下来,Connection确实被动态代理了:
ConnectionLogger.newInstance(connection, statementLog, this.queryStack)
具体代码如下:
public final class ConnectionLogger extends BaseJdbcLogger implements InvocationHandler {
private final Connection connection;
private ConnectionLogger(Connection conn, Log statementLog, int queryStack) {
super(statementLog, queryStack);
this.connection = conn;
}
public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, params);
} else {
PreparedStatement stmt;
if ("prepareStatement".equals(method.getName())) {
if (this.isDebugEnabled()) {
this.debug(" Preparing: " + this.removeBreakingWhitespace((String)params[0]), true);
}
stmt = (PreparedStatement)method.invoke(this.connection, params);
stmt = PreparedStatementLogger.newInstance(stmt, this.statementLog, this.queryStack);
return stmt;
} else if ("prepareCall".equals(method.getName())) {
if (this.isDebugEnabled()) {
this.debug(" Preparing: " + this.removeBreakingWhitespace((String)params[0]), true);
}
stmt = (PreparedStatement)method.invoke(this.connection, params);
stmt = PreparedStatementLogger.newInstance(stmt, this.statementLog, this.queryStack);
return stmt;
} else if ("createStatement".equals(method.getName())) {
Statement stmt = (Statement)method.invoke(this.connection, params);
stmt = StatementLogger.newInstance(stmt, this.statementLog, this.queryStack);
return stmt;
} else {
return method.invoke(this.connection, params);
}
}
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
}
public static Connection newInstance(Connection conn, Log statementLog, int queryStack) {
InvocationHandler handler = new ConnectionLogger(conn, statementLog, queryStack);
ClassLoader cl = Connection.class.getClassLoader();
return (Connection)Proxy.newProxyInstance(cl, new Class[]{Connection.class}, handler);
}
public Connection getConnection() {
return this.connection;
}
}
从代码中可以看出,在代理类中对日志进行输出,从源码还可以发现,ConnectionLogger 中调用了PreparedStatementLogger,PreparedStatementLogger中调用了ResultSetLogger,从Connection开始,逐层进行代理,分别完成各自对象的日志输出。
本文从slf4j日志原理,mybatis日志适配,再到SQL执行日志通过关键对象的动态代理进行输出,作了一个简易的分析,从整个源码分析,你会发现这当中用了很多设计模式,比如工厂模式、代理模式、适配器模式,大家好好体会这些设计模式的运用,希望本文对大家有帮助。
参考资料:
https://blog.csdn.net/kxcfzyk/article/details/38613861