Skip to content

Commit

Permalink
Add service.name to MDC (#9647)
Browse files Browse the repository at this point in the history
Co-authored-by: Lauri Tulmin <ltulmin@splunk.com>
  • Loading branch information
cleverchuk and laurit committed Nov 13, 2023
1 parent fa42343 commit 81f8bf6
Show file tree
Hide file tree
Showing 19 changed files with 232 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Settings for the Log4j MDC instrumentation

| System property | Type | Default | Description |
|-------------------------------------------------------|---------|---------|--------------------------------------------------------------------|
| `otel.instrumentation.common.mdc.resource-attributes` | String | | Comma separated list of resource attributes to expose through MDC. |
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ testing {
testTask.configure {
jvmArgs("-Dlog4j2.is.webapp=false")
jvmArgs("-Dlog4j2.enable.threadlocals=false")
jvmArgs("-Dotel.instrumentation.common.mdc.resource-attributes=service.name,telemetry.sdk.language")
}
}
}
Expand Down Expand Up @@ -74,6 +75,7 @@ tasks {
test {
jvmArgs("-Dlog4j2.is.webapp=false")
jvmArgs("-Dlog4j2.enable.threadlocals=true")
jvmArgs("-Dotel.instrumentation.common.mdc.resource-attributes=service.name,telemetry.sdk.language")
}

named("check") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,27 @@
* SPDX-License-Identifier: Apache-2.0
*/

import io.opentelemetry.instrumentation.log4j.contextdata.ListAppender
import io.opentelemetry.instrumentation.test.AgentTestTrait
import org.apache.logging.log4j.LogManager

class AutoLog4j2Test extends Log4j2Test implements AgentTestTrait {

def "resource attributes"() {
given:
def logger = LogManager.getLogger("TestLogger")

when:
logger.info("log message 1")

def events = ListAppender.get().getEvents()

then:
events.size() == 1
events[0].message == "log message 1"
events[0].contextData["trace_id"] == null
events[0].contextData["span_id"] == null
events[0].contextData["service.name"] == "unknown_service:java"
events[0].contextData["telemetry.sdk.language"] == "java"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ plugins {
base.archivesName.set("${base.archivesName.get()}-autoconfigure")

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

testImplementation(project(":instrumentation:log4j:log4j-context-data:log4j-context-data-common:testing"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import io.opentelemetry.api.trace.SpanContext;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.internal.ConfigPropertiesUtil;
import io.opentelemetry.javaagent.bootstrap.internal.ConfiguredResourceAttributesHolder;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
Expand All @@ -25,9 +26,39 @@
* #supplyContextData()} is called when a log entry is created.
*/
public class OpenTelemetryContextDataProvider implements ContextDataProvider {

private static final boolean BAGGAGE_ENABLED =
ConfigPropertiesUtil.getBoolean("otel.instrumentation.log4j-context-data.add-baggage", false);

private static final boolean configuredResourceAttributeAccessible =
isConfiguredResourceAttributeAccessible();

private static final Map<String, String> staticContextData = getStaticContextData();

private static Map<String, String> getStaticContextData() {
if (configuredResourceAttributeAccessible) {
return ConfiguredResourceAttributesHolder.getResourceAttributes();
}
return Collections.emptyMap();
}

/**
* Checks whether {@link ConfiguredResourceAttributesHolder} is available in classpath. The result
* is true if {@link ConfiguredResourceAttributesHolder} can be loaded, false otherwise.
*
* @return A boolean
*/
private static boolean isConfiguredResourceAttributeAccessible() {
try {
Class.forName(
"io.opentelemetry.javaagent.bootstrap.internal.ConfiguredResourceAttributesHolder");
return true;

} catch (ClassNotFoundException ok) {
return false;
}
}

/**
* Returns context from the current span when available.
*
Expand All @@ -39,10 +70,12 @@ public Map<String, String> supplyContextData() {
Context context = Context.current();
Span currentSpan = Span.fromContext(context);
if (!currentSpan.getSpanContext().isValid()) {
return Collections.emptyMap();
return staticContextData;
}

Map<String, String> contextData = new HashMap<>();
contextData.putAll(staticContextData);

SpanContext spanContext = currentSpan.getSpanContext();
contextData.put(TRACE_ID, spanContext.getTraceId());
contextData.put(SPAN_ID, spanContext.getSpanId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ tasks {
filter {
excludeTestsMatching("Log4j27BaggageTest")
}
jvmArgs("-Dotel.instrumentation.common.mdc.resource-attributes=service.name,telemetry.sdk.language")
}

val testAddBaggage by registering(Test::class) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanContext;
import io.opentelemetry.context.Context;
import io.opentelemetry.javaagent.bootstrap.internal.ConfiguredResourceAttributesHolder;
import io.opentelemetry.javaagent.bootstrap.internal.InstrumentationConfig;
import java.util.List;
import java.util.Map;
Expand All @@ -28,6 +29,8 @@ public final class SpanDecoratingContextDataInjector implements ContextDataInjec
InstrumentationConfig.get()
.getBoolean("otel.instrumentation.log4j-context-data.add-baggage", false);

private static final StringMap staticContextData = getStaticContextData();

private final ContextDataInjector delegate;

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

if (contextData.containsKey(TRACE_ID)) {
// Assume already instrumented event if traceId is present.
return contextData;
return staticContextData.isEmpty() ? contextData : newContextData(contextData);
}

Context context = Context.current();
Span span = Span.fromContext(context);
SpanContext currentContext = span.getSpanContext();
if (!currentContext.isValid()) {
return contextData;
return staticContextData.isEmpty() ? contextData : newContextData(contextData);
}

StringMap newContextData = new SortedArrayStringMap(contextData);
StringMap newContextData = newContextData(contextData);
newContextData.putValue(TRACE_ID, currentContext.getTraceId());
newContextData.putValue(SPAN_ID, currentContext.getSpanId());
newContextData.putValue(TRACE_FLAGS, currentContext.getTraceFlags().asHex());
Expand All @@ -69,4 +72,19 @@ public StringMap injectContextData(List<Property> list, StringMap stringMap) {
public ReadOnlyStringMap rawContextData() {
return delegate.rawContextData();
}

private static StringMap newContextData(StringMap contextData) {
StringMap newContextData = new SortedArrayStringMap(contextData);
newContextData.putAll(staticContextData);
return newContextData;
}

private static StringMap getStaticContextData() {
StringMap map = new SortedArrayStringMap();
for (Map.Entry<String, String> entry :
ConfiguredResourceAttributesHolder.getResourceAttributes().entrySet()) {
map.putValue(entry.getKey(), entry.getValue());
}
return map;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,27 @@
* SPDX-License-Identifier: Apache-2.0
*/

import io.opentelemetry.instrumentation.log4j.contextdata.ListAppender
import io.opentelemetry.instrumentation.test.AgentTestTrait
import org.apache.logging.log4j.LogManager

class Log4j27Test extends Log4j2Test implements AgentTestTrait {

def "resource attributes"() {
given:
def logger = LogManager.getLogger("TestLogger")

when:
logger.info("log message 1")

def events = ListAppender.get().getEvents()

then:
events.size() == 1
events[0].message == "log message 1"
events[0].contextData["trace_id"] == null
events[0].contextData["span_id"] == null
events[0].contextData["service.name"] == "unknown_service:java"
events[0].contextData["telemetry.sdk.language"] == "java"
}
}
5 changes: 5 additions & 0 deletions instrumentation/log4j/log4j-mdc-1.2/javaagent/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Settings for the Log4j MDC instrumentation

| System property | Type | Default | Description |
|-------------------------------------------------------|---------|---------|--------------------------------------------------------------------|
| `otel.instrumentation.common.mdc.resource-attributes` | String | | Comma separated list of resource attributes to expose through MDC. |
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,9 @@ configurations {
exclude("javax.jms", "jms")
}
}

tasks {
test {
jvmArgs("-Dotel.instrumentation.common.mdc.resource-attributes=service.name,telemetry.sdk.language")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.util.VirtualField;
import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge;
import io.opentelemetry.javaagent.bootstrap.internal.ConfiguredResourceAttributesHolder;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import net.bytebuddy.asm.Advice;
Expand Down Expand Up @@ -79,6 +80,8 @@ public static void onExit(
default:
// do nothing
}
} else if (value == null) {
value = ConfiguredResourceAttributesHolder.getAttributeValue(key);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,19 @@ void idsWhenSpan() {
assertEquals(events.get(2).getMDC("span_id"), span2.getSpanContext().getSpanId());
assertEquals(events.get(2).getMDC("trace_flags"), "01");
}

@Test
void resourceAttributes() {
logger.info("log message 1");

List<LoggingEvent> events = ListAppender.getEvents();

assertEquals(1, events.size());
assertEquals("log message 1", events.get(0).getMessage());
assertNull(events.get(0).getMDC("trace_id"));
assertNull(events.get(0).getMDC("span_id"));
assertNull(events.get(0).getMDC("trace_flags"));
assertEquals("unknown_service:java", events.get(0).getMDC("service.name"));
assertEquals("java", events.get(0).getMDC("telemetry.sdk.language"));
}
}
7 changes: 4 additions & 3 deletions instrumentation/logback/logback-mdc-1.0/javaagent/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Settings for the Logback MDC instrumentation

| System property | Type | Default | Description |
| ---------------------------------------------- | ------- | ------- | ----------------------------------------------- |
| `otel.instrumentation.logback-mdc.add-baggage` | Boolean | `false` | Enable exposing baggage attributes through MDC. |
| System property | Type | Default | Description |
|-------------------------------------------------------|---------|---------|--------------------------------------------------------------------|
| `otel.instrumentation.logback-mdc.add-baggage` | Boolean | `false` | Enable exposing baggage attributes through MDC. |
| `otel.instrumentation.common.mdc.resource-attributes` | String | | Comma separated list of resource attributes to expose through MDC. |
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ dependencies {
}

tasks {
test {
jvmArgs("-Dotel.instrumentation.common.mdc.resource-attributes=service.name,telemetry.sdk.language")
}

named("check") {
dependsOn(testing.suites)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import io.opentelemetry.instrumentation.api.util.VirtualField;
import io.opentelemetry.instrumentation.logback.mdc.v1_0.internal.UnionMap;
import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge;
import io.opentelemetry.javaagent.bootstrap.internal.ConfiguredResourceAttributesHolder;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import java.util.HashMap;
Expand Down Expand Up @@ -80,6 +81,7 @@ public static void onExit(
spanContextData.put(SPAN_ID, spanContext.getSpanId());
spanContextData.put(TRACE_FLAGS, spanContext.getTraceFlags().asHex());
}
spanContextData.putAll(ConfiguredResourceAttributesHolder.getResourceAttributes());

if (LogbackSingletons.addBaggage()) {
Baggage baggage = Java8BytecodeBridge.baggageFromContext(context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,14 @@

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

import static org.assertj.core.api.Assertions.assertThat;

import ch.qos.logback.classic.spi.ILoggingEvent;
import io.opentelemetry.instrumentation.logback.mdc.v1_0.AbstractLogbackTest;
import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

class LogbackTest extends AbstractLogbackTest {
Expand All @@ -19,4 +24,20 @@ class LogbackTest extends AbstractLogbackTest {
public InstrumentationExtension getInstrumentationExtension() {
return agentTesting;
}

@Test
void resourceAttributes() {
logger.info("log message 1");

List<ILoggingEvent> events = listAppender.list;

assertThat(events.size()).isEqualTo(1);
assertThat(events.get(0).getMessage()).isEqualTo("log message 1");
assertThat(events.get(0).getMDCPropertyMap().get("trace_id")).isNull();
assertThat(events.get(0).getMDCPropertyMap().get("span_id")).isNull();
assertThat(events.get(0).getMDCPropertyMap().get("trace_flags")).isNull();
assertThat(events.get(0).getMDCPropertyMap().get("service.name"))
.isEqualTo("unknown_service:java");
assertThat(events.get(0).getMDCPropertyMap().get("telemetry.sdk.language")).isEqualTo("java");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.bootstrap.internal;

import static io.opentelemetry.api.common.AttributeKey.stringKey;

import io.opentelemetry.api.common.Attributes;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;

/**
* This class is internal and is hence not for public use. Its APIs are unstable and can change at
* any time.
*/
public final class ConfiguredResourceAttributesHolder {

private static final Map<String, String> resourceAttributes = new HashMap<>();

public static Map<String, String> getResourceAttributes() {
return resourceAttributes;
}

public static void initialize(Attributes resourceAttribute) {
List<String> mdcResourceAttributes =
InstrumentationConfig.get()
.getList(
"otel.instrumentation.common.mdc.resource-attributes", Collections.emptyList());
for (String key : mdcResourceAttributes) {
String value = resourceAttribute.get(stringKey(key));
if (value != null) {
ConfiguredResourceAttributesHolder.resourceAttributes.put(key, value);
}
}
}

@Nullable
public static String getAttributeValue(String key) {
return resourceAttributes.get(key);
}

private ConfiguredResourceAttributesHolder() {}
}
Loading

0 comments on commit 81f8bf6

Please sign in to comment.