System.out.println vs Loggers – System.out.println vs Loggers

最后修改: 2019年 10月 2日

中文/混合/英文(键盘快捷键:t)

1. Why Loggers?

1.为什么是记录仪?

While writing a program or developing an enterprise production application, using System.out.println seems to be the simplest and easiest option. There are no extra libraries to be added to the classpath and no additional configurations to be made.

在编写程序或开发企业生产应用时,使用System.out.println似乎是最简单和最容易的选择。没有额外的库需要添加到classpath中,也没有额外的配置需要做。

But using System.out.println comes with several disadvantages that affect its usability in many situations. In this tutorial, we’ll discuss why and when we’d want to use a Logger over plain old System.out and System.err. We’ll also show some quick examples using the Log4J2 logging framework.

但使用System.out.println有几个缺点,在很多情况下会影响其可用性。在本教程中,我们将讨论为什么以及何时要使用记录器而不是普通的System.outSystem.err。我们还将展示一些使用Log4J2日志框架的快速示例。

2. Setup

2.设置

Before we begin, let’s look into the Maven dependencies and configurations required.

在开始之前,让我们先了解一下所需的Maven依赖性和配置。

2.1. Maven Dependencies

2.1.Maven的依赖性

Let’s start by adding the Log4J2 dependency to our pom.xml:

让我们先把Log4J2的依赖关系添加到我们的pom.xml

<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-api</artifactId>
    <version>2.12.1</version>
</dependency>
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <version>2.12.1</version>
</dependency>

We can find the latest versions of log4j-api and log4j-core on Maven Central.

我们可以在Maven中心找到log4j-apilog4j-core的最新版本。

2.2. Log4J2 Configuration

2.2.Log4J2配置

The use of System.out doesn’t require any additional configuration. However, to use Log4J2, we need a log4j.xml configuration file:

使用System.out不需要任何额外的配置。然而,为了使用Log4J2,我们需要一个log4j.xml配置文件。

<Configuration status="debug" name="baeldung" packages="">
    <Appenders>
        <Console name="stdout" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} %p %m%n"/>
        </Console>
    </Appenders>
    <Root level="error">
        <AppenderRef ref="STDOUT"/>
    </Root>
</Configuration>

Almost all logger frameworks will require some level of configuration, either programmatically or through an external configuration file, such as the XML file shown here.

几乎所有的记录器框架都需要某种程度的配置,可以是以编程方式,也可以是通过外部配置文件,例如此处显示的XML文件。

3. Separating Log Output

3.分离日志输出

3.1. System.out and System.err

3.1.System.outSystem.err

When we deploy our application to a server like Tomcat, the server uses its own logger. If we use System.out, the logs end up in catalina.out. It’s much easier to debug our application if logs are put in a separate file. With Log4j2, we need to include a file appender in the configuration to save application logs in a separate file.

当我们把应用程序部署到Tomcat这样的服务器上时,服务器会使用它自己的日志器。如果我们使用System.out,日志最终会出现在catalina.out。如果将日志放在一个单独的文件中,调试我们的应用程序就容易多了。使用Log4j2,我们需要在配置中包含一个文件appender,以将应用程序的日志保存在一个单独的文件中。

Also, with System.out.println, there’s no control or filtering of which logs are to be printed. The only possible way to separate the logs is to use System.out.println for information logs and System.err.println for error logs:

此外,使用System.out.println,无法控制或过滤哪些日志要被打印。唯一可能分离日志的方法是使用System.out.println处理信息日志,使用System.err.println处理错误日志。

System.out.println("This is an informational message");
System.err.println("This is an error message");

3.2. Log4J2 Logging Levels

3.2.Log4J2的记录级别

In debug or development environments, we want to see all the information the application is printing. But in a live enterprise application, more logs means an increase in latency. Logger frameworks like Log4J2 provide multiple log level controls:

在调试或开发环境中,我们希望看到应用程序正在打印的所有信息。但在实时企业应用中,更多的日志意味着延迟的增加。Log4J2等日志框架提供了多种日志级别的控制:

  • FATAL
  • ERROR
  • WARN
  • INFO
  • DEBUG
  • TRACE
  • ALL

Using these levels, we can easily filter when and where to print what information:

利用这些级别,我们可以轻松地过滤何时何地打印什么信息

logger.trace("Trace log message");
logger.debug("Debug log message");
logger.info("Info log message");
logger.error("Error log message");
logger.warn("Warn log message");
logger.fatal("Fatal log message");

We may also configure the levels for each source code package individually. For more details on log level configuration, refer to our Java Logging article.

我们也可以为每个源代码包单独配置级别。关于日志级别配置的更多细节,请参考我们的Java Logging文章。

4. Writing Logs to Files

4.将日志写到文件中

4.1. Rerouting System.out and System.err

4.1.重新分配System.outSystem.err

It is possible to route System.out.println to a file using the System.setOut() method:

可以使用System.out.println方法将System.setOut() 路由到一个文件。

PrintStream outStream = new PrintStream(new File("outFile.txt"));
System.setOut(outStream);
System.out.println("This is a baeldung article");

And in case of System.err:

而在System.err的情况下。

PrintStream errStream = new PrintStream(new File("errFile.txt"));
System.setErr(errStream);
System.err.println("This is a baeldung article error");

When redirecting the output to a file using System.out or System.err, we can’t control the file size, thus the file keeps growing for the duration of the run of the application.

当使用System.outSystem.err将输出重定向到一个文件时,我们无法控制文件的大小,因此文件会在应用程序的运行期间不断增长。

As the file size grows, it might be difficult to open or analyze these bigger logs.

随着文件大小的增长,可能难以打开或分析这些较大的日志。

4.2. Logging to Files With Log4J2

4.2.使用Log4J2对文件进行记录

Log4J2 provides a mechanism to systematically write logs in files and also roll the files based on certain policies. For example, we can configure the files to be rolled over based on a date/time pattern:

Log4J2提供了一种机制,可以系统地在文件中写入日志,还可以根据某些策略滚动文件。例如,我们可以配置文件,使其根据日期/时间模式进行滚动

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
    <Appenders>
        <File name="fout" fileName="log4j/target/baeldung-log4j2.log"
          immediateFlush="false" append="false">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss} %p %m%n"/>
        </File>
    <Loggers>
        <AsyncRoot level="DEBUG">
            <AppenderRef ref="stdout"/>
            <AppenderRef ref="fout"/>
        </AsyncRoot>
    </Loggers>
</Configuration>

Or we can roll the files based on size once they reach a given threshold:

或者我们可以一旦文件达到一个给定的阈值,就根据大小来滚动文件

...
<RollingFile name="roll-by-size"
  fileName="target/log4j2/roll-by-size/app.log" filePattern="target/log4j2/roll-by-size/app.%i.log.gz"
  ignoreExceptions="false">
    <PatternLayout>
        <Pattern>%d{yyyy-MM-dd HH:mm:ss} %p %m%n</Pattern>
    </PatternLayout>
    <Policies>
        <OnStartupTriggeringPolicy/>
        <SizeBasedTriggeringPolicy size="5 KB"/>
    </Policies>
</RollingFile>

5. Logging to External Systems

5.登录到外部系统

As we’ve seen in the previous section, logger frameworks allow writing the logs to a file. Similarly, they also provide appenders to send logs to other systems and applications. This makes it possible to send logs to a Kafka Stream or an Elasticsearch database using Log4J appenders rather than using System.out.println.

正如我们在上一节所看到的,记录器框架允许将日志写入文件。同样地,它们也提供了appenders来发送日志到其他系统和应用程序。这使得使用Log4J应用程序将日志发送到Kafka流或Elasticsearch数据库成为可能,而不是使用System.out.println。

Please refer to our Log4j appender article for more details on how to use such appenders.

请参考我们的Log4j appender文章,以了解关于如何使用此类appenders的更多细节。

6. Customizing Log Output

6.自定义日志输出

With the use of Loggers, we can customize what information is to be printed along with the actual message. The information that we can print includes the package name, log level, line number, timestamp, method name, etc.

通过使用日志记录器,我们可以定制哪些信息将与实际信息一起被打印。我们可以打印的信息包括软件包名称、日志级别、行号、时间戳、方法名称等。

While this would be possible with System.out.println, it would require a lot of manual work, while logging frameworks provide this functionality out of the box. With loggers, we can simply define a pattern in the logger configuration:

虽然这可以通过System.out.println实现,但它需要大量的手工工作,而日志框架则开箱即提供了这一功能。有了记录器,我们可以简单地在记录器配置中定义一个模式

<Console name="ConsoleAppender" target="SYSTEM_OUT">
    <PatternLayout pattern="%style{%date{DEFAULT}}{yellow}
      %highlight{%-5level}{FATAL=bg_red, ERROR=red, WARN=yellow, INFO=green} %message"/>
</Console>

If we consider Log4J2 for our logger framework, there are several patterns that we can choose from or customize. Refer to the official Log4J2 documentation to learn more about them.

如果我们考虑将Log4J2作为我们的记录器框架,有几种模式我们可以选择或定制。请参考官方Log4J2文档以了解更多信息。

7. Avoid printStackTrace() by Logging Exception Output Instead

7.避免使用printStackTrace()来记录异常输出

When we handle exceptions in our code, we often need to learn what exceptions actually occurred at runtime. There are two common options for this: printStackTrace() or using a logger call.

当我们在代码中处理异常时,我们经常需要了解在运行时到底发生了什么异常。这方面有两个常见的选择。printStackTrace()或使用记录器调用。

It’s pretty common to see exception handling that uses printStackTrace() to print details about the exception:

使用printStackTrace()来打印异常细节的异常处理是非常常见的。

try {
    // some code
} catch (Exception e) {
    e.printStackTrace();
}

The problem here is that printStackTrace() prints its information to System.err, and we’ve already said we want to avoid that.

这里的问题是,printStackTrace()将其信息打印到System.err,而我们已经说过我们要避免这种情况。

Instead, we can log the exception using the logging framework, and then, we’ll be able to easily retrieve the logs:

相反,我们可以使用日志框架来记录异常,然后,我们就可以很容易地检索到日志了。

try {
    // some code
} catch (Exception e) {
    logger.log("Context message", e);
}

8. Conclusion

8.结语

This article explains various reasons why to use a logger framework and why not to rely only on System.out.println for our application logs. While it is justifiable to use System.out.println for small test programs, we’d prefer not to use it as our main source of logging for an enterprise production application.

这篇文章解释了使用日志框架的各种原因,以及为什么不能只依靠System.out.println来获得我们的应用日志。虽然在小型测试程序中使用System.out.println是合理的,但我们最好不要将其作为企业生产应用程序的主要日志来源。

As always, the code examples in the article are available over on GitHub.

一如既往,文章中的代码示例可在GitHub上获得超过