Skip to content

Commit a76505b

Browse files
authored
Add SLF4J MDC support for JUL (#79)
1 parent 4e690e0 commit a76505b

File tree

4 files changed

+103
-4
lines changed

4 files changed

+103
-4
lines changed

jul-ecs-formatter/pom.xml

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,17 @@
2727
<type>test-jar</type>
2828
<scope>test</scope>
2929
</dependency>
30+
<dependency>
31+
<groupId>org.slf4j</groupId>
32+
<artifactId>slf4j-api</artifactId>
33+
<version>1.7.30</version>
34+
<optional>true</optional>
35+
</dependency>
36+
<dependency>
37+
<groupId>org.slf4j</groupId>
38+
<artifactId>slf4j-jdk14</artifactId>
39+
<version>1.7.30</version>
40+
<scope>test</scope>
41+
</dependency>
3042
</dependencies>
31-
32-
</project>
43+
</project>

jul-ecs-formatter/src/main/java/co/elastic/logging/jul/EcsFormatter.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
public class EcsFormatter extends Formatter {
3434

3535
private static final String UNKNOWN_FILE = "<Unknown>";
36+
private static final MdcSupplier mdcSupplier = MdcSupplier.Resolver.INSTANCE.resolve();
3637

3738
private boolean stackTraceAsArray;
3839
private String serviceName;
@@ -57,6 +58,7 @@ public String format(final LogRecord record) {
5758
EcsJsonSerializer.serializeObjectStart(builder, record.getMillis());
5859
EcsJsonSerializer.serializeLogLevel(builder, record.getLevel().getName());
5960
EcsJsonSerializer.serializeFormattedMessage(builder, super.formatMessage(record));
61+
EcsJsonSerializer.serializeMDC(builder, mdcSupplier.getMDC());
6062
EcsJsonSerializer.serializeServiceName(builder, serviceName);
6163
EcsJsonSerializer.serializeEventDataset(builder, eventDataset);
6264
EcsJsonSerializer.serializeThreadId(builder, record.getThreadID());
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*-
2+
* #%L
3+
* Java ECS logging
4+
* %%
5+
* Copyright (C) 2019 - 2020 Elastic and contributors
6+
* %%
7+
* Licensed to Elasticsearch B.V. under one or more contributor
8+
* license agreements. See the NOTICE file distributed with
9+
* this work for additional information regarding copyright
10+
* ownership. Elasticsearch B.V. licenses this file to you under
11+
* the Apache License, Version 2.0 (the "License"); you may
12+
* not use this file except in compliance with the License.
13+
* You may obtain a copy of the License at
14+
*
15+
* http://www.apache.org/licenses/LICENSE-2.0
16+
*
17+
* Unless required by applicable law or agreed to in writing,
18+
* software distributed under the License is distributed on an
19+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
20+
* KIND, either express or implied. See the License for the
21+
* specific language governing permissions and limitations
22+
* under the License.
23+
* #L%
24+
*/
25+
package co.elastic.logging.jul;
26+
27+
import org.slf4j.MDC;
28+
29+
import java.util.Collections;
30+
import java.util.Map;
31+
32+
public interface MdcSupplier {
33+
Map<String, String> getMDC();
34+
35+
enum Resolver {
36+
INSTANCE;
37+
38+
MdcSupplier resolve() {
39+
try {
40+
Class.forName("org.slf4j.MDC");
41+
42+
// The SLF4J API dependency does not contain an MDC binder by itself, the MDC binders come from an SLF4j
43+
// implementation. When no MDC bindings are available calls to MDC.put will be ignored by slf4j.
44+
// That is why we want to ensure that the StaticMDCBinder exists
45+
Class.forName("org.slf4j.impl.StaticMDCBinder");
46+
return (MdcSupplier) Class.forName("co.elastic.logging.jul.MdcSupplier$Available").getEnumConstants()[0];
47+
} catch (Exception e) {
48+
return Unavailable.INSTANCE;
49+
} catch (LinkageError e ) {
50+
return Unavailable.INSTANCE;
51+
}
52+
}
53+
}
54+
55+
enum Available implements MdcSupplier {
56+
INSTANCE;
57+
58+
@Override
59+
public Map<String, String> getMDC() {
60+
Map<String, String> copyOfContextMap = MDC.getCopyOfContextMap();
61+
if (copyOfContextMap != null ) {
62+
return copyOfContextMap;
63+
}
64+
return Collections.emptyMap();
65+
}
66+
}
67+
68+
enum Unavailable implements MdcSupplier {
69+
INSTANCE;
70+
71+
Unavailable() {
72+
}
73+
74+
@Override
75+
public Map<String, String> getMDC() {
76+
return Collections.emptyMap();
77+
}
78+
}
79+
}

jul-ecs-formatter/src/test/java/co/elastic/logging/jul/JulLoggingTestTest.java renamed to jul-ecs-formatter/src/test/java/co/elastic/logging/jul/JulLoggingTest.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,9 @@
4848
import com.fasterxml.jackson.databind.JsonNode;
4949

5050
import co.elastic.logging.AbstractEcsLoggingTest;
51+
import org.slf4j.MDC;
5152

52-
public class JulLoggingTestTest extends AbstractEcsLoggingTest {
53+
public class JulLoggingTest extends AbstractEcsLoggingTest {
5354

5455
private static final class InMemoryStreamHandler extends StreamHandler {
5556
private InMemoryStreamHandler(OutputStream out, Formatter formatter) {
@@ -86,6 +87,12 @@ public void debug(String message) {
8687
logger.log(Level.FINE, message);
8788
}
8889

90+
@Override
91+
public boolean putMdc(String key, String value) {
92+
MDC.put(key, value);
93+
return true;
94+
}
95+
8996
@Override
9097
public ParameterizedLogSupport getParameterizedLogSettings() {
9198
return ParameterizedLogSupport.NUMBER_AND_BRACKETS;
@@ -131,7 +138,7 @@ void testLogException() throws Exception {
131138
String stackTrace = StreamSupport.stream(getLastLogLine().get("error.stack_trace").spliterator(), false)
132139
.map(JsonNode::textValue)
133140
.collect(Collectors.joining("\n", "", "\n"));
134-
assertThat(stackTrace).contains("at co.elastic.logging.jul.JulLoggingTestTest.testLogException");
141+
assertThat(stackTrace).contains("at co.elastic.logging.jul.JulLoggingTest.testLogException");
135142
}
136143

137144
@Test

0 commit comments

Comments
 (0)