JavaRush /Java 博客 /Random-ZH /Java 日志记录:什么、如何、在哪里以及使用什么?
Roman Beekeeper
第 35 级

Java 日志记录:什么、如何、在哪里以及使用什么?

已在 Random-ZH 群组中发布
大家好,JavaRush 社区!今天我们来谈谈Java日志记录:
  1. 这是什么,为什么会这样。什么情况下最好用,什么情况下不宜用?
  2. Java 中有哪些类型的日志记录实现以及我们应该如何利用这种多样性?
  3. 记录级别。让我们讨论一下什么是appender以及如何正确配置它。
  4. 记录节点以及如何正确配置它们,以便一切按照我们想要的方式运行。
本材料面向广大受众。对于那些刚刚熟悉 Java 的人以及那些已经在工作但只是通过logger.info(“log something”); Let's Go!才弄清楚的人来说,这一点都会很清楚。

为什么需要日志记录?

让我们看一下日志记录可以解决问题的真实案例。这是我工作中的一个例子。有一些与其他服务集成的应用程序点。我使用记录这些点作为“不在场证据”:如果集成不起作用,很容易找出问题源自哪一方。还建议记录保存到数据库的重要信息。例如,创建管理员用户。这正是记录的好地方。

Java 日志工具

日志记录:什么、如何、在哪里以及用什么? - 2众所周知的 Java 日志记录解决方案包括:
  • 日志4j
  • 七月 - java.util.logging
  • JCL - 雅加达公共日志记录
  • 回溯
  • SLF4J - java 的简单日志记录门面
让我们快速浏览一下它们中的每一个,在材料的实际部分中,我们将以Slf4j - log4j 的连接作为基础。现在这可能看起来很奇怪,但别担心:在文章结束时一切都会清楚。

系统错误.println

当然,最初有System.err.println(将输出记录到控制台)。仍然是在调试时用来快速获取日志。当然,这里不需要谈论任何设置,所以我们记住它并继续。

Log4j

这已经是一个成熟的解决方案,是根据开发人员的需求创建的。事实证明这是一个非常有趣的工具。由于各种情况,这个解决方案从未进入 JDK,这让整个社区感到非常沮丧。log4j 具有配置选项,以便可以在包中打开日志记录com.example.type并在子包中关闭日志记录com.example.type.generic。这使得快速区分需要记录的内容和不需要的内容成为可能。这里需要注意的是,log4j有两个版本:1.2.x和2.x.x,它们彼此不兼容log4j增加了appender这样一个概念,即记录日志和布局日志格式化的工具。这使您可以仅记录您需要的内容以及您需要的方式。稍后我们将详细讨论appender。

七月 - java.util.logging

主要优点之一是解决方案 - JUL 包含在 JDK(Java 开发工具包)中。不幸的是,在它的开发过程中,并不是以流行的log4j为基础,而是IBM的解决方案,影响了它的发展。事实上,目前有JUL,但没有人使用它。从“马马虎虎”来看:JUL 中的日志记录级别与 Logback、Log4j、Slf4j 中的日志级别不同,这使得它们之间的理解变得更加糟糕。创建记录器或多或少相似。为此,您需要导入:
java.util.logging.Logger log = java.util.logging.Logger.getLogger(LoggingJul.class.getName());
专门传递类名是为了了解日志记录来自哪里。从 Java 8 开始,可以通过Supplier<String>. 这有助于仅在真正需要时才计算和创建字符串,而不是像以前那样每次都进行计数和创建。直到Java 8的发布,开发人员才解决了重要问题,JUL才真正可用。即带有参数的方法Supplier<String> msgSupplier如下所示:
public void info(Supplier<String> msgSupplier) {
   log(Level.INFO, msgSupplier);
}

JCL - 雅加达公共日志记录

由于很长一段时间以来日志记录都没有行业标准,并且有一段时间许多人创建了自己的自定义记录器,因此他们决定发布 JCL - 一种可以在其他日志记录器上使用的通用包装器。为什么?当某些依赖项添加到项目中时,它们可以使用与项目上的记录器不同的记录器。因此,它们被传递到项目中,这在尝试将它们放在一起时产生了真正的问题。不幸的是,该包装器的功能非常差,并且没有引入任何附加功能。如果每个人都使用 JCL 来完成他们的工作,那可能会很方便。但实际上情况并非如此,因此目前使用 JCL 并不是一个好主意。

回溯

开源之路是多么的荆棘啊……Logback 是由与 log4j 相同的开发者编写的,旨在创建它的后继者。这个想法与 log4j 相同。差异在于 logback:
  • 提高性能;
  • 添加了对 slf4j 的原生支持;
  • 过滤选项已扩展。
默认情况下,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>

SLF4J - java 的简单日志记录门面

2006 年左右,log4j 的创始人之一离开了该项目并创建了 slf4j - Simple Logging Facade for Java - log4j、JUL、common-loggins 和 logback 的包装器。正如你所看到的,进展已经达到了他们在包装器之上创建了一个包装器的地步......此外,它分为两部分:应用程序中使用的 API 和实现,添加为每种类型的日志记录都有单独的依赖关系。例如,slf4j-log4j12.jarslf4j-jdk14.jar。连接正确的实现就足够了:整个项目都将与它一起工作。Slf4j 支持所有新功能,例如日志记录的字符串格式。之前也有这样的问题。假设有一个日志条目:
log.debug("User " + user + " connected from " + request.getRemoteAddr());
由于字符串连接, 对象中user存在隐式转换,这需要时间,从而减慢系统速度。user.toString()如果我们调试应用程序,一切都会正常。如果此类的日志记录级别为 INFO 或更高,就会出现问题。即这条日志不应该被写下来,也不应该进行字符串拼接。理论上,这应该是由日志库本身决定的。而且,这竟然是log4j第一个版本的最大问题。他们没有提供正常的解决方案,但建议这样做:
if (log.isDebugEnabled()) {
    log.debug("User " + user + " connected from " + request.getRemoteAddr());
}
也就是说,他们建议编写 3(!),而不是一行日志。日志记录应该最大限度地减少对代码的更改,三行显然与一般方法相矛盾。slf4j 与 JDK 和 API 没有兼容性问题,因此一个漂亮的解决方案立即出现:
log.debug("User {} connected from {}", user, request.getRemoteAddr());
其中{}表示插入在方法中传递的参数。也就是说,第一个{}对应于user,第二个对应于{}- request.getRemoteAddr()。因此,只有当日志记录级别允许记录时,该消息才能串联成一条消息。此后,SJF4J迅速流行起来,是目前最好的解决方案。因此,我们将考虑使用bundle 的示例进行日志记录slf4j-log4j12

需要记录什么

当然,您不应该记录所有内容。有时这是不必要的,甚至是危险的。例如,如果你承诺某人的个人数据,但不知何故被曝光,就会出现真正的问题,特别是在面向西方的项目上。但还有一些事情是必须记录的
  1. 应用程序的开始/结束。我们需要知道应用程序实际上按我们的预期启动并按预期结束。
  2. 安全问题。在这里最好记录密码猜测尝试、记录重要用户的登录等。
  3. 一些应用程序声明。例如,业务流程中从一种状态转换到另一种状态。
  4. 一些用于调试的信息,以及适当级别的日志记录。
  5. 一些 SQL 脚本。确实有需要这样做的案例。再次,通过巧妙地调整级别,可以取得优异的效果。
  6. 在检查正确操作的情况下,可以记录执行的线程(Thread) 。

常见的日志记录错误

有很多细微差别,但以下是一些常见错误:
  1. 过多的日志记录。您不应该记录理论上可能重要的每一个步骤。有一个规则:日志对性能的负载不能超过10%。否则会出现性能问题。
  2. 将所有数据记录到一个文件中。这将使读/写在某个时刻变得非常困难,更不用说某些系统上有文件大小限制。
  3. 使用不正确的日志记录级别。每个日志记录级别都有明确的界限,应该受到尊重。如果边界模糊,您可以商定使用哪个级别。

记录级别

x:可见
致命的 错误 警告 信息 调试 痕迹 全部
离开
致命的 X
错误 X X
警告 X X X
信息 X X X X
调试 X X X X X
痕迹 X X X X X X
全部 X X X X X X X
什么是日志记录级别?为了以某种方式对日志进行排名,有必要给出某些名称和区别。为此,引入了日志记录级别。级别在应用程序中设置。如果条目属于指定级别以下的级别,则不会将其输入日志中。例如,我们有用于调试应用程序的日志。在正常的生产工作中(当应用程序用于其预期目的时),不需要此类日志。因此,日志记录的级别将高于调试级别。让我们以 log4j 为例来看看级别。除 JUL 之外的其他解决方案都使用相同的级别。这里它们按降序排列:
  • OFF:不写入日志,全部将被忽略;
  • FATAL:应用程序将无法再工作并停止的错误,例如 JVM 内存不足错误;
  • ERROR:出现需要解决的问题时的错误率。该错误不会使整个应用程序停止。其他查询可能会正常工作;
  • WARN:表示包含警告的日志。尽管系统拒绝并完成了请求,但发生了意外的操作;
  • INFO:记录应用程序中重要操作的日志。这些不是错误,这些不是警告,这些是系统的预期操作;
  • DEBUG:调试应用程序所需的日志。确保系统完全按照预期执行,或描述系统的操作:“method1开始工作”;
  • TRACE:用于调试的较低优先级日志,具有最低的日志记录级别;
  • ALL:记录系统所有日志的级别。
事实证明,如果在应用程序中的某个位置启用了 INFO 日志记录级别,则所有级别都将被记录,从 INFO 开始一直到 FATAL。如果日志级别为FATAL,则仅记录该级别的日志。

记录和发送日志:Appender

我们将使用 log4j 作为示例来考虑此过程:它提供了充足的记录/发送日志的机会:
  • 用于写入文件 - 解决方案DailyRollingFileAppender
  • 将数据接收到应用程序控制台 - ConsoleAppender
  • 将日志写入数据库 - JDBCAppender
  • 通过 TCP/IP 控制传输 - TelnetAppender
  • 确保日志记录不会影响性能 - AsyncAppender
还有其他几种实现:可以在此处找到完整列表。顺便说一句,如果所需的附加程序不可用,这不是问题。您可以通过实现Appender接口来编写自己的appender ,该接口只接受log4j。

记录节点

为了进行演示,我们将使用 slf4j 接口和 log4j 的实现。创建记录器非常简单:您需要在名为 的类中编写以下内容MainDemo,在其中完成日志记录:
org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(MainDemo.class);
这将为我们创建一个记录器。要创建日志条目,您可以使用多种方法来指示将在哪个级别创建条目。例如:
logger.trace("Method 1 started with argument={}", argument);
logger.debug("Database updated with script = {}", script);
logger.info("Application has started on port = {}", port);
logger.warn("Log4j didn't find log4j.properties. Please, provide them");
logger.error("Connection refused to host = {}", host);
尽管我们传递的是类,但最终写下的是类和包的全名。这样做是为了让您可以将日志记录划分为节点,并为每个节点配置日志记录级别和附加程序。例如,类的名称:com.github.romankh3.logginglecture.MainDemo- 在其中创建了一个记录器。这就是它可以被划分为日志节点的方式。主节点是 null RootLogger。这是接收整个应用程序所有日志的节点。其余部分可以如下所示: 日志记录:什么、如何、在哪里以及用什么? - 4Appender 专门在日志节点上配置其工作。现在,以 log4j.properties为例,我们将了解如何配置它们。

逐步配置Log4j.properties

现在我们将逐步设置一切,看看可以做什么:
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
这一行表示我们正在注册一个使用 org.apache.log4j.ConsoleAppender 实现的 CONSOLE 附加程序。该附加程序将数据写入控制台。接下来,让我们注册另一个将写入文件的附加程序:
log4j.appender.FILE=org.apache.log4j.RollingFileAppender
值得注意的是,附加程序仍然需要配置。一旦我们已经注册了附加程序,我们就可以确定节点中的日志记录级别以及将使用哪些附加程序。

log4j.rootLogger=调试、控制台、文件

  • log4j.rootLogger 表示我们将配置主节点,其中包含所有日志;
  • 等号后面的第一个单词表示日志将记录在哪个级别及更高级别(在我们的例子中,这是 DEBUG);
  • 然后在逗号之后指示将使用的所有附加程序。
要配置特定的日志记录节点,您需要使用以下条目:
log4j.logger.com.github.romankh3.logginglecture=TRACE, OWN, CONSOLE
它用于log4j.logger.配置特定节点,在我们的例子中它是com.github.romankh3.logginglecture. 现在让我们讨论设置 CONSOLE 附加程序:
# CONSOLE appender customisation
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.CONSOLE.threshold=DEBUG
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern=[%-5p] : %c:%L : %m%n
在这里我们看到我们可以设置附加程序处理的级别。真实情况:日志节点收到一条 info 级别的消息,并将其传递给分配给它的appender,但是 warn 级别及更高级别的appender 接受了该日志,但没有对其执行任何操作。接下来,您需要决定消息中使用什么模板。我在示例中使用 PatternLayout,但有很多解决方案。它们不会在本文中披露。设置 FILE 附加程序的示例:
# File appender customisation
log4j.appender.FILE=org.apache.log4j.RollingFileAppender
log4j.appender.FILE.File=./target/logging/logging.log
log4j.appender.FILE.MaxFileSize=1MB
log4j.appender.FILE.threshold=DEBUG
log4j.appender.FILE.MaxBackupIndex=2
log4j.appender.FILE.layout=org.apache.log4j.PatternLayout
log4j.appender.FILE.layout.ConversionPattern=[ %-5p] - %c:%L - %m%n
在这里您可以配置日志将写入哪个文件,如下所示
log4j.appender.FILE.File=./target/logging/logging.log
录音进入文件logging.log。为了避免文件大小出现问题,您可以设置最大值:在本例中为 1MB。MaxBackupIndex - 告诉有多少个这样的文件。如果创建的文件数量超过此数量,则第一个文件将被删除。要查看配置日志记录的真实示例,您可以访问GitHub 上的 开放存储库。

我们来合并一下结果

尝试做你自己描述的一切:
  • 创建您自己的项目,类似于上例中的项目。
  • 如果您了解使用 Maven,我们将使用它;如果没有,那么这里有一篇描述如何连接该库的文章的链接。

让我们总结一下

  1. 我们讨论了Java有哪些解决方案。
  2. 几乎所有已知的日志库都是在一个人的控制下编写的:D
  3. 我们了解了哪些内容需要记录,哪些内容不需要记录。
  4. 我们计算出了日志记录级别。
  5. 我们熟悉了日志节点。
  6. 我们了解了附加程序是什么以及它的用途。
  7. 我们一步步配置了log4j.proterties文件。

附加材料

  1. JavaRush:日志记录。展开 strace 球
  2. JavaRush:记录器讲座
  3. Habr:Java 日志记录。你好世界
  4. Habr:Java 日志记录:噩梦的故事
  5. Youtube:戈洛瓦赫课程。记录。第 1 部分第 2 部分第 3 部分第 4 部分
  6. Log4j:附加程序
  7. Log4j:布局
另请参阅我的其他文章:
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION