2.1 第一个记录器——log4j
如您所知,日志的历史始于System.err.println()
向控制台输出一条记录。它仍然积极用于调试,例如,Intellij IDEA 使用它向控制台显示错误消息。但是这个选项没有任何设置,所以让我们继续。
第一个也是最受欢迎的记录器被称为Log4j
. 这是一个很好的、高度可定制的解决方案。由于各种情况,这个决定没有进入 JDK,这极大地扰乱了整个社区。
这个记录器不仅能够记录,它是由程序员为程序员创建的,并允许他们解决与日志记录相关的不断出现的问题。
正如您已经知道的那样,日志是最后写入的,以便有人阅读它们并试图了解程序运行期间发生的事情 - 如预期的那样出错的原因和时间。
log4j
为此有三件事:
- 子包日志记录;
- 一组附加程序(结果);
- 热重载设置。
log4j
首先,可以以这样一种方式编写设置,即在一个包中启用日志记录并在另一个包中禁用它。例如,可以在 中启用日志记录com.codegym.server
,但在 中禁用它com.codegym.server.payment
。这使得从日志中快速删除不必要的信息成为可能。
其次,log4j
它允许一次将日志记录结果写入多个日志文件。每个输出都可以单独配置。例如,在一个文件中可以只写入有关严重错误的信息,在另一个文件中 - 来自特定模块的日志,在第三个文件中 - 特定时间的日志。
因此,每个日志文件都针对特定类型的预期问题进行了调整。这极大地简化了不喜欢手动查看千兆字节日志文件的程序员的生活。
最后,第三,log4j
它允许在程序运行时直接更改日志设置,而无需重新启动。当需要更正日志的工作以查找有关特定错误的附加信息时,这非常方便。
重要的!日志有两个版本log4j
:1.2.x和2.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
,所以单一的标准从未出现过。但是出现了一个完整的伐木者动物园,每个伐木者都想变得一样。
然而,普通的 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
的包装器。这个记录器解决了一个真正的问题——管理一个记录器动物园,所以每个人都立即开始使用它。log4j
JUL
common-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
。
GO TO FULL VERSION