25
25
import net .logstash .logback .decorate .JsonGeneratorDecorator ;
26
26
import net .logstash .logback .decorate .NullJsonFactoryDecorator ;
27
27
import net .logstash .logback .decorate .NullJsonGeneratorDecorator ;
28
- import net .logstash .logback .util .ObjectPool ;
29
28
import net .logstash .logback .util .ProxyOutputStream ;
29
+ import net .logstash .logback .util .ThreadLocalHolder ;
30
30
31
31
import ch .qos .logback .access .spi .IAccessEvent ;
32
32
import ch .qos .logback .classic .spi .ILoggingEvent ;
50
50
* and then ends the JSON object ('}').
51
51
*
52
52
* <p>Jackson {@link JsonGenerator} are initially created with a "disconnected" output stream so they can be
53
- * reused multiple times with different target output stream. They are kept in an internal pool whose
54
- * size is technically unbounded. It will however never hold more entries than the number of concurrent
55
- * threads accessing it. Entries are kept in the pool using soft references so they can be garbage
56
- * collected by the JVM when running low in memory.
53
+ * reused multiple times with different target output stream.
57
54
*
58
55
* <p>{@link JsonGenerator} instances are *not* reused after they threw an exception. This is to prevent
59
56
* reusing an instance whose internal state may be unpredictable.
@@ -93,7 +90,7 @@ public abstract class AbstractCompositeJsonFormatter<Event extends DeferredProce
93
90
94
91
private volatile boolean started ;
95
92
96
- private ObjectPool <JsonFormatter > pool ;
93
+ private ThreadLocalHolder <JsonFormatter > threadLocalJsonFormatter ;
97
94
98
95
99
96
public AbstractCompositeJsonFormatter (ContextAware declaredOrigin ) {
@@ -119,14 +116,14 @@ public void start() {
119
116
jsonProviders .setJsonFactory (jsonFactory );
120
117
jsonProviders .start ();
121
118
122
- pool = new ObjectPool <>(this ::createJsonFormatter );
119
+ threadLocalJsonFormatter = new ThreadLocalHolder <>(this ::createJsonFormatter );
123
120
started = true ;
124
121
}
125
122
126
123
@ Override
127
124
public void stop () {
128
125
if (isStarted ()) {
129
- pool . clear ();
126
+ threadLocalJsonFormatter . close ();
130
127
jsonProviders .stop ();
131
128
jsonFactory = null ;
132
129
started = false ;
@@ -152,7 +149,7 @@ public void writeEvent(Event event, OutputStream outputStream) throws IOExceptio
152
149
throw new IllegalStateException ("Formatter is not started" );
153
150
}
154
151
155
- try (JsonFormatter formatter = this .pool .acquire ()) {
152
+ try (JsonFormatter formatter = this .threadLocalJsonFormatter .acquire ()) {
156
153
formatter .writeEvent (outputStream , event );
157
154
}
158
155
}
@@ -174,7 +171,7 @@ private JsonFormatter createJsonFormatter() {
174
171
175
172
}
176
173
177
- private class JsonFormatter implements ObjectPool .Lifecycle , Closeable {
174
+ private class JsonFormatter implements ThreadLocalHolder .Lifecycle , Closeable {
178
175
private final JsonGenerator generator ;
179
176
private final DisconnectedOutputStream stream ;
180
177
private boolean recyclable = true ;
@@ -206,11 +203,18 @@ public boolean recycle() {
206
203
@ Override
207
204
public void dispose () {
208
205
CloseUtil .closeQuietly (this .generator );
206
+
207
+ // Note:
208
+ // The stream is disconnected at this point.
209
+ // Closing the JsonGenerator may throw additional exception if it is flagged as not recyclable,
210
+ // meaning it already threw a exception earlier during the writeEvent() method. The generator
211
+ // is disposed here and won't be reused anymore - we can safely ignore these new exceptions
212
+ // here.
209
213
}
210
214
211
215
@ Override
212
216
public void close () throws IOException {
213
- AbstractCompositeJsonFormatter .this .pool .release (this );
217
+ AbstractCompositeJsonFormatter .this .threadLocalJsonFormatter .release ();
214
218
}
215
219
}
216
220
0 commit comments