Enterprise Java

Log JDBC operations with Log4jdbc

When we develop any application, after we finish it or when we end up any of its module, we start optimization process. Most applications contain database access, and if you are using an ORM, probably you will use hibernate. Optimizing hibernate persistence layer,requiresto be prepared to read, understand and evaluate sql.

If we take an overview on hibernate configuration, two properties, hibernate.format_sqland hibernate.use_sql_comments,should be enabled to printperformed sqlcode through console.

This is a good start but it seems that we need more information to make an accurate diagnosis of performance like connection events, returned data on queries, or parameters binding ( hibernate shows parameters values with question mark ?) . Hence, we need another way to inspect generated sql. Log4jdbcis a jdbc driver that can log sql/jdbc calls. In fact log4jdbcis an implementation of proxy pattern which will automatically load popular jdbc drivers ( Oracle, Derby, MySql, PostgreSql, H2, Hsqldb, …), intercept calls, log information, and then send data to ‘ spied‘ driver.

In log4jdbc, there are 5 loggers that can be used depending on data to monitor:

  • jdbc.sqlonly: logs executed sql with binding arguments replaced with bound data.
  • jdbc.sqltiming: logs how long a sql took to execute.
  • jdbc.audit: logs all jdbc calls except for ResultSets.
  • jdbc.resultset: same as jdbc.audit plus ResultsSets.
  • jdbc.connection: logs open and close connection events.

In this post we are going to see how to configure log4jdbc-remix, a fork of log4jdbc, which apart from inheriting log4jdbc capabilities, also let us:

  • jdbc.resultsettable: log results set in table format.
  • configure it as datasource.
  • available in maven repository (log4jdbc is not present on maven repositories).

For this example we are going to use the project created by JPA Spring Template which contains two entities Order and Item associated with one-to-many relationship, and one test that executes some database operations.
First thing to do is add log4jdb-remixand slf4j-log4j12 dependencies to project:

 <dependency>
  <groupId>org.slf4j<groupId>
  <artifactId>slf4j-log4j12<artifactId>
  <version>1.6.4<version>
 <dependency>
 
 <dependency>
  <groupId>org.lazyluke<groupId>
  <artifactId>log4jdbc-remix<artifactId>
  <version>0.2.7<version>
 <dependency>

Next thing to do is configure active loggers. Depending on the data we are interested to monitor, we activate the required loggers. As an example let’s configure log4j.xml so result set is printed in table format and also time taken to execute each query is shown.

 <?xml version='1.0' encoding='UTF-8' ?>
 <!DOCTYPE log4j:configuration SYSTEM 'log4j.dtd'>
 
 <log4j:configuration xmlns:log4j='http:jakarta.apache.orglog4j'>
  <appender name='console-log4jdbc' class='org.apache.log4j.ConsoleAppender'>
   <param name='Target' value='System.out' >
   <layout class='org.apache.log4j.PatternLayout'>
    <param name='ConversionPattern' value='%m%n' >
   <layout>
  <appender>
 
 
 <!--  <logger name='jdbc.sqlonly' additivity='false'> -->
 <!--   <level value='debug' > -->
 <!--   <appender-ref ref='console-log4jdbc' > -->
 <!--  <logger> -->
  <logger name='jdbc.sqltiming' additivity='false'>
   <level value='info' >
   <appender-ref ref='console-log4jdbc' >
  <logger>
 <!--  <logger name='jdbc.connection' additivity='false'> -->
 <!--   <level value='info' > -->
 <!--   <appender-ref ref='console-log4jdbc' > -->
 <!--  <logger> -->
 
  <!-- log4jdbc option log the jdbc results as a table -->
  <logger name='jdbc.resultsettable' additivity='false'>
   <level value='info' >
   <appender-ref ref='console-log4jdbc' >
  <logger>
 
 <log4j:configuration>

After configuring loggers, run test and inspect the output

 create table Item (id bigint generated by default as identity, price double not null, product 
 varchar(255), quantity integer not null, order_id bigint, primary key (id))  {executed in 10 msec}
 create table T_ORDER (id bigint generated by default as identity, customer varchar(255), primary 
 key (id))  {executed in 1 msec}
 alter table Item add constraint FK22EF339F325255 foreign key (order_id) references T_ORDER 
 {executed in 11 msec}
 insert into T_ORDER (id, customer) values (null, NULL)  {executed in 1 msec}
 insert into Item (id, order_id, price, product, quantity) values (null, NULL, 0.0, NULL, 0) 
 {executed in 0 msec}
 batching 1 statements: 0: update Item set ORDER_ID=1 where id=1  {executed in 2 msec}
 insert into T_ORDER (id, customer) values (null, NULL)  {executed in 0 msec}
 insert into Item (id, order_id, price, product, quantity) values (null, NULL, 0.0, NULL, 0) 
 {executed in 0 msec}
 batching 1 statements: 0: update Item set ORDER_ID=2 where id=2  {executed in 0 msec}
 select order0_.id as id1_0_, order0_.customer as customer1_0_ from T_ORDER order0_ where order0_.id=2 
 {executed in 0 msec}
 |---------|---------|
 |ID       |CUSTOMER |
 |---------|---------|
 |[unread] |null     |
 |---------|---------|
 select items0_.ORDER_ID as ORDER5_1_2_, items0_.id as id2_, items0_.id as id0_1_, items0_.order_id 
 as order5_0_1_, items0_.price as price0_1_, items0_.product as product0_1_, items0_.quantity 
 as quantity0_1_, order1_.id as id1_0_, order1_.customer as customer1_0_ from Item items0_ left 
 outer join T_ORDER order1_ on items0_.order_id=order1_.id where items0_.ORDER_ID=2  {executed in 0 msec}
 |---------|---|---|---------|------|--------|---------|---|---------|
 |ORDER_ID |ID |ID |ORDER_ID |PRICE |PRODUCT |QUANTITY |ID |CUSTOMER |
 |---------|---|---|---------|------|--------|---------|---|---------|
 |2        |2  |2  |2        |0.0   |null    |0        |2  |[unread] |
 |---------|---|---|---------|------|--------|---------|---|---------|
 insert into T_ORDER (id, customer) values (null, NULL)  {executed in 0 msec}
 insert into Item (id, order_id, price, product, quantity) values (null, NULL, 0.0, 'foo', 0) 
 {executed in 0 msec}
 batching 1 statements: 0: update Item set ORDER_ID=3 where id=3  {executed in 0 msec}
 select order0_.id as id1_, order0_.customer as customer1_ from T_ORDER order0_ inner join Item 
 items1_ on order0_.id=items1_.ORDER_ID where items1_.product='foo' limit 2  {executed in 6 msec}
 |---|---------|
 |ID |CUSTOMER |
 |---|---------|
 |3  |null     |
 |---|---------|
 select items0_.ORDER_ID as ORDER5_1_2_, items0_.id as id2_, items0_.id as id0_1_, items0_.order_id 
 as order5_0_1_, items0_.price as price0_1_, items0_.product as product0_1_, items0_.quantity 
 as quantity0_1_, order1_.id as id1_0_, order1_.customer as customer1_0_ from Item items0_ left 
 outer join T_ORDER order1_ on items0_.order_id=order1_.id where items0_.ORDER_ID=3  {executed in 0 msec}
 |---------|---|---|---------|------|--------|---------|---|---------|
 |ORDER_ID |ID |ID |ORDER_ID |PRICE |PRODUCT |QUANTITY |ID |CUSTOMER |
 |---------|---|---|---------|------|--------|---------|---|---------|
 |3        |3  |3  |3        |0.0   |foo     |0        |3  |[unread] |
 |---------|---|---|---------|------|--------|---------|---|---------|

Output is printed in a fashion format, queries contains bind parameters (not a question mark (?)), and process time is also informed.

Notice that logging more or less information is simply a matter of configuring a log. Moreover depending on log level, more or less information will be provided in each case. If logger is configured in DEBUG class name and line number (if available) at which the sql was executed will be included. In INFO will include only sql, and finally ERROR which show stacktraces if any SQLException occurs.

Optimizing hibernate applications can imply touching many parts of an application ( JVM configuration, database engine, network, …) but one very important aspect to take care is the number of queries that are sent to RDBMS (for example N+1 problem), and the amount of data that is retrieved from database (Projection problem), and log4jdbc-remix perfectly fits to help in this purpose.

As final note, log4jdbc(- remix) is a jdbc logger, so it is not necessary to use only in hibernate applications, can be used with any framework that uses a datasource.

I wish this library would help you.

Keep Learning,

Download Code

Reference: Log JDBC operations with Log4jdbc from our JCG partner Alex Soto at the One Jar To Rule Them All blog.

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

7 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
SeedBox Bul
11 years ago

can we use this and print this logs to a mongo db or mysql db for quick analizing ? 
if we can, it would be a good blog post to show how ? 

SeedBox Bul
11 years ago

also you should improve your site’s 
You might also like:  part. it shows irrelevant posts.

Basil Abbas
Basil Abbas
11 years ago

This article is missing important stuff:
Please make sure to read this first:http://code.google.com/p/log4jdbc/

Make sure to perform the following:
1. In your hibernate.cfg.xml replace the line:
com.mysql.jdbc.Driver

with:
net.sf.log4jdbc.DriverSpy

2. In your hibernate.cfg.xml replace the line:
jdbc:mysql://localhost:3306/oncall

with:
jdbc:log4jdbc:mysql://localhost:3306/oncall

3. If you are using a log4j.properties file, use the following instead:
log4j.logger.jdbc.sqlonly=debug
log4j.logger.jdbc.sqltiming=debug
log4j.logger.jdbc.audit=debug
log4j.logger.jdbc.resultset=debug
log4j.logger.jdbc.connection=debug
log4j.logger.jdbc.resultsettable=debug

Thats it.

Isotherm
Isotherm
10 years ago

Maybe you’d like to know that there is now a log4jdbc fork based on Log4j 2, using one single logger (so easier configuration than described here): http://code.google.com/p/log4jdbc-log4j2/

Amiga
Amiga
7 years ago

There are a number of log4jdbc fork , there is one that has been completely redone and is currently maintained :
https://github.com/marcosemiao/log4jdbc
readme is only in French but it remains simple

Csini
Csini
7 years ago

don’t forget to use Log4jdbcProxyDataSource (!)

for example:

final Log4jdbcProxyDataSourceds = new Log4jdbcProxyDataSource(new BasicDataSource());

ds.setUrl(“jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE;MODE=DB2;create=true”);
ds.setUsername(“sa”);
ds.setDriverClassName(“org.h2.jdbcx.JdbcDataSource”);
ds.setPassword(“”);

Nguyen Van Thao
Nguyen Van Thao
1 year ago
Reply to  Csini

How to use log4jdbc to quarkus project? Can it apply to quarkus?

Back to top button