Architecture

Main Components

Log4j 使用下图中所示的类。

使用 Log4j 2 API 的应用程序将从 LogManager 请求具有特定名称的 Logger。 LogManager 将找到适当的 LoggerContext,然后从中获取 Logger。如果必须创建 Logger,它将与 LoggerConfig 关联,该 LoggerConfig 包含 a)与 Logger 相同的名称,b)父程序包的名称或 c)根 LoggerConfig。 LoggerConfig 对象是根据配置中的 Logger 声明创建的。 LoggerConfig 与实际提供 LogEvent 的 Appender 关联。

Logger Hierarchy

与普通 System.out.println 相比,任何日志记录 API 的第一个也是最重要的优势在于它能够禁用某些日志语句,同时允许其他日志不受阻碍地进行打印。此功能假定已根据开发人员选择的标准对日志记录空间(即所有可能的日志记录语句的空间)进行了分类。

在 Log4j 1.x 中,通过 Logger 之间的关系维护 Logger 层次结构。在 Log4j 2 中,此关系不再存在。而是在 LoggerConfig 对象之间的关系中维护层次结构。

Logger 和 LoggerConfigs 是命名实体。Logger 名称区分大小写,并且遵循分层命名规则:

例如,名为“ com.foo”的 LoggerConfig 是名为“ com.foo.Bar”的 LoggerConfig 的父级。同样,“ java”是“ java.util”的父代,也是“ java.util.Vector”的祖先。大多数开发人员都应该熟悉这种命名方案。

根 LoggerConfig 位于 LoggerConfig 层次结构的顶部。它的特殊之处在于它始终存在,并且是每个层次结构的一部分。直接链接到根 LoggerConfig 的 Logger 可以通过以下方式获得:

Logger logger = LogManager.getLogger(LogManager.ROOT_LOGGER_NAME);

或者,更简单地说:

Logger logger = LogManager.getRootLogger();

可以通过传递所需 Logger 的名称,使用LogManager.getLogger静态方法检索所有其他 Logger。有关 Logging API 的更多信息,请参见Log4j 2 API

LoggerContext

LoggerContext充当 Logging 系统的定位点。但是,根据情况,一个应用程序中可能有多个活动 LoggerContext。有关 LoggerContext 的更多详细信息,请参见Log Separation部分。

Configuration

每个 LoggerContext 都有一个活动的Configuration。该配置包含所有 Appender,上下文范围的过滤器,LoggerConfigs,并包含对 StrSubstitutor 的引用。在重新配置期间,将存在两个配置对象。一旦所有 Logger 都重定向到新的配置,旧的配置将被停止并丢弃。

Logger

如前所述,Logger 是通过调用LogManager.getLogger来创建的。Logger 本身不执行任何直接操作。它仅具有一个名称,并与 LoggerConfig 关联。它扩展了AbstractLogger并实现了所需的方法。修改配置后,Logger 可能与其他 LoggerConfig 关联,从而导致其行为被修改。

Retrieving Loggers

用相同名称调用 LogManager.getLogger 方法将始终返回对完全相同的 Logger 对象的引用。

例如,在

Logger x = LogManager.getLogger("wombat");
Logger y = LogManager.getLogger("wombat");

x 和 y 引用完全相同的 Logger 对象。

log4j 环境的配置通常在应用程序初始化时完成。首选方法是读取配置文件。 Configuration中对此进行了讨论。

Log4j 使按软件组件命名 Logger 变得容易。这可以通过在每个类中实例化一个 Logger 来实现,其 Logger 名称等于该类的完全限定名称。这是定义 Logger 的有用且直接的方法。由于日志输出带有生成的 Logger 的名称,因此这种命名策略使识别日志消息的来源变得容易。但是,这只是命名 Logger 的一种可能的策略,尽管很常见。 Log4j 不会限制可能的 Logger 集。开发人员可以根据需要随意命名 Logger。

由于在他们自己的类后给 Logger 命名是一种常见的习惯用法,因此提供了便捷方法 LogManager.getLogger()以自动使用调用类的完全限定类名作为 Logger 名称。

不过,到目前为止,在 Logger 上课后命名 Logger 似乎是最好的策略。

LoggerConfig

在日志记录配置中声明日志 Logger 时,将创建LoggerConfig对象。 LoggerConfig 包含一组过滤器,必须允许 LogEvent 通过,然后才能将其传递给任何 Appender。它包含对应用于处理事件的一组 Appender 的引用。

Log Levels

LoggerConfigs 将被分配一个 Log Level。内置级别集包括 TRACE,DEBUG,INFO,WARN,ERROR 和 FATAL。 Log4j 2 还支持自定义日志级别。获得更多粒度的另一种机制是使用Markers

Log4j 1.xLogback都具有“级别继承”的概念。在 Log4j 2 中,Logger 和 LoggerConfig 是两个不同的对象,因此此概念的实现方式有所不同。每个 Logger 都引用适当的 LoggerConfig,后者又可以引用其父级,从而达到相同的效果。

以下是五个表,这些表具有各种分配的级别值以及将与每个 Logger 关联的级别。请注意,在所有这些情况下,如果未配置根 LoggerConfig,则将为其分配默认级别。

Example 1

Logger Name Assigned LoggerConfig LoggerConfig Level Logger Level
root root DEBUG DEBUG
X root DEBUG DEBUG
X.Y root DEBUG DEBUG
X.Y.Z root DEBUG DEBUG

在上面的示例 1 中,仅配置了根 Logger 并具有日志级别。其他所有 Logger 都引用根 LoggerConfig 并使用其级别。

Example 2

Logger Name Assigned LoggerConfig LoggerConfig Level Level
root root DEBUG DEBUG
X X ERROR ERROR
X.Y X.Y INFO INFO
X.Y.Z X.Y.Z WARN WARN

在示例 2 中,所有 Logger 都配置了 LoggerConfig 并从中获取其 Level。

Example 3

Logger Name Assigned LoggerConfig LoggerConfig Level Level
root root DEBUG DEBUG
X X ERROR ERROR
X.Y X ERROR ERROR
X.Y.Z X.Y.Z WARN WARN

在示例 3 中,loggersroot,X 和 X.Y.Z 每个都具有一个配置的具有相同名称的 LoggerConfig。 Logger X.Y 没有配置的名称匹配的 LoggerConfig,因此使用 LoggerConfig X 的配置,因为它是名称与 Logger 名称的开头最长匹配的 LoggerConfig。

Example 4

Logger Name Assigned LoggerConfig LoggerConfig Level level
root root DEBUG DEBUG
X X ERROR ERROR
X.Y X ERROR ERROR
X.Y.Z X ERROR ERROR

在示例 4 中,Logger 的根和 X 均具有相同名称的 Configured LoggerConfig。LoggerX.Y 和 X.Y.Z 尚未配置 LoggerConfig,因此从分配给它们 X 的 LoggerConfig 中获取其级别,因为它是 LoggerConfig,其名称与 Logger 名称的开头最长匹配。

Example 5

Logger Name Assigned LoggerConfig LoggerConfig Level level
root root DEBUG DEBUG
X X ERROR ERROR
X.Y X.Y INFO INFO
X.YZ X ERROR ERROR

在示例 5 中,loggersroot.X 和 X.Y 都具有一个具有相同名称的 Configured LoggerConfig。LoggerX.YZ 尚未配置 LoggerConfig,因此从分配给它的 LoggerConfig X 获取其级别,因为它是名称与 Logger 名称的开头最长匹配的 LoggerConfig。它与 LoggerConfig X.Y 不关联,因为句点后的令牌必须完全匹配。

Example 6

Logger Name Assigned LoggerConfig LoggerConfig Level Level
root root DEBUG DEBUG
X X ERROR ERROR
X.Y X.Y ERROR
X.Y.Z X.Y ERROR

在示例 6 中,LoggerConfig X.Y 没有配置的级别,因此它从 LoggerConfig X 继承其级别。LoggerX.Y.Z 使用 LoggerConfig X.Y,因为它没有名称完全匹配的 LoggerConfig。它也从 LoggerConfig X 继承其日志记录级别。

下表说明了级别过滤的工作原理。在表中,垂直标题显示 LogEvent 的级别,而水平标题显示与适当的 LoggerConfig 关联的级别。相交标识是否允许 LogEvent 通过以进行进一步处理(是)还是将其丢弃(否)。

Event Level LoggerConfig Level
TRACE DEBUG INFO WARN ERROR FATAL OFF
ALL YES YES YES YES YES YES NO
TRACE YES NO NO NO NO NO NO
DEBUG YES YES NO NO NO NO NO
INFO YES YES YES NO NO NO NO
WARN YES YES YES YES NO NO NO
ERROR YES YES YES YES YES NO NO
FATAL YES YES YES YES YES YES NO
OFF NO NO NO NO NO NO NO

Filter

除了上一节所述的自动日志级别过滤之外,Log4j 还提供了Filter,可以在将控制权传递给任何 LoggerConfig 之前,将控制权传递给 LoggerConfig 之后但在调用任何 Appender 之前,在控制完成之后应用Filter。传递给 LoggerConfig,但在调用特定的 Appender 之前以及在每个 Appender 上传递。以与防火墙过滤器非常相似的方式,每个过滤器可以返回以下三个结果之一:“接受”,“拒绝”或“中性”。接受的响应表示不应调用其他任何筛选器,并且事件应 continue 进行。拒绝的响应意味着应立即忽略该事件,并将控制权返回给调用方。中性的响应表示该事件应传递给其他过滤器。如果没有其他过滤器,则事件将被处理。

尽管某个事件可能被过滤器接受,但该事件可能仍未记录。当事件被 LoggerConfig 之前的过滤器接受但被 LoggerConfig 过滤器拒绝或被所有 Appender 拒绝时,就会发生这种情况。

Appender

根据 Logger 有选择地启用或禁用日志记录请求的功能只是图片的一部分。 Log4j 允许记录请求打印到多个目标。用 log4j 来说,输出目标称为Appender。当前,存在用于控制台,文件,远程套接字服务器,Apache Flume,JMS,远程 UNIX Syslog 守护程序和各种数据库 API 的附加程序。有关各种可用类型的更多详细信息,请参见Appenders部分。一个 Logger 可以连接多个 Appender。

可以通过调用当前配置的addLoggerAppender方法将 Appender 添加到 Logger。如果不存在与 Logger 名称匹配的 LoggerConfig,将创建一个 Logger,将附加 Appender,然后将通知所有 Logger 更新其 LoggerConfig 引用。

针对给定 Logger 的每个启用的记录请求都将转发给该 Logger 的 LoggerConfig 中的所有添加程序以及 LoggerConfig 的父项的 Appender. 换句话说,Appender 继承自 LoggerConfig 层次结构。例如,如果将控制台附加程序添加到根 Logger,则所有启用的记录请求将至少在控制台上打印。如果另外将文件追加程序添加到 LoggerConfig 中,例如 C,则针对 C 和 C 的子级启用的日志记录请求将在文件中和控制台上打印。可以覆盖此默认行为,以便通过在配置文件中的 Logger 声明中设置 additivity =“ false”来不再增加 Appender 的累积量。

下面概述了控制追加程序可加性的规则。

但是,如果与 Logger L 关联的 LoggerConfig 的祖先(例如 P)将可加性标志设置为 false,则 L 的输出将定向到 L 的 LoggerConfig 中的所有追加器,并且其祖先直到 P(包括 P),但不包括 Pender P 的任何祖先。

Logger 默认将其可加性标志设置为 true。

下表显示了一个示例:

Logger
Name
Added
Appenders
Additivity
Flag
Output Targets Comment
root A1 不适用 A1 rootLogger 没有父项,因此加性不适用于它。
x A-x1,A-x2 true A1,A-x1,A-x2 “ x”和根的附加项。
x.y none true A1,A-x1,A-x2 “ x”和根的附加项。配置没有 Appender 的 Logger 并不常见。
x.y.z A-xyz1 true A1,A-x1,A-x2,A-xyz1 在“ x.y.z”,“ x”和根目录中附加。
security A-sec false A-sec 由于可加性标志设置为 false,因此没有附加器累积。
security.access none true A-sec 仅“ security”的附加程序,因为“ security”中的可加性标志设置为 false。

Layout

通常,用户不仅希望自定义输出目标,还希望自定义输出格式。这是通过将Layout与 Appender 相关联来实现的。布局负责根据用户的意愿格式化 LogEvent,而附加程序负责将格式化的输出发送到其目的地。标准 log4j 发行版的PatternLayout,使用户可以根据类似于 C 语言 printf 函数的转换模式来指定输出格式。

例如,带有转换模式“%r [%t]%-5p%c-%m%n”的 PatternLayout 将输出类似于:

176 [main] INFO  org.foo.Bar - Located nearest gas station.

第一个字段是自程序启动以来经过的毫秒数。第二个字段是发出日志请求的线程。第三个字段是 log 语句的级别。第四个字段是与日志请求关联的 Logger 的名称。 “-”之后的文本是该语句的消息。

Log4j 带有许多不同的Layouts,用于各种用例,例如 JSON,XML,HTML 和 Syslog(包括新的 RFC 5424 版本)。其他附加程序(例如数据库连接器)将填充指定的字段,而不是特定的文本布局。

同样重要的是,log4j 将根据用户指定的标准呈现日志消息的内容。例如,如果您经常需要记录当前项目中使用的对象类型 Oranges,则可以创建一个 OrangeMessage 来接受 Orange 实例并将其传递给 Log4j,以便在出现以下情况时可以将 Orange 对象格式化为适当的字节数组:需要。

StrSubstitutor 和 StrLookup

Apache Commons Lang借用了StrSubstitutor类和StrLookup接口,然后对其进行了修改以支持评估 LogEvents。另外,Interpolator类是从 Apache Commons Configuration 借用的,以允许 StrSubstitutor 评估来自多个 StrLookups 的变量。它也被修改为支持评估 LogEvents。这些一起提供了一种机制,允许配置引用来自 LogEvent 中的系统属性,配置文件,ThreadContextMap,StructuredData 的变量。如果组件能够处理,则可以在处理配置时或在处理每个事件时解析变量。有关更多信息,请参见Lookups

首页