Grails throws ClassNotFoundException when I setup log4j with JDBCAppender

927 Views Asked by At

Context

I am trying to setup log4j with JDBCAppender on Grails 2.4.3, I was trying with H2, MySQL and PostgreSQL, but it throws an ClassNotFoundException exception with each driver.

Config.groovy configuration for log4j JDBCAppender:

I have tested user and password credentials information, I mean, I can connect to these databases through Datasource.groovy file.

Test1 with H2:

log4j = {
    appenders {
        jdbc name: "db",
            databaseURL: "jdbc:h2:mem:devDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE",
            user: "sa",
            password: "",
            driverClassName: "org.h2.Driver",
            sql: "INSERT INTO event_log (log_date, log_message) VALUES ('%d{yyyy.MM.dd HH:mm:ss}', '[%-5p]. Category: %c. Message: %m. User: %X{sessionUserName} DU:[%X{sessionUserDU}]');"
    }

root {
    info 'grails.app.controller','db'
}

}

Test2 with Postgres:

log4j = {
    appenders {
        jdbc name: "db",
            URL: "jdbc:postgresql://localhost/grailsTestDB",
            user: "userDB",
            password: "*******",
            driver: "org.postgresql.Driver",
            layout: pattern(conversionPattern: "[%t] %-5p %c{2} %x - %m%n - %X{username}'"),
            sql: "INSERT INTO event_log (accion,nivel,req,logString,usuario) VALUES('%c{2} %x','%-5p','[%t]','%m%n','%X{username}');"
    }

root {
    info 'grails.app.controller','db'
}

}

Test3 with MySQL:

The next code was taken from an Grails 1.3.8 application, in that application this code works fine.

log4j = {
    appenders {
        appender new JDBCAppender(
            name: "db",
            databaseURL: "jdbc:mysql://dbdevsie.db.hostname.com/dbdevsie",
            driver: "com.mysql.jdbc.Driver",
            user: "dbdevsie231",
            password: "********",
            layout: pattern(conversionPattern: "[%t] %-5p %c{2} %x - %m%n - %X{username}'"),
            sql: "INSERT INTO activity_log (accion,nivel,req,logString,usuario) VALUES('%c{2} %x','%-5p','[%t]','%m%n','%X{username}');"
    )
}

root {
    info 'grails.app.controller','db'
}

}

Buildconfig.groovy dependencies setup:

dependencies {
    runtime 'mysql:mysql-connector-java:5.1.29'
    runtime 'org.postgresql:postgresql:9.3-1101-jdbc41'
    test "org.grails:grails-datastore-test-support:1.0-grails-2.4"
}

Test results:

When I test h2 it throws com.mysql.jdbc.Driver exception.

When I test Mysql it throws sun.jdbc.odbc.JdbcOdbcDriver exception.

When I test Postgresql it throws org.postgresql.Driver exception.

| Error log4j:ERROR Failed to load driver
| Error java.lang.ClassNotFoundException: sun.jdbc.odbc.JdbcOdbcDriver
| Error     at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
| Error     at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
| Error     at java.security.AccessController.doPrivileged(Native Method)
| Error     at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
| Error     at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
| Error     at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
| Error     at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
| Error     at java.lang.Class.forName0(Native Method)
| Error     at java.lang.Class.forName(Class.java:191)
| Error     at org.apache.log4j.jdbc.JDBCAppender.setDriver(JDBCAppender.java:391)
| Error     at org.apache.log4j.jdbc.JDBCAppender.getConnection(JDBCAppender.java:248)
| Error     at org.apache.log4j.jdbc.JDBCAppender.execute(JDBCAppender.java:215)
| Error     at org.apache.log4j.jdbc.JDBCAppender.flushBuffer(JDBCAppender.java:289)
| Error     at org.apache.log4j.jdbc.JDBCAppender.append(JDBCAppender.java:186)
| Error     at org.apache.log4j.AppenderSkeleton.doAppend(AppenderSkeleton.java:251)    

Additional comments

I also test downloading log4j 1.2.17, PostgreSQL 9.3, and MySQL 5.1.34 jar files and placed them on lib folder, but I have the same result.

Could anyone help me with this thing?

thanks in advance.

Updated

This issue shows in Mac OS, however I ran the same code in linux and there is no problem with it.

  • OS X Yosemite version 10.10.1
  • Java 1.7.0_71
2

There are 2 best solutions below

0
On BEST ANSWER

You're correct in using the DSL instead of instantiating the appender inline; dependencies and classpath haven't been resolved when your code runs in Config.groovy, so you need some way to delay the actual initialization of the appender until after that happens and the DSL config is a good option.

I got this working, but had different failures than you. In my case both H2 and MySQL reliably crashed my 1.7 JVM early on, but it worked when I switched to 1.8 and now it works in 1.7 also even after deleting pretty much everything. Try getting the dependencies resolved independently by commenting out these config changes, then restarting with them enabled. This might also be due to forking - I almost always disable it by deleting the whole grails.project.fork block in BuildConfig.groovy - if nothing else works, see if that helps.

This is what I had in Config.groovy:

log4j.main = {

   appenders {
      jdbc name: 'jdbcAppender', driver: 'com.mysql.jdbc.Driver', user: '...',
           password: '...', URL: 'jdbc:mysql://localhost/<dbname>',
           layout: pattern(conversionPattern:
                "insert into logs(log_date, logger, log_level, message) " +
                "values('%d{yyyy-MM-dd HH:mm:ss}','%c','%p','%m')")
   }

   root {
      info 'stdout', 'jdbcAppender'
   }

   error 'org.codehaus.groovy.grails',
         'org.springframework',
         'org.hibernate',
         'net.sf.ehcache.hibernate'
}

and I created this table to log into:

create table logs (
   log_date datetime not null,
   log_level varchar(10) not null,
   logger varchar(100) not null,
   message varchar(1000) not null
) ENGINE=InnoDB;
0
On

We utilized a workaround in which we used a dataSource for the jdbc appender

Add a log datasource in Datasource.groovy:

dataSource_log {
    url = "jdbc:sqlserver://DBServer:1433;databaseName=Logs;integratedSecurity=true" 
    dialect = "org.hibernate.dialect.SQLServerDialect" 
    driverClassName = "com.microsoft.sqlserver.jdbc.SQLServerDriver" 
}

Then override where the JDBCAppender gets its connections

public class DataSourceAppender extends JDBCAppender {
    SessionFactory sessionFactory;
    protected java.sql.Connection getConnection() throws java.sql.SQLException {
        SessionImplementor imp = (SessionImplementor) sessionFactory.getCurrentSession();
        return imp.getJdbcConnectionAccess().obtainConnection();
    }
}

And add this custom appender to log4j in Bootstrap.groovy:

def grailsApplication
def sessionFactory_log

def init = { servletContext ->
    def sqlAppender = new DataSourceAppender(
            grailsApplication.config.dataSourceAppender
    )
    sqlAppender.sessionFactory = sessionFactory_log
    Logger.getRootLogger().addAppender(sqlAppender)
}

Would love to see a real fix for this though.