1

I have a web application that is deployed locally to a Liberty Profile server, and that is already working with log4j2. My end goal is to log all of the PreparedStatements with their parameter values included in the query string, just before they are run against a DB2 database.

I've been following the instructions at https://code.google.com/p/log4jdbc-log4j2 to set up log4jdbc-log4j2. I was able to pull down the dependency files with Maven:

<dependency>
    <groupId>org.bgee.log4jdbc-log4j2</groupId>
    <artifactId>log4jdbc-log4j2-jdbc4</artifactId>
    <version>1.16</version>
</dependency>

However, I've been stuck at steps 3.1 and 3.2 for awhile, and so far, nothing on stackoverflow or instructional blogs has helped me move forward, so I thought it was time to ask my own question.

Could someone please let me know in which file(s), and how, I should make the changes mentioned in steps 3.1 ("Change your JDBC URL") and 3.2 ("Change the driver used")? Please let me know if there's something I can clarify further in order to help get my question answered, and thank you in advance for any help or guidance you can provide.

Update

After making the changes to server.xml suggested aguibert and including all log4j*.jar files from the dependency in the db2 drivers directory, my server.xml entry looks like this:

<dataSource id="myDataSource" jndiName="jdbc/myDataSource" type="javax.sql.DataSource">
    <jdbcDriver javax.sql.DataSource="net.sf.log4jdbc.sql.jdbcapi.DataSourceSpy">
        <library>
            <fileset dir="<path to dir>/db2" includes="db2jcc_license_cisuz.jar db2jcc4.jar log4j-api-2.3.jar log4j-core-2.3.jar log4jdbc-log4j2-jdbc4-1.16-sources.jar log4jdbc-log4j2-jdbc4-1.16.jar"/>
        </library>
    </jdbcDriver>   
    <properties
        password="password"
        user="user"
        URL="jdbc:log4jdbc:db2://<normal jdbc url>" />
</dataSource>

Now, when the first query is made, I get an InstantiationException on net.sf.log4jdbc.sql.jdbcapi.DataSourceSpy:

java.lang.Exception:
    at <my files>
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:595) [com.ibm.ws.javaee.servlet.3.0_1.0.8.jar:?]
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:668) [com.ibm.ws.javaee.servlet.3.0_1.0.8.jar:?]
    at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1285) [com.ibm.ws.webcontainer_1.0.8.jar:?]
    at com.ibm.ws.webcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:776) [com.ibm.ws.webcontainer_1.0.8.jar:?]
    at com.ibm.ws.webcontainer.servlet.ServletWrapper.handleRequest(ServletWrapper.java:473) [com.ibm.ws.webcontainer_1.0.8.jar:?]
    at com.ibm.ws.webcontainer.filter.WebAppFilterManager.invokeFilters(WebAppFilterManager.java:1104) [com.ibm.ws.webcontainer_1.0.8.jar:?]
    at com.ibm.ws.webcontainer.webapp.WebApp.handleRequest(WebApp.java:4845) [com.ibm.ws.webcontainer_1.0.8.jar:?]
    at com.ibm.ws.webcontainer.osgi.DynamicVirtualHost$2.handleRequest(DynamicVirtualHost.java:297) [com.ibm.ws.webcontainer_1.0.8.jar:?]
    at com.ibm.ws.webcontainer.WebContainer.handleRequest(WebContainer.java:981) [com.ibm.ws.webcontainer_1.0.8.jar:?]
    at com.ibm.ws.webcontainer.osgi.DynamicVirtualHost$2.run(DynamicVirtualHost.java:262) [com.ibm.ws.webcontainer_1.0.8.jar:?]
    at com.ibm.ws.http.dispatcher.internal.channel.HttpDispatcherLink$TaskWrapper.run(HttpDispatcherLink.java:955) [com.ibm.ws.transport.http_1.0.8.jar:?]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) [?:1.7.0_60]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) [?:1.7.0_60]
    at java.lang.Thread.run(Thread.java:745) [?:1.7.0_60]
Caused by: javax.naming.NamingException: CWWKN0008E: An object could not be obtained for name jdbc/myDataSource.
    at com.ibm.ws.jndi.internal.WSContext.resolveObject(WSContext.java:128) ~[?:?]
    at com.ibm.ws.jndi.internal.WSContext.lookup(WSContext.java:364) ~[?:?]
    at com.ibm.ws.jndi.internal.WSContext.lookup(WSContext.java:359) ~[?:?]
    at org.apache.aries.jndi.DelegateContext.lookup(DelegateContext.java:161) ~[?:?]
    at javax.naming.InitialContext.lookup(InitialContext.java:411) ~[?:1.7.0_60]
    at  <my files>
    ... 16 more
[ERROR   ] CWWKE0701E: FrameworkEvent ERROR Bundle:com.ibm.ws.jdbc(id=69) org.osgi.framework.ServiceException: Exception in com.ibm.ws.resource.internal.ResourceFactoryTrackerData$1.getService()
    at org.eclipse.osgi.internal.serviceregistry.ServiceFactoryUse.factoryGetService(ServiceFactoryUse.java:222)
    at [internal classes]
    at javax.naming.InitialContext.lookup(InitialContext.java:411)
    at  <my files>
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:595)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:668)
    at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1285)
    at [internal classes]
Caused by: java.lang.RuntimeException: java.sql.SQLNonTransientException: java.lang.InstantiationException: net.sf.log4jdbc.sql.jdbcapi.DataSourceSpy
    at com.ibm.ws.resource.internal.ResourceFactoryTrackerData$1.getService(ResourceFactoryTrackerData.java:109)
    ... 10 more
Caused by: java.sql.SQLNonTransientException: java.lang.InstantiationException: net.sf.log4jdbc.sql.jdbcapi.DataSourceSpy
    at com.ibm.ws.jdbc.internal.JDBCDriverService.create(JDBCDriverService.java:287)
    ... 10 more
Caused by: java.lang.InstantiationException: net.sf.log4jdbc.sql.jdbcapi.DataSourceSpy
    at java.lang.Class.newInstance(Class.java:359)
    at com.ibm.ws.jdbc.internal.JDBCDriverService$1.run(JDBCDriverService.java:228)
    ... 10 more
 Event:org.osgi.framework.FrameworkEvent[source=com.ibm.ws.jdbc_1.0.8.cl50520150305-2202 [69]]

If it looks like there's anything that I've missed, please let me know. Searching for the errors in the stack trace hasn't resulted in any solutions.

Final Status

As aguibert pointed out, it seems like a different direction will be best here. Based on a comment in Logging PreparedStatements in Java, I've decided to implement a LoggableStatement wrapper as described here: ibm.com/developerworks/java/library/j-loggable

Tyndareus
  • 13
  • 6

1 Answers1

1

For a WebSphere Liberty server, all of the global server config is done in the server.xml file (located by defualt at WLP_INSTALL/usr/servers//server.xml).

You will probably want something along these lines in your server.xml:

<dataSource id="myDataSource" jndiname="jdbc/myDataSource" type="javax.sql.DataSource">
    <jdbcDriver javax.sql.DataSource="net.sf.log4jdbc.sql.jdbcapi.DataSourceSpy">
      <library>
        <fileset dir="C:/path/to/libs" includes="thedb2jar.jar log4j.jar" />
      </library>
    </jdbcDriver>
    <properties user="user" password="password"
                url="jdbc:log4jdbc:<the normal jdbc url>"/>
</dataSource>

The key parts here is that the element has the "javax.sql.DataSource" property set and the value is the name of the DataSource class for the log4j jar. Also, in the element, you'll see that the url is specified with the "jdbc:log4jdbc" prefix as described in section 3.1.

This is untested advice, but you may need to include both jars (the db2 jar and the log4j jar) in the same folder so they are picked up in the same element.

Andy Guibert
  • 41,446
  • 8
  • 38
  • 61
  • Thank you for your response. I've included all log4j*.jar files from the dependency in the db2 drivers directory, and updated my server.xml to contain exactly what you suggested (with my application's names/paths populated, of course). I've also tried to use instead of . Either way, when the first query is made, I get an InstantiationException on net.sf.log4jdbc.sql.jdbcapi.DataSourceSpy. Any insight will be very much appreciated. Thank you again. I'll continue to make modifications to server.xml and see if I get the desired results. – Tyndareus Jun 30 '15 at 15:10
  • @Tyndareus If you want more help, then I would suggest updating the question with your new error. Alternatively, if this answer helped you make progress, then you should accept it. – Brett Kail Jun 30 '15 at 15:31
  • @Tyndareus without the stack I can't help you at all on the InstantiationException, but that sounds like an entirely separate problem. By setting javax.sql.DataSource="net.blah.DataSourceSpy" you're telling Liberty what the datasource impl class is. If that throws an exception then it's an issue with the driver now allowing Liberty to instantiate it. – Andy Guibert Jun 30 '15 at 15:50
  • Duly noted and updated accordingly, @bkail - thanks. – Tyndareus Jun 30 '15 at 16:09
  • Thanks for the explanation, @aguibert - I've updated the original question with some hopefully useful information. – Tyndareus Jun 30 '15 at 16:09
  • It appears that DataSourceSpy does not have a no-arg constructor (https://code.google.com/p/log4jdbc-log4j2/source/browse/trunk/log4jdbc-log4j2-jdbc4.1/src/main/java/net/sf/log4jdbc/sql/jdbcapi/DataSourceSpy.java), but the WebSphere JDBC service requires one with this configuration. I'm not sure if there's a configuration option that will prevent it from trying to instantiate the data source instance, but perhaps @aguibert knows. – Brett Kail Jun 30 '15 at 16:54
  • @bkail is right about the no args constructor being the issue here. If you want to keep going down the log4j path, you're going to have to open an RFE with Liberty, or write a jdbc driver wrapper that provides a no-args constructor to Liberty. Going in a different direction though, since you just want to log your prepared statements, have you checked out the liberty trace settings? You could get what you want without even having to use log4j. If you want PS values though, Liberty doesn't log those since they can contain sensitive information in production. – Andy Guibert Jun 30 '15 at 19:34
  • Thank you both for all of your help and explanations. It does seem like a different direction will be best here. Based on a comment in https://stackoverflow.com/questions/218113/logging-preparedstatements-in-java, I've decided to implement a LoggableStatement wrapper as described here: http://www.ibm.com/developerworks/java/library/j-loggable – Tyndareus Jul 01 '15 at 13:24