CodeGym /Java 博客 /随机的 /日志记录:什么、如何、在哪里以及用什么?
John Squirrels
第 41 级
San Francisco

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

已在 随机的 群组中发布
CodeGym 社区的大家好! 日志记录:什么、如何、在哪里以及用什么? - 1 今天我们来谈谈日志记录:
  1. 它是什么,为什么存在,什么时候应该使用它,什么时候应该避免它。
  2. Java 中有哪些日志记录实现可用,以及您应该如何使用所有这些日志记录选项。
  3. 和日志级别。我们将讨论什么是 appender 以及如何正确配置它。
  4. 记录节点以及如何正确配置它们,以便一切都按我们想要的方式工作。
本材料适用于广泛的受众。任何刚开始了解 Java 的人,以及已经在工作但只探索过的人都会清楚logger.info("log something"); Let's go!

为什么需要日志记录?

让我们看一些日志可以解决问题的真实案例。这是我工作中的一个例子。在某些点上,应用程序可以与其他服务集成。我在这些点上使用日志记录来建立一种“不在场证明”:如果集成不起作用,那么很容易找出哪一方有问题。还希望记录存储在数据库中的重要信息。例如,创建管理员用户。这正是那种适合记录的事情。

Java 中的日志记录工具

在 Java 中著名的日志记录解决方案中,我们可以强调以下内容:
  • 日志4j
  • JUL — java.util.logging
  • JCL——雅加达公共日志记录
  • 登录
  • SLF4J — Java 的简单日志外观
我们将对它们中的每一个进行概述。然后我们将以slf4j - log4j绑定作为实际讨论的基础。这现在可能看起来很奇怪,但不要担心:到本文结尾时,一切都会一目了然。

系统.err.println

一开始,有System.err.println(在控制台上显示日志条目)。即使在今天,这种技术也被用来在调试时快速记录日志。当然,这里没有设置可以讨论,所以只要记住这个方法,我们就可以继续了。

日志4j

这是开发人员出于需要创建的完整解决方案。结果是您可以使用一个非常有趣的工具。由于各种情况,这个解决方案最终没有出现在 JDK 中,这让整个社区都非常不满。Log4j 具有配置选项,可让您在包中启用日志记录com.example.type并在子包中将其关闭com.example.type.generic。这使得快速排除不需要记录的代码成为可能。这里需要注意的是,Log4j有两个版本:1.2.x和2.xx,并且相互不兼容Log4j 添加了appender的概念(用于编写日志的工具)和布局(日志格式化)。这使您可以只记录您需要的内容,并根据需要记录它。我们稍后会详细讨论 appender。

JUL — 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 并不是最好的主意。

登录

开源之路充满荆棘……编写 Log4j 的同一位开发人员还编写了 Logback 作为后继日志记录框架。它基于与 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(Java 简单日志外观),这是 Log4j、JUL、通用日志记录和 Logback 的包装器。如您所见,我们已经发展到在包装器之上创建包装器的地步......在这种情况下,它分为两部分:在应用程序中使用的 API,以及添加单独的实现每种类型的日志记录的依赖关系。例如,slf4j-log4j12.jarslf4j-jdk14.jar。您需要连接正确的实现,仅此而已:您的整个项目都将使用它。Slf4j 支持所有最新功能,例如用于日志记录的格式化字符串。以前,有这样的问题。假设我们创建了一个这样的日志条目:

log.debug("User " + user + " connected from " + request.getRemoteAddr());
由于连接运算符,user对象默默地变成了一个字符串,这要归功于user.toString(). 这需要时间并减慢系统速度。如果我们正在调试应用程序,那可能没问题。如果此类的日志级别为 INFO 或更高,我们就会开始遇到问题。换句话说,我们不应该写这个日志条目(对于 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 开始迅速流行起来。目前,这是最好的解决方案。因此,让我们看一下使用绑定进行日志记录slf4j-log4j12

需要记录什么

当然,您不应该记录所有内容。这通常是不必要的,有时甚至是危险的。例如,如果您记录某人的个人数据,但不知何故被泄露,就会出现真正的问题,尤其是在专注于西方市场的项目中。但也有一些你绝对应该记录的东西
  1. 应用程序的开始/结束。我们需要知道应用程序是否真的按预期开始和结束。
  2. 安全问题。在这里最好记录猜测某人密码的尝试、管理员登录时的实例等。
  3. 某些应用程序状态。例如,业务流程中从一种状态到另一种状态的转换。
  4. 某些调试信息以及相应的日志级别。
  5. 某些 SQL 脚本。当这是必要的时候,有现实世界的案例。但同样,通过巧妙地调整日志级别,您可以获得出色的效果。
  6. 当验证事情是否正常工作时,可以记录正在运行的线程。

日志记录中的常见错误

这里有很多细微差别,但我们会特别提到一些常见错误:
  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:表示警告的日志条目。发生了意外情况,但系统能够应对并完成了请求
  • 信息:指示应用程序中重要操作的日志条目。这些不是错误或警告。它们是预期的系统事件。
  • DEBUG:日志条目需要调试应用程序。用于确保应用程序完全符合预期,或用于描述应用程序采取的操作,即“进入方法 1”。
  • TRACE:用于调试的低优先级日志条目。最低日志级别。
  • ALL:写入所有应用程序日志条目的日志级别。
在应用程序的某处启用了 INFO 日志级别,然后将记录每个级别的条目,从 INFO 到 FATAL。如果设置了 FATAL 日志级别,则只会写入具有该级别的日志条目。

记录和发送日志:Appender

让我们考虑一下当我们使用 Log4j 时这一切是如何工作的,它为写入/发送日志提供了充足的机会:
  • 写入文件——DailyRollingFileAppender
  • 将信息写入控制台 —ConsoleAppender
  • 将日志写入数据库——JDBCAppender
  • 管理通过 TCP/IP 发送日志 —TelnetAppender
  • 确保日志记录不会对性能产生负面影响——AsyncAppender
还有一些实现:完整列表可在此处获得。顺便说一句,如果您需要的附加程序不存在,那也没问题。您可以通过实现Log4j 支持的 Appender接口来编写自己的 appender 。

记录节点

出于演示目的,我们将使用 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 the log4j.properties file. Please fix this.");
logger.error("Connection refused to host = {}", host);
虽然我们传递的是类,但最终的名称是类的全名,包括包。这样做是为了稍后您可以将日志记录划分为节点并为每个节点配置日志记录级别和附加程序。例如,记录器是在com.github.romankh3.logginglecture.MainDemo类中创建的。该名称为创建日志节点层次结构提供了基础。主节点是顶级RootLogger。这是接收整个应用程序的所有日志条目的节点。其余节点可以如下图所示: 日志记录:什么、如何、在哪里以及用什么? - 3Appenders 是为特定的日志节点配置的。现在我们将查看log4j.properties文件以查看如何配置它们的示例。

log4j.properties 文件的分步指南

我们将一次一步地设置所有内容,看看有什么可能:

log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
这一行表示我们正在注册 CONSOLE appender,它使用 org.apache.log4j.ConsoleAppender 实现。此附加程序将信息写入控制台。接下来,我们注册另一个附加程序。这将写入一个文件:

log4j.appender.FILE=org.apache.log4j.RollingFileAppender
重要的是要注意附加程序本身仍然需要配置。一旦我们注册了我们的附加程序,我们就可以确定哪些日志级别和哪些附加程序将在节点上使用。

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

  • log4j.rootLogger 表示我们正在配置根节点,其中包含所有日志条目
  • 等号后的第一个词表示要写入的最低日志级别(在我们的例子中,它是 DEBUG)
  • 在逗号之后,我们指示要使用的所有附加程序。
要配置更具体的日志记录节点,您可以使用如下条目:

log4j.logger.com.github.romankh3.logginglecture=TRACE, OWN, CONSOLE
wherelog4j.logger.用于引用特定节点。在我们的例子中,com.github.romankh3.logginglecture. 现在让我们谈谈配置 CONSOLE appender:

# CONSOLE appender customization
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
在这里我们看到可以设置 appender 开始工作的特定级别。下面是一个实际发生的例子:假设一个 INFO 级别的消息被日志节点接收并传递给分配给它的附加程序。如果 appender 的阈值设置为 WARN,那么它会收到日志条目但不对其执行任何操作。接下来,我们需要决定消息将使用哪种布局。我在示例中使用了 PatternLayout,但还有许多其他选项。我们不会在本文中介绍它们。配置 FILE appender 的示例:

# File appender customization
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. 我们研究了 appender 是什么以及它的用途。
  7. 我们一步步创建了一个log4j.proterties文件。

附加材料

  1. CodeGym:记录器课程
  2. 每周 Geekly:Java 日志记录。你好世界
  3. 编码恐怖:日志记录的问题
  4. YouTube:了解 Java Logging Hell - 基础知识。Java 日志地狱以及如何远离它
  5. Log4j:追加器
  6. Log4j:布局
另见我的另一篇文章:
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION