介绍

Log4j 1.X通过在大多数配置声明上要求类属性来允许扩展。

对于某些元素,特别是PatternLayout,添加新模式转换器的唯一方法是扩展PatternLayout类,并通过代码添加它们。

Log4j 2的一个目标是通过使用插件使扩展变得非常容易。

在Log4j 2中,插件是通过在类声明中添加 @Plugin 注解来声明的。

在初始化过程中,配置将调用PluginManager来加载内置的Log4j插件和任何自定义插件。

PluginManager通过查找以下5个位置来查找插件:

  • 在classpath中列出文件的序列化插件。这些文件是在构建过程中自动生成的(更多细节见下文)。

  • (仅适用于OSGi)在每个活动的OSGi包中列出文件的序列化插件。激活时添加BundleListener,以便在log4j-core启动后继续检查新的bundle。

  • (已弃用)log4j.plugin指定的一个逗号分隔的包列表。Packages系统属性。

  • (已弃用)包传递给静态PluginManager。addPackages方法(在Log4j配置发生之前)。

  • (已弃用)log4j2配置文件中声明的包。

如果多个插件指定相同(不区分大小写)的名称,那么上面的加载顺序将决定使用哪个插件。

例如,要覆盖内置FileAppender类提供的File插件,需要将插件放在log4j-core.JAR前面的类路径中的JAR文件中。

我们不推荐这样做;插件名称冲突将导致发出警告。

注意,在OSGi环境中,bundle扫描插件的顺序通常与bundle安装到框架中的顺序相同。参见getBundles()和synchronousbundlellistener。简而言之,名称冲突在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利用了几个不同类别的插件,这些插件将在以下几节中描述。

核心 core

核心插件是那些直接通过配置文件中的元素表示的插件,例如Appender、Layout、Logger或Filter。

符合下一段中列出的规则的自定义插件可以在配置中简单地引用,前提是它们经过适当的配置,可以由PluginManager加载。

每个核心插件都必须声明一个带有@PluginFactory或@PluginBuilderFactory注解的静态方法。

前者用于提供所有选项作为方法参数的静态工厂方法,后者用于构造一个新的 Builder<T> 类,该类的字段用于注入属性和子节点。

要让配置将正确的参数传递给方法,方法的每个参数都必须标注为以下属性类型之一。

每个属性或元素注释都必须包含配置中必须存在的名称,以便将配置项与其各自的参数匹配。

对于插件构建器,如果注释中没有指定字段的名称,则默认使用字段的名称。

Log4j Core中有许多插件可以用作更复杂场景的示例,包括分层的builder类(例如,请参阅FileAppender)。更多细节请参见使用插件构建器扩展Log4j。

属性类型 Attribute Types

PluginAttribute

参数必须使用TypeConverter从字符串转换而来。大多数内置类型已经被支持,但是自定义的TypeConverter插件也可以提供更多的类型支持。

注意,PluginBuilderAttribute可以在builder类字段中使用,作为提供默认值的一种更简单的方式。

PluginElement

参数可以表示一个复杂的对象,该对象本身具有可配置的参数。这也支持注入一个元素数组。

PluginConfiguration

当前配置对象将作为参数传递给插件。

PluginNode

被解析的当前节点将作为参数传递给插件。

PluginValue

当前节点或其属性value的值。

约束验证器

受Bean验证规范的启发,插件工厂字段和参数可以在运行时使用约束验证器自动验证。

Log4j中捆绑了以下注解,但也可以创建自定义的constraintvalidator。

要求 Required

这个注解验证值是否非空。这包括对null以及其他几种情况的检查:空CharSequence对象、空数组、空Collection实例和空Map实例。

ValidHost

这个注释验证一个值是否对应于一个有效的主机名。这使用了与InetAddress::getByName相同的验证。

ValidPort

此注释验证值对应于0到65535之间的有效端口号。

转换器 Converters

PatternLayout使用转换器来呈现由转换模式标识的元素。每个转换器都必须在@Plugin注解上指定它的类别为“转换器”,有一个接受字符串数组作为唯一参数并返回转换器实例的静态newInstance方法,还必须有一个@ConverterKeys注解,其中包含转换器模式的数组,以便选择转换器。

用于处理LogEvents的转换器必须扩展LogEventPatternConverter类,并且必须实现一个接受LogEvent和StringBuilder作为参数的format方法。转换器应该将其操作的结果附加到StringBuilder对象。

第二种转换器是FileConverter——必须在@Plugin注解的category属性中指定“FileConverter”。

虽然类似于LogEventPatternConverter,但这些转换器将有两种变体,而不是单一的格式方法;一个接收一个对象,另一个接收一个对象数组而不是LogEvent。

它们都以与LogEventPatternConverter相同的方式附加到提供的StringBuilder中。RollingFileAppender通常使用这些转换器来构造要记录日志的文件名称。

如果多个转换器指定相同的ConverterKeys,那么上面的加载顺序将决定使用哪个。

例如,要覆盖内置DatePatternConverter类提供的%date转换器,你需要将插件放在log4j-core.JAR前面的类路径中的JAR文件中。我们不推荐这样做;模式ConverterKeys冲突将导致发出警告。尝试为你的自定义模式转换器使用唯一的ConverterKeys。

KeyProviders

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

Lookups 查找

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

TypeConverters

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

与其他插件不同,TypeConverter的插件名称纯粹是装饰性的。通过type接口而不是只有 Class<?> 对象。

请注意,TypeConverter插件必须有一个默认构造函数。当多个转换器匹配一个类型时,将返回第一个。如果有 Comparable<TypeConverter<?>>,它将用于确定顺序。

开发人员指出

如果插件类实现了Collection或Map,则不使用工厂方法。

相反,使用默认构造函数实例化类,并将所有子配置节点添加到集合或映射中。

参考资料

https://logging.apache.org/log4j/2.x/manual/plugins.html