Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you configure logging in Hibernate 4 to use SLF4J

Look to https://github.com/jboss-logging/jboss-logging/blob/master/src/main/java/org/jboss/logging/LoggerProviders.java:

static final String LOGGING_PROVIDER_KEY = "org.jboss.logging.provider";

private static LoggerProvider findProvider() {
    // Since the impl classes refer to the back-end frameworks directly, if this classloader can't find the target
    // log classes, then it doesn't really matter if they're possibly available from the TCCL because we won't be
    // able to find it anyway
    final ClassLoader cl = LoggerProviders.class.getClassLoader();
    try {
        // Check the system property
        final String loggerProvider = AccessController.doPrivileged(new PrivilegedAction<String>() {
            public String run() {
                return System.getProperty(LOGGING_PROVIDER_KEY);
            }
        });
        if (loggerProvider != null) {
            if ("jboss".equalsIgnoreCase(loggerProvider)) {
                return tryJBossLogManager(cl);
            } else if ("jdk".equalsIgnoreCase(loggerProvider)) {
                return tryJDK();
            } else if ("log4j".equalsIgnoreCase(loggerProvider)) {
                return tryLog4j(cl);
            } else if ("slf4j".equalsIgnoreCase(loggerProvider)) {
                return trySlf4j();
            }
        }
    } catch (Throwable t) {
    }
    try {
        return tryJBossLogManager(cl);
    } catch (Throwable t) {
        // nope...
    }
    try {
        return tryLog4j(cl);
    } catch (Throwable t) {
        // nope...
    }
    try {
        // only use slf4j if Logback is in use
        Class.forName("ch.qos.logback.classic.Logger", false, cl);
        return trySlf4j();
    } catch (Throwable t) {
        // nope...
    }
    return tryJDK();
}

So possible values for org.jboss.logging.provider are: jboss, jdk, log4j, slf4j.

If you don't set org.jboss.logging.provider it tries jboss, then log4j, then slf4j (only if logback used) and fallback to jdk.

I use slf4j with logback-classic:

    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
        <version>1.0.13</version>
        <scope>${logging.scope}</scope>
    </dependency>

and all work fine!

UPDATE Some users uses in very main App.java:

static { //runs when the main class is loaded.
    System.setProperty("org.jboss.logging.provider", "slf4j");
}

but for container based solutions this is not worked.

UPDATE 2 Those who think that they manage Log4j with SLF4J for jboss-logging it is not exactly thus. jboss-logging directly uses Log4j without SLF4J!


To get SLF4J to work with JBoss Logging without Logback as backend requires usage of a system property org.jboss.logging.provider=slf4j. log4j-over-slf4j tactics doesn't seem to be working in this case because the logging will fall back to JDK if neither Logback nor log4j isn't actually present in classpath.

This is a bit of a nuisance and in order to get autodetection to work you have see that the classloader contains at least ch.qos.logback.classic.Logger from logback-classic or org.apache.log4j.Hierarchy from log4j to trick the JBoss Logging from not falling back to JDK logging.

The magic is interpreted at org.jboss.logging.LoggerProviders

UPDATE: Service loader support has been added so it is possible to avoid problems with autodetection by declaring META-INF/services/org.jboss.logging.LoggerProvider (with org.jboss.logging.Slf4jLoggerProvider as a value). There seems to be added support log4j2 as well.


Inspired by Leif's Hypoport post, this is how I "bent" Hibernate 4 back to slf4j:

Let's assume you are using Maven.

  • Add org.slf4j:log4j-over-slf4j as a dependency to your pom.xml
  • Using the command mvn dependency:tree, make sure none of the artifacts you're using depende on slf4j:slf4j (to be precise, no artifact shall have a compile scope dependency or runtime scope dependency on slf4j:slf4j)

Background: Hibernate 4.x has a dependency on the artifact org.jboss.logging:jboss-logging. Transitively, this artifact has a provided scope dependency on the artifact slf4j:slf4j.

As we now have added the org.slf4j:log4j-over-slf4j artifact, org.slf4j:log4j-over-slf4j mimicks the slf4j:slf4j artifact. Therefore everything that JBoss Logging logs will now actually go via slf4j.

Let's say you're using Logback as your logging backend. Here is a sample pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>

    ....
    <properties>
        ....
        <slf4j-api-version>1.7.2</slf4j-api-version>
        <log4j-over-slf4j-version>1.7.2</log4j-over-slf4j-version>
        <jcl-over-slf4j-version>1.7.2</jcl-over-slf4j-version> <!-- no problem to have yet another slf4j bridge -->
        <logback-core-version>1.0.7</logback-core-version>
        <logback-classic-version>1.0.7</logback-classic-version>
        <hibernate-entitymanager-version>4.1.7.Final</hibernate-entitymanager-version> <!-- our logging problem child -->
    </properties>

    <dependencies>
            <!-- begin: logging-related artifacts .... -->
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-api</artifactId>
                <version>${slf4j-api-version}</version>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>jcl-over-slf4j</artifactId>
                <version>${jcl-over-slf4j-version}</version>
            </dependency>
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>log4j-over-slf4j</artifactId>
                <version>${log4j-over-slf4j-version}</version>
            </dependency>   
            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-core</artifactId>
                <version>${logback-core-version}</version>
            </dependency>
            <dependency>
                <groupId>ch.qos.logback</groupId>
                <artifactId>logback-classic</artifactId>
                <version>${logback-classic-version}</version>
            </dependency>
            <!-- end: logging-related artifacts .... -->

            <!-- begin: some artifact with direct dependency on log4j:log4j ....  -->
            <dependency>
            <groupId>org.foo</groupId>
                <artifactId>some-artifact-with-compile-or-runtime-scope-dependency-on-log4j:log4j</artifactId>
                <version>${bla}</version>
                <exclusions>
                    <exclusion>
                        <groupId>log4j</groupId>
                        <artifactId>log4j</artifactId>
                    </exclusion>
                </exclusions>   
            </dependency>
            <!-- begin: some artifact with direct dependency on log4j:log4j ....  -->

            <!-- begin: a hibernate 4.x problem child........... -->
            <dependency>
                <groupId>org.hibernate</groupId>
                <artifactId>hibernate-entitymanager</artifactId>
                <version>${hibernate-entitymanager-version}</version>
            </dependencies>
            <!-- end: a hibernate 4.x problem child........... -->
    ....
</project>

On your classpath, have a logback.xml, such as this one located in src/main/java:

<!-- begin: logback.xml -->
<configuration>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
        <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
</appender> 

<logger name="org.hibernate" level="debug"/>

<root level="info">
    <appender-ref ref="console"/>
</root>

</configuration>
<!-- end: logback.xml -->

Some components may want to have access to logback.xml at JVM start-up time for proper logging, for instance the Jetty Maven Plugin. In that case, add a Java system logback.configurationFile=./path/to/logback.xml to your command (e.g. mvn -Dlogback.configurationFile=./target/classes/logback.xml jetty:run).

In case you are still getting "raw" console stdout Hibernate output (like Hibernate: select ...), then Stack Overflow question " Turn off hibernate logging to console " may apply.


First you do realize that SLF4J is not a logging library right, its a logging wrapper. It itself does not log anything, it simply delegates to "backends".

To "configure" jboss-logging you just add whatever log framework you want to use on your classpath (along with jboss-logging) and jboss-logging figures out the rest.

I created a Hibernate-focused guide to JBoss Logging config: http://docs.jboss.org/hibernate/orm/4.3/topical/html/logging/Logging.html


I'm using Hibernate Core 4.1.7.Final plus Spring 3.1.2.RELEASE in a standalone app. I added Log4j 1.2.17 to my dependencies and it seems, as JBoss Logging logs directly to log4j if available and Spring uses Commons Logging, witch also uses Log4j if available, all Logging could be configured via Log4J.

Here's my list of relevant dependencies:

<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>4.1.7.Final</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>3.1.2.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-orm</artifactId>
    <version>3.1.2.RELEASE</version>
</dependency>