如何在Hibernate 4中配置日志记录以使用SLF4J

123
Hibernate 3.x使用进行日志记录。Hibernate 4.x使用。我正在编写一个独立应用程序,它使用Hibernate 4和SLF4J进行日志记录。
如何配置Hibernate以记录到SLF4J?
如果不可能,如何完全配置Hibernate的日志记录?
Hibernate 4.1手册日志记录部分开始警告说它已经过时。Hibernate在4.0中开始使用JBoss Logging。随着我们将这些内容迁移到开发人员指南,这将得到记录。

...继续讨论SLF4J,因此毫无用处。入门指南开发人员指南都没有讨论日志记录。迁移指南也没有。

我已经寻找了关于jboss-logging的文档,但是没有找到任何相关内容。GitHub页面上没有信息,而JBoss的社区项目页面甚至没有列出jboss-logging。我想知道该项目的bug跟踪器是否有与提供文档相关的问题,但是并没有发现。好消息是,在应用服务器(如JBoss AS7)中使用Hibernate 4时,大部分日志记录工作已经为您处理。但是,如何在独立应用程序中进行配置呢?

14
感谢您强调Hibernate文档中关于日志记录的内容已经过时,给您点个赞。 - mhnagaoka
可以设置系统属性org.jboss.logging.provide=slf4j。更多详细信息请访问链接http://docs.jboss.org/hibernate/orm/4.3/topical/html/logging/Logging.html,适用于大于3的Hibernate版本。 - Abhishek Ranjan
11个回答

66
看一下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();
}

所以org.jboss.logging.provider的可能值为:jbossjdklog4jslf4j

如果您没有设置org.jboss.logging.provider,它会尝试使用jboss(请确保从类路径中排除相应的jar包!),然后是log4j,再然后是slf4j(仅在使用logback时),最后回退到jdk。

我使用slf4jlogback-classic

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

一切都工作正常!

更新 一些用户在主要的 App.java 中使用:

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

但对于基于容器的解决方案,这并不适用。

更新2 那些认为使用SLF4J来管理Log4j的jboss-logging的人并不完全正确。jboss-logging直接使用Log4j而没有使用SLF4J!


1
在哪里设置 org.jboss.logging.provider - Suzan Cioc
1
根据 System.getProperty(LOGGING_PROVIDER_KEY); ,您需要设置系统属性。可以通过 java -D...=... 或参考您的容器文档进行设置。 - gavenkoa
1
你关于无法通过slf4j使用log4j的第二次更新很有帮助。将org.jboss.logging.provider设置为slf4j让我以为我的log4j后端会启动,但实际上并没有。我必须直接将其设置为log4j才能使其正常工作。这很奇怪。那么,将slf4j作为此配置的选项有什么意义呢? - Travis Spencer

29
为了让SLF4J能够在没有Logback作为后端的情况下与JBoss Logging配合使用,需要使用系统属性org.jboss.logging.provider=slf4j。在这种情况下,log4j-over-slf4j策略似乎不起作用,因为如果classpath中不存在Logback或log4j,则日志记录将回退到JDK。
这有点麻烦,为了使自动检测起作用,您必须确保类加载器包含来自logback-classic的ch.qos.logback.classic.Logger或来自log4j的org.apache.log4j.Hierarchy,以防止JBoss Logging回退到JDK日志记录。
这个魔术被解释为org.jboss.logging.LoggerProviders 更新:添加了服务加载器支持,因此可以通过声明META-INF/services/org.jboss.logging.LoggerProvider(值为org.jboss.logging.Slf4jLoggerProvider)来避免自动检测问题。似乎也增加了对log4j2的支持。

1
我应该在哪里设置这个系统属性? - jhegedus
根据您的设置而定,但通常一个命令行开关-Dorg.jboss.logging.provider=slf4j就足够了。LoggingProviders.java可以更好地了解当前接受的值以及类路径中应该存在什么。 - Tuomas Kiviaho
3
我认为服务加载器的方法行不通,因为 Slf4jLoggerProvider 不是一个 public 类? - holmis83
我需要在Weblogic WAR中的源代码中设置org.jboss.logging.provider,但是任何静态类初始化器都会在LoggingProviders之后被调用! - Antonio Petricca

12

Leif的Hypoport博客文章启发,以下是我如何将Hibernate 4“弯曲”回到slf4j:

假设您正在使用Maven。

  • 在您的pom.xml中添加org.slf4j:log4j-over-slf4j依赖项
  • 使用命令mvn dependency:tree,确保您使用的任何工件都不依赖于slf4j:slf4j(准确来说,没有工件应该有一个编译范围的依赖关系或运行时范围的依赖关系slf4j:slf4j

背景:Hibernate 4.x依赖于工件org.jboss.logging:jboss-logging。 因此,在传递依赖项中,这个工件对工件slf4j:slf4j具有提供范围的依赖关系。

现在,我们已经添加了org.slf4j:log4j-over-slf4j工件,org.slf4j:log4j-over-slf4j模仿slf4j:slf4j工件。因此,所有JBoss Logging记录的内容现在实际上将通过slf4j进行。

假设您正在使用Logback作为日志记录后端。以下是一个示例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>

在你的类路径中,有一个名为 logback.xml 的文件,比如位于 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 -->

有些组件可能需要在JVM启动时访问logback.xml以进行适当的记录,例如Jetty Maven插件。此时,将Java系统logback.configurationFile=./path/to/logback.xml添加到您的命令中(例如mvn -Dlogback.configurationFile=./target/classes/logback.xml jetty:run)。

如果您仍然在控制台上获得“原始”控制台stdout Hibernate输出(例如Hibernate: select ...),则Stack Overflow问题“关闭Hibernate日志记录到控制台”可能适用。


1
确保没有其他库包含log4j,否则这将无法工作。例如:activemq-all.jar包含log4j。提示:打开您的IDE并在代码中轻松找到log4j。 - Dimitri Dewaele
我在使用 JBoss Hibernate4 和一台(太)老的服务器时遇到了问题。这篇文章,包括 application.properties 中的 1 行代码,为我解决了问题。所以非常感谢!我的 properties 文件中最后一行是从另一个答案中复制过来的:org.jboss.logging.provider=slf4j - Jeroen van Dijk-Jun

8
首先,您需要明白SLF4J并不是一个记录日志的库,而是一个记录日志的封装器。它本身并没有记录任何日志,仅仅是委派给“后端”进行记录。
要“配置”jboss-logging,您只需在类路径中添加您想使用的任何日志框架(以及jboss-logging),然后jboss-logging就会自动完成其余工作。
我为Hibernate编写了一份重点介绍JBoss Logging配置的指南:http://docs.jboss.org/hibernate/orm/4.3/topical/html/logging/Logging.html

3
我知道SLF4J是一个外观模式。将Hibernate日志发送到SLF4J意味着它将最终到达我选择的应用程序其余部分的后端,这正是我想要的。 - Tom Anderson
11
关于配置,你的意思是没有配置(这很好!),但是jboss-logging会自动检测并选择后端?噢,现在我花时间看代码,我看到确实是这样的。具体来说,jboss-logging 会尝试按顺序使用 JBoss LogManager、log4j、通过 SLF4J 的 Logback 和 JDK logging。但是这个行为可以通过 org.jboss.logging.provider 系统属性进行覆盖。 - Tom Anderson
4
我们中的许多人都曾被Commons Logging“替你想好”的方式所困扰,因此了解JBoss Logging的工作原理对于在真实世界中支持它以应对意外情况至关重要。 - ams
1
上面的链接实际上展示了如果你真的想看到的情况,所以不要跟随... - Steve Ebersole

3
Hibernate 4.3有一些关于如何控制org.jboss.logging的文档:文档
  • 它在类路径中搜索日志提供程序,先搜索log4j,然后是slf4j。因此,理论上,确保您的类路径(WAR)不包括log4j并包括slf4j API和一个后端应该可以工作。

  • 作为最后的手段,您可以将org.jboss.logging.provider系统属性设置为slf4j


尽管文档声称如此,但org.jboss.logging仍然坚持尝试使用log4j,导致在我的Tomcat日志文件 (/var/log/tomcat/catalina.out) 中出现以下消息:
 log4j:WARN No appenders could be found for logger (org.jboss.logging).
 log4j:WARN Please initialize the log4j system properly.
 log4j:WARN See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.

我不得不遵循dasAnderl ausMinga的建议,包含log4j-over-slf4j桥接器。


3
我在一个独立应用中使用 Hibernate Core 4.1.7.Final 加上 Spring 3.1.2.RELEASE。我添加了 Log4j 1.2.17 作为我的依赖项,似乎 JBoss Logging 直接记录到 log4j(如果可用),而 Spring 使用 Commons Logging,如果可用,也使用 Log4j,因此所有的日志记录都可以通过 Log4J 进行配置。
以下是相关依赖项列表:
<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>

3

所以,我已经在我的项目中让它工作了。使用的技术包括:Hibernate 4、SLF4J和Logback。我的项目是Gradle,但对于Maven应该也是一样的。

基本上Abdull是正确的。他不正确的地方在于你不需要从依赖项中删除SLF4J。

  1. 将以下内容包含到编译范围中:

    org.slf4j:slf4j-api

    org.slf4j:log4j-over-slf4j

    例如,对于Logback(ch.qos.logback:logback-classic,ch.qos.logback:logback-core:1.0.12)

  2. 完全排除依赖项中的Log4j库

结果:Hibernate通过SLF4J记录到Logback。 当然,您应该能够使用与Logback不同的日志实现。

为确保没有Log4j存在,请检查类路径或WAR文件的WEB-INF / lib中的库。

当然,您必须在logback.xml中设置记录器,例如:

<logger name="org.hibernate.SQL" level="TRACE"/>


我曾经遇到过这个问题。log4j是从另一个库作为传递依赖项引入的。将其排除掉后,使用logback和slf4j log4j桥接器时,hibernate日志记录开始按预期工作。 - Paul Zepernick

1

对于任何可能遇到我曾经遇到的问题的人。如果你已经尝试了这里解释的所有其他解决方案,但仍然看不到使用slf4j的hibernate日志记录,那么可能是因为你正在使用一个在其文件夹库中具有jboss-logging.jar的容器。这意味着在你甚至可以设置任何配置来影响它之前,它就被预加载了。 为了避免在Weblogic中出现这个问题,你可以在你的ear/META-INF文件夹下的weblogic-application.xml文件中指定优先使用从应用程序加载的库。其他服务器容器应该有类似的机制。 在我的情况下,我不得不添加:

<?xml version="1.0" encoding="UTF-8"?>
<wls:weblogic-application xmlns:wls="http://xmlns.oracle.com/weblogic/weblogic-application" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/javaee_5.xsd http://xmlns.oracle.com/weblogic/weblogic-application http://xmlns.oracle.com/weblogic/weblogic-application/1.5/weblogic-application.xsd">
   <wls:prefer-application-packages>    
       <!-- logging -->
       <wls:package-name>org.slf4j.*</wls:package-name>
       <wls:package-name>org.jboss.logging.*</wls:package-name>             
   </wls:prefer-application-packages>
   <wls:prefer-application-resources>
        <wls:resource-name>org/slf4j/impl/StaticLoggerBinder.class</wls:resource-name>
    </wls:prefer-application-resources>     
</wls:weblogic-application>

1

我在使用Hibernate 4和Weblogic 12c以及Log4j时遇到了问题。解决方法是将以下内容放入weblogic-application.xml文件中:

<prefer-application-packages>
    <package-name>org.apache.log4j.*</package-name>
    <package-name>org.jboss.logging.*</package-name>
</prefer-application-packages>

1

我使用Maven并添加了以下依赖项:

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.6.6</version>
</dependency>

接着,在 /src/main/resources 下创建了一个 log4j.properties 文件:

# direct log messages to stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
# set log levels
log4j.rootLogger=warn

这将把它放在您的.jar根目录下。它像魔法一样运行...


5
这配置了log4j的使用。原帖作者不想使用log4j,他们想使用slf4j。 - Raedwald

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接