Log4j2分析与实践-插件

简介

    Log4j1可以通过在配置声明中添加类属性来进行扩展。在有些元素中,特别是PatternLayout,添加新的模式转换器(pattern converters)的唯一方法是继承PatternLayout这个类,然后通过代码添加转换器。Log4j2的一个目标就是通过使用插件来使得扩展变得非常容易。

    在Log4j2中,通过在类上面添加@Plugin注解来定义一个插件。在初始化期间,Configuration会调用PluginManager来加载Log4j编译好的插件和自定义插件。PluginManager会在下面五个地方去查找插件:

(1)classpath下序列化的插件文件。这些文件是在构建之初自动创建的。
(2)(仅OSGi)在每个激活的OSGi bundle下序列化的插件中。
(3)系统属性log4j.plugin.packages指定的包列表。
(4)传递给静态方法PluginManager.addPackages的包(会在Log4j配置文件生效之前)。
(5)Log4j2配置文件中定义的packages属性。
    如果指定的多个插件有相同的名字name,那么就会按照以上顺序进行加载,最后一个才生效。


核心插件

    核心插件就是在配置文件中可以直接使用的Appender、Layout、Logger或者Filter。也可以在配置文件中引用下文将介绍的自定义插件。
    每个核心插件都必须定义一个使用注解@PluginFactory或者@PluginBuilderFactory标注的静态方法。前者用于静态工厂方法,提供了所有的可选项,比如方法参数;后者用于构建一个新的Builder<T>类,它的字段用于注入到属性或者子节点中。为了使得Configuration能够正确地向方法传递参数,方法中的每个参数必须使用下文所列属性注解中其中一个进行标注。每个属性或者元素注解必须包含一个名称,这个名称需要和配置文件中的相对应。对于插件构建器,如果没有在注解中指定名称的话,会默认使用字段的名称。



属性注解

PluginAttribute
    参数必须满足:可以使用TypeConverter工具由String类型的数据转换而来。Log4j2已提供的大多数类型都已经支持,也可以自定义TypeConverter来提供更多的类型支持。PluginBuilderAttribute可以在构建器类的字段上使用,可以方便地设置默认值。
PluginElement
    参数可能是个复杂的对象,它自己有一些可以配置的参数。这也支持注入一个元素数组。
PluginConfiguration
    当前的Configuration对象会作为参数传递给插件。
PluginNode
    当前解析的节点会作为参数传递给插件。
PluginValue
    当前节点或者属性的值。


约束验证器
    约束验证器参考了Bean Validation规范的思想,它会在运行时对插件工厂字段和参数自动进行校验。下面的注解是由Log4j2提供的,也可以创建自定义的约束 验证器。
Required
    这个注解用于验证一个值为非空。它做以下具体的验证:null、空CharSequence对象、空数组、空集合和空的Map。
ValidHost
    这个注解用于校验给定值为一个有效的主机名,和InetAddress.getByName校验方式相同。
ValidPort
    这个注解用于校验给定值为介于0到65535之间的有效端口。


Converters

    Converter被PatternLayout用来渲染被转换模式(conversion pattern)标记的元素。每个Converter必须把@Plugin注解中的category参数指定为"Converter",还需要有个newInstance方法,这个方法仅接收一个字符数组作为参数,然后返回一个Converter实例。Converter还需要有一个@ConverterKeys注解,它包含了字符串数组作为参数标识,可以使得Converter被选中。如果Converter打算处理LogEvent,那么必须继承LogEventPatternConverter类,且实现一个format方法,这个方法接收一个LogEvent和一个StringBuilder类型的参数。Converter应该将操作结果append到StringBuilder上面。
    还有一种类型的Converter就是FileConverter,必须将@Plugin注解中的category参数设置为"FileConverter"。跟LogEventPatternConverter类似,不过它有两个format方法,一个接收Object类型的参数,另一个接收Object数组参数,它们都接收StringBuilder参数,且都会将结果append到StringBuilder上面。典型的应用就是在RollingAppender中,这类型的Converter被用来构建日志文件的名称。
    如果多个Converter被指定了相同的ConverterKeys,那么前文所述的加载顺序将会决定最终使用哪一个。比如,为了覆盖%date 转换器(由DatePatternConverter提供),可以在配置文件中的packages参数指定自定义的差距包路径,但是不建议这样做,这样的冲突也会导致警告信息,尽量使用唯一的ConverterKeys。



KeyProviders

    Log4j的一些组件也许会提供数据加密的功能,这些组件需要一个secret key来执行加密操作。应用可以通过创建一个实现SecretKeyProvider接口的类来提供这个key。


Lookups

    Lookups也许是最简单的插件。它必须实现StrLookup接口,并且需要在@Plugin注解上将类型定义为"Lookup"。它会有两个lookup方法,一个lookup方法接收一个String类型的key,返回String类型的value;另一个lookup方法接收一个logEvent和一个String类型的key,返回一个String。Lookups可以通过${name:key}来进行引用,name就是@Plugin注解的名字,key就是需要查找的项目。


TypeConverters

    TypeConverter某种程度上来说是一种元插件,可以将插件工厂方法中的String参数转换为其它类型。一些插件已经可以通过@PluginElement注解进行注入,现在,任何支持类型转换系统的参数都可以使用@PluginAttribute。枚举类型的转换已经支持了,不需要自定义的TypeConverter。
跟其它插件不同,TypeConverter插件名称仅仅是个摆设,类型转换器是通过Type接口来进行查找的。注意,TypeConverter插件必须有个默认的构造器。