Skip to content

Commit 9349d64

Browse files
committed
In response to issue 450, moved the binding of the MDCAdapter in the
MDC class from the static initializer to the LoggerFactory.bind method. In case MDC methods are called before LoggerFactory, the various methods in MDC have been modified to go through the getMDCAdapter method which checks for this rare case. #450 Signed-off-by: Ceki Gulcu <ceki@qos.ch>
1 parent 0def25e commit 9349d64

File tree

7 files changed

+110
-64
lines changed

7 files changed

+110
-64
lines changed

slf4j-api/src/main/java/org/slf4j/LoggerFactory.java

+15
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import org.slf4j.helpers.SubstituteLogger;
4848
import org.slf4j.helpers.SubstituteServiceProvider;
4949
import org.slf4j.helpers.Util;
50+
import org.slf4j.spi.MDCAdapter;
5051
import org.slf4j.spi.SLF4JServiceProvider;
5152

5253
/**
@@ -195,6 +196,7 @@ private final static void bind() {
195196
reportMultipleBindingAmbiguity(providersList);
196197
if (providersList != null && !providersList.isEmpty()) {
197198
PROVIDER = providersList.get(0);
199+
earlyBindMDCAdapter();
198200
// SLF4JServiceProvider.initialize() is intended to be called here and nowhere else.
199201
PROVIDER.initialize();
200202
INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
@@ -215,6 +217,19 @@ private final static void bind() {
215217
}
216218
}
217219

220+
/**
221+
* The value of PROVIDER.getMDCAdapter() can be null while PROVIDER has not yet initialized.
222+
*
223+
* However,
224+
*
225+
*/
226+
private static void earlyBindMDCAdapter() {
227+
MDCAdapter mdcAdapter = PROVIDER.getMDCAdapter();
228+
if(mdcAdapter != null) {
229+
MDC.setMDCAdapter(mdcAdapter);
230+
}
231+
}
232+
218233
static SLF4JServiceProvider loadExplicitlySpecified(ClassLoader classLoader) {
219234
String explicitlySpecified = System.getProperty(PROVIDER_PROPERTY_KEY);
220235
if (null == explicitlySpecified || explicitlySpecified.isEmpty()) {

slf4j-api/src/main/java/org/slf4j/MDC.java

+59-32
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,7 @@
2828
import java.util.Deque;
2929
import java.util.Map;
3030

31-
import org.slf4j.helpers.BasicMDCAdapter;
32-
import org.slf4j.helpers.NOPMDCAdapter;
33-
import org.slf4j.helpers.Reporter;
34-
import org.slf4j.helpers.Util;
31+
import org.slf4j.helpers.*;
3532
import org.slf4j.spi.MDCAdapter;
3633
import org.slf4j.spi.SLF4JServiceProvider;
3734

@@ -68,7 +65,7 @@ public class MDC {
6865
static final String NULL_MDCA_URL = "http://www.slf4j.org/codes.html#null_MDCA";
6966
private static final String MDC_APAPTER_CANNOT_BE_NULL_MESSAGE = "MDCAdapter cannot be null. See also " + NULL_MDCA_URL;
7067
static final String NO_STATIC_MDC_BINDER_URL = "http://www.slf4j.org/codes.html#no_static_mdc_binder";
71-
static MDCAdapter mdcAdapter;
68+
static MDCAdapter MDC_ADAPTER;
7269

7370
/**
7471
* An adapter to remove the key when done.
@@ -88,17 +85,30 @@ public void close() {
8885
private MDC() {
8986
}
9087

91-
static {
88+
private static MDCAdapter getMDCAdapterGivenByProvider() {
9289
SLF4JServiceProvider provider = LoggerFactory.getProvider();
93-
if (provider != null) {
90+
if(provider != null) {
91+
// If you wish to change the mdc adapter, setting the MDC.MDCAdapter variable might be insufficient.
92+
// Keep in mind that the provider *might* perform additional internal mdcAdapter assignments that
93+
// you would also need to replicate/adapt.
94+
9495
// obtain and attach the MDCAdapter from the provider
95-
// If you wish to change the adapter, Setting the MDC.mdcAdapter variable might not be enough as
96-
// the provider might perform additional assignments that you would need to replicate/adapt.
97-
mdcAdapter = provider.getMDCAdapter();
96+
97+
final MDCAdapter anAdapter = provider.getMDCAdapter();
98+
emitTemporaryMDCAdapterWarningIfNeeded(provider);
99+
return anAdapter;
98100
} else {
99101
Reporter.error("Failed to find provider.");
100102
Reporter.error("Defaulting to no-operation MDCAdapter implementation.");
101-
mdcAdapter = new NOPMDCAdapter();
103+
return new NOPMDCAdapter();
104+
}
105+
}
106+
107+
private static void emitTemporaryMDCAdapterWarningIfNeeded(SLF4JServiceProvider provider) {
108+
boolean isSubstitute = provider instanceof SubstituteServiceProvider;
109+
if(isSubstitute) {
110+
Reporter.info("Temporary mdcAdapter given by SubstituteServiceProvider.");
111+
Reporter.info("This mdcAdapter will be replaced after backend initialization has completed.");
102112
}
103113
}
104114

@@ -121,10 +131,10 @@ public static void put(String key, String val) throws IllegalArgumentException {
121131
if (key == null) {
122132
throw new IllegalArgumentException("key parameter cannot be null");
123133
}
124-
if (mdcAdapter == null) {
134+
if (getMDCAdapter() == null) {
125135
throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
126136
}
127-
mdcAdapter.put(key, val);
137+
getMDCAdapter().put(key, val);
128138
}
129139

130140
/**
@@ -177,10 +187,10 @@ public static String get(String key) throws IllegalArgumentException {
177187
throw new IllegalArgumentException("key parameter cannot be null");
178188
}
179189

180-
if (mdcAdapter == null) {
190+
if (getMDCAdapter() == null) {
181191
throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
182192
}
183-
return mdcAdapter.get(key);
193+
return getMDCAdapter().get(key);
184194
}
185195

186196
/**
@@ -198,20 +208,20 @@ public static void remove(String key) throws IllegalArgumentException {
198208
throw new IllegalArgumentException("key parameter cannot be null");
199209
}
200210

201-
if (mdcAdapter == null) {
211+
if (getMDCAdapter() == null) {
202212
throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
203213
}
204-
mdcAdapter.remove(key);
214+
getMDCAdapter().remove(key);
205215
}
206216

207217
/**
208218
* Clear all entries in the MDC of the underlying implementation.
209219
*/
210220
public static void clear() {
211-
if (mdcAdapter == null) {
221+
if (getMDCAdapter() == null) {
212222
throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
213223
}
214-
mdcAdapter.clear();
224+
getMDCAdapter().clear();
215225
}
216226

217227
/**
@@ -222,10 +232,10 @@ public static void clear() {
222232
* @since 1.5.1
223233
*/
224234
public static Map<String, String> getCopyOfContextMap() {
225-
if (mdcAdapter == null) {
235+
if (getMDCAdapter() == null) {
226236
throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
227237
}
228-
return mdcAdapter.getCopyOfContextMap();
238+
return getMDCAdapter().getCopyOfContextMap();
229239
}
230240

231241
/**
@@ -240,23 +250,40 @@ public static Map<String, String> getCopyOfContextMap() {
240250
* @since 1.5.1
241251
*/
242252
public static void setContextMap(Map<String, String> contextMap) {
243-
if (mdcAdapter == null) {
253+
if (getMDCAdapter() == null) {
244254
throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
245255
}
246-
mdcAdapter.setContextMap(contextMap);
256+
getMDCAdapter().setContextMap(contextMap);
247257
}
248258

249259
/**
250260
* Returns the MDCAdapter instance currently in use.
251-
*
261+
*
262+
* Since 2.0.17, if the MDCAdapter instance is null, then this method set it to use
263+
* the adapter returned by the SLF4JProvider. However, in the vast majority of cases
264+
* the MDCAdapter will be set earlier (during initialization) by {@link LoggerFactory}.
265+
*
252266
* @return the MDcAdapter instance currently in use.
253267
* @since 1.4.2
254268
*/
255269
public static MDCAdapter getMDCAdapter() {
256-
return mdcAdapter;
270+
if(MDC_ADAPTER == null) {
271+
MDC_ADAPTER = getMDCAdapterGivenByProvider();
272+
}
273+
return MDC_ADAPTER;
257274
}
258275

259-
276+
/**
277+
* Set MDCAdapter instance to use.
278+
*
279+
* @since 2.0.17
280+
*/
281+
static void setMDCAdapter(MDCAdapter anMDCAdapter) {
282+
if(anMDCAdapter == null) {
283+
throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
284+
}
285+
MDC_ADAPTER = anMDCAdapter;
286+
}
260287

261288
/**
262289
* Push a value into the deque(stack) referenced by 'key'.
@@ -266,10 +293,10 @@ public static MDCAdapter getMDCAdapter() {
266293
* @since 2.0.0
267294
*/
268295
static public void pushByKey(String key, String value) {
269-
if (mdcAdapter == null) {
296+
if (getMDCAdapter() == null) {
270297
throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
271298
}
272-
mdcAdapter.pushByKey(key, value);
299+
getMDCAdapter().pushByKey(key, value);
273300
}
274301

275302
/**
@@ -280,10 +307,10 @@ static public void pushByKey(String key, String value) {
280307
* @since 2.0.0
281308
*/
282309
static public String popByKey(String key) {
283-
if (mdcAdapter == null) {
310+
if (getMDCAdapter() == null) {
284311
throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
285312
}
286-
return mdcAdapter.popByKey(key);
313+
return getMDCAdapter().popByKey(key);
287314
}
288315

289316
/**
@@ -295,9 +322,9 @@ static public String popByKey(String key) {
295322
* @since 2.0.0
296323
*/
297324
public Deque<String> getCopyOfDequeByKey(String key) {
298-
if (mdcAdapter == null) {
325+
if (getMDCAdapter() == null) {
299326
throw new IllegalStateException(MDC_APAPTER_CANNOT_BE_NULL_MESSAGE);
300327
}
301-
return mdcAdapter.getCopyOfDequeByKey(key);
328+
return getMDCAdapter().getCopyOfDequeByKey(key);
302329
}
303330
}

slf4j-api/src/test/java/org/slf4j/basicTests/NoBindingTest.java

+12
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929

3030
import java.util.Random;
3131

32+
import org.junit.After;
33+
import org.junit.Before;
3234
import org.junit.Test;
3335
import org.slf4j.Logger;
3436
import org.slf4j.LoggerFactory;
@@ -37,11 +39,21 @@
3739
import org.slf4j.MarkerFactory;
3840
import org.slf4j.helpers.BasicMarker;
3941
import org.slf4j.helpers.NOPLogger;
42+
import org.slf4j.helpers.Reporter;
4043

4144
public class NoBindingTest {
4245

4346
int diff = new Random().nextInt(10000);
4447

48+
@Before
49+
public void setUp() throws Exception {
50+
System.setProperty(Reporter.SLF4J_INTERNAL_VERBOSITY_KEY, "debug");
51+
}
52+
@After
53+
public void tearDown() throws Exception {
54+
System.clearProperty(Reporter.SLF4J_INTERNAL_VERBOSITY_KEY);
55+
}
56+
4557
@Test
4658
public void testLogger() {
4759
Logger logger = LoggerFactory.getLogger(NoBindingTest.class);

slf4j-jdk14/src/main/java/org/slf4j/jul/JULServiceProvider.java

+2-4
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ public class JULServiceProvider implements SLF4JServiceProvider {
1717
public static String REQUESTED_API_VERSION = "2.0.99"; // !final
1818

1919
private ILoggerFactory loggerFactory;
20-
private IMarkerFactory markerFactory;
21-
private MDCAdapter mdcAdapter;
20+
private IMarkerFactory markerFactory = new BasicMarkerFactory();
21+
private MDCAdapter mdcAdapter = new BasicMDCAdapter();
2222

2323
@Override
2424
public ILoggerFactory getLoggerFactory() {
@@ -42,7 +42,5 @@ public String getRequestedApiVersion() {
4242
@Override
4343
public void initialize() {
4444
loggerFactory = new JDK14LoggerFactory();
45-
markerFactory = new BasicMarkerFactory();
46-
mdcAdapter = new BasicMDCAdapter();
4745
}
4846
}

slf4j-nop/src/test/java/org/slf4j/nop/MultithreadedInitializationTest.java

+18-19
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,28 @@
11
/**
22
* Copyright (c) 2004-2016 QOS.ch
33
* All rights reserved.
4-
*
4+
* <p>
55
* Permission is hereby granted, free of charge, to any person obtaining
66
* a copy of this software and associated documentation files (the
77
* "Software"), to deal in the Software without restriction, including
88
* without limitation the rights to use, copy, modify, merge, publish,
99
* distribute, sublicense, and/or sell copies of the Software, and to
1010
* permit persons to whom the Software is furnished to do so, subject to
1111
* the following conditions:
12-
*
12+
* <p>
1313
* The above copyright notice and this permission notice shall be
1414
* included in all copies or substantial portions of the Software.
15-
*
15+
* <p>
1616
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
1717
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
1818
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
1919
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
2020
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
2121
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
2222
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23-
*
2423
*/
2524
package org.slf4j.nop;
2625

27-
import static org.junit.Assert.assertEquals;
28-
import static org.slf4j.helpers.Reporter.SLF4J_INTERNAL_VERBOSITY_KEY;
29-
30-
import java.io.PrintStream;
31-
import java.util.ArrayList;
32-
import java.util.List;
33-
import java.util.Random;
34-
import java.util.concurrent.BrokenBarrierException;
35-
import java.util.concurrent.CyclicBarrier;
36-
import java.util.concurrent.atomic.AtomicLong;
37-
3826
import org.junit.After;
3927
import org.junit.Before;
4028
import org.junit.Test;
@@ -43,6 +31,15 @@
4331
import org.slf4j.LoggerFactoryFriend;
4432
import org.slf4j.helpers.StringPrintStream;
4533

34+
import java.io.PrintStream;
35+
import java.util.Random;
36+
import java.util.concurrent.BrokenBarrierException;
37+
import java.util.concurrent.CyclicBarrier;
38+
import java.util.concurrent.atomic.AtomicLong;
39+
40+
import static org.junit.Assert.assertEquals;
41+
import static org.slf4j.helpers.Reporter.SLF4J_INTERNAL_VERBOSITY_KEY;
42+
4643
public class MultithreadedInitializationTest {
4744

4845
static int NUM_LINES_IN_SLF4J_CONNECTED_WITH_PROVIDER_INFO = 1;
@@ -76,7 +73,7 @@ public void multiThreadedInitialization() throws InterruptedException, BrokenBar
7673
System.out.println("THREAD_COUNT=" + THREAD_COUNT);
7774
LoggerAccessingThread[] accessors = harness();
7875

79-
for (LoggerAccessingThread accessor : accessors) {
76+
for(LoggerAccessingThread accessor : accessors) {
8077
EVENT_COUNT.getAndIncrement();
8178
accessor.logger.info("post harness");
8279
}
@@ -91,13 +88,13 @@ public void multiThreadedInitialization() throws InterruptedException, BrokenBar
9188
private static LoggerAccessingThread[] harness() throws InterruptedException, BrokenBarrierException {
9289
LoggerAccessingThread[] threads = new LoggerAccessingThread[THREAD_COUNT];
9390
final CyclicBarrier barrier = new CyclicBarrier(THREAD_COUNT + 1);
94-
for (int i = 0; i < THREAD_COUNT; i++) {
91+
for(int i = 0; i < THREAD_COUNT; i++) {
9592
threads[i] = new LoggerAccessingThread(barrier, i);
9693
threads[i].start();
9794
}
9895

9996
barrier.await();
100-
for (int i = 0; i < THREAD_COUNT; i++) {
97+
for(int i = 0; i < THREAD_COUNT; i++) {
10198
threads[i].join();
10299
}
103100
return threads;
@@ -123,7 +120,9 @@ public void run() {
123120
logger.info("in run method");
124121
EVENT_COUNT.getAndIncrement();
125122
}
126-
};
123+
}
124+
125+
;
127126

128127
// public static class StringPrintStream extends PrintStream {
129128
//

0 commit comments

Comments
 (0)