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

Posted

技术标签:

【中文标题】如何在 Hibernate 4 中配置日志记录以使用 SLF4J【英文标题】:How do you configure logging in Hibernate 4 to use SLF4J 【发布时间】:2012-07-23 07:26:33 【问题描述】:

Hibernate 3.x 使用 slf4j 进行日志记录。 Hibernate 4.x 使用jboss-logging。我正在编写一个使用 Hibernate 4 和 SLF4J 进行日志记录的独立应用程序。

如何配置 Hibernate 以登录到 SLF4J?

如果这不可能,我该如何配置 Hibernate 的日志记录?

Hibernate 4.1 手册section on logging 以警告开头...

完全过时了。 Hibernate 从 4.0 开始使用 JBoss Logging。当我们将此内容迁移到开发者指南时,这将被记录下来。

...继续谈论 SLF4J,所以没用。 getting started guide 和 developer guide 都没有谈论日志记录。 migration guide 也没有。

我已经查找了有关 jboss-logging 本身的文档,但我根本找不到任何文档。 GitHub page is silent 和 JBoss 的 community projects page 甚至没有列出 jboss-logging。我想知道项目的bug tracker 是否有与提供文档相关的任何问题,但事实并非如此。

好消息是,当在 JBoss AS7 等应用服务器中使用 Hibernate 4 时,日志记录在很大程度上会为您处理好。但是如何在独立应用程序中配置它?

【问题讨论】:

+1 用于突出显示 Hibernate 日志记录文档已过时 可以设置系统属性org.jboss.logging.provide=slf4j。有关更多详细信息,请访问链接 docs.jboss.org/hibernate/orm/4.3/topical/html/logging/… 获取大于 3 的休眠版本。 【参考方案1】:

你试过了吗:

- slf4j-log4j12.jar 在 Log4J 的情况下。有关更多详细信息,请参阅 SLF4J 文档。要使用 Log4j,您还需要在类路径中放置一个 log4j.properties 文件。一个示例属性文件与 Hibernate 一起分发在 src/ 目录中

只需在类路径中添加这些 jar 和属性或 log4j xml

【讨论】:

这是来自 Hibernate 3.x 文档的引用。你认为这仍然适用于不使用 SLF4J 的 Hibernate 4.x 吗? 据我所知log4j就足够了【参考方案2】:

首先你确实意识到 SLF4J 不是一个日志库,它是一个日志包装器。它本身不记录任何东西,它只是委托给“后端”。

要“配置” jboss-logging,您只需在类路径中添加您想要使用的任何日志框架(以及 jboss-logging),其余部分由 jboss-logging 解决。

我创建了一个以 Hibernate 为中心的 JBoss 日志配置指南:http://docs.jboss.org/hibernate/orm/4.3/topical/html/logging/Logging.html

【讨论】:

我确实意识到 SLF4J 是一个门面,是的。将 Hibernate 日志发送到 SLF4J 意味着它会在我为应用程序的其余部分选择的任何后端结束,这就是我想要的。 那么您所说的配置是没有配置(这很好!),但是 jboss-logging 以某种方式检测并选择了一个后端?啊,现在我花时间实际查看代码,我看到那是exactly what happens。具体来说,jboss-logging 按顺序尝试 JBoss LogManager、log4j、通过 SLF4J 的 Logback 和 JDK 日志记录。但是这个可以org.jboss.logging.provider系统属性覆盖。 我们中的许多人都被 commons-logging 为您找出问题所困扰,因此准确了解 jboss-logging 的工作原理对于能够在意外发生时在现实世界中支持它至关重要。 上面的链接实际上显示了如果那是你真正想看到的会发生什么,所以不要关注......【参考方案3】:

我使用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=%dABSOLUTE %5p %c1:%L - %m%n
# set log levels
log4j.rootLogger=warn

这会将它放在您的.jar 的根目录下。它就像一个魅力......

【讨论】:

这配置了 log4j 的使用。 OP 不想使用 log4j;他们想使用 slf4j。【参考方案4】:

我在独立应用程序中使用 Hibernate Core 4.1.7.Final 和 Spring 3.1.2.RELEASE。我将 Log4j 1.2.17 添加到我的依赖项中,似乎 JBoss Logging 直接记录到 log4j(如果可用)和 Spring 使用 Commons Logging,女巫也使用 Log4j(如果可用),所有 Logging 都可以通过 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>

【讨论】:

【参考方案5】:

受Leif's Hypoport post 的启发,这就是我将 Hibernate 4 “弯曲”回 slf4j 的方式:

假设您使用的是 Maven。

org.slf4j:log4j-over-slf4j 添加为pom.xml 的依赖项 使用命令mvn dependency:tree,确保没有您正在使用的工件依赖于slf4j:slf4j(准确地说,任何工件都不应有编译范围依赖或 runtime 范围依赖于 slf4j:slf4j)

背景:Hibernate 4.x 依赖于工件 org.jboss.logging:jboss-logging。传递地,该工件具有对工件slf4j:slf4jprovided 范围依赖。

由于我们现在添加了 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>%dHH:mm:ss.SSS [%thread] %-5level %logger36 - %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)。

如果您仍然获得“原始”控制台标准输出 Hibernate 输出(如 Hibernate: select ...),则可能适用堆栈溢出问题“Turn off hibernate logging to console”。

【讨论】:

确保没有其他库包含 log4j,否则这将不起作用。示例:activemq-all.jar 包含 log4j。提示:打开 IDE 并在代码中轻松找到 log4j。 我在使用 JBoss Hibernate4 和(太)旧的服务器时遇到了这个问题。这篇文章,包括 application.properties 中的 1 行,对我有用。所以TNX!!!我属性中的最后一行写在另一个答案中:org.jboss.logging.provider=slf4j【参考方案6】:

要让 SLF4J 在不使用 Logback 作为后端的情况下使用 JBoss Logging,需要使用系统属性 org.jboss.logging.provider=slf4jlog4j-over-slf4j 策略在这种情况下似乎不起作用,因为如果类路径中实际上不存在 Logback 和 log4j,则日志记录将回退到 JDK。

这有点麻烦,为了让自动检测正常工作,你看到类加载器至少包含来自 logback-classic 的 ch.qos.logback.classic.Logger 或来自 log4j 的 org.apache.log4j.Hierarchy 以欺骗 JBoss 日志记录不回退到JDK 日志记录。

魔法在org.jboss.logging.LoggerProviders解释

更新:已添加服务加载程序支持,因此可以通过声明 META-INF/services/org.jboss.logging.LoggerProvider(以 org.jboss.logging.Slf4jLoggerProvider 作为值)来避免自动检测问题。似乎也增加了对 log4j2 的支持。

【讨论】:

我在哪里设置这个系统属性? 取决于您的设置,但通常命令行开关-Dorg.jboss.logging.provider=slf4j 就足够了。 LoggingProviders.java 让您更好地了解当前接受的值是什么以及类路径中应该出现什么。 我认为服务加载器方法不起作用,因为Slf4jLoggerProvider 不是public 类? 我需要将 weblogic WAR 中的 org.jboss.logging.provider 设置到源代码中,但是在 LoggingProviders 之后调用任何静态类初始化器!【参考方案7】:

所以,刚刚让它在我的项目中工作。休眠 4,slf4j,logback。我的项目是 gradle,但对于 maven 应该是一样的。

基本上阿卜杜勒是对的。他不对的地方是,您不必从依赖项中删除 slf4j。

    包含编译范围:

    org.slf4j:slf4j-api

    org.slf4j:log4j-over-slf4j

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

    从依赖项中完全排除 log4j 库

结果:通过 slf4j 将日志休眠到 logback。 当然,您应该能够使用与 logback 不同的日志实现

为确保不存在 log4j,请检查类路径或 web-inf/lib 上的库以获取战争文件。

当然,您已经在 logback.xml 中设置了记录器,例如:

&lt;logger name="org.hibernate.SQL" level="TRACE"/&gt;

【讨论】:

有这个确切的问题。 log4j 是作为另一个库的传递依赖项引入的。排除它,休眠日志开始使用 logback 和 slf4j log4j 桥按预期工作【参考方案8】:

看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,然后是 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>

一切正常!

UPDATE一些用户在非常主要的App.java中使用:

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

但对于基于容器的解决方案,这是行不通的。

更新 2 那些认为他们使用 SLF4J 为 jboss-logging 管理 Log4j 的人并非完全如此。 jboss-logging 直接用Log4j,不用SLF4J!

【讨论】:

在哪里设置org.jboss.logging.provider @SuzanCioc 根据System.getProperty(LOGGING_PROVIDER_KEY);,您需要设置系统属性。通过java -D...=... 或查看容器的文档。 您关于无法通过 slf4j 使用 log4j 的第二次更新很有帮助。将 org.jboss.logging.provider 设置为 slf4j 让我觉得我对 log4j 的支持会起作用。但事实并非如此。我必须将其直接设置为 log4j 才能使其正常工作。奇怪的。那么 slf4j 作为这个配置的一个选项有什么意义呢?【参考方案9】:

Hibernate 4.3有some documentation关于如何控制org.jboss.logging

它在类路径中搜索 日志记录提供程序。它在搜索 log4j 之后搜索 slf4j。因此,理论上,确保您的类路径 (WAR) 不包含 log4j 并且包含 slf4j API,并且后端应该可以工作。

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


尽管文档声称,org.jboss.logging 坚持尝试使用 log4j,尽管 log4j 不存在且 SLF4J 存在,导致我的 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 桥。

【讨论】:

【参考方案10】:

我在将 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>

【讨论】:

【参考方案11】:

致任何可能面临与我相同问题的人。如果您尝试了这里解释的所有其他解决方案,但仍然没有看到休眠日志记录与您的 slf4j 一起工作,这可能是因为您使用的容器在他的文件夹库中包含 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>

【讨论】:

以上是关于如何在 Hibernate 4 中配置日志记录以使用 SLF4J的主要内容,如果未能解决你的问题,请参考以下文章

使用 Log4j XML 配置文件配置 Hibernate 日志记录?

Hibernate入门

Hibernate统计日志记录

如何在 JBoss Wildfly 9 中关闭 Hibernate 调试日志记录?

如何将 BIRT 日志记录集成到应用程序日志记录中?

记录日志系统ELKB 5.6.4的搭建过程