Tip. SQL문을 로그로 남기기
Query 서비스를 통해 수행되는 SQL을 로그로 남기기 위해서는 log4jdbc(http://log4jdbc.sourceforge.net/)라는 오픈소스를 활용할 수 있다.
log4jdbc는 JDBC 호출이나 SQL문에 대해 로그를 남길 수 있는 JDBC Driver를 제공하고 있다. log4jdbc에 대한 보다 자세한 내용은
http://log4jdbc.sourceforge.net
를 참조한다.
다음에서는 log4jdbc를 사용하여 SQL을 로그로 남기기 위한 절차를 5개의 STEP으로 나누어 설명하고자 한다.
Step 1. Log4jdbc 라이브러리 다운로드
다음을 참고하여, 필요한 log4jdbcX-1.jar 파일을 다운로드한 후,
[Anyframe Core 설치 폴더] 또는 [Web Root/WEB-INF/lib 폴더] 내에 복사
한다.
WebLogic JNDIDataSource를 사용할 경우에는
WAS 시작시 로드될 수 있도록 해당 WAS의 클래스패스 상에 복사한다.
| 파일명 |
설명 |
다운로드 |
| log4jdbc3-1.1.jar |
If you are using JDK 1.4 or 1.5, you should use the JDBC 3 version of log4jdbc. |
Download
|
Step 2. Simple Logging Facade for Java 라이브러리 다운로드
log4jdbc는
Simple Logging Facade for Java (SLF4J)
를 이용하여 어플리케이션에서 사용하는 Logging 서비스와
유연하게 연동할 수 있도록 하고 있다.
따라서 다음 파일들을 다운로드한 후,
[Anyframe Core 설치 폴더] 또는 [Web Root/WEB-INF/lib 폴더] 내에 복사
한다.
WebLogic JNDIDataSource를 사용할 경우에는
WAS 시작시 로드될 수 있도록 해당 WAS의 클래스패스 상에 복사한다.
| 파일명 |
설명 |
다운로드 |
| slf4j-api-1.5.3.jar |
log4jdbc와 logging 서비스 연동을 위한 API 제공 |
Download
|
| slf4j-log4j12-1.5.3.jar |
log4jdbc와 Log4j 기반의 Logging 서비스 연동을 위한 구현 라이브러리 제공 |
Download
|
Step 3. DataSource 속성 정의
JDBCDataSource를 사용할 경우
- 기본적으로 지원되는 JDBC Driver일 경우
DataSource 속성 정의시 driverClassName은 net.sf.log4jdbc.DriverSpy로 정의하고 url은 사용하고 있는
url 앞에 'jdbc:log4'를 추가한다.
다음은 일반적인 유형의 DataSource 속성 정의 파일인 applicationContext-datasource.xml
이다.
<bean id="commonDataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="net.sf.log4jdbc.DriverSpy"/>
<property name="url" value="jdbc:log4jdbc:hsqldb:file:/./DATABASE/db/sampledb"/>
<property name="username" value="sa"/>
</bean>
[참고] DriverSpy에서 지원하는 기본 JDBC Driver 목록은 다음과 같다.
- oracle.jdbc.driver.OracleDriver
- com.sybase.jdbc2.jdbc.SybDriver
- net.sourceforge.jtds.jdbc.Driver
- com.microsoft.jdbc.sqlserver.SQLServerDriver
- weblogic.jdbc.sqlserver.SQLServerDriver
- com.informix.jdbc.IfxDriver
- org.apache.derby.jdbc.ClientDriver
- org.apache.derby.jdbc.EmbeddedDriver
- com.mysql.jdbc.Driver
- org.postgresql.Driver
- org.hsqldb.jdbcDriver
- org.h2.Driver
- 기본적으로 지원되지 않는 JDBC Driver일 경우
net.sf.log4jdbc.DriverSpy에서 기본적으로 지원하는 JDBC Driver가 아닌 경우에는 앞서 언급한 기본 정의 방식과 동일하게
정의하되, System Property에 대한 추가 셋팅이 필요하다.
Eclipse를 통해 작업하는 경우 Open Run Diaglog > Arguments 탭 > VM arguments 에 log4jdbc.drivers
를 속성키로,
실제 DB의 Driver 클래스명을 속성값으로 정의해주면 된다.
-Dlog4jdbc.drivers=com.ibm.db2.jcc.DB2Driver
JNDIDataSource를 사용할 경우
JNDIDataSource를 사용하는 경우 해당하는 WAS에 사용하고자 하는 DataSource에 대해 정의되어 있어야 한다.
- WebLogic인 경우
- DataSource 추가
WebLogic에 사용하고자 하는 DataSource를 정의한다. 이 때, JDBCDataSource 정의시와 마찬가지로 driverClassName은
net.sf.log4jdbc.DriverSpy로 정의하고 url은 사용하고 있는 url 앞에 'jdbc:log4'를 추가
하도록 한다. url 정의시
Step 3의 JDBCDataSource를 사용할 경우
를 참고하도록 한다.
* 참고
WebLogic Server에 net.sf.log4jdbc.DriverSpy를 이용한 DataSource를 추가하기 위해서는
WebLogic과 log4jdbc 사이에서 정상적인 호출이 발생할 수 있도록 해야 한다.
따라서, WebLogic Server 실행 전에 [Domain Server Home/lib]에
log4jdbcX-1.jar, slf4j-api-1.5.3.jar, slf4j-log4j12-1.5.3.jar를
복사해두어야 함에 유의하도록 한다.
다음은 WebLogic 9.2 Server에 추가한 TestDataSource에 대한 설정 내용이다.

- DataSource 속성 정의
JNDIDataSource를 정의한 후에 해당 JNDIDataSource를 사용하기 위해서는 다음을 참조하여
applicationContext-datasource-jndi.xml
파일을 정의할 수 있다.
<bean id="commonDataSource"
class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="TestDataSource" />
<property name="jndiTemplate" ref="jnditemplate" />
</bean>
<bean id="jnditemplate"
class="org.springframework.jndi.JndiTemplate">
<property name="environment">
<props>
<prop key="java.naming.factory.initial">
weblogic.jndi.WLInitialContextFactory
</prop>
<prop key="java.naming.provider.url">
t3://localhost:7001
</prop>
</props>
</property>
</bean>
- JEUS인 경우
- DataSource 추가
JEUS에 사용하고자 하는 JNDIDataSource를 추가한다. 다음은 사용하고자 하는 JNDIDataSource가 추가된 JEUS Server의
JEUSMain.xml 파일 내용의 일부이다.
1. XA 모드일 경우
<resource>
<data-source>
<database>
<vendor>oracle</vendor>
<export-name>OracleDS</export-name>
<data-source-class-name>
oracle.jdbc.xa.client.OracleXADataSource
</data-source-class-name>
<data-source-type>XADataSource</data-source-type>
<database-name>test2</database-name>
<data-source-name>
oracle.jdbc.xa.client.OracleXADataSource
</data-source-name>
<port-number>1521</port-number>
<server-name>server.ip</server-name>
<user>anyframe</user>
<password>anyframe</password>
<driver-type>thin</driver-type>
<connection-pool>
<pooling>
<min>2</min>
<max>30</max>
<step>4</step>
<period>3600000</period>
</pooling>
</connection-pool>
</database>
</data-source>
</resource>
2. ConnectionPool 모드일 경우
<resource>
<data-source>
<database>
<vendor>oracle</vendor>
<export-name>OraclePoolDS</export-name>
<data-source-class-name>
oracle.jdbc.pool.OracleConnectionPoolDataSource
</data-source-class-name>
<data-source-type>ConnectionPoolDataSource</data-source-type>
<database-name>test2</database-name>
<data-source-name>
oracle.jdbc.pool.OracleConnectionPoolDataSource
</data-source-name>
<port-number>1521</port-number>
<server-name>server.ip</server-name>
<user>anyframe</user>
<password>anyframe</password>
<driver-type>thin</driver-type>
<connection-pool>
<pooling>
<min>2</min>
<max>30</max>
<step>4</step>
<period>3600000</period>
</pooling>
<check-query>select sysdate from dual</check-query>
<check-query-period>10000</check-query-period>
</connection-pool>
</database>
</data-source>
</resource>
- DataSource Wrapper 정의 및 컴파일
WAS가 JEUS인 경우 JNDIDataSource 추가시 DB URL을 별도로 정의할 수 없으므로 DriverSpy를 통한 SQL Logging을 수행할 수 없다.
이 경우에는 다음의 AnyframeDataSourceSpy와 같이 별도의 DataSource Wrapper 클래스를 정의하여 사용할 수 있다.
package net.sf.log4jdbc;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import net.sf.log4jdbc.ConnectionSpy;
import net.sf.log4jdbc.SpyLogDelegator;
import net.sf.log4jdbc.SpyLogFactory;
import net.sf.log4jdbc.RdbmsSpecifics;
public class AnyframeDataSourceSpy implements DataSource {
private DataSource dataSource = null;
static final SpyLogDelegator log = SpyLogFactory.getSpyLogDelegator();
static RdbmsSpecifics defaultRdbmsSpecifics = new RdbmsSpecifics();
public AnyframeDataSourceSpy() {
}
public Connection getConnection() throws SQLException {
return getWrappedConnection(dataSource.getConnection());
}
public Connection getConnection(String username, String password)
throws SQLException {
return getWrappedConnection(dataSource
.getConnection(username, password));
}
public PrintWriter getLogWriter() throws SQLException {
return dataSource.getLogWriter();
}
public int getLoginTimeout() throws SQLException {
return dataSource.getLoginTimeout();
}
public void setLogWriter(PrintWriter out) throws SQLException {
dataSource.setLogWriter(out);
}
public void setLoginTimeout(int seconds) throws SQLException {
dataSource.setLoginTimeout(seconds);
}
private Connection getWrappedConnection(Connection con) {
if (log.isJdbcLoggingEnabled())
{
ConnectionSpy cspy = new ConnectionSpy(con);
cspy.setRdbmsSpecifics(defaultRdbmsSpecifics);
return cspy;
}
else
{
return con;
}
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
}
- DataSource 속성 정의
앞서 정의한 DataSource Wrapper를 통해 해당 어플리케이션의 DataSource가 실행될 수 있도록 다음을 참조하여
applicationContext-datasource-wrapper.xml
파일을 정의한다.
<bean id="realDataSource"
class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
<property name="url" value="jdbc:hsqldb:file:/./DATABASE/db/sampledb"/>
<property name="username" value="sa"/>
</bean>
<bean id="commonDataSource" class="net.sf.log4jdbc.AnyframeDataSourceSpy
">
<property name="dataSource" ref="realDataSource"/>
</bean>
Step 4. Query 서비스 속성 정의
Query 서비스에서 참조하는 DataSource를 앞서 정의한 dataSource의 Bean Id인 'commonDataSource'로 정의
한다.
다음은 Query 서비스 속성을 정의한 샘플
applicationContext-query.xml
파일의 일부 내용이다.
<bean name="queryService" class="anyframe.core.query.impl.QueryServiceImpl">
<property name="jdbcTemplate">
<ref bean="jdbcTemplate"/>
</property>
<config:configuration>
<filename>classpath:mappings/emp-user-userservice-mapping.xml</filename>
<nullcheck type="VARCHAR" default-value=""/>
<sqlload dynamic="false" frequency="5"/>
<skiperror>true</skiperror>
</config:configuration>
</bean>
<bean id="jdbcTemplate" class="anyframe.core.query.impl.util.PagingJdbcTemplate">
<property name="dataSource" ref="commonDataSource" />
</bean>
Step 5. Logger 정의
lo4jdbc를 사용하여 로그를 남기기 위해서는 log4j.xml 파일 내에 다음을 참고하여, 필요한 Logger를 정의하도록 한다.
- jdbc.sqlonly :
SQL문만을 로그로 남기며, PreparedStatement일 경우 관련된 argument 값으로 대체된 SQL문이 보여진다.
- jdbc.sqltiming :
SQL문과 해당 SQL을 실행시키는데 수행된 시간 정보(milliseconds)를 포함한다.
- jdbc.audit :
ResultSet을 제외한 모든 JDBC 호출 정보를 로그로 남긴다. 많은 양의 로그가 생성되므로 특별히 JDBC 문제를 추적해야 할 필요가 있는 경우를 제외하고는 사용을 권장하지 않는다.
- jdbc.resultset :
ResultSet을 포함한 모든 JDBC 호출 정보를 로그로 남기므로 매우 방대한 양의 로그가 생성된다.
또한, 각 Logger에 대한 로그 레벨은 DEBUG, INFO, ERROR 중 하나를 선택할 수 있다.
- DEBUG - SQL이 실행된 클래스명과 Line 번호를 로그로 남긴다.
- INFO - SQL문을 로그로 남긴다.
- ERROR - SQL 실행 에러가 발생한 경우 stack trace 정보를 로그로 남긴다.
단, WebLogic Server에 정의된 JNDIDataSource를 사용할 경우
WAS에 추가한 JNDIDataSource에서 log4j.xml 파일을 읽어낼 수 있어야
하므로, log4j-1.3alpha-8.jar와 log4j.xml은 해당 WAS의 클래스패스 상([Domain Server Home/lib])에 놓여 있어야 함에 유의해야 한다.
WebLogic 9.2 기반에서 log4j.xml 파일의 경우 jar 파일 형태로 압축하여 [Domain Server Home/lib]에 위치시키거나,
WebLogic Server 실행을 위한 자바 옵션에 다음과 같이 추가해 줄 수 있다.
-Dlog4j.configuration=file:///path../log4j.xml
* 단, 해당 웹어플리케이션에 대해 Anyframe Monitoring Tool을 통해 모니터링하는 경우, Monitoring Tool의 Logging
처리 로직으로 인해 WAS 로드시 ClassCastException이 발생하므로 -Dlog4j.defaultInitOverride=true 옵션을 추가해
주어야 한다. 이러한 경우 Monitoring Agent에 대한 로그는 확인할 수 없게 된다.
Resources
다운로드
다음은 앞서 제시한 예제 속성 파일 목록이다.
| Name |
Download |
| applicationContext-datasource.xml |
Download
|
| applicationContext-datasource-jndi.xml |
Download
|
| applicationContext-datasource-wrapper.xml |
Download
|
| applicationContext-query.xml |
Download
|
| log4j.xml |
Download
|
| AnyframeDataSourceSpy.java |
Download
|
참고자료