2.1 第一个记录器——log4j

如您所知,日志的历史始于System.err.println()向控制台输出一条记录。它仍然积极用于调试,例如,Intellij IDEA 使用它向控制台显示错误消息。但是这个选项没有任何设置,所以让我们继续。

第一个也是最受欢迎的记录器被称为Log4j. 这是一个很好的、高度可定制的解决方案。由于各种情况,这个决定没有进入 JDK,这极大地扰乱了整个社区。

这个记录器不仅能够记录,它是由程序员为程序员创建的,并允许他们解决与日志记录相关的不断出现的问题。

正如您已经知道的那样,日志是最后写入的,以便有人阅读它们并试图了解程序运行期间发生的事情 - 如预期的那样出错的原因和时间。

log4j为此有三件事:

  • 子包日志记录;
  • 一组附加程序(结果);
  • 热重载设置。

log4j首先,可以以这样一种方式编写设置,即在一个包中启用日志记录并在另一个包中禁用它。例如,可以在 中启用日志记录com.codegym.server,但在 中禁用它com.codegym.server.payment。这使得从日志中快速删除不必要的信息成为可能。

其次,log4j它允许一次将日志记录结果写入多个日志文件。每个输出都可以单独配置。例如,在一个文件中可以只写入有关严重错误的信息,在另一个文件中 - 来自特定模块的日志,在第三个文件中 - 特定时间的日志。

因此,每个日志文件都针对特定类型的预期问题进行了调整。这极大地简化了不喜欢手动查看千兆字节日志文件的程序员的生活。

最后,第三,log4j它允许在程序运行时直接更改日志设置,而无需重新启动。当需要更正日志的工作以查找有关特定错误的附加信息时,这非常方便。

重要的!日志有两个版本log4j1.2.x2.xx彼此不兼容

您可以使用代码将记录器连接到项目:

<dependencies>
  <dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.17.2</version>
  </dependency>

  <dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.17.2</version>
  </dependency>
</dependencies>

2.2 第一个官方记录器 - JUL:java.util.logging

在 Java 社区中出现大量记录器之后,开发人员JDK决定制作一个标准的记录器,供所有人使用。这就是记录器出现的方式JUL:包java.util.logging

然而,在其开发过程中,记录器的创建者不是基于log4j,而是 IBM 记录器的变体,这影响了它的发展。好消息是JUL包含了记录器JDK,坏消息是很少有人使用它。

七月

开发人员不仅JUL制定了“另一个通用标准”,还为其制定了自己的日志级别,这与当时流行的日志程序所接受的级别不同。

这是一个大问题。毕竟,产品Java通常是从大量图书馆收集而来的,而每个这样的图书馆都有自己的记录器。因此有必要配置应用程序中的所有记录器。

虽然记录器本身很不错。创建一个记录器或多或少是一样的。为此,您需要导入:


java.util.logging.Logger log = java.util.logging.Logger.getLogger(LoggingJul.class.getName());

类名是专门传递的,以便知道日志记录的来源。

只有发布,开发人员解决了重要问题,之后JUL使用起来才真正方便。在此之前,它是某种二流记录器。

此记录器还支持 lambda 表达式和惰性求值。从 开始Java 8,你可以通过Supplier<String>。这有助于仅在真正需要时才读取和创建字符串,而不是像以前那样每次都读取和创建。

带有参数的方法Supplier<String> msgSupplier如下所示:

public void info(Supplier msgSupplier) {
   log(Level.INFO, msgSupplier);
}

2.3 第一个记录器包装器——JCL:jakarta commons logging

很长一段时间,记录器之间没有单一的标准,它JUL应该成为一个,但更糟糕的是log4j,所以单一的标准从未出现过。但是出现了一个完整的伐木者动物园,每个伐木者都想变得一样。

JCL公司

然而,普通的 Java 开发人员并不喜欢几乎每个库都有自己的记录器,并且需要以某种特殊方式进行配置。因此,社区决定为其他记录器创建一个特殊的包装器——这就是JCL: jakarta commons logging

再一次,这个项目是作为一个领导者而创建的,但并没有成为一个领导者。你无法创造赢家,你只能成为赢家。功能JCL很差,没有人愿意使用它。旨在取代所有记录器的记录器遇到了同样的命运,因为它JUL没有被使用。

尽管它已被添加到 Apache 社区发布的许多库中,但记录器的数量却有增无减。

2.4 First last logger - Logback

但这还不是全部。开发人员log4j认为他是最聪明的(好吧,毕竟大多数人都使用他的记录器)并决定编写一个新的改进记录器,它将结合log4j其他记录器的优点。

新的记录器被称为Logback. 正是这个记录器本应成为未来每个人都会使用的单一记录器。它基于与log4j.

您可以使用以下代码将此记录器连接到项目:

<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.2.6</version>
</dependency>

不同之处在于Logback

  • 提高性能;
  • 添加本机支持slf4j
  • 扩展过滤选项。

这个记录器的另一个优点是它有很好的默认设置。只有当你想改变其中的某些东西时,你才必须配置记录器。此外,设置文件更适合企业软件 - 它的所有配置都设置为xml/.

默认情况下,Logback它不需要任何设置并记录该级别DEBUG及以上级别的所有日志。如果您需要不同的行为,可以通过xml配置进行配置:

<configuration>
    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
        <file>app.log</file>
        <encoder>
            <pattern>%d{HH:mm:ss,SSS} %-5p [%c] - %m%n</pattern>
        </encoder>
    </appender>
    <logger name="org.hibernate.SQL" level="DEBUG" />
    <logger name="org.hibernate.type.descriptor.sql" level="TRACE" />
    <root level="info">
        <appender-ref ref="FILE" />
    </root>
</configuration>

2.5 最新的通用记录器 - SLF4J: Simple Logging Facade for Java

多久才能找到中庸...

2006 年,其中一位创建者log4j离开了该项目,并决定再次尝试创建一个通用记录器。但这一次不是新的记录器,而是新的通用标准(包装器),可以让不同的记录器一起交互。

这个记录器被称为,它是, ,slf4j — Simple Logging Facade for Java的包装器。这个记录器解决了一个真正的问题——管理一个记录器动物园,所以每个人都立即开始使用它。log4jJULcommon-loggins and logback

我们英勇地解决我们为自己制造的问题。正如您所看到的,进展已经到了我们已经在包装器之上创建了一个包装器的地步......

包装本身由两部分组成:

  • API, 在应用程序中使用;
  • 作为每个记录器的单独依赖项添加的实现。

您可以使用代码将记录器连接到项目:

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.17.2</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.17.2</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-slf4j-impl</artifactId>
    <version>2.17.2</version>
</dependency>

连接正确的实现就足够了,仅此而已:整个项目都将使用它。

2.6 slf4j中的优化

Slf4j支持所有新功能,例如用于日志记录的字符串格式。在此之前就有这样的问题。假设您要在日志中打印一条消息:

log.debug("User " + user + " connected from " + request.getRemoteAddr());

这段代码有问题。假设您的应用程序正在运行production并且没有向日志写入任何内容DEBUG-messages,但是该方法log.debug()仍然会被调用,并且在调用它时,还会调用以下方法:

  • user.toString();
  • request.getRemoteAddr();

调用这些方法会降低应用程序的速度。只有在调试期间才需要调用它们,但无论如何都会调用它们。

从逻辑的角度来看,这个问题必须在日志库本身中解决。在 log4j 的第一个版本中,解决方案出现了:

if (log.isDebugEnabled()) {
    log.debug("User " + user + " connected from " + request.getRemoteAddr());
}

日志不再是一行,现在需要写三行。这极大地恶化了代码的可读性,并降低了log4j.

通过提供智能日志记录器,记录器slf4j能够稍微改善这种情况。它看起来像这样:

log.debug("User {} connected from {}", user, request.getRemoteAddr());

其中{}表示插入方法中传递的参数。也就是说,第一个{}对应于用户,第二个对应{}request.getRemoteAddr()

仅当日志记录级别允许记录时,这些参数才会连接成一条消息。不完美,但比所有其他选项都要好。

之后,SLF4J它开始迅速流行起来,目前这是最好的解决方案。

因此,我们将考虑使用 bundle 的示例进行日志记录slf4j-log4j12