myBatis 로깅을 좀 더 가독성을 높이기 위한 방법을 고민하다가 아래와 같이
myBatis 플러그인 인터셉터를 이용한 방법을 사용해보았다.
플러그인 등록 mybatis-config.xml
<plugins>
<plugin interceptor="<path>.SqlLogInterceptor" />
</plugins>
플러그인 구현 SqlLogInterceptor.java
import java.util.Collection;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.TypeHandlerRegistry;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Intercepts({
@Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class }),
@Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class }),
@Signature(type = Executor.class, method = "queryCursor", args = { MappedStatement.class, Object.class, RowBounds.class }),
@Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }),
})
public class MyBatisLogInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
if (log.isDebugEnabled()) {
long stime = System.currentTimeMillis();
Object proceed = invocation.proceed();
long etime = System.currentTimeMillis();
MappedStatement ms = null;
Object parameter = null;
BoundSql boundSql = null;
for (Object arg : invocation.getArgs()) {
if (arg instanceof MappedStatement) {
ms = (MappedStatement) arg;
} else if (arg instanceof RowBounds
|| arg instanceof ResultHandler
|| arg instanceof CacheKey) {
// skip
} else if (arg instanceof BoundSql) {
boundSql = (BoundSql) arg;
} else if (arg != null) {
parameter = arg;
}
}
Configuration configuration = ms.getConfiguration();
TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
if (boundSql == null) {
boundSql = ms.getBoundSql(parameter);
}
// remove empty line
StringBuilder sql = new StringBuilder(boundSql.getSql().replaceAll("(?m)^[ \t]*\r?\n", ""));
// @see: DefaultParameterHandler 참조
if (parameter != null) {
MetaObject metaObject = configuration.newMetaObject(parameter);
for (ParameterMapping parameterMapping : boundSql.getParameterMappings()) {
String property = parameterMapping.getProperty();
Object value = "<NotFound>";
if (boundSql.hasAdditionalParameter(property)) {
value = boundSql.getAdditionalParameter(property);
} else if (parameter == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameter.getClass())) {
value = parameter;
} else {
if (metaObject == null) {
metaObject = configuration.newMetaObject(parameter);
}
value = metaObject.getValue(property);
}
int start = sql.indexOf("?");
int end = start + 1;
if (value == null) {
sql.replace(start, end, "NULL");
} else if (value instanceof String) {
sql.replace(start, end, "'" + value.toString() + "'");
} else {
sql.replace(start, end, value.toString());
}
}
}
if (proceed != null && proceed instanceof Collection) {
int count = ((Collection<?>) proceed).size();
sql.append(System.lineSeparator()).append(String.format(" -> (took %d msec, row count %d)", (etime - stime), count));
} else {
sql.append(System.lineSeparator()).append(String.format(" -> (took %d msec)", (etime - stime)));
}
sql.insert(0, System.lineSeparator() + "[mybatis query]" + System.lineSeparator());
log.debug("{}", sql.toString());
return proceed;
}
return invocation.proceed();
}
}
끝.
'ORM' 카테고리의 다른 글
myBatis Java에서 Consumer 이용한 페이징 쿼리 처리 (0) | 2022.09.17 |
---|---|
myBatis list/array parameter type 처리 (0) | 2018.10.23 |
Java + MyBatis (0) | 2011.09.21 |
댓글