跳到主要内容
版本:4.1.1

日志子系统 AMQP Appender

Doubao 中英对照 Logging Subsystem AMQP Appenders

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

  • logback(自 Spring AMQP 1.4 版起)
  • log4j2(自 Spring AMQP 1.6 版起)

这些 appender 通过日志子系统的常规机制进行配置,可用属性在以下各节中说明。

常用属性​

以下属性对所有 appender 都可用:

表 1. 常用 Appender 属性

属性默认值描述
exchangeNamelogs要发布日志事件的 exchange 名称。
exchangeTypetopic要发布日志事件的 exchange 类型——仅当 appender 声明该 exchange 时才需要。参见 declareExchange。
routingKeyPattern%c.%p用于生成 routing key 的日志子系统 pattern 格式。
applicationIdApplication ID——如果 pattern 包含 %X{applicationId},则将其添加到 routing key。
senderPoolSize2用于发布日志事件的线程数。
maxSenderRetries30当 broker 不可用或出现其他错误时,重试发送消息的次数。重试延迟如下:N ^ log(N),其中 N 是重试次数。
addresses以以下形式给出的、逗号分隔的 broker 地址列表:host:port[,host:port]*——覆盖 host 和 port。
hostlocalhost要连接的 RabbitMQ host。
port5672要连接的 RabbitMQ 端口。
virtualHost/要连接的 RabbitMQ virtual host。
usernameguest连接时使用的 RabbitMQ 用户。
passwordguest该用户的 RabbitMQ 密码。
useSslfalse是否对 RabbitMQ 连接使用 SSL。参见 RabbitConnectionFactoryBean 与配置 SSL
verifyHostnametrue为 TLS 连接启用服务器主机名验证。参见 RabbitConnectionFactoryBean 与配置 SSL
sslAlgorithmnull要使用的 SSL 算法。
sslPropertiesLocationnullSSL 属性文件的位置。
keyStorenullkeystore 的位置。
keyStorePassphrasenullkeystore 的口令。
keyStoreTypeJKSkeystore 类型。
trustStorenulltruststore 的位置。
trustStorePassphrasenulltruststore 的口令。
trustStoreTypeJKStruststore 类型。
saslConfignull (RabbitMQ client default applies)saslConfig——有效值参见 RabbitUtils.stringToSaslConfig 的 javadoc。
contentTypetext/plain日志消息的 content-type 属性。
contentEncoding日志消息的 content-encoding 属性。
declareExchangefalse此 appender 启动时是否声明所配置的 exchange。另见 durable 和 autoDelete。
durabletrue当 declareExchange 为 true 时,durable 标志被设置为此值。
autoDeletefalse当 declareExchange 为 true 时,auto-delete 标志被设置为此值。
charsetnull将 String 转换为 byte[] 时使用的字符集。默认值:null(使用系统默认字符集)。如果当前平台不支持该字符集,则回退到使用系统字符集。
deliveryModePERSISTENTPERSISTENT 或 NON_PERSISTENT,用于决定 RabbitMQ 是否持久化消息。
generateIdfalse用于决定是否将 messageId 属性设置为唯一值。
clientConnectionPropertiesnull逗号分隔的 key:value 键值对列表,用于为 RabbitMQ 连接设置自定义 client 属性。
addMdcAsHeaderstrue在引入此属性之前,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>
important

从 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 下使用异步发布,事件很可能因串扰(cross-talk)而损坏。

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 通过 encoder 选项支持 Logback 编码器。encoder 和 layout 选项互斥。

自定义消息​

默认情况下,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);
}
}

}

自定义 Client 属性​

你可以通过添加字符串属性或更复杂的属性来添加自定义 client 属性。

简单字符串属性​

每个 appender 都支持向 RabbitMQ 连接添加 client 属性。

以下示例展示如何添加自定义 client 属性:

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

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

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

Logback 的高级技巧​

你可以子类化 Logback appender。这样做可以让你在连接建立之前修改 client 连接属性。以下示例展示如何这样做:

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 或数值属性)。

提供自定义队列实现​

AmqpAppender 使用 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>