Docker镜像加速实战:国内开发者必备的镜像源配置指南
1. Docker镜像加速的必要性
1. 概述
本文,我们来分享 MyBatis 的日志模块,对应 logging 包。如下图所示:
logging 包
在 Java 世界里,有很多优秀的日志框架,例如 Log4j、 Log4j2、Slf4j 等等。MyBatis 除了提供了详细的日志输出信息,还能够集成多种日志框架,其日志模块的主要功能就是集成第三方日志框架到 MyBatis 中。
2. Log 接口
在 logging 包中,定义了 Log 接口,为 MyBatis 日志模块的核心接口。代码如下:
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);
}
- 定义了日志的五个级别:
error、debug、trace、warn。当然,还有info级别,暂时没定义。
2.1 LogFactory
org.apache.ibatis.logging.LogFactory ,Log 工厂类,负责创建 Log 对象。代码如下:
public final class LogFactory {
/**
* Marker to be used by logging implementations that support markers.
*/
public static final String MARKER = "MYBATIS";
/**
* 使用的 Log 的构造方法
*/
private static Constructor<? extends Log> logConstructor;
static {
// <1> 逐个尝试,判断使用哪个 Log 的实现类,即初始化 logConstructor 属性
tryImplementation(LogFactory::useSlf4jLogging);
tryImplementation(LogFactory::useCommonsLogging);
tryImplementation(LogFactory::useLog4J2Logging);
tryImplementation(LogFactory::useLog4JLogging);
tryImplementation(LogFactory::useJdkLogging);
tryImplementation(LogFactory::useNoLogging);
}
private LogFactory() {
// disable construction
}
public static Log getLog(Class<?> clazz) {
return getLog(clazz.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<? extends Log> 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 {
runnable.run();
} catch (Throwable t) {
// ignore
}
}
}
private static void setImplementation(Class<? extends Log> implClass) {
try {
// 获得参数为 String 的构造方法
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
logConstructor = candidate;
} catch (Throwable t) {
throw new LogException("Error setting Log implementation. Cause: " + t, t);
}
}
}
-
logConstructor静态属性,使用的 Log 的构造方法。后续,通过该构造方法,创建对应的 Log 对象。 -
<1>处,在静态代码块中,基于tryImplementation方法,逐个尝试,判断使用哪个 Log 的实现类,即初始化logConstructor属性。因为只要logConstructor静态属性被设置成功,tryImplementation方法,就会直接返回。 -
#setImplementation(Class<? extends Log> implClass)方法,尝试设置logConstructor静态属性。代码如下:- 首先,获得参数为 String 的构造方法。
- 然后,创建 Log 对象。若创建成功,则意味着可以使用,设置为
logConstructor静态属性。
-
#getLog(...)静态方法,创建 Log 对象。代码如下:public static Log getLog(Class<?> clazz) { return getLog(clazz.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); } }- 通过
logConstructor静态属性,创建 Log 对象。
- 通过
-
其它方法,主要是
#useXXX()方法,尝试设置对应的 Log 实现类到logConstructor静态属性。当然,#useCustomLogging(Class<? extends Log> clazz)方法,是设置自定义的 Log 实现类。
2.2 Log 实现类
在 logging 包下,有多个子包,对应多种不同的日志框架的实现。如下图所示:
Log 实现类
-
每个子包下,基本只有一个 Log 实现类。当然,
jcl和slf4j包下,还有XXXLoggerImpl类,也是 Log 实现类。 -
以
slf4j包为例子,Slf4jImpl代码如下:public class Slf4jImpl implements Log { private Log log; public Slf4jImpl(String clazz) { // 使用 SLF4J 的 LoggerFactory 创建 org.slf4j.Logger 对象 Logger logger = LoggerFactory.getLogger(clazz); // 如果是 LocationAwareLogger ,则创建 LocationAwareLoggerImpl 对象 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 LocationAwareLoggerImpl((LocationAwareLogger) logger); return; } catch (SecurityException e) { // fail-back to Slf4jLoggerImpl } catch (NoSuchMethodException e) { // fail-back to Slf4jLoggerImpl } } // Logger is not LocationAwareLogger or slf4j version < 1.6 // 否则,创建 Slf4jLoggerImpl 对象 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); } }- 在构造方法中,使用 SLF4J 的
org.slf4j.LoggerFactory创建org.slf4j.Logger对象。 - 如果是
org.slf4j.spi.LocationAwareLogger类型,则创建LocationAwareLoggerImpl对象。 - 否则,创建
Slf4jLoggerImpl对象。 - 具体的每个方法,调用对应的
log对应的方法。
- 在构造方法中,使用 SLF4J 的
-
其它 Log 实现类,胖友可以自己看看。
3. 日志自动发现机制
在 「2.1 LogFactory」 的静态代码块中,我们可以看到,MyBatis 会尝试加载不同日志框架的对应的 Log 实现类。那么,如果我们有多个日志框架存在的情况下,到底使用哪个呢?这里就涉及到了日志的自动发现机制。
- 第一步,
org.apache.ibatis.logging.LogFactory的静态代码块,会尝试按顺序加载不同日志框架的对应的 Log 实现类。 - 第二步,在尝试加载的日志框架存在的条件下,使用该日志框架的对应的 Log 实现类。例如:
- 配置了
-Dorg.apache.commons.logging.Log=org.apache.commons.logging.impl.Log4JLogger参数,则useCommonsLogging()方法,会设置为使用commons-logging日志框架。 - 当然,如果
slf4j存在,则useSlf4jLogging()方法,会设置为使用slf4j日志框架。 - 等等等。
- 配置了
- 第三步,如果都找不到,则使用
useNoLogging()方法,即不使用日志框架。
4. JDBC 日志
在 logging 包中,还有 jdbc 包,基于 JDBC 相关的操作,进行日志打印。核心类如下:
BaseJdbcLogger:所有 JDBC 日志实现类的基类ConnectionLogger:连接日志PreparedStatementLogger:预处理语句日志StatementLogger:语句日志ResultSetLogger:结果集日志
4.1 BaseJdbcLogger
org.apache.ibatis.logging.jdbc.BaseJdbcLogger ,所有 JDBC 日志实现类的基类。代码如下:
public abstract class BaseJdbcLogger {
/**
* SET 方法集合
*/
protected static final Set<String> SET_METHODS;
/**
* EXECUTE 方法集合
*/
protected static final Set<String> EXECUTE_METHODS = new HashSet<>();
/**
* SET 方法名称的集合
*/
private static final Set<String> SET_METHOD_NAMES = Arrays.stream(PreparedStatement.class.getMethods())
.filter(method -> method.getName().startsWith("set"))
.filter(method -> method.getParameterCount() > 1)
.map(Method::getName)
.collect(Collectors.toSet());
/**
* 构造方法
*/
static {
SET_METHODS = Collections.unmodifiableSet(SET_METHOD_NAMES);
EXECUTE_METHODS.add("execute");
EXECUTE_METHODS.add("executeUpdate");
EXECUTE_METHODS.add("executeQuery");
EXECUTE_METHODS.add("addBatch");
}
/**
* Log 对象
*/
protected final Log statementLog;
/**
* 查询的列数
*/
protected final int queryStack;
/**
* 记录 SQL 的参数
*/
protected Map<Integer, String> columnMap = new HashMap<>();
/**
* 记录的参数列表
*/
protected List<Object> columnValues = new ArrayList<>();
protected BaseJdbcLogger(Log statementLog, int queryStack) {
this.statementLog = statementLog;
if (queryStack == 0) {
this.queryStack = 1;
} else {
this.queryStack = queryStack;
}
}
}
SET_METHODS静态属性,java.sql.PreparedStatement所有set方法名的集合。即,PreparedStatement所有set方法名,如下图所示:
SET_METHODSEXECUTE_METHODS静态属性,java.sql.Statement所有execute相关方法名的集合。即,Statement所有execute相关方法名,如下图所示:
EXECUTE_METHODSstatementLog属性,Log 对象。queryStack属性,查询的列数。columnMap属性,记录的 SQL 的参数集合。KEY:参数的编号。VALUE:参数名。columnValues属性,记录的参数列表。
4.2 ConnectionLogger
org.apache.ibatis.logging.jdbc.ConnectionLogger ,继承 BaseJdbcLogger 类,Connection 日志。代码如下:
public final class ConnectionLogger extends BaseJdbcLogger {
/**
* Connection 对象
*/
private final Connection connection;
private ConnectionLogger(Connection conn, Log statementLog, int queryStack) {
super(statementLog, queryStack);
this.connection = conn;
}
@Override
public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
try {
// 如果来自 Object 的方法,则直接调用
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, params);
}
// 如果为 prepareStatement 方法,则打印日志,并创建 PreparedStatementLogger 对象
if ("prepareStatement".equals(method.getName())) {
if (isDebugEnabled()) {
debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true);
}
// 创建 PreparedStatement
PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
// 创建 PreparedStatementLogger
stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);
return stmt;
}
// 如果为 prepareCall 方法,则打印日志,并创建 PreparedStatementLogger 对象
else if ("prepareCall".equals(method.getName())) {
if (isDebugEnabled()) {
debug(" Preparing: " + removeBreakingWhitespace((String) params[0]), true);
}
// 创建 PreparedStatement
PreparedStatement stmt = (PreparedStatement) method.invoke(connection, params);
// 创建 PreparedStatementLogger
stmt = PreparedStatementLogger.newInstance(stmt, statementLog, queryStack);
return stmt;
}
// 如果为 createStatement 方法,则打印日志,并创建 StatementLogger 对象
else if ("createStatement".equals(method.getName())) {
// 创建 Statement
Statement stmt = (Statement) method.invoke(connection, params);
// 创建 StatementLogger
stmt = StatementLogger.newInstance(stmt, statementLog, queryStack);
return stmt;
} else {
return method.invoke(connection, params);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
/**
* Creates a logging version of a connection.
*
* @param conn - the original connection
* @return - the connection with logging
*/
public static Connection newInstance(Connection conn, Log statementLog, int queryStack) {
// 创建 InvocationHandler 对象
InvocationHandler handler = new ConnectionLogger(conn, statementLog, queryStack);
// 创建 Connection 代理
ClassLoader cl = Connection.class.getClassLoader();
return (Connection) Proxy.newProxyInstance(cl, new Class[]{Connection.class}, handler);
}
/**
* return the wrapped connection.
*
* @return the connection
*/
public Connection getConnection() {
return connection;
}
}
connection属性,Connection 对象。#invoke(Object proxy, Method method, Object[] params)方法,代理方法。代码如下:- 如果是
prepareStatement、prepareCall、createStatement方法,则打印日志,并创建对应的PreparedStatementLogger或StatementLogger对象。
- 如果是
#newInstance(Connection conn, Log statementLog, int queryStack)静态方法,创建 Connection 的代理对象。
4.3 PreparedStatementLogger
org.apache.ibatis.logging.jdbc.PreparedStatementLogger ,继承 BaseJdbcLogger 类,PreparedStatement 日志。代码如下:
public final class PreparedStatementLogger extends BaseJdbcLogger {
/**
* PreparedStatement 对象
*/
private final PreparedStatement statement;
private PreparedStatementLogger(PreparedStatement stmt, Log statementLog, int queryStack) {
super(statementLog, queryStack);
this.statement = stmt;
}
@Override
public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
try {
// 如果来自 Object 的方法,则直接调用
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, params);
}
// 如果为 execute 相关方法
if (EXECUTE_METHODS.contains(method.getName())) {
if (isDebugEnabled()) {
debug("Parameters: " + getParameterValueString(), true); // <1> 打印参数
}
// 清空 columnMap 和 columnValues
clearColumnInfo();
// 执行方法
if ("executeQuery".equals(method.getName())) {
ResultSet rs = (ResultSet) method.invoke(statement, params);
return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
} else {
return method.invoke(statement, params);
}
}
// 如果为 set 相关方法
else if (SET_METHODS.contains(method.getName())) {
if ("setNull".equals(method.getName())) { // 设置 NULL
// 添加到 columnMap 和 columnValues 中
setColumn(params[0], null);
} else {
// 添加到 columnMap 和 columnValues 中
setColumn(params[0], params[1]);
}
return method.invoke(statement, params);
}
// 如果为 get 相关方法
else if ("getResultSet".equals(method.getName())) {
ResultSet rs = (ResultSet) method.invoke(statement, params);
return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
}
// 如果为 getUpdateCount 方法
else if ("getUpdateCount".equals(method.getName())) {
int updateCount = (Integer) method.invoke(statement, params);
if (updateCount != -1) {
debug(" Updates: " + updateCount, false);
}
return updateCount;
} else {
return method.invoke(statement, params);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
/**
* Creates a logging version of a PreparedStatement.
*
* @param stmt - the statement
* @return - the proxy
*/
public static PreparedStatement newInstance(PreparedStatement stmt, Log statementLog, int queryStack) {
// 创建 InvocationHandler 对象
InvocationHandler handler = new PreparedStatementLogger(stmt, statementLog, queryStack);
// 创建 PreparedStatement 代理
ClassLoader cl = PreparedStatement.class.getClassLoader();
return (PreparedStatement) Proxy.newProxyInstance(cl, new Class[]{PreparedStatement.class}, handler);
}
/**
* Return the wrapped prepared statement.
*
* @return the PreparedStatement
*/
public PreparedStatement getPreparedStatement() {
return statement;
}
}
statement属性,PreparedStatement 对象。#invoke(Object proxy, Method method, Object[] params)方法,代理方法。代码如下:-
<1>处,如果是execute相关方法,则打印参数。打印的格式如下:16:50:09.149 [main] DEBUG o.a.i.l.j.PreparedStatementLogger - Parameters: 1(Integer), 101(String)- 其中,
Parameters:前缀,是通过#getParameterValueString()方法,拼接columnMap和columnValues属性,组合而成。
- 其中,
-
如果是
set相关方法,则添加到columnMap和columnValues属性中。 -
如果是
getResultSet方法,则创建 ResultSetLogger 对象。 -
如果是
getUpdateCount方法,则打印更新行数。
-
#newInstance(PreparedStatement stmt, Log statementLog, int queryStack)静态方法,创建 PreparedStatement 的代理对象。
4.4 StatementLogger
org.apache.ibatis.logging.jdbc.StatementLogger ,继承 BaseJdbcLogger 类,Statement 日志。代码如下:
public final class StatementLogger extends BaseJdbcLogger {
/**
* Statement 对象
*/
private final Statement statement;
private StatementLogger(Statement stmt, Log statementLog, int queryStack) {
super(statementLog, queryStack);
this.statement = stmt;
}
@Override
public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
try {
// 如果来自 Object 的方法,则直接调用
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, params);
}
// 如果为 execute 相关方法
if (EXECUTE_METHODS.contains(method.getName())) {
if (isDebugEnabled()) {
debug("Parameters: " + getParameterValueString(), true); // 打印参数
}
// 清空 columnMap 和 columnValues
clearColumnInfo();
// 执行方法
if ("executeQuery".equals(method.getName())) {
ResultSet rs = (ResultSet) method.invoke(statement, params);
return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
} else {
return method.invoke(statement, params);
}
}
// 如果为 get 相关方法
else if ("getResultSet".equals(method.getName())) {
ResultSet rs = (ResultSet) method.invoke(statement, params);
return rs == null ? null : ResultSetLogger.newInstance(rs, statementLog, queryStack);
}
// 如果为 getUpdateCount 方法
else if ("getUpdateCount".equals(method.getName())) {
int updateCount = (Integer) method.invoke(statement, params);
if (updateCount != -1) {
debug(" Updates: " + updateCount, false);
}
return updateCount;
} else {
return method.invoke(statement, params);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
/**
* Creates a logging version of a Statement.
*
* @param stmt - the statement
* @return - the proxy
*/
public static Statement newInstance(Statement stmt, Log statementLog, int queryStack) {
// 创建 InvocationHandler 对象
InvocationHandler handler = new StatementLogger(stmt, statementLog, queryStack);
// 创建 Statement 代理
ClassLoader cl = Statement.class.getClassLoader();
return (Statement) Proxy.newProxyInstance(cl, new Class[]{Statement.class}, handler);
}
/**
* Return the wrapped prepared statement.
*
* @return the Statement
*/
public Statement getStatement() {
return statement;
}
}
- 和 PreparedStatementLogger 类似,胖友自己看。
4.5 ResultSetLogger
org.apache.ibatis.logging.jdbc.ResultSetLogger ,继承 BaseJdbcLogger 类,ResultSet 日志。代码如下:
public final class ResultSetLogger extends BaseJdbcLogger {
/**
* ResultSet 对象
*/
private final ResultSet rs;
/**
* 读取的列数
*/
private final int rows;
/**
* 第一行
*/
private boolean first = true;
private ResultSetLogger(ResultSet rs, Log statementLog, int queryStack) {
super(statementLog, queryStack);
this.rs = rs;
this.rows = 0;
}
@Override
public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
try {
// 如果来自 Object 的方法,则直接调用
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, params);
}
// 调用方法
Object o = method.invoke(rs, params);
// 如果为 next 方法
if ("next".equals(method.getName())) {
// 遍历是否还有下一条
if (((Boolean) o)) {
// 增加 rows
rows++;
// 如果是 debug 日志级别,并且是第一行,则打印表头
if (isTraceEnabled()) {
ResultSetMetaData rsmd = rs.getMetaData();
final int columnCount = rsmd.getColumnCount();
if (first) {
first = false;
// 打印列头
printColumnHeaders(rsmd, columnCount);
}
// 打印该行的记录
printColumnValues(columnCount);
}
} else {
// 打印总行数
debug(" Total: " + rows, false);
}
}
// 清空 columnMap 和 columnValues
clearColumnInfo();
return o;
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
/**
* Creates a logging version of a ResultSet.
*
* @param rs - the ResultSet to proxy
* @return - the ResultSet with logging
*/
public static ResultSet newInstance(ResultSet rs, Log statementLog, int queryStack) {
// 创建 InvocationHandler 对象
InvocationHandler handler = new ResultSetLogger(rs, statementLog, queryStack);
// 创建 ResultSet 代理
ClassLoader cl = ResultSet.class.getClassLoader();
return (ResultSet) Proxy.newProxyInstance(cl, new Class[]{ResultSet.class}, handler);
}
/**
* Get the wrapped result set.
*
* @return the resultSet
*/
public ResultSet getResultSet() {
return rs;
}
private void printColumnHeaders(ResultSetMetaData rsmd, int columnCount) throws SQLException {
StringBuilder row = new StringBuilder();
row.append(" Columns: ");
for (int i = 1; i <= columnCount; i++) {
if (i > 1) {
row.append(", ");
}
row.append(rsmd.getColumnLabel(i));
}
trace(row.toString(), false);
}
private void printColumnValues(int columnCount) {
StringBuilder row = new StringBuilder();
row.append(" Row: ");
for (int i = 1; i <= columnCount; i++) {
if (i > 1) {
row.append(", ");
}
try {
if (rs.getObject(i) != null) {
row.append(rs.getObject(i).toString());
} else {
row.append("NULL");
}
} catch (SQLException e) {
row.append("Cannot read value: " + e.getMessage());
}
}
trace(row.toString(), false);
}
}
-
rows属性,读取的列数。 -
first属性,是否是第一行。 -
#invoke(Object proxy, Method method, Object[] params)方法,代理方法。代码如下:-
如果是
next方法,则根据是否还有下一条记录,打印相关日志。打印的格式如下:16:50:09.156 [main] DEBUG o.a.i.l.j.ResultSetLogger - Row: 1, 101, 101 16:50:09.156 [main] DEBUG o.a.i.l.j.ResultSetLogger - Row: 2, 102, 102 16:50:09.156 [main] DEBUG o.a.i.l.j.ResultSetLogger - Total: 2- 其中,
Row:前缀,是通过#printColumnValues(int columnCount)方法,拼接该行的记录。 - 其中,
Total:前缀,是打印总行数。
- 其中,
-
-
#newInstance(ResultSet rs, Log statementLog, int queryStack)静态方法,创建 ResultSet 的代理对象。
4.6 使用示例
在 BaseExecutor 中,代码如下:
protected Connection getConnection(Log statementLog) throws SQLException {
Connection connection = transaction.getConnection();
if (statementLog.isDebugEnabled()) { // 开启 debug 的情况下,才返回代理
return ConnectionLogger.newInstance(connection, statementLog, queryStack);
} else {
return connection;
}
}
- 在开启 debug 的情况下,才返回 Connection 的代理对象。这样,所有该 Connection 产生的 PreparedStatement、ResultSet 等等,都会被代理,从而实现日志的打印。
5. 集成三方框架
在 logging 包中,还有 commons-logging、log4j、log4j2、slf4j 等等包,实现集成三方日志框架到 MyBatis 中。以 log4j 包为例子,代码如下:
public class Log4jImpl implements Log {
private static final String FQCN = Log4jImpl.class.getName();
private final Logger log;
public Log4jImpl(String clazz) {
this.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);
}
}
- 通过集成
org.apache.log4j.Logger类,实现 Log 接口,从而集成到 MyBatis 的日志体系中。
更多推荐

所有评论(0)