项目开发中,在调试的时候,经常会有查看“完整的执行的SQL语句”的需求。
假如我们采用"SLF4J+Logback"的日志框架解决方案,那么该如何配置Logback,使得能够记录“完整的执行的SQL语句”呢?

(假定通过"logback.xml"文件进行Logback配置)


首先有三点需要注意:
1)除非数据库管理模块自身(比如"mysql:mysql-connector-java")包含有记录“完整的执行的SQL语句”的日志操作语句,否则,我们再怎么配置Logback也无济于事。
2)数据库管理模块有可能不是使用SLF4J作为日志框架,此时需要"jcl-over-slf4j","log4j-over-slf4j","jul-to-slf4j"等日志框架桥接器。可以通过数据库管理模块的pom.xml中声明的日志框架依赖,来确定数据库管理模块所使用的日志框架。不过有个特殊情况,当数据库管理模块使用"Java Logging API"时,无需在pom.xml中声明依赖。因此,我们在搭建实验环境的时候总是添加"jul-to-slf4j"这个日志框架桥接器,并且修改"JDK_HOME/jre/lib/logging.properties"文件中的handlers参数值为"org.slf4j.bridge.SLF4JBridgeHandler"
3)在搭建实验环境,配置"logback.xml"文件的时候,为了排除“由于数据库管理模块的日志记录请求的Level值小于相应Logger实例的Level值,不能达到记录‘完整的执行的SQL语句’的目标”的干扰因素,配置<root>标签中的level值为"all"。
实验环境中的"logback.xml"文件内容如下:
<!--配置debug=true,从而打印任何内部状态下的信息-->
<configuration debug="true">

<!--配置ConsoleAppender实例-->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%-5level %logger %C %M %d{MM/dd-HH:mm:ss.SSS} [%thread] - %msg%n</pattern>
</encoder>
</appender>

<!--可以通过<logger>标签,改变指定的Logger实例的Level值和绑定的Appender-->

<!--在Logger实例层次结构中,该Logger实例处于最顶层,其下的子孙Logger实例会继承它的Level值和绑定的Appender,除非特别指定-->
<root level="all">
<appender-ref ref="console"/>
</root>
</configuration>


接下来分别介绍4种场景下的Logback配置方案。

一、直接使用"mysql:mysql-connector-java"
在本场景中,查询SQL语句的时候,直接使用"mysql:mysql-connector-java"。查看"mysql:mysql-connector-java"的pom.xml文件,可以发现没有声明对任何日志框架的依赖,但还是有可能使用"Java Logging API"这个日志框架。
接下来搭建一个实验环境进行实验。
1、pom.xml
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.30</version>
</dependency>
<!--针对数据库管理模块使用Java Logging API日志框架的情况,配置"org.slf4j:jul-to-slf4j"桥接器-->
<!--并且修改JDK_HOME/jre/lib/logging.properties文件中的handlers参数值为org.slf4j.bridge.SLF4JBridgeHandler-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>1.7.12</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.12</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.0.13</version>
</dependency>
</dependencies>


2、logback.xml

见“三点注意”中的logback.xml配置。


3、Java代码
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;


public class Simple {
public static void main(String[] args) throws Exception {
Class.forName("com.mysql.jdbc.Driver");


String mysqlUrl =
"jdbc:mysql://127.0.0.1:3306/dslztx?useUnicode=true&characterEncoding=gbk&user=dsl&password=dsl";
String sql = "update activity set origin='china' where id=?";


Connection connection = DriverManager.getConnection(mysqlUrl);
PreparedStatement statement = connection.prepareStatement(sql);
statement.setInt(1, 1);
statement.executeUpdate();
}
}


4、运行结果

如图1,通过实验可以得出结论:在本场景中,无论如何配置"logback.xml",都不能达到记录“完整的执行的SQL语句”的目标。

图1



二、使用"com.googlecode.log4jdbc:log4jdbc"[1]
在本场景中,查询SQL语句的时候,使用"com.googlecode.log4jdbc:log4jdbc","com.googlecode.log4jdbc:log4jdbc"是一个中间层,底层还是使用"mysql:mysql-connector-java"。查看"com.googlecode.log4jdbc:log4jdbc"的pom.xml,可以发现依赖"org.slf4j:slf4j-api",即它使用SLF4J作为日志框架。
接下来搭建一个实验环境进行实验。
1、pom.xml
<dependencies>
<dependency>
<groupId>com.googlecode.log4jdbc</groupId>
<artifactId>log4jdbc</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.30</version>
</dependency>
<!--针对数据库管理模块使用Java Logging API日志框架的情况,配置"org.slf4j:jul-to-slf4j"桥接器-->
<!--并且修改JDK_HOME/jre/lib/logging.properties文件中的handlers参数值为org.slf4j.bridge.SLF4JBridgeHandler-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>1.7.12</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.12</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.0.13</version>
</dependency>
</dependencies>


2、logback.xml

见“三点注意”中的logback.xml配置。


3、Java代码
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;


public class Log4JDBC {
public static void main(String[] args) throws Exception {
Class.forName("net.sf.log4jdbc.DriverSpy");


String mysqlUrl =
"jdbc:log4jdbc:mysql://127.0.0.1:3306/dslztx?useUnicode=true&characterEncoding=gbk&user=dsl&password=dsl";
String sql = "update activity set origin='china' where id=?";


Connection connection = DriverManager.getConnection(mysqlUrl);
PreparedStatement statement = connection.prepareStatement(sql);
statement.setInt(1, 1);
statement.executeUpdate();
}
}

4、运行结果

如图2,通过实验可以得出结论:在本场景下,能够达到记录“完整的执行的SQL语句”的目标。

图2


详细分析运行结果,可以发现名称为"jdbc.sqlonly"和"jdbc.sqltiming"的Logger实例能够记录“完整的执行的SQL语句”。因此,我们应该在"logback.xml"中配置这两个Logger实例相应的<logger>标签,将level值设为合适的值,而将<root>标签的level值从"all"改为合适的"非all"值,因为如果Logger实例的Level过低,在项目运行中会有太多有效的日志记录请求,影响项目运行性能。
修改后"logback.xml"内容如下:
<!--配置debug=true,从而打印任何内部状态下的信息-->
<configuration debug="true">

<!--配置ConsoleAppender实例-->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%-5level %logger %C %M %d{MM/dd-HH:mm:ss.SSS} [%thread] - %msg%n</pattern>
</encoder>
</appender>

<!--可以通过<logger>标签,改变指定的Logger实例的Level值和绑定的Appender-->
<logger name="jdbc.sqlonly" level="debug" additivitt="true"/>

<logger name="jdbc.sqltiming" level="debug" additivitt="true"/>

<!--在Logger实例层次结构中,该Logger实例处于最顶层,其下的子孙Logger实例会继承它的Level值和绑定的Appender,除非特别指定-->
<root level="info">
<appender-ref ref="console"/>
</root>
</configuration>

三、使用"org.springframework:spring-jdbc"
在本场景中,查询SQL语句的时候,使用"org.springframework:spring-jdbc","org.springframework:spring-jdbc"是一个中间层,底层还是使用"mysql:mysql-connector-java"。查看"org.springframework:spring-jdbc"的pom.xml,可以发现它递归依赖"commons-logging:commons-logging",即它使用JCL作为日志框架,因而我们需要"jcl-over-slf4j"这个日志框架桥接器,并且需要在包含"org.springframework:spring-jdbc"依赖的时候,通过<exclusion>标签排除掉"commons-logging:commons-logging"依赖。
本场景中还需要有"com.mchange:c3p0"依赖,查看它的pom.xml,可以发现没有对日志框架的依赖,但是它很有可能选用"Java Logging API"作为日志框架。
接下来搭建一个实验环境进行实验。
1、pom.xml
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>3.2.3.RELEASE</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.30</version>
</dependency>
<!--针对数据库管理模块使用Java Logging API日志框架的情况,配置"org.slf4j:jul-to-slf4j"桥接器-->
<!--并且修改JDK_HOME/jre/lib/logging.properties文件中的handlers参数值为org.slf4j.bridge.SLF4JBridgeHandler-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>1.7.12</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>1.7.12</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.12</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.0.13</version>
</dependency>
</dependencies>


2、logback.xml
见“三点注意”中的logback.xml配置。

3、Java代码

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.jdbc.core.JdbcTemplate;

public class SpringJdbcTemplate {
public static void main(String[] args) throws Exception {
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://127.0.0.1:3306/dslztx?useUnicode=true&characterEncoding=gbk");
dataSource.setUser("dsl");
dataSource.setPassword("dsl");

JdbcTemplate template = new JdbcTemplate(dataSource);

String sql = "update activity set origin='china' where id=?";
template.update(sql, 1);
}
}


4、运行结果

如图3,通过实验得出结论:能够达到记录“完整的执行的SQL语句”的目标。

图3


详细分析运行结果,可以发现名称为"org.springframework.jdbc.core.StatementCreatorUtils"的Logger实例能够记录“完整的执行的SQL语句”。因此,我们应该在“logback.xml”中配置这个Logger实例相应的<logger>标签,将level值设为合适的值,而将<root>标签的level值从"all"改为合适的"非all"值,因为如果Logger实例的Level过低,在项目运行中会有太多有效的日志记录请求,影响项目运行性能。
修改后“logback.xml”内容如下:
<!--配置debug=true,从而打印任何内部状态下的信息-->
<configuration debug="true">

<!--配置ConsoleAppender实例-->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%-5level %logger %C %M %d{MM/dd-HH:mm:ss.SSS} [%thread] - %msg%n</pattern>
</encoder>
</appender>

<!--可以通过<logger>标签,改变指定的Logger实例的Level值和绑定的Appender-->
<logger name="org.springframework.jdbc.core.StatementCreatorUtils" level="trace" additivity="true"/>

<!--在Logger实例层次结构中,该Logger实例处于最顶层,其下的子孙Logger实例会继承它的Level值和绑定的Appender,除非特别指定-->
<root level="info">
<appender-ref ref="console"/>
</root>
</configuration>


四、使用Mybatis

在本场景中,查询SQL语句的时候,使用“org.mybatis:mybatis","org.mybatis:mybatis"是一个中间层,底层还是使用"mysql:mysql-connector-java"。根据文章 《Java日志框架——Mybatis选用的日志框架解决方案》,"org.mybatis:mybatis"动态确定选用的第三方日志框架,再结合"org.mybatis:mybatis"的pom.xml文件中的对日志框架依赖的"optional"属性值,可以知道,我们只要在项目中加入对SLF4J的依赖,就可以令mybatis使用SLF4J日志框架。
接下来搭建一个实验环境进行实验。
1、pom.xml
 <dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.7</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.30</version>
</dependency>
<!--针对数据库管理模块使用Java Logging API日志框架的情况,配置"org.slf4j:jul-to-slf4j"桥接器-->
<!--并且修改JDK_HOME/jre/lib/logging.properties文件中的handlers参数值为org.slf4j.bridge.SLF4JBridgeHandler-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jul-to-slf4j</artifactId>
<version>1.7.12</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.12</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.0.13</version>
</dependency>
</dependencies>


2、logback.xml

见“三点注意”中的logback.xml配置。


3、Java代码

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;

public class MybatisLog {
public static void main(String[] args) throws Exception {
BufferedReader reader =
new BufferedReader(new InputStreamReader(MybatisLog.class.getResourceAsStream("/mybatis-config.xml")));
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, "mybatisDB");
reader.close();
SqlSession sqlSession = factory.openSession();
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("id", 1);
sqlSession.update("Activity.updateActivity", map);
}
}

4、运行结果

如图4,通过实验得到结论:能够达到记录“完整的执行的SQL语句”的目标。

图4


详细分析运行结果,可以发现名称为"Activity.updateActivity"的Logger实例能够记录“完整的执行的SQL语句”。因此,我们应该在"logback.xml"中配置这个Logger实例相应的<logger>标签,将level值设为合适的值,而将<root>标签的level值从"all"改为合适的"非all"值,因为如果Logger实例的Level过低,在项目运行中会有太多有效的日志记录请求,影响项目运行性能。
修改后"logback.xml"内容如下:
<!--配置debug=true,从而打印任何内部状态下的信息-->
<configuration debug="true">

<!--配置ConsoleAppender实例-->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>%-5level %logger %C %M %d{MM/dd-HH:mm:ss.SSS} [%thread] - %msg%n</pattern>
</encoder>
</appender>

<!--可以通过<logger>标签,改变指定的Logger实例的Level值和绑定的Appender-->
<logger name="Activity.updateActivity" level="debug" additivity="true"/>

<!--在Logger实例层次结构中,该Logger实例处于最顶层,其下的子孙Logger实例会继承它的Level值和绑定的Appender,除非特别指定-->
<root level="info">
<appender-ref ref="console"/>
</root>
</configuration>



参考文献:

[1]http://code.google.com/p/log4jdbc/

更多相关文章

  1. JavaScript框架是什么 (share)

随机推荐

  1. Android开源 - Luban(鲁班)可能是最接近
  2. 如何通过HttpURLConnection得到http 302
  3. 想在android中使用node.js和socket.io发
  4. 在Android主屏幕上删除启动器图标(就像Goo
  5. Android Studio 问题总结
  6. Android常用控件学习(1)
  7. Android 开发,从入门-初级-中级-高级 该怎
  8. android studio Dagger2的使用(环境搭建和
  9. 求高手推荐几本ANDROID开发初学者书籍
  10. Android Filetransfer错误码3