- What is logging? 什么是日志记录?
- What should a logging framework do?日志记录框架应该做什么?
- Log Levels and Subsystem Classification日志级别和子系统分类
- Where to put logs?把原木放在哪里?
- Transparent Logging 透明日志记录
- How to do proper logging如何进行正确的日志记录
- Other things to worry about when logging记录时需要注意的其他事项
- Features that the Apache Logging Services projects haveApache 日志记录服务项目具有的功能
- Hierarchichal Logging Hierarchichal 日志记录
- Location Information 位置信息
- Static and Single-instance Loggers静态和单实例记录器
- Nested and Mapped Diagnostic Contexts嵌套和映射的诊断上下文
- Flexible Log Message Formatting灵活的日志消息格式
- Configuration via config file通过配置文件进行配置
- Garbage-free(Log4j2) 无垃圾(Log4j2)
- Formatting with replacements使用替换进行格式化
- Filtering 滤波
- Conclusion 结论
What is logging? 什么是日志记录?
At a high level, logging is used to capture information during the run of an application. This can include different types of information about both the state of the application and inputs to the application. For example, an HTTP server may log information about incoming connections(such as an IP address).
概括地说,日志记录用于在应用程序运行期间捕获信息。这可以包括有关应用程序状态和应用程序输入的不同类型的信息。例如,HTTP 服务器可能会记录有关传入连接(如 IP 地址)的信息。
Logging can also be at different levels of verbosity, in order to allow a user of the application to not be overwhelmed with too much information.
日志记录也可以处于不同的详细程度,以便应用程序的用户不会被太多信息淹没。
Logging can be broadly classified into two categories. The two main categories of logging are debug logging and audit logging. Debug logging is generally useful for application developers who can turn on messages that an end-user does not care about - for example, if data is being parsed correctly, or if an exception is thrown. Audit logging is more interesting to system administrators. This type of data would include information such as when somebody logged in, who changed a piece of information, etc. Debug logging and audit logging are not mutually exclusive, and there can be plenty of overlap between these two categories.
日志记录大致可分为两类。日志记录的两个主要类别是调试日志记录和审核日志记录。调试日志记录通常对应用程序开发人员很有用,他们可以打开最终用户不关心的消息 - 例如,如果数据被正确解析,或者是否引发异常。审核日志记录对系统管理员来说更有趣。这种类型的数据将包括诸如某人何时登录、谁更改了一条信息等信息。调试日志记录和审核日志记录并不相互排斥,这两个类别之间可能有很多重叠。
What should a logging framework do?
日志记录框架应该做什么?
At a minimum, a logging framework needs to be able to do the following:
日志记录框架至少需要能够执行以下操作:
- Easily allow you to create log messages
轻松创建日志消息 - Send log messages to stdout and/or stderr
将日志消息发送到 stdout 和/或 stderr - Be completely transparent to your application - your application should behave exactly the same no matter if logging is enabled or disabled
对应用程序完全透明 - 无论启用还是禁用日志记录,应用程序的行为都应完全相同
However, a logging framework that does only these things is not particularly useful, and no better than using System.out.println()
or printf()
. Common features of logging frameworks generally include:
但是,只做这些事情的日志框架并不是特别有用,也不比使用 System.out.println()
或 printf()
更好。日志记录框架的常见功能通常包括:
- Ability to send log messages to other places than just stdout/stderr. For example, common places to send logs could be:
能够将日志消息发送到其他位置,而不仅仅是 stdout/stderr。例如,发送日志的常见位置可能是:- syslog 系统日志
- A file on the local filesystem
本地文件系统上的文件 - Remote log server(e.g. ELK, Splunk, kibana, etc.)
远程日志服务器(如 ELK、Splunk、kibana 等) - Windows event log Windows 事件日志
- Database 数据库
- Filtering based off of levels and data
基于级别和数据进行筛选 - Location information of where the log statement is in the code
日志语句在代码中的位置信息 - Allow for rotation of files when logging to a file. This allows you to do things such as creating a new log whenever you start up, doing one log file per day, limiting files to a specific size, etc.
允许在记录到文件时轮换文件。这允许您执行某些操作,例如在启动时创建新日志、每天执行一个日志文件、将文件限制为特定大小等。 - Log to multiple locations at once(send the same data to stdout and a file for example)
一次记录到多个位置(例如,将相同的数据发送到 stdout 和文件) - Classify messages based off of the particular subsystem that they are from
根据消息来自的特定子系统对消息进行分类 - Format log messages according to some user-defined pattern
根据某些用户定义的模式设置日志消息的格式
This is not an exhaustive list of all features that could exist. There are many different logging frameworks that exist which may implement different subsets of these features, and may also have more features than are listed here.
这并不是可能存在的所有功能的详尽列表。存在许多不同的日志记录框架,它们可能实现这些功能的不同子集,并且还可能具有比此处列出的更多的功能。
Log Levels and Subsystem Classification
日志级别和子系统分类
When you are logging information, two of the most important things to know are the level of the log message and the subsystem that the log message is from.
在记录信息时,需要了解的最重要的两件事是日志消息的级别和日志消息来自的子系统。
This allows you to determine how severe something is, as well as where it is coming from.
这使您可以确定某事的严重程度以及它来自哪里。
The traditional log levels are as follows(from most verbose to least verbose): TRACE DEBUG INFO WARN ERROR
传统的日志级别如下(从最详细到最不详细): 跟踪调试信息警告错误
In addition, levels of CRITICAL and FATAL may exist as well. Generally, FATAL indicates that the application is about to exit, while CRITICAL is similar to ERROR.
此外,还可能存在 CRITICAL 和 FATAL 级别。通常,FATAL 表示应用程序即将退出,而 CRITICAL 类似于 ERROR。
Levels make it easy to quickly filter messages. Since more verbose messages still allow less verbose messages to be printed, you don’t lose information by turning the verbosity up. When you are working on a particular subsystem, you may want that subsystem to be at the DEBUG level to see all the messages, or TRACE if there is something that you really need to see. As a general rule of thumb, when your application is in production the default level of all loggers should be at the INFO level.
通过级别,可以轻松快速地筛选消息。由于更详细的消息仍然允许打印更少的详细消息,因此您不会通过调高详细程度来丢失信息。在处理特定子系统时,您可能希望该子系统处于 DEBUG 级别以查看所有消息,或者如果确实需要查看某些内容,则使用 TRACE。作为一般经验法则,当应用程序处于生产状态时,所有记录器的默认级别应为 INFO 级别。
Subsystems help to classify the data as well. In Java, this is generally done by the package that a class is in. Being able to set a particular subsystem that you are debugging to the DEBUG level(and not just a single class) is an important part of being able to easily filter information and to not overload the programmer who is debugging the application.
子系统也有助于对数据进行分类。在 Java 中,这通常是由类所在的包完成的。能够将要调试的特定子系统设置为 DEBUG 级别(而不仅仅是单个类)是能够轻松过滤信息并且不会使正在调试应用程序的程序员过载的重要部分。
Within the Apache Logging Services projects(log4j2, log4cxx, and log4net) this subsystem classification follows the Java hierarchical approach. If you have a logger org.apache.Foo and org.apache.Bar, you can set the level of org.apache to DEBUG and both loggers will be at the DEBUG level, but can also be individually set if required. This hierarchical way of logging is not a feature that all logging frameworks have, however it is very useful if your code is organized in a way such that all classes have individual loggers and are logically separated into subsystems appropriately. This also allows for more granular control over your debug statements.
在 Apache 日志记录服务项目(log4j2、log4cxx 和 log4net)中,此子系统分类遵循 Java 分层方法。如果你有记录器 org.apache.Foo 和 org.apache.Bar,你可以将 org.apache 的级别设置为 DEBUG,两个记录器都将处于 DEBUG 级别,但如果需要,也可以单独设置。这种分层的日志记录方式并不是所有日志记录框架都具有的功能,但是,如果您的代码以所有类都具有单独的记录器并在逻辑上适当地划分为子系统的方式进行组织,则它非常有用。这还允许对调试语句进行更精细的控制。
Where to put logs?
把原木放在哪里?
Logging to different locations(sometimes called sinks) is an important part of logging frameworks. For simple applications, you may not need to do anything more complicated than just send data to stdout and/or a file. However, more complicated applications quickly need to have more flexibility in how and where they log information. Modern applications can log to web-based log aggregators in addition to a local log mechanism such as syslog.
记录到不同的位置(有时称为接收器)是日志记录框架的重要组成部分。对于简单的应用程序,您可能不需要做任何更复杂的事情,只需将数据发送到 stdout 和/或文件。但是,更复杂的应用程序需要在记录信息的方式和位置方面具有更大的灵活性。除了本地日志机制(如 syslog)之外,现代应用程序还可以记录到基于 Web 的日志聚合器。
By configuring these log locations through an external source such as a config file, it also makes it possible for the end-user to configure the system for their particular use-case.
通过外部源(如配置文件)配置这些日志位置,最终用户还可以针对其特定用例配置系统。
Transparent Logging 透明日志记录
One of the most basic tenets of logging is that it needs to be completely transparent to your application. Your application must not care if logging is enabled or disabled: all it does is send messages to the logging framework, which then handles all of the filtering and routing required to get the message to its final destination. This also makes it possible to write unit tests that don’t care about logging at all. If the logging system is not initialized, the worst that should happen is that nothing gets logged.
日志记录最基本的原则之一是它需要对应用程序完全透明。应用程序必须不关心日志记录是启用还是禁用:它所做的只是将消息发送到日志记录框架,然后日志记录框架处理将消息发送到其最终目标所需的所有筛选和路由。这也使得编写完全不关心日志记录的单元测试成为可能。如果日志记录系统未初始化,则最坏的情况是不会记录任何内容。
How to do proper logging
如何进行正确的日志记录
Now that we understand what the normal features of logging libraries are, it’s time to talk about how to do proper logging. As we’ve mentioned before, the two main things to worry about when logging are what the level of the message is, and the classification of the log message. For the classification of the log message, this will often be the name of the class that is doing the logging. Assuming that you have a good design already, then the classification derives clearly from the name of the class and the package/namespace the class is in.
现在我们了解了日志记录库的正常功能是什么,是时候讨论如何进行正确的日志记录了。正如我们之前提到的,记录日志记录时要担心的两个主要事情是消息的级别和日志消息的分类。对于日志消息的分类,这通常是执行日志记录的类的名称。假设你已经有一个好的设计,那么分类显然来自类的名称和类所在的包/命名空间。
Choosing the level of the log message can be a bit trickier. A good rule of thumb is to leave your log messages at INFO level during normal operation, so what should we put at that level and what should be more verbose? Let’s assume that we are opening a network socket for remote communications. At the INFO level we may inform that the network subsystem is starting up and ready for work. DEBUG information could include new connections from clients with their IP/port combination, while TRACE information could be output whenever a packet comes in from a client. In addition, we may have ERROR messages if we are unable to open a socket for reading/writing.
选择日志消息的级别可能有点棘手。一个好的经验法则是在正常操作期间将日志消息保留在 INFO 级别,那么我们应该在该级别放置什么,应该更详细地表达什么?假设我们正在打开一个用于远程通信的网络套接字。在 INFO 级别,我们可能会通知网络子系统正在启动并准备工作。调试信息可以包括来自客户端及其 IP/端口组合的新连接,而每当数据包从客户端传入时,都可以输出 TRACE 信息。此外,如果我们无法打开套接字进行读/写,我们可能会收到错误消息。
By having different pieces of the data flow at different log levels, it makes it easy to quickly look at the logs and determine how serious a problem is and what the system is doing during operation.
通过在不同的日志级别拥有不同的数据流片段,可以轻松快速查看日志并确定问题的严重程度以及系统在运行期间正在执行的操作。
Other things to worry about when logging
记录时需要注意的其他事项
As we have seen, having a good logging framework is critical to being able to find and fix errors in applications. However, just because we have logging doesn’t mean that it is infallible. Since logging code that you insert into your application is still code that you have written, it is always possible that the log messages you get out are lying to you due to a bug in a log statement.
正如我们所看到的,拥有一个良好的日志记录框架对于能够查找和修复应用程序中的错误至关重要。但是,仅仅因为我们有日志记录并不意味着它是万无一失的。由于插入到应用程序中的日志记录代码仍然是您编写的代码,因此由于日志语句中的错误,您获取的日志消息始终有可能对您撒谎。
Features that the Apache Logging Services projects have
Apache 日志记录服务项目具有的功能
While there are many different logging frameworks that exist for Java, C++, and .NET, there are a number of built-in features that Log4j2, Log4cxx, and Log4net have that are generally useful.
虽然 Java、C++ 和 .NET 存在许多不同的日志记录框架,但 Log4j2、Log4cxx 和 Log4net 具有许多通常有用的内置功能。
Hierarchichal Logging Hierarchichal 日志记录
As mentioned previously, hierarchical logging allows for easy grouping of subsystem information. This allows you to have fine-grained control over the information that you are logging and easily allows for one logger per-class.
如前所述,分层日志记录允许对子系统信息进行轻松分组。这使您可以对正在记录的信息进行细粒度控制,并轻松允许每个类使用一个记录器。
This hierarchical logging also means that you don’t need to explicitly share a sink between loggers. While it is possible to configure Log4j2 and Log4cxx to send each logger to a separate location, the parent/child relationship means that children will log their messages to any sinks that their parents have.
这种分层日志记录还意味着不需要在记录器之间显式共享接收器。虽然可以将 Log4j2 和 Log4cxx 配置为将每个记录器发送到单独的位置,但父/子关系意味着子项会将其消息记录到其父级拥有的任何接收器中。
Location Information 位置信息
Knowing exactly where a log statement is can be vital in order to track down the location of a log statement. Both Log4j2 and Log4cxx are able to determine the filename, class name, method name, and line number where the log message is coming from. With Log4cxx, this is able to be done at compile-time.
确切地知道日志语句的位置对于跟踪日志语句的位置至关重要。Log4j2 和 Log4cxx 都能够确定日志消息来自的文件名、类名、方法名和行号。使用 Log4cxx,这可以在编译时完成。
Static and Single-instance Loggers
静态和单实例记录器
All loggers that are created with Log4j2 and Log4cxx are able to be declared as static variables in your source file, eliminating the need for a dedicated instance variable of a logger per-class. A side effect of this is that there is a global logger registry, so that you can access the same logger via a static LogManager in different classes without having to pass the logger around.
使用 Log4j2 和 Log4cxx 创建的所有记录器都可以在源文件中声明为静态变量,从而无需每个类的记录器的专用实例变量。这样做的副作用是存在一个全局记录器注册表,因此您可以通过不同类中的静态 LogManager 访问相同的记录器,而不必传递记录器。
Nested and Mapped Diagnostic Contexts
嵌套和映射的诊断上下文
When you have multiple objects of the same type, using a static logger can be problematic when you need to only view data for one instance of the class. By providing a Nested Diagnostic Context and a Mapped Diagnostic Context, it is possible to add context information to your log statements.
当有多个相同类型的对象时,当您只需要查看类的一个实例的数据时,使用静态记录器可能会有问题。通过提供嵌套诊断上下文和映射诊断上下文,可以将上下文信息添加到日志语句中。
Flexible Log Message Formatting
灵活的日志消息格式
When sending a log message to a flat text file, it is often desirable to have the messages in a simple to understand format. This information can include data such as the date/time of the log message, the level of the message, the logger that produced the message, and the message itself. With the Log4j2 and Log4cxx PatternLayout classes, it is possible to quickly configure the logging system to output information in a convenient format.
将日志消息发送到平面文本文件时,通常希望消息采用简单易懂的格式。此信息可以包括日志消息的日期/时间、消息级别、生成消息的记录器以及消息本身等数据。使用 Log4j2 和 Log4cxx PatternLayout 类,可以快速配置日志记录系统以方便的格式输出信息。
Different types of log message formatting is also important when you need to send messages to different locations. The code that creates the log messages should not know anything about how the message is formatted on the backend. This allows for the same log message to be formatted in different ways with potentially different metadata - for example, JSON format for a log aggregating service, or plain text for a local configuration file.
当您需要将消息发送到不同位置时,不同类型的日志消息格式也很重要。创建日志消息的代码不应知道消息在后端的格式设置方式。这允许以不同的方式格式化相同的日志消息,并可能使用不同的元数据 - 例如,日志聚合服务的 JSON 格式或本地配置文件的纯文本格式。
Configuration via config file
通过配置文件进行配置
Both Log4j2 and Log4cxx can be configured with a configuration file in one of several different formats. While it is always possible to configure via code, configuration files make it easy to reconfigure the system without having to recompile your code. This also means it is possible to edit a configuration file and dynamically change the configuration at runtime. For example, you can turn on a logger that was previously off if you are debugging a live system.
Log4j2 和 Log4cxx 都可以使用几种不同格式之一的配置文件进行配置。虽然始终可以通过代码进行配置,但配置文件使重新配置系统变得容易,而无需重新编译代码。这也意味着可以在运行时编辑配置文件并动态更改配置。例如,如果要调试实时系统,则可以打开以前关闭的记录器。
Garbage-free(Log4j2) 无垃圾(Log4j2)
In Java, it is important to reduce the amount of time that the garbage collector takes to run. By not allocating and freeing objects, your log statements will add very little to the runtime overhead of your application.
在 Java 中,减少垃圾回收器运行所需的时间非常重要。如果不分配和释放对象,日志语句不会增加应用程序的运行时开销。
Formatting with replacements
使用替换进行格式化
When creating a log statement, it is often slow and annoying to perform string concatenation to create a log statement. In Java, the simplest way to concatenate a string is to simply use the + operator:
创建日志语句时,执行字符串连接以创建日志语句通常很慢且很烦人。在 Java 中,连接字符串的最简单方法是简单地使用 + 运算符:
log("Logging information: " + someVariable + " and the other variable is " + otherVariable);
While in C++, you can get into a chevron overload:
在 C++ 中,您可能会遇到 V 形重载:
log("Logging information: " << someVariable << " and the other variable is " << otherVariable);
Both Log4j2 and Log4cxx allow for variable replacements in strings, such that the above log statements can both be written similar to the following:
Log4j2 和 Log4cxx 都允许在字符串中进行变量替换,因此上述日志语句都可以编写为类似于以下内容:
log("Logging information: {} and the other variable is {}", someVariable, otherVariable);
Filtering 滤波
When you have many different log messages that you are trying to make sense of, it can be difficult to focus on the correct piece of information. Because of this, it is possible to configure both Log4j2 and Log4cxx to filter certain messages based off of various attributes, such as a text string that is contained within the log message. Since these filters are also able to be configured through the confgiuration file, adding or removing filters is a simple operation.
当您有许多不同的日志消息试图理解时,可能很难专注于正确的信息。因此,可以同时配置 Log4j2 和 Log4cxx 以根据各种属性(例如日志消息中包含的文本字符串)筛选某些消息。由于这些过滤器也可以通过 confgiuration 文件进行配置,因此添加或删除过滤器是一个简单的操作。
Conclusion 结论
Logging is a rather complicated topic that covers many different aspects. While different applications will have different requirements for their log statements, the projects of the Apache Logging Services Commitee provide a generic and widely applicable implemenation of logging for all kinds of applications.
日志记录是一个相当复杂的主题,涵盖了许多不同的方面。虽然不同的应用程序对其日志语句有不同的要求,但 Apache 日志服务委员会的项目为各种应用程序提供了通用且广泛适用的日志记录实现。