日志子系统 AMQP Appender

该框架为一些流行的日志子系统提供了日志 Appender:

  • logback(自 Spring AMQP 1.4 版起)

  • log4j2(自 Spring AMQP 1.6 版起)

Appender 通过日志子系统的常规机制进行配置,可用属性在以下部分中指定。

常用属性

所有 Appender 都支持以下属性:

Table 1. 常用 Appender 属性
属性 默认值 描述
    exchangeName
    logs

用于发布日志事件的交换机名称。

    exchangeType
    topic

用于发布日志事件的交换机类型——仅当 Appender 声明交换机时才需要。 请参阅 declareExchange

    routingKeyPattern
    %c.%p

用于生成路由键的日志子系统模式格式。

    applicationId

应用程序 ID — 如果模式包含 %X{applicationId},则添加到路由键中。

    senderPoolSize
    2

用于发布日志事件的线程数。

    maxSenderRetries
    30

如果 Broker 不可用或出现其他错误,重试发送消息的次数。 重试延迟如下:N ^ log(N),其中 N 是重试次数。

    addresses

host:port[,host:port]* 形式的 Broker 地址的逗号分隔列表 - 覆盖 hostport

    host
    localhost

要连接的 RabbitMQ 主机。

    port
    5672

要连接的 RabbitMQ 端口。

    virtualHost
    /

要连接的 RabbitMQ 虚拟主机。

    username
    guest

连接时使用的 RabbitMQ 用户名。

    password
    guest

该用户的 RabbitMQ 密码。

    useSsl
    false

是否为 RabbitMQ 连接使用 SSL。 请参阅 RabbitConnectionFactoryBean 和配置 SSL

    verifyHostname
    true

为 TLS 连接启用服务器主机名验证。 请参阅 RabbitConnectionFactoryBean 和配置 SSL

    sslAlgorithm
    null

要使用的 SSL 算法。

    sslPropertiesLocation
    null

SSL 属性文件的位置。

    keyStore
    null

密钥库的位置。

    keyStorePassphrase
    null

密钥库的密码。

    keyStoreType
    JKS

密钥库类型。

    trustStore
    null

信任库的位置。

    trustStorePassphrase
    null

信任库的密码。

    trustStoreType
    JKS

信任库类型。

    saslConfig
    null (应用 RabbitMQ 客户端默认值)

saslConfig - 请参阅 RabbitUtils.stringToSaslConfig 的 javadoc 以获取有效值。

    contentType
    text/plain

日志消息的 content-type 属性。

    contentEncoding

日志消息的 content-encoding 属性。

    declareExchange
    false

此 Appender 启动时是否声明配置的交换机。 另请参阅 durableautoDelete

    durable
    true

declareExchangetrue 时,持久化标志设置为此值。

    autoDelete
    false

declareExchangetrue 时,自动删除标志设置为此值。

    charset
    null

String 转换为 byte[] 时使用的字符集。 默认值:null(使用系统默认字符集)。 如果当前平台不支持该字符集,则回退到使用系统字符集。

    deliveryMode
    PERSISTENT

PERSISTENTNON_PERSISTENT,用于确定 RabbitMQ 是否持久化消息。

    generateId
    false

用于确定 messageId 属性是否设置为唯一值。

    clientConnectionProperties
    null

用于 RabbitMQ 连接的自定义客户端属性的 key:value 对的逗号分隔列表。

    addMdcAsHeaders
    true

MDC 属性总是被添加到 RabbitMQ 消息头中,直到引入此属性。 这可能导致大 MDC 的问题,因为 RabbitMQ 对所有头部的缓冲区大小有限,而且这个缓冲区很小。 引入此属性是为了避免大 MDC 的情况下的问题。 默认情况下,此值设置为 true 以实现向后兼容。 false 关闭 MDC 到头部的序列化。 请注意,JsonLayout 默认将 MDC 添加到消息中。

Log4j 2 Appender

以下示例演示了如何配置 Log4j 2 Appender:

<Appenders>
    ...
    <RabbitMQ name="rabbitmq"
        addresses="foo:5672,bar:5672" user="guest" password="guest" virtualHost="/"
        exchange="log4j2" exchangeType="topic" declareExchange="true" durable="true" autoDelete="false"
        applicationId="myAppId" routingKeyPattern="%X{applicationId}.%c.%p"
        contentType="text/plain" contentEncoding="UTF-8" generateId="true" deliveryMode="NON_PERSISTENT"
        charset="UTF-8"
        senderPoolSize="3" maxSenderRetries="5"
        addMdcAsHeaders="false">
    </RabbitMQ>
</Appenders>

从 1.6.10 和 1.7.3 版本开始,默认情况下,log4j2 Appender 在调用线程上将消息发布到 RabbitMQ。 这是因为 Log4j 2 默认不创建线程安全事件。 如果 Broker 关闭,maxSenderRetries 用于重试,重试之间没有延迟。 如果您希望恢复以前的在单独线程 (senderPoolSize) 上发布消息的行为,可以将 async 属性设置为 true。 但是,您还需要配置 Log4j 2 使用 DefaultLogEventFactory 而不是 ReusableLogEventFactory。 一种方法是设置系统属性 -Dlog4j2.enable.threadlocals=false。 如果您使用 ReusableLogEventFactory 进行异步发布,事件由于交叉干扰而很可能损坏。

Logback Appender

以下示例演示了如何配置 logback Appender:

<appender name="AMQP" class="org.springframework.amqp.rabbit.logback.AmqpAppender">
    <layout>
        <pattern><![CDATA[ %d %p %t [%c] - <%m>%n ]]></pattern>
    </layout>
    <addresses>foo:5672,bar:5672</addresses>
    <abbreviation>36</abbreviation>
    <includeCallerData>false</includeCallerData>
    <applicationId>myApplication</applicationId>
    <routingKeyPattern>%property{applicationId}.%c.%p</routingKeyPattern>
    <generateId>true</generateId>
    <charset>UTF-8</charset>
    <durable>false</durable>
    <deliveryMode>NON_PERSISTENT</deliveryMode>
    <declareExchange>true</declareExchange>
    <addMdcAsHeaders>false</addMdcAsHeaders>
</appender>

从 1.7.1 版本开始,Logback AmqpAppender 提供了一个 includeCallerData 选项,默认为 false。 提取调用者数据可能相当昂贵,因为日志事件必须创建一个 throwable 并检查它以确定调用位置。 因此,默认情况下,当事件添加到事件队列时,与事件关联的调用者数据不会被提取。 您可以通过将 includeCallerData 属性设置为 true 来配置 Appender 以包含调用者数据。

从 2.0.0 版本开始,Logback AmqpAppender 支持 Logback 编码器 以及 encoder 选项。 encoderlayout 选项是互斥的。

自定义消息

默认情况下,AMQP Appender 填充以下消息属性:

  • deliveryMode

  • contentType

  • contentEncoding(如果已配置)

  • messageId(如果已配置 generateId

  • 日志事件的 timestamp

  • appId(如果已配置 applicationId)

此外,它们还使用以下值填充头部:

  • 日志事件的 categoryName

  • 日志事件的级别

  • thread:发生日志事件的线程名称

  • 日志事件调用的堆栈跟踪位置

  • 所有 MDC 属性的副本(除非 addMdcAsHeaders 设置为 false

每个 Appender 都可以被子类化,让您在发布之前修改消息。 以下示例演示了如何自定义日志消息:

public class MyEnhancedAppender extends AmqpAppender {

    @Override
    public Message postProcessMessageBeforeSend(Message message, Event event) {
        message.getMessageProperties().setHeader("foo", "bar");
        return message;
    }

}

从 2.2.4 版本开始,log4j2 AmqpAppender 可以使用 @PluginBuilderFactory 并扩展 AmqpAppender.Builder 来扩展。

@Plugin(name = "MyEnhancedAppender", category = "Core", elementType = "appender", printObject = true)
public class MyEnhancedAppender extends AmqpAppender {

	public MyEnhancedAppender(String name, Filter filter, Layout<? extends Serializable> layout,
			boolean ignoreExceptions, AmqpManager manager, BlockingQueue<Event> eventQueue, String foo, String bar) {
		super(name, filter, layout, ignoreExceptions, manager, eventQueue);

	@Override
	public Message postProcessMessageBeforeSend(Message message, Event event) {
			message.getMessageProperties().setHeader("foo", "bar");
		return message;
	}

	@PluginBuilderFactory
	public static Builder newBuilder() {
		return new Builder();
	}

	protected static class Builder extends AmqpAppender.Builder {

		@Override
		protected AmqpAppender buildInstance(String name, Filter filter, Layout<? extends Serializable> layout,
				boolean ignoreExceptions, AmqpManager manager, BlockingQueue<Event> eventQueue) {
			return new MyEnhancedAppender(name, filter, layout, ignoreExceptions, manager, eventQueue);
		}
	}

}

自定义客户端属性

您可以通过添加字符串属性或更复杂的属性来添加自定义客户端属性。

简单字符串属性

每个 Appender 都支持向 RabbitMQ 连接添加客户端属性。

以下示例演示了如何为 logback 添加自定义客户端属性:

<appender name="AMQP" ...>
    ...
    <clientConnectionProperties>thing1:thing2,cat:hat</clientConnectionProperties>
    ...
</appender>
log4j2
<Appenders>
    ...
    <RabbitMQ name="rabbitmq"
        ...
        clientConnectionProperties="thing1:thing2,cat:hat"
        ...
    </RabbitMQ>
</Appenders>

这些属性是 key:value 对的逗号分隔列表。 键和值不能包含逗号或冒号。

当查看连接时,这些属性会显示在 RabbitMQ Admin UI 上。

Logback 的高级技术

您可以子类化 Logback Appender。 这样做可以让您在建立连接之前修改客户端连接属性。 以下示例演示了如何执行此操作:

public class MyEnhancedAppender extends AmqpAppender {

    private String thing1;

    @Override
    protected void updateConnectionClientProperties(Map<String, Object> clientProperties) {
        clientProperties.put("thing1", this.thing1);
    }

    public void setThing1(String thing1) {
        this.thing1 = thing1;
    }

}

然后您可以将 <thing1>thing2</thing1> 添加到 logback.xml。

对于如上例所示的字符串属性,可以使用前面的技术。 子类允许添加更丰富的属性(例如添加 Map 或数字属性)。

提供自定义队列实现

AmqpAppenders 使用 BlockingQueue 异步地将日志事件发布到 RabbitMQ。 默认情况下,使用 LinkedBlockingQueue。 但是,您可以提供任何类型的自定义 BlockingQueue 实现。

以下示例演示了如何为 Logback 执行此操作:

public class MyEnhancedAppender extends AmqpAppender {

    @Override
    protected BlockingQueue<Event> createEventQueue() {
        return new ArrayBlockingQueue();
    }

}

Log4j 2 Appender 支持使用 BlockingQueueFactory,如下例所示:

<Appenders>
    ...
    <RabbitMQ name="rabbitmq"
              bufferSize="10" ... >
        <ArrayBlockingQueue/>
    </RabbitMQ>
</Appenders>