Skip to content

Commit 028b28a

Browse files
committed
GH-1225: Fix Log4j2 Appender Termination
Replaces #1225 `manager.stop()` was never called to destroy the connection factory, preventing JVM exit. Also protect for re-connecting after stop (both appenders). Tested with a Spring Boot application. **cherry-pick to 2.2.x, 2.1.x, 1.7.x**
1 parent 11c4367 commit 028b28a

File tree

2 files changed

+24
-6
lines changed

2 files changed

+24
-6
lines changed

spring-rabbit/src/main/java/org/springframework/amqp/rabbit/log4j2/AmqpAppender.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,15 @@
5555
import org.springframework.amqp.core.MessageDeliveryMode;
5656
import org.springframework.amqp.core.MessageProperties;
5757
import org.springframework.amqp.core.TopicExchange;
58-
import org.springframework.amqp.rabbit.connection.AbstractConnectionFactory;
5958
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
6059
import org.springframework.amqp.rabbit.connection.RabbitConnectionFactoryBean;
6160
import org.springframework.amqp.rabbit.core.DeclareExchangeConnectionListener;
6261
import org.springframework.amqp.rabbit.core.RabbitAdmin;
6362
import org.springframework.amqp.rabbit.core.RabbitTemplate;
6463
import org.springframework.amqp.rabbit.support.LogAppenderUtils;
64+
import org.springframework.context.ApplicationContext;
65+
import org.springframework.context.event.ContextClosedEvent;
66+
import org.springframework.context.support.GenericApplicationContext;
6567
import org.springframework.retry.RetryPolicy;
6668
import org.springframework.retry.policy.SimpleRetryPolicy;
6769
import org.springframework.retry.support.RetryTemplate;
@@ -308,6 +310,9 @@ protected void doSend(final Event event, LogEvent logEvent, MessageProperties am
308310
message = postProcessMessageBeforeSend(message, event);
309311
this.rabbitTemplate.send(this.manager.exchangeName, routingKey, message);
310312
}
313+
catch (IllegalStateException e) {
314+
getHandler().error("Could not send log message " + logEvent.getMessage() + " appender is stopped");
315+
}
311316
catch (AmqpException e) {
312317
int retries = event.incrementRetries();
313318
if (this.manager.async && retries < this.manager.maxSenderRetries) {
@@ -334,7 +339,7 @@ public void run() {
334339
@Override
335340
protected boolean stop(long timeout, TimeUnit timeUnit, boolean changeLifeCycleState) {
336341
boolean stopped = super.stop(timeout, timeUnit, changeLifeCycleState);
337-
return stopped & this.manager.stop(timeout, timeUnit);
342+
return this.manager.stop(timeout, timeUnit) || stopped;
338343
}
339344

340345
/**
@@ -397,6 +402,8 @@ public int incrementRetries() {
397402

398403
protected static class AmqpManager extends AbstractManager {
399404

405+
private final ApplicationContext context = new GenericApplicationContext();
406+
400407
/**
401408
* True to send events on separate threads.
402409
*/
@@ -440,7 +447,7 @@ protected static class AmqpManager extends AbstractManager {
440447
/**
441448
* RabbitMQ ConnectionFactory.
442449
*/
443-
private AbstractConnectionFactory connectionFactory;
450+
private CachingConnectionFactory connectionFactory;
444451

445452
/**
446453
* RabbitMQ host to connect to.
@@ -537,6 +544,7 @@ private boolean activateOptions() {
537544
.replaceAll("%X\\{applicationId\\}", this.applicationId),
538545
null, null, null, Charset.forName(this.charset), false, true, null, null);
539546
this.connectionFactory = new CachingConnectionFactory(createRabbitConnectionFactory());
547+
this.connectionFactory.setApplicationContext(this.context);
540548
if (this.addresses != null) {
541549
this.connectionFactory.setAddresses(this.addresses);
542550
}
@@ -588,6 +596,7 @@ protected boolean releaseSub(long timeout, TimeUnit timeUnit) {
588596
this.retryTimer.cancel();
589597
this.senderPool.shutdownNow();
590598
this.connectionFactory.destroy();
599+
this.connectionFactory.onApplicationEvent(new ContextClosedEvent(this.context));
591600
try {
592601
return this.senderPool.awaitTermination(timeout, timeUnit);
593602
}

spring-rabbit/src/main/java/org/springframework/amqp/rabbit/logback/AmqpAppender.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2014-2017 the original author or authors.
2+
* Copyright 2014-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -38,12 +38,14 @@
3838
import org.springframework.amqp.core.MessageDeliveryMode;
3939
import org.springframework.amqp.core.MessageProperties;
4040
import org.springframework.amqp.core.TopicExchange;
41-
import org.springframework.amqp.rabbit.connection.AbstractConnectionFactory;
4241
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
4342
import org.springframework.amqp.rabbit.core.DeclareExchangeConnectionListener;
4443
import org.springframework.amqp.rabbit.core.RabbitAdmin;
4544
import org.springframework.amqp.rabbit.core.RabbitTemplate;
4645
import org.springframework.amqp.rabbit.support.LogAppenderUtils;
46+
import org.springframework.context.ApplicationContext;
47+
import org.springframework.context.event.ContextClosedEvent;
48+
import org.springframework.context.support.GenericApplicationContext;
4749

4850
import ch.qos.logback.classic.Level;
4951
import ch.qos.logback.classic.PatternLayout;
@@ -104,6 +106,8 @@ public class AmqpAppender extends AppenderBase<ILoggingEvent> {
104106
*/
105107
public static final String THREAD_NAME = "thread";
106108

109+
private final ApplicationContext context = new GenericApplicationContext();
110+
107111
/**
108112
* Name of the exchange to publish log events to.
109113
*/
@@ -162,7 +166,7 @@ public class AmqpAppender extends AppenderBase<ILoggingEvent> {
162166
/**
163167
* RabbitMQ ConnectionFactory.
164168
*/
165-
private AbstractConnectionFactory connectionFactory;
169+
private CachingConnectionFactory connectionFactory;
166170

167171
/**
168172
* Additional client connection properties added to the rabbit connection, with the form
@@ -472,6 +476,7 @@ public void start() {
472476
this.connectionFactory.setUsername(this.username);
473477
this.connectionFactory.setPassword(this.password);
474478
this.connectionFactory.setVirtualHost(this.virtualHost);
479+
this.connectionFactory.setApplicationContext(this.context);
475480
LogAppenderUtils.updateClientConnectionProperties(this.connectionFactory, this.clientConnectionProperties);
476481
updateConnectionClientProperties(this.connectionFactory.getRabbitConnectionFactory().getClientProperties());
477482
setUpExchangeDeclaration();
@@ -502,6 +507,7 @@ public void stop() {
502507
}
503508
if (null != this.connectionFactory) {
504509
this.connectionFactory.destroy();
510+
this.connectionFactory.onApplicationEvent(new ContextClosedEvent(this.context));
505511
}
506512
this.retryTimer.cancel();
507513
this.routingKeyLayout.stop();
@@ -640,6 +646,9 @@ public void run() {
640646
message = postProcessMessageBeforeSend(message, event);
641647
rabbitTemplate.send(AmqpAppender.this.exchangeName, routingKey, message);
642648
}
649+
catch (IllegalStateException e) {
650+
addError("Could not send log message " + logEvent.getMessage() + " appender is stopped");
651+
}
643652
catch (AmqpException e) {
644653
int retries = event.incrementRetries();
645654
if (retries < AmqpAppender.this.maxSenderRetries) {

0 commit comments

Comments
 (0)