/*
* Copyright 2014 NAVER Corp.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.navercorp.pinpoint.web.dao.ibatis;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.StatementType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.scripting.defaults.DefaultParameterHandler;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.util.Assert;
/**
* Plugin for printing out the bind variables of {@link java.sql.PreparedStatement} and {@link java.sql.CallableStatement} with Query string.
* format of Query string can be changed with {@link BindLogFormatter}.
* base implementation is {@link com.navercorp.pinpoint.web.dao.ibatis.DefaultBindingLogFormatter}.
* removeWhitespace option is supported
*
* @author emeroad
*/
@Intercepts({
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.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 = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})}
)
public class BindingLogPlugin32 implements Interceptor {
private static final Log pLogger = LogFactory.getLog(PreparedStatement.class);
private static final Log internalLogger = LogFactory.getLog(BindingLogPlugin32.class);
private BindLogFormatter bindLogFormatter;
public BindingLogPlugin32() {
this.bindLogFormatter = new DefaultBindingLogFormatter();
}
public BindingLogPlugin32(BindLogFormatter bindLogFormatter) {
Assert.notNull(bindLogFormatter, "bindLogFormatter must no be null");
this.bindLogFormatter = bindLogFormatter;
}
@Override
public Object intercept(Invocation invocation) throws Throwable {
if (internalLogger.isTraceEnabled()) {
internalLogger.trace("target:" + invocation.getTarget()
+ " method:" + invocation.getMethod() + " args:" + Arrays.toString(invocation.getArgs()));
}
try {
return invocation.proceed();
} finally {
bindingLog(invocation);
}
}
private void bindingLog(Invocation invocation) throws SQLException {
Object[] args = invocation.getArgs();
MappedStatement ms = (MappedStatement) args[0];
Object parameterObject = args[1];
StatementType statementType = ms.getStatementType();
if (StatementType.PREPARED == statementType || StatementType.CALLABLE == statementType) {
Log statementLog = ms.getStatementLog();
if (isDebugEnable(statementLog)) {
BoundSql boundSql = ms.getBoundSql(parameterObject);
String sql = boundSql.getSql();
List<String> parameterList = getParameters(ms, parameterObject, boundSql);
debug(statementLog, "==> BindingLog: " + bindLogFormatter.format(sql, parameterList));
}
}
}
public boolean isDebugEnable(Log statementLogger) {
return statementLogger.isDebugEnabled() || pLogger.isDebugEnabled();
}
public void debug(Log statementLogger, String msg) {
if (statementLogger.isDebugEnabled()) {
statementLogger.debug(msg);
} else {
pLogger.debug(msg);
}
}
private List<String> getParameters(MappedStatement ms, Object parameterObject, BoundSql boundSql) throws SQLException {
// DefaultParameterHandler is the only implementation of parameterHandler interface currently. it may be changed later.
// need additional codes to find a appropriate implementation in that case.
ParameterHandler parameterHandler = new DefaultParameterHandler(ms, parameterObject, boundSql);
PreparedStatementParameterLogger parameterLogger = new PreparedStatementParameterLogger();
parameterHandler.setParameters(parameterLogger);
return parameterLogger.getParameters();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
/**
* {@link com.navercorp.pinpoint.web.dao.ibatis.DefaultBindingLogFormatter} is the implementation of {@link BindLogFormatter} supports removeWhitespace option.
* removeWhitespace : setting for printing out query format in a row.
*
* @param properties option properties
*/
@Override
public void setProperties(Properties properties) {
bindLogFormatter.setProperties(properties);
}
public void setBindLogFormatter(BindLogFormatter bindLogFormatter) {
Assert.notNull(bindLogFormatter, "bindLogFormatter must no be null");
this.bindLogFormatter = bindLogFormatter;
}
}