插件

介绍

Log4j 1.x 允许通过在大多数配置声明中要求类属性来进行扩展。在某些元素的情况下,特别是 PatternLayout,添加新模式转换器的唯一方法是扩展 PatternLayout 类并通过代码添加它们。Log4j 2 的一个目标是通过使用插件使扩展它变得极其容易。

在 Log4j 2 中,插件通过在类声明中添加 @Plugin 注释来声明。在初始化期间,Configuration 将调用 PluginManager 来加载内置的 Log4j 插件以及任何自定义插件。PluginManager 通过在五个地方查找插件

  1. 类路径上的序列化插件列表文件。这些文件在构建期间自动生成(更多详细信息见下文)。
  2. (仅限 OSGi) 每个活动 OSGi 捆绑包中的序列化插件列表文件。在激活时添加一个 BundleListener 以在 log4j-core 启动后继续检查新捆绑包。
  3. (已弃用)log4j.plugin.packages 系统属性指定的以逗号分隔的包列表。
  4. (已弃用) 传递给静态 PluginManager.addPackages 方法的包(在 Log4j 配置发生之前)。
  5. (已弃用) 在您的 log4j2 配置文件中声明的 packages

如果多个插件指定相同的(不区分大小写)name,则上面的加载顺序决定将使用哪个插件。例如,要覆盖由内置 FileAppender 类提供的 File 插件,您需要将您的插件放在 CLASSPATH 中的 JAR 文件中,该文件位于 log4j-core.jar 之前。不建议这样做;插件名称冲突会导致发出警告。请注意,在 OSGi 环境中,扫描插件的捆绑包的顺序通常与捆绑包安装到框架中的顺序相同。参见 getBundles()SynchronousBundleListener。简而言之,名称冲突在 OSGi 环境中更加不可预测。

序列化插件列表文件由 log4j-core 工件中包含的注释处理器生成,该处理器将自动扫描您的代码以查找 Log4j 2 插件并在您的处理类中输出元数据文件。无需执行任何额外操作来启用此功能;除非您明确禁用它,否则 Java 编译器将自动拾取类路径上的注释处理器。在这种情况下,将重要的一点是向您的构建过程添加另一个编译步骤,该步骤仅使用 Log4j 2 注释处理器类 org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor 处理注释处理。要使用 Apache Maven 执行此操作,请将以下执行添加到您的 maven-compiler-plugin(版本 2.2 或更高版本)构建插件中

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-compiler-plugin</artifactId>
  <version>3.1</version>
  <executions>
    <execution>
      <id>log4j-plugin-processor</id>
      <goals>
        <goal>compile</goal>
      </goals>
      <phase>process-classes</phase>
      <configuration>
        <proc>only</proc>
        <annotationProcessors>
          <annotationProcessor>org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor</annotationProcessor>
        </annotationProcessors>
      </configuration>
    </execution>
  </executions>
</plugin>
          

在处理配置时,将自动配置和初始化相应的插件。Log4j 2 利用几种不同的插件类别,这些类别在以下部分中描述。

核心

核心插件是那些直接由配置文件中的元素表示的插件,例如追加器、布局、记录器或过滤器。符合下一段中概述的规则的自定义插件可能只需在配置中引用,前提是它们已正确配置以由 PluginManager 加载。

每个核心插件都必须声明一个用 @PluginFactory@PluginBuilderFactory 注释的静态方法。前者用于提供所有选项作为方法参数的静态工厂方法,而后者用于构造一个新的 Builder<T> 类,其字段用于注入属性和子节点。为了允许 Configuration 将正确的参数传递给该方法,该方法的每个参数都必须用以下属性类型之一进行注释。每个属性或元素注释都必须包含必须存在于配置中的名称,以便将配置项与其各自的参数匹配。对于插件构建器,如果注释中未指定名称,则将默认使用字段的名称。Log4j Core 中有数十个插件可以用作更复杂场景的示例,包括分层构建器类(例如,参见 FileAppender)。有关更多详细信息,请参见 使用插件构建器扩展 Log4j

属性类型

PluginAttribute
参数必须使用 TypeConverter 从字符串转换。大多数内置类型已得到支持,但也可以提供自定义 TypeConverter 插件以提供更多类型支持。请注意,PluginBuilderAttribute 可用作构建器类字段,作为提供默认值的一种更简单方法。
PluginElement
参数可以表示一个复杂对象,该对象本身具有可以配置的参数。这也支持注入元素数组。
PluginConfiguration
当前 Configuration 对象将作为参数传递给插件。
PluginNode
将把正在解析的当前 Node 作为参数传递给插件。
PluginValue
当前 Node 的值或其名为 value 的属性。

约束验证器

插件工厂字段和参数可以在运行时使用受 Bean Validation 规范 启发的约束验证器自动验证。Log4j 中捆绑了以下注释,但也可以创建自定义 ConstraintValidators

Required
此注释验证值是否非空。这涵盖了对 null 的检查以及其他几种情况:空 CharSequence 对象、空数组、空 Collection 实例和空 Map 实例。
ValidHost
此注释验证值是否对应于有效的主机名。这使用与 InetAddress::getByName 相同的验证。
ValidPort
此注释验证值是否对应于 0 到 65535 之间的有效端口号。

转换器

转换器由 PatternLayout 用于呈现由转换模式标识的元素。每个转换器都必须在 @Plugin 注释中指定其类别为“Converter”,具有一个静态 newInstance 方法,该方法接受一个 String 数组作为其唯一参数并返回一个 Converter 实例,并且必须具有一个包含将导致选择 Converter 的转换器模式数组的 @ConverterKeys 注释。旨在处理 LogEvent 的转换器必须扩展 LogEventPatternConverter 类,并且必须实现一个接受 LogEventStringBuilder 作为参数的格式方法。Converter 应将操作结果追加到 StringBuilder

第二种类型的转换器是 FileConverter - 它必须在 @Plugin 注解的 category 属性中指定 "FileConverter"。虽然与 LogEventPatternConverter 类似,但这些转换器没有单个格式方法,而是有两个变体;一个接受 Object,另一个接受 Object 数组,而不是 LogEvent。两者都以与 LogEventPatternConverter 相同的方式追加到提供的 StringBuilder。这些转换器通常由 RollingFileAppender 用于构建要记录到的文件的名称。

如果多个转换器指定相同的 ConverterKeys,则上面的加载顺序决定了将使用哪一个。例如,要覆盖由内置 DatePatternConverter 类提供的 %date 转换器,您需要将您的插件放在 CLASSPATH 中的 JAR 文件中,该文件位于 log4j-core.jar 之前。不建议这样做;模式 ConverterKeys 冲突会导致发出警告。尝试为您的自定义模式转换器使用唯一的 ConverterKeys。

密钥提供者

Log4j 中的一些组件可能提供执行数据加密的功能。这些组件需要一个密钥来执行加密。应用程序可以通过创建一个实现 SecretKeyProvider 接口的类来提供密钥。

查找

查找可能是最简单的插件。它们必须在插件注解中声明其类型为 "Lookup",并且必须实现 StrLookup 接口。它们将有两个方法;一个查找方法,它接受一个字符串键并返回一个字符串值,以及第二个查找方法,它接受一个 LogEvent 和一个字符串键并返回一个字符串。查找可以通过指定 ${name:key} 来引用,其中 name 是在 Plugin 注解中指定的名称,key 是要查找的项目的名称。

类型转换器

TypeConverter 是一种元插件,用于将字符串转换为插件工厂方法参数中的其他类型。其他插件可以通过 @PluginElement 注解注入;现在,类型转换系统支持的任何类型都可以在 @PluginAttribute 参数中使用。枚举类型的转换按需支持,不需要自定义 TypeConverter 类。大量内置的 Java 类已经支持;有关更详尽的列表,请参阅 TypeConverters

与其他插件不同,TypeConverter 的插件名称纯粹是装饰性的。合适的类型转换器是通过 Type 接口而不是通过 Class<?> 对象查找的。请注意,TypeConverter 插件必须具有默认构造函数。当多个转换器匹配一个类型时,将返回第一个。如果任何扩展自 Comparable<TypeConverter<?>>,它将用于确定顺序。

开发者说明

如果插件类实现了 CollectionMap,则不使用工厂方法。相反,该类使用默认构造函数实例化,所有子配置节点都添加到 CollectionMap 中。