Skip to content

Commit 81f8bf6

Browse files
cleverchuklaurit
andauthored
Add service.name to MDC (#9647)
Co-authored-by: Lauri Tulmin <[email protected]>
1 parent fa42343 commit 81f8bf6

File tree

19 files changed

+232
-7
lines changed

19 files changed

+232
-7
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Settings for the Log4j MDC instrumentation
2+
3+
| System property | Type | Default | Description |
4+
|-------------------------------------------------------|---------|---------|--------------------------------------------------------------------|
5+
| `otel.instrumentation.common.mdc.resource-attributes` | String | | Comma separated list of resource attributes to expose through MDC. |

instrumentation/log4j/log4j-context-data/log4j-context-data-2.17/javaagent/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ testing {
4040
testTask.configure {
4141
jvmArgs("-Dlog4j2.is.webapp=false")
4242
jvmArgs("-Dlog4j2.enable.threadlocals=false")
43+
jvmArgs("-Dotel.instrumentation.common.mdc.resource-attributes=service.name,telemetry.sdk.language")
4344
}
4445
}
4546
}
@@ -74,6 +75,7 @@ tasks {
7475
test {
7576
jvmArgs("-Dlog4j2.is.webapp=false")
7677
jvmArgs("-Dlog4j2.enable.threadlocals=true")
78+
jvmArgs("-Dotel.instrumentation.common.mdc.resource-attributes=service.name,telemetry.sdk.language")
7779
}
7880

7981
named("check") {

instrumentation/log4j/log4j-context-data/log4j-context-data-2.17/javaagent/src/test/groovy/AutoLog4j2Test.groovy

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,27 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6+
import io.opentelemetry.instrumentation.log4j.contextdata.ListAppender
67
import io.opentelemetry.instrumentation.test.AgentTestTrait
8+
import org.apache.logging.log4j.LogManager
79

810
class AutoLog4j2Test extends Log4j2Test implements AgentTestTrait {
11+
12+
def "resource attributes"() {
13+
given:
14+
def logger = LogManager.getLogger("TestLogger")
15+
16+
when:
17+
logger.info("log message 1")
18+
19+
def events = ListAppender.get().getEvents()
20+
21+
then:
22+
events.size() == 1
23+
events[0].message == "log message 1"
24+
events[0].contextData["trace_id"] == null
25+
events[0].contextData["span_id"] == null
26+
events[0].contextData["service.name"] == "unknown_service:java"
27+
events[0].contextData["telemetry.sdk.language"] == "java"
28+
}
929
}

instrumentation/log4j/log4j-context-data/log4j-context-data-2.17/library-autoconfigure/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ plugins {
55
base.archivesName.set("${base.archivesName.get()}-autoconfigure")
66

77
dependencies {
8+
compileOnly(project(":javaagent-extension-api"))
89
library("org.apache.logging.log4j:log4j-core:2.17.0")
910

1011
testImplementation(project(":instrumentation:log4j:log4j-context-data:log4j-context-data-common:testing"))

instrumentation/log4j/log4j-context-data/log4j-context-data-2.17/library-autoconfigure/src/main/java/io/opentelemetry/instrumentation/log4j/contextdata/v2_17/OpenTelemetryContextDataProvider.java

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import io.opentelemetry.api.trace.SpanContext;
1616
import io.opentelemetry.context.Context;
1717
import io.opentelemetry.instrumentation.api.internal.ConfigPropertiesUtil;
18+
import io.opentelemetry.javaagent.bootstrap.internal.ConfiguredResourceAttributesHolder;
1819
import java.util.Collections;
1920
import java.util.HashMap;
2021
import java.util.Map;
@@ -25,9 +26,39 @@
2526
* #supplyContextData()} is called when a log entry is created.
2627
*/
2728
public class OpenTelemetryContextDataProvider implements ContextDataProvider {
29+
2830
private static final boolean BAGGAGE_ENABLED =
2931
ConfigPropertiesUtil.getBoolean("otel.instrumentation.log4j-context-data.add-baggage", false);
3032

33+
private static final boolean configuredResourceAttributeAccessible =
34+
isConfiguredResourceAttributeAccessible();
35+
36+
private static final Map<String, String> staticContextData = getStaticContextData();
37+
38+
private static Map<String, String> getStaticContextData() {
39+
if (configuredResourceAttributeAccessible) {
40+
return ConfiguredResourceAttributesHolder.getResourceAttributes();
41+
}
42+
return Collections.emptyMap();
43+
}
44+
45+
/**
46+
* Checks whether {@link ConfiguredResourceAttributesHolder} is available in classpath. The result
47+
* is true if {@link ConfiguredResourceAttributesHolder} can be loaded, false otherwise.
48+
*
49+
* @return A boolean
50+
*/
51+
private static boolean isConfiguredResourceAttributeAccessible() {
52+
try {
53+
Class.forName(
54+
"io.opentelemetry.javaagent.bootstrap.internal.ConfiguredResourceAttributesHolder");
55+
return true;
56+
57+
} catch (ClassNotFoundException ok) {
58+
return false;
59+
}
60+
}
61+
3162
/**
3263
* Returns context from the current span when available.
3364
*
@@ -39,10 +70,12 @@ public Map<String, String> supplyContextData() {
3970
Context context = Context.current();
4071
Span currentSpan = Span.fromContext(context);
4172
if (!currentSpan.getSpanContext().isValid()) {
42-
return Collections.emptyMap();
73+
return staticContextData;
4374
}
4475

4576
Map<String, String> contextData = new HashMap<>();
77+
contextData.putAll(staticContextData);
78+
4679
SpanContext spanContext = currentSpan.getSpanContext();
4780
contextData.put(TRACE_ID, spanContext.getTraceId());
4881
contextData.put(SPAN_ID, spanContext.getSpanId());

instrumentation/log4j/log4j-context-data/log4j-context-data-2.7/javaagent/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ tasks {
2626
filter {
2727
excludeTestsMatching("Log4j27BaggageTest")
2828
}
29+
jvmArgs("-Dotel.instrumentation.common.mdc.resource-attributes=service.name,telemetry.sdk.language")
2930
}
3031

3132
val testAddBaggage by registering(Test::class) {

instrumentation/log4j/log4j-context-data/log4j-context-data-2.7/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/log4j/contextdata/v2_7/SpanDecoratingContextDataInjector.java

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import io.opentelemetry.api.trace.Span;
1515
import io.opentelemetry.api.trace.SpanContext;
1616
import io.opentelemetry.context.Context;
17+
import io.opentelemetry.javaagent.bootstrap.internal.ConfiguredResourceAttributesHolder;
1718
import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig;
1819
import java.util.List;
1920
import java.util.Map;
@@ -28,6 +29,8 @@ public final class SpanDecoratingContextDataInjector implements ContextDataInjec
2829
InstrumentationConfig.get()
2930
.getBoolean("otel.instrumentation.log4j-context-data.add-baggage", false);
3031

32+
private static final StringMap staticContextData = getStaticContextData();
33+
3134
private final ContextDataInjector delegate;
3235

3336
public SpanDecoratingContextDataInjector(ContextDataInjector delegate) {
@@ -40,17 +43,17 @@ public StringMap injectContextData(List<Property> list, StringMap stringMap) {
4043

4144
if (contextData.containsKey(TRACE_ID)) {
4245
// Assume already instrumented event if traceId is present.
43-
return contextData;
46+
return staticContextData.isEmpty() ? contextData : newContextData(contextData);
4447
}
4548

4649
Context context = Context.current();
4750
Span span = Span.fromContext(context);
4851
SpanContext currentContext = span.getSpanContext();
4952
if (!currentContext.isValid()) {
50-
return contextData;
53+
return staticContextData.isEmpty() ? contextData : newContextData(contextData);
5154
}
5255

53-
StringMap newContextData = new SortedArrayStringMap(contextData);
56+
StringMap newContextData = newContextData(contextData);
5457
newContextData.putValue(TRACE_ID, currentContext.getTraceId());
5558
newContextData.putValue(SPAN_ID, currentContext.getSpanId());
5659
newContextData.putValue(TRACE_FLAGS, currentContext.getTraceFlags().asHex());
@@ -69,4 +72,19 @@ public StringMap injectContextData(List<Property> list, StringMap stringMap) {
6972
public ReadOnlyStringMap rawContextData() {
7073
return delegate.rawContextData();
7174
}
75+
76+
private static StringMap newContextData(StringMap contextData) {
77+
StringMap newContextData = new SortedArrayStringMap(contextData);
78+
newContextData.putAll(staticContextData);
79+
return newContextData;
80+
}
81+
82+
private static StringMap getStaticContextData() {
83+
StringMap map = new SortedArrayStringMap();
84+
for (Map.Entry<String, String> entry :
85+
ConfiguredResourceAttributesHolder.getResourceAttributes().entrySet()) {
86+
map.putValue(entry.getKey(), entry.getValue());
87+
}
88+
return map;
89+
}
7290
}

instrumentation/log4j/log4j-context-data/log4j-context-data-2.7/javaagent/src/test/groovy/Log4j27Test.groovy

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,27 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6+
import io.opentelemetry.instrumentation.log4j.contextdata.ListAppender
67
import io.opentelemetry.instrumentation.test.AgentTestTrait
8+
import org.apache.logging.log4j.LogManager
79

810
class Log4j27Test extends Log4j2Test implements AgentTestTrait {
11+
12+
def "resource attributes"() {
13+
given:
14+
def logger = LogManager.getLogger("TestLogger")
15+
16+
when:
17+
logger.info("log message 1")
18+
19+
def events = ListAppender.get().getEvents()
20+
21+
then:
22+
events.size() == 1
23+
events[0].message == "log message 1"
24+
events[0].contextData["trace_id"] == null
25+
events[0].contextData["span_id"] == null
26+
events[0].contextData["service.name"] == "unknown_service:java"
27+
events[0].contextData["telemetry.sdk.language"] == "java"
28+
}
929
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Settings for the Log4j MDC instrumentation
2+
3+
| System property | Type | Default | Description |
4+
|-------------------------------------------------------|---------|---------|--------------------------------------------------------------------|
5+
| `otel.instrumentation.common.mdc.resource-attributes` | String | | Comma separated list of resource attributes to expose through MDC. |

instrumentation/log4j/log4j-mdc-1.2/javaagent/build.gradle.kts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,9 @@ configurations {
3030
exclude("javax.jms", "jms")
3131
}
3232
}
33+
34+
tasks {
35+
test {
36+
jvmArgs("-Dotel.instrumentation.common.mdc.resource-attributes=service.name,telemetry.sdk.language")
37+
}
38+
}

instrumentation/log4j/log4j-mdc-1.2/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/log4j/mdc/v1_2/LoggingEventInstrumentation.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import io.opentelemetry.context.Context;
1919
import io.opentelemetry.instrumentation.api.util.VirtualField;
2020
import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge;
21+
import io.opentelemetry.javaagent.bootstrap.internal.ConfiguredResourceAttributesHolder;
2122
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
2223
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
2324
import net.bytebuddy.asm.Advice;
@@ -79,6 +80,8 @@ public static void onExit(
7980
default:
8081
// do nothing
8182
}
83+
} else if (value == null) {
84+
value = ConfiguredResourceAttributesHolder.getAttributeValue(key);
8285
}
8386
}
8487
}

instrumentation/log4j/log4j-mdc-1.2/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/log4j/mdc/v1_2/Log4j1MdcTest.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,4 +90,19 @@ void idsWhenSpan() {
9090
assertEquals(events.get(2).getMDC("span_id"), span2.getSpanContext().getSpanId());
9191
assertEquals(events.get(2).getMDC("trace_flags"), "01");
9292
}
93+
94+
@Test
95+
void resourceAttributes() {
96+
logger.info("log message 1");
97+
98+
List<LoggingEvent> events = ListAppender.getEvents();
99+
100+
assertEquals(1, events.size());
101+
assertEquals("log message 1", events.get(0).getMessage());
102+
assertNull(events.get(0).getMDC("trace_id"));
103+
assertNull(events.get(0).getMDC("span_id"));
104+
assertNull(events.get(0).getMDC("trace_flags"));
105+
assertEquals("unknown_service:java", events.get(0).getMDC("service.name"));
106+
assertEquals("java", events.get(0).getMDC("telemetry.sdk.language"));
107+
}
93108
}
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Settings for the Logback MDC instrumentation
22

3-
| System property | Type | Default | Description |
4-
| ---------------------------------------------- | ------- | ------- | ----------------------------------------------- |
5-
| `otel.instrumentation.logback-mdc.add-baggage` | Boolean | `false` | Enable exposing baggage attributes through MDC. |
3+
| System property | Type | Default | Description |
4+
|-------------------------------------------------------|---------|---------|--------------------------------------------------------------------|
5+
| `otel.instrumentation.logback-mdc.add-baggage` | Boolean | `false` | Enable exposing baggage attributes through MDC. |
6+
| `otel.instrumentation.common.mdc.resource-attributes` | String | | Comma separated list of resource attributes to expose through MDC. |

instrumentation/logback/logback-mdc-1.0/javaagent/build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ dependencies {
6363
}
6464

6565
tasks {
66+
test {
67+
jvmArgs("-Dotel.instrumentation.common.mdc.resource-attributes=service.name,telemetry.sdk.language")
68+
}
69+
6670
named("check") {
6771
dependsOn(testing.suites)
6872
}

instrumentation/logback/logback-mdc-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/logback/mdc/v1_0/LoggingEventInstrumentation.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import io.opentelemetry.instrumentation.api.util.VirtualField;
2525
import io.opentelemetry.instrumentation.logback.mdc.v1_0.internal.UnionMap;
2626
import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge;
27+
import io.opentelemetry.javaagent.bootstrap.internal.ConfiguredResourceAttributesHolder;
2728
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
2829
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
2930
import java.util.HashMap;
@@ -80,6 +81,7 @@ public static void onExit(
8081
spanContextData.put(SPAN_ID, spanContext.getSpanId());
8182
spanContextData.put(TRACE_FLAGS, spanContext.getTraceFlags().asHex());
8283
}
84+
spanContextData.putAll(ConfiguredResourceAttributesHolder.getResourceAttributes());
8385

8486
if (LogbackSingletons.addBaggage()) {
8587
Baggage baggage = Java8BytecodeBridge.baggageFromContext(context);

instrumentation/logback/logback-mdc-1.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/logback/v1_0/LogbackTest.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,14 @@
55

66
package io.opentelemetry.javaagent.instrumentation.logback.v1_0;
77

8+
import static org.assertj.core.api.Assertions.assertThat;
9+
10+
import ch.qos.logback.classic.spi.ILoggingEvent;
811
import io.opentelemetry.instrumentation.logback.mdc.v1_0.AbstractLogbackTest;
912
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
1013
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
14+
import java.util.List;
15+
import org.junit.jupiter.api.Test;
1116
import org.junit.jupiter.api.extension.RegisterExtension;
1217

1318
class LogbackTest extends AbstractLogbackTest {
@@ -19,4 +24,20 @@ class LogbackTest extends AbstractLogbackTest {
1924
public InstrumentationExtension getInstrumentationExtension() {
2025
return agentTesting;
2126
}
27+
28+
@Test
29+
void resourceAttributes() {
30+
logger.info("log message 1");
31+
32+
List<ILoggingEvent> events = listAppender.list;
33+
34+
assertThat(events.size()).isEqualTo(1);
35+
assertThat(events.get(0).getMessage()).isEqualTo("log message 1");
36+
assertThat(events.get(0).getMDCPropertyMap().get("trace_id")).isNull();
37+
assertThat(events.get(0).getMDCPropertyMap().get("span_id")).isNull();
38+
assertThat(events.get(0).getMDCPropertyMap().get("trace_flags")).isNull();
39+
assertThat(events.get(0).getMDCPropertyMap().get("service.name"))
40+
.isEqualTo("unknown_service:java");
41+
assertThat(events.get(0).getMDCPropertyMap().get("telemetry.sdk.language")).isEqualTo("java");
42+
}
2243
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.bootstrap.internal;
7+
8+
import static io.opentelemetry.api.common.AttributeKey.stringKey;
9+
10+
import io.opentelemetry.api.common.Attributes;
11+
import java.util.Collections;
12+
import java.util.HashMap;
13+
import java.util.List;
14+
import java.util.Map;
15+
import javax.annotation.Nullable;
16+
17+
/**
18+
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
19+
* any time.
20+
*/
21+
public final class ConfiguredResourceAttributesHolder {
22+
23+
private static final Map<String, String> resourceAttributes = new HashMap<>();
24+
25+
public static Map<String, String> getResourceAttributes() {
26+
return resourceAttributes;
27+
}
28+
29+
public static void initialize(Attributes resourceAttribute) {
30+
List<String> mdcResourceAttributes =
31+
InstrumentationConfig.get()
32+
.getList(
33+
"otel.instrumentation.common.mdc.resource-attributes", Collections.emptyList());
34+
for (String key : mdcResourceAttributes) {
35+
String value = resourceAttribute.get(stringKey(key));
36+
if (value != null) {
37+
ConfiguredResourceAttributesHolder.resourceAttributes.put(key, value);
38+
}
39+
}
40+
}
41+
42+
@Nullable
43+
public static String getAttributeValue(String key) {
44+
return resourceAttributes.get(key);
45+
}
46+
47+
private ConfiguredResourceAttributesHolder() {}
48+
}

0 commit comments

Comments
 (0)