Skip to content

Commit 6ec3c67

Browse files
committed
Only stop subcomponents that were started directly.
Each component now keeps track of which subcomponents it started, and only stops those that it started directly. Allows lifecycle of subcomponents to be managed outside of the components that use them (e.g. for programmatic configuration, and reuse of subcomponents)
1 parent 1a7b1e5 commit 6ec3c67

17 files changed

+392
-69
lines changed
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/**
2+
* Licensed under the Apache License, Version 2.0 (the "License");
3+
* you may not use this file except in compliance with the License.
4+
* You may obtain a copy of the License at
5+
*
6+
* http://www.apache.org/licenses/LICENSE-2.0
7+
*
8+
* Unless required by applicable law or agreed to in writing, software
9+
* distributed under the License is distributed on an "AS IS" BASIS,
10+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11+
* See the License for the specific language governing permissions and
12+
* limitations under the License.
13+
*/
14+
package net.logstash.logback;
15+
16+
import java.util.Collections;
17+
import java.util.HashSet;
18+
import java.util.Set;
19+
import java.util.concurrent.ConcurrentHashMap;
20+
21+
import ch.qos.logback.core.spi.LifeCycle;
22+
23+
/**
24+
* Manages the lifecycle of subcomponents.
25+
*
26+
* <p>Specifically:</p>
27+
*
28+
* <ul>
29+
* <li>Only starts a subcomponent if the subcomponent is not already started.</li>
30+
* <li>Only stops a subcomponent if this lifecycle manager started the subcomponent.</li>
31+
* </ul>
32+
*/
33+
public class LifeCycleManager {
34+
35+
private final Set<LifeCycle> started = Collections.newSetFromMap(new ConcurrentHashMap<>());
36+
37+
/**
38+
* Starts the given lifecycle component if and only if it is not already started.
39+
*
40+
* @param lifeCycle the component to start
41+
* @return true if this method execution started the component
42+
*/
43+
public boolean start(LifeCycle lifeCycle) {
44+
if (lifeCycle.isStarted()) {
45+
return false;
46+
}
47+
lifeCycle.start();
48+
started.add(lifeCycle);
49+
return true;
50+
}
51+
52+
/**
53+
* Stops the given lifecycle component if and only if it is currently started,
54+
* AND was started by this lifecycle manager via {@link #start(LifeCycle)}.
55+
*
56+
* @param lifeCycle the component to stop
57+
* @return true if this method execution stopped the component
58+
*/
59+
public boolean stop(LifeCycle lifeCycle) {
60+
if (!lifeCycle.isStarted()) {
61+
return false;
62+
}
63+
if (!started.remove(lifeCycle)) {
64+
return false;
65+
}
66+
lifeCycle.stop();
67+
return true;
68+
}
69+
70+
/**
71+
* Stops all of the lifecycle components that were started by {@link #start(LifeCycle)}
72+
* and are currently started.
73+
*
74+
* @return the lifecycle components that this method execution stopped
75+
*/
76+
public Set<LifeCycle> stopAll() {
77+
Set<LifeCycle> stopped = new HashSet<>();
78+
79+
for (LifeCycle lifeCycle : started) {
80+
if (lifeCycle.isStarted()) {
81+
lifeCycle.stop();
82+
stopped.add(lifeCycle);
83+
}
84+
}
85+
86+
started.clear();
87+
return Collections.unmodifiableSet(stopped);
88+
}
89+
90+
91+
}

src/main/java/net/logstash/logback/appender/AbstractLogstashTcpSocketAppender.java

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import javax.net.ssl.SSLSocket;
4343
import javax.net.ssl.SSLSocketFactory;
4444

45+
import net.logstash.logback.LifeCycleManager;
4546
import net.logstash.logback.appender.destination.DelegateDestinationConnectionStrategy;
4647
import net.logstash.logback.appender.destination.DestinationConnectionStrategy;
4748
import net.logstash.logback.appender.destination.DestinationParser;
@@ -359,6 +360,11 @@ private class TcpSendingEventHandler implements EventHandler<LogEvent<Event>>, L
359360
*/
360361
private Future<?> readerFuture;
361362

363+
/**
364+
* Manages the lifecycle of subcomponents
365+
*/
366+
private final LifeCycleManager lifecycleManager = new LifeCycleManager();
367+
362368
/**
363369
* When run, if the {@link AbstractLogstashTcpSocketAppender#keepAliveDuration}
364370
* has elapsed since the last event was sent,
@@ -631,6 +637,7 @@ private boolean hasKeepAliveDurationElapsed(long lastSentNanoTime, long currentN
631637
public void onStart() {
632638
this.destinationAttemptStartTimes = new long[destinations.size()];
633639
openSocket();
640+
lifecycleManager.start(encoder);
634641
scheduleKeepAlive(System.nanoTime());
635642
scheduleWriteTimeout();
636643
}
@@ -639,7 +646,7 @@ public void onStart() {
639646
public void onShutdown() {
640647
unscheduleWriteTimeout();
641648
unscheduleKeepAlive();
642-
closeEncoder();
649+
lifecycleManager.stop(encoder);
643650
closeSocket();
644651
}
645652

@@ -788,10 +795,6 @@ private synchronized void closeSocket() {
788795
}
789796
}
790797

791-
private void closeEncoder() {
792-
encoder.stop();
793-
}
794-
795798
private synchronized void scheduleKeepAlive(long basedOnNanoTime) {
796799
if (isKeepAliveEnabled() && !Thread.currentThread().isInterrupted()) {
797800
if (keepAliveRunnable == null) {
@@ -967,10 +970,6 @@ public synchronized void start() {
967970
if (errorCount == 0) {
968971

969972
encoder.setContext(getContext());
970-
if (!encoder.isStarted()) {
971-
encoder.start();
972-
}
973-
974973
/*
975974
* Increase the core size to handle the reader thread
976975
*/

src/main/java/net/logstash/logback/appender/AbstractLogstashUdpSocketAppender.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import ch.qos.logback.core.net.SyslogAppenderBase;
2525
import ch.qos.logback.core.net.SyslogOutputStream;
2626
import ch.qos.logback.core.spi.DeferredProcessingAware;
27+
import net.logstash.logback.LifeCycleManager;
2728
import net.logstash.logback.appender.listener.AppenderListener;
2829

2930
/**
@@ -40,6 +41,11 @@ public class AbstractLogstashUdpSocketAppender<Event extends DeferredProcessingA
4041

4142
private SyslogOutputStream syslogOutputStream;
4243

44+
/**
45+
* Manages the lifecycle of subcomponents
46+
*/
47+
private final LifeCycleManager lifecycleManager = new LifeCycleManager();
48+
4349
public AbstractLogstashUdpSocketAppender() {
4450
setFacility("NEWS"); // NOTE: this value is never used
4551
}
@@ -53,15 +59,15 @@ public void start() {
5359

5460
super.start();
5561
if (isStarted()) {
56-
getLayout().start();
62+
lifecycleManager.start(getLayout());
5763
fireAppenderStarted();
5864
}
5965
}
6066

6167
@Override
6268
public void stop() {
6369
super.stop();
64-
getLayout().stop();
70+
lifecycleManager.stop(getLayout());
6571
fireAppenderStopped();
6672
}
6773

src/main/java/net/logstash/logback/appender/AsyncDisruptorAppender.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
import ch.qos.logback.core.status.OnConsoleStatusListener;
2929
import ch.qos.logback.core.status.Status;
30+
import net.logstash.logback.LifeCycleManager;
3031
import net.logstash.logback.appender.listener.AppenderListener;
3132
import ch.qos.logback.access.spi.IAccessEvent;
3233
import ch.qos.logback.classic.AsyncAppender;
@@ -248,6 +249,11 @@ public abstract class AsyncDisruptorAppender<Event extends DeferredProcessingAwa
248249
*/
249250
protected final List<Listener> listeners = new ArrayList<>();
250251

252+
/**
253+
* Manages the lifecycle of subcomponents
254+
*/
255+
private final LifeCycleManager lifecycleManager = new LifeCycleManager();
256+
251257
/**
252258
* Event wrapper object used for each element of the {@link RingBuffer}.
253259
*/
@@ -369,7 +375,7 @@ public void start() {
369375
statusListener.setLevelValue(Status.WARN);
370376
statusListener.setDelegate(new OnConsoleStatusListener());
371377
statusListener.setContext(getContext());
372-
statusListener.start();
378+
lifecycleManager.start(statusListener);
373379
getStatusManager().add(statusListener);
374380
}
375381

@@ -439,6 +445,8 @@ public void stop() {
439445
} catch (InterruptedException e) {
440446
addWarn("Some queued events have not been logged due to requested shutdown", e);
441447
}
448+
449+
lifecycleManager.stopAll();
442450
fireAppenderStopped();
443451
}
444452

src/main/java/net/logstash/logback/appender/DelegatingAsyncDisruptorAppender.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import ch.qos.logback.core.spi.AppenderAttachable;
3131
import ch.qos.logback.core.spi.AppenderAttachableImpl;
3232
import ch.qos.logback.core.spi.DeferredProcessingAware;
33+
import net.logstash.logback.LifeCycleManager;
3334
import net.logstash.logback.appender.listener.AppenderListener;
3435

3536
/**
@@ -52,7 +53,12 @@ public abstract class DelegatingAsyncDisruptorAppender<Event extends DeferredPro
5253
* The delegate appenders.
5354
*/
5455
private final AppenderAttachableImpl<Event> appenders = new AppenderAttachableImpl<Event>();
55-
56+
57+
/**
58+
* Manages the lifecycle of subcomponents
59+
*/
60+
private final LifeCycleManager lifecycleManager = new LifeCycleManager();
61+
5662
private class DelegatingEventHandler implements EventHandler<LogEvent<Event>> {
5763
/**
5864
* Whether exceptions should be reported with a error status or not.
@@ -142,18 +148,14 @@ private void startDelegateAppenders() {
142148
if (appender.getContext() == null) {
143149
appender.setContext(getContext());
144150
}
145-
if (!appender.isStarted()) {
146-
appender.start();
147-
}
151+
lifecycleManager.start(appender);
148152
}
149153
}
150154

151155
private void stopDelegateAppenders() {
152156
for (Iterator<Appender<Event>> appenderIter = appenders.iteratorForAppenders(); appenderIter.hasNext();) {
153157
Appender<Event> appender = appenderIter.next();
154-
if (appender.isStarted()) {
155-
appender.stop();
156-
}
158+
lifecycleManager.stop(appender);
157159
}
158160
}
159161

src/main/java/net/logstash/logback/composite/AbstractNestedJsonProvider.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import ch.qos.logback.access.spi.IAccessEvent;
2222
import ch.qos.logback.classic.spi.ILoggingEvent;
2323
import ch.qos.logback.core.spi.DeferredProcessingAware;
24+
import net.logstash.logback.LifeCycleManager;
2425

2526
/**
2627
* A {@link JsonProvider} that nests other providers within a subobject.
@@ -35,21 +36,26 @@ public abstract class AbstractNestedJsonProvider<Event extends DeferredProcessin
3536
* The providers that are used to populate the output nested JSON object.
3637
*/
3738
private JsonProviders<Event> jsonProviders = new JsonProviders<Event>();
38-
39+
40+
/**
41+
* Manages the lifecycle of subcomponents
42+
*/
43+
private final LifeCycleManager lifecycleManager = new LifeCycleManager();
44+
3945
public AbstractNestedJsonProvider() {
4046
setFieldName(FIELD_NESTED);
4147
}
4248

4349
@Override
4450
public void start() {
4551
super.start();
46-
getProviders().start();
52+
lifecycleManager.start(getProviders());
4753
}
4854

4955
@Override
5056
public void stop() {
5157
super.stop();
52-
getProviders().stop();
58+
lifecycleManager.stop(getProviders());
5359
}
5460

5561
@Override

src/main/java/net/logstash/logback/composite/CompositeJsonFormatter.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import java.lang.ref.SoftReference;
2020
import java.util.ServiceConfigurationError;
2121

22+
import net.logstash.logback.LifeCycleManager;
2223
import net.logstash.logback.decorate.JsonFactoryDecorator;
2324
import net.logstash.logback.decorate.JsonGeneratorDecorator;
2425
import net.logstash.logback.decorate.NullJsonFactoryDecorator;
@@ -86,6 +87,11 @@ protected SoftReference<BufferRecycler> initialValue() {
8687
*/
8788
private JsonProviders<Event> jsonProviders = new JsonProviders<Event>();
8889

90+
/**
91+
* Manages the lifecycle of subcomponents
92+
*/
93+
private final LifeCycleManager lifecycleManager = new LifeCycleManager();
94+
8995
private JsonEncoding encoding = JsonEncoding.UTF8;
9096

9197
private boolean findAndRegisterJacksonModules = true;
@@ -104,13 +110,13 @@ public void start() {
104110
jsonFactory = createJsonFactory();
105111
jsonProviders.setContext(context);
106112
jsonProviders.setJsonFactory(jsonFactory);
107-
jsonProviders.start();
113+
lifecycleManager.start(jsonProviders);
108114
started = true;
109115
}
110116

111117
@Override
112118
public void stop() {
113-
jsonProviders.stop();
119+
lifecycleManager.stop(jsonProviders);
114120
started = false;
115121
}
116122

src/main/java/net/logstash/logback/composite/GlobalCustomFieldsJsonProvider.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,5 +98,8 @@ public void setCustomFieldsNode(JsonNode customFields) {
9898
@Override
9999
public void setJsonFactory(JsonFactory jsonFactory) {
100100
this.jsonFactory = jsonFactory;
101+
if (isStarted()) {
102+
initializeCustomFields();
103+
}
101104
}
102105
}

0 commit comments

Comments
 (0)