diff --git a/instrumentation/logback/logback-mdc-1.0/javaagent/build.gradle.kts b/instrumentation/logback/logback-mdc-1.0/javaagent/build.gradle.kts index 5c315ed3394f..9d616d079f9a 100644 --- a/instrumentation/logback/logback-mdc-1.0/javaagent/build.gradle.kts +++ b/instrumentation/logback/logback-mdc-1.0/javaagent/build.gradle.kts @@ -10,6 +10,42 @@ muzzle { } } +testing { + suites { + val addBaggageTest by registering(JvmTestSuite::class) { + targets { + all { + testTask.configure { + jvmArgs("-Dotel.instrumentation.logback-mdc.add-baggage=true") + } + } + } + } + + withType(JvmTestSuite::class) { + dependencies { + if (findProperty("testLatestDeps") as Boolean) { + implementation("ch.qos.logback:logback-classic:+") + } else { + implementation("ch.qos.logback:logback-classic") { + version { + strictly("1.0.0") + } + } + implementation("org.slf4j:slf4j-api") { + version { + strictly("1.6.4") + } + } + } + + implementation(project(":instrumentation:logback:logback-mdc-1.0:testing")) + implementation(project(":instrumentation:logback:logback-mdc-1.0:javaagent")) + } + } + } +} + dependencies { implementation(project(":instrumentation:logback:logback-mdc-1.0:library")) @@ -24,21 +60,10 @@ dependencies { strictly("1.6.4") } } +} - if (findProperty("testLatestDeps") as Boolean) { - testImplementation("ch.qos.logback:logback-classic:+") - } else { - testImplementation("ch.qos.logback:logback-classic") { - version { - strictly("1.0.0") - } - } - testImplementation("org.slf4j:slf4j-api") { - version { - strictly("1.6.4") - } - } +tasks { + named("check") { + dependsOn(testing.suites) } - - testImplementation(project(":instrumentation:logback:logback-mdc-1.0:testing")) } diff --git a/instrumentation/logback/logback-mdc-1.0/javaagent/src/addBaggageTest/groovy/io/opentelemetry/javaagent/instrumentation/logback/v1_0/LogbackWithBaggageTest.groovy b/instrumentation/logback/logback-mdc-1.0/javaagent/src/addBaggageTest/groovy/io/opentelemetry/javaagent/instrumentation/logback/v1_0/LogbackWithBaggageTest.groovy new file mode 100644 index 000000000000..e2468e46cc9a --- /dev/null +++ b/instrumentation/logback/logback-mdc-1.0/javaagent/src/addBaggageTest/groovy/io/opentelemetry/javaagent/instrumentation/logback/v1_0/LogbackWithBaggageTest.groovy @@ -0,0 +1,12 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.logback.v1_0 + +import io.opentelemetry.instrumentation.logback.mdc.v1_0.AbstractLogbackWithBaggageTest +import io.opentelemetry.instrumentation.test.AgentTestTrait + +class LogbackWithBaggageTest extends AbstractLogbackWithBaggageTest implements AgentTestTrait { +} diff --git a/instrumentation/logback/logback-mdc-1.0/javaagent/src/addBaggageTest/resources/logback.xml b/instrumentation/logback/logback-mdc-1.0/javaagent/src/addBaggageTest/resources/logback.xml new file mode 100644 index 000000000000..3434fbaaab59 --- /dev/null +++ b/instrumentation/logback/logback-mdc-1.0/javaagent/src/addBaggageTest/resources/logback.xml @@ -0,0 +1,19 @@ + + + + + + + %coloredLevel %logger{15} - %message%n%xException{10} + + + + + + + + + + + + diff --git a/instrumentation/logback/logback-mdc-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/logback/mdc/v1_0/LogbackSingletons.java b/instrumentation/logback/logback-mdc-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/logback/mdc/v1_0/LogbackSingletons.java new file mode 100644 index 000000000000..a14cac81706e --- /dev/null +++ b/instrumentation/logback/logback-mdc-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/logback/mdc/v1_0/LogbackSingletons.java @@ -0,0 +1,19 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.logback.mdc.v1_0; + +import io.opentelemetry.instrumentation.api.internal.ConfigPropertiesUtil; + +public final class LogbackSingletons { + private static final boolean ADD_BAGGAGE = + ConfigPropertiesUtil.getBoolean("otel.instrumentation.logback-mdc.add-baggage", false); + + public static boolean addBaggage() { + return ADD_BAGGAGE; + } + + private LogbackSingletons() {} +} diff --git a/instrumentation/logback/logback-mdc-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/logback/mdc/v1_0/LoggingEventInstrumentation.java b/instrumentation/logback/logback-mdc-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/logback/mdc/v1_0/LoggingEventInstrumentation.java index aeb4ce31bd64..e2f8130e9406 100644 --- a/instrumentation/logback/logback-mdc-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/logback/mdc/v1_0/LoggingEventInstrumentation.java +++ b/instrumentation/logback/logback-mdc-1.0/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/logback/mdc/v1_0/LoggingEventInstrumentation.java @@ -17,6 +17,8 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArguments; import ch.qos.logback.classic.spi.ILoggingEvent; +import io.opentelemetry.api.baggage.Baggage; +import io.opentelemetry.api.baggage.BaggageEntry; import io.opentelemetry.api.trace.SpanContext; import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.api.util.VirtualField; @@ -54,11 +56,11 @@ public void transform(TypeTransformer transformer) { @SuppressWarnings("unused") public static class GetMdcAdvice { - @Advice.OnMethodExit(suppress = Throwable.class) public static void onExit( @Advice.This ILoggingEvent event, @Advice.Return(typing = Typing.DYNAMIC, readOnly = false) Map contextData) { + if (contextData != null && contextData.containsKey(TRACE_ID)) { // Assume already instrumented event if traceId is present. return; @@ -69,15 +71,28 @@ public static void onExit( return; } + Map spanContextData = new HashMap<>(); + SpanContext spanContext = Java8BytecodeBridge.spanFromContext(context).getSpanContext(); - if (!spanContext.isValid()) { - return; + + if (spanContext.isValid()) { + spanContextData.put(TRACE_ID, spanContext.getTraceId()); + spanContextData.put(SPAN_ID, spanContext.getSpanId()); + spanContextData.put(TRACE_FLAGS, spanContext.getTraceFlags().asHex()); } - Map spanContextData = new HashMap<>(); - spanContextData.put(TRACE_ID, spanContext.getTraceId()); - spanContextData.put(SPAN_ID, spanContext.getSpanId()); - spanContextData.put(TRACE_FLAGS, spanContext.getTraceFlags().asHex()); + if (LogbackSingletons.addBaggage()) { + Baggage baggage = Java8BytecodeBridge.baggageFromContext(context); + + // using a lambda here does not play nicely with instrumentation bytecode process + // (Java 6 related errors are observed) so relying on for loop instead + for (Map.Entry entry : baggage.asMap().entrySet()) { + spanContextData.put( + entry.getKey(), + // prefix all baggage values to avoid clashes with existing context + "baggage." + entry.getValue().getValue()); + } + } if (contextData == null) { contextData = spanContextData; diff --git a/instrumentation/logback/logback-mdc-1.0/library/build.gradle.kts b/instrumentation/logback/logback-mdc-1.0/library/build.gradle.kts index 6bf9d8c2bf2e..76537740d178 100644 --- a/instrumentation/logback/logback-mdc-1.0/library/build.gradle.kts +++ b/instrumentation/logback/logback-mdc-1.0/library/build.gradle.kts @@ -2,7 +2,44 @@ plugins { id("otel.library-instrumentation") } +testing { + suites { + val addBaggageTest by registering(JvmTestSuite::class) { + targets { + all { + testTask.configure { + jvmArgs("-Dotel.instrumentation.logback-mdc.add-baggage=true") + } + } + } + } + + withType(JvmTestSuite::class) { + dependencies { + if (findProperty("testLatestDeps") as Boolean) { + implementation("ch.qos.logback:logback-classic:+") + } else { + implementation("ch.qos.logback:logback-classic") { + version { + strictly("1.0.0") + } + } + implementation("org.slf4j:slf4j-api") { + version { + strictly("1.6.4") + } + } + } + + implementation(project(":instrumentation:logback:logback-mdc-1.0:testing")) + implementation(project(":instrumentation:logback:logback-mdc-1.0:library")) + } + } + } +} + dependencies { + // pin the version strictly to avoid overriding by dependencyManagement versions compileOnly("ch.qos.logback:logback-classic") { version { @@ -14,21 +51,10 @@ dependencies { strictly("1.6.4") } } +} - if (findProperty("testLatestDeps") as Boolean) { - testImplementation("ch.qos.logback:logback-classic:+") - } else { - testImplementation("ch.qos.logback:logback-classic") { - version { - strictly("1.0.0") - } - } - testImplementation("org.slf4j:slf4j-api") { - version { - strictly("1.6.4") - } - } +tasks { + named("check") { + dependsOn(testing.suites) } - - testImplementation(project(":instrumentation:logback:logback-mdc-1.0:testing")) } diff --git a/instrumentation/logback/logback-mdc-1.0/library/src/addBaggageTest/groovy/io/opentelemetry/instrumentation/logback/mdc/v1_0/LogbackWithBaggageTest.groovy b/instrumentation/logback/logback-mdc-1.0/library/src/addBaggageTest/groovy/io/opentelemetry/instrumentation/logback/mdc/v1_0/LogbackWithBaggageTest.groovy new file mode 100644 index 000000000000..b1ddcc750212 --- /dev/null +++ b/instrumentation/logback/logback-mdc-1.0/library/src/addBaggageTest/groovy/io/opentelemetry/instrumentation/logback/mdc/v1_0/LogbackWithBaggageTest.groovy @@ -0,0 +1,12 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.logback.mdc.v1_0 + + +import io.opentelemetry.instrumentation.test.LibraryTestTrait + +class LogbackWithBaggageTest extends AbstractLogbackWithBaggageTest implements LibraryTestTrait { +} diff --git a/instrumentation/logback/logback-mdc-1.0/library/src/addBaggageTest/resources/logback.xml b/instrumentation/logback/logback-mdc-1.0/library/src/addBaggageTest/resources/logback.xml new file mode 100644 index 000000000000..6d7db9d2cc18 --- /dev/null +++ b/instrumentation/logback/logback-mdc-1.0/library/src/addBaggageTest/resources/logback.xml @@ -0,0 +1,31 @@ + + + + + + + + true + + + + + + + + diff --git a/instrumentation/logback/logback-mdc-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/mdc/v1_0/OpenTelemetryAppender.java b/instrumentation/logback/logback-mdc-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/mdc/v1_0/OpenTelemetryAppender.java index 17bf842f4964..026c5ba9e2f9 100644 --- a/instrumentation/logback/logback-mdc-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/mdc/v1_0/OpenTelemetryAppender.java +++ b/instrumentation/logback/logback-mdc-1.0/library/src/main/java/io/opentelemetry/instrumentation/logback/mdc/v1_0/OpenTelemetryAppender.java @@ -15,8 +15,10 @@ import ch.qos.logback.core.UnsynchronizedAppenderBase; import ch.qos.logback.core.spi.AppenderAttachable; import ch.qos.logback.core.spi.AppenderAttachableImpl; +import io.opentelemetry.api.baggage.Baggage; import io.opentelemetry.api.trace.Span; import io.opentelemetry.api.trace.SpanContext; +import io.opentelemetry.context.Context; import io.opentelemetry.instrumentation.logback.mdc.v1_0.internal.UnionMap; import java.lang.reflect.Proxy; import java.util.HashMap; @@ -25,15 +27,22 @@ public class OpenTelemetryAppender extends UnsynchronizedAppenderBase implements AppenderAttachable { + private volatile boolean addBaggage; private final AppenderAttachableImpl aai = new AppenderAttachableImpl<>(); - public static ILoggingEvent wrapEvent(ILoggingEvent event) { - Span currentSpan = Span.current(); - if (!currentSpan.getSpanContext().isValid()) { - return event; - } + /** + * When set to true this will enable addition of all baggage entries to MDC. This can be done by + * adding the following to the logback.xml config for this appender. {@code + * true} + * + * @param addBaggage True if baggage should be added to MDC + */ + public void setAddBaggage(boolean addBaggage) { + this.addBaggage = addBaggage; + } + public ILoggingEvent wrapEvent(ILoggingEvent event) { Map eventContext = event.getMDCPropertyMap(); if (eventContext != null && eventContext.containsKey(TRACE_ID)) { // Assume already instrumented event if traceId is present. @@ -41,10 +50,25 @@ public static ILoggingEvent wrapEvent(ILoggingEvent event) { } Map contextData = new HashMap<>(); - SpanContext spanContext = currentSpan.getSpanContext(); - contextData.put(TRACE_ID, spanContext.getTraceId()); - contextData.put(SPAN_ID, spanContext.getSpanId()); - contextData.put(TRACE_FLAGS, spanContext.getTraceFlags().asHex()); + Context context = Context.current(); + Span currentSpan = Span.fromContext(context); + + if (currentSpan.getSpanContext().isValid()) { + SpanContext spanContext = currentSpan.getSpanContext(); + contextData.put(TRACE_ID, spanContext.getTraceId()); + contextData.put(SPAN_ID, spanContext.getSpanId()); + contextData.put(TRACE_FLAGS, spanContext.getTraceFlags().asHex()); + } + + if (addBaggage) { + Baggage baggage = Baggage.fromContext(context); + baggage.forEach( + (key, value) -> + contextData.put( + key, + // prefix all baggage values to avoid clashes with existing context + "baggage." + value.getValue())); + } if (eventContext == null) { eventContext = contextData; diff --git a/instrumentation/logback/logback-mdc-1.0/testing/src/main/groovy/io/opentelemetry/instrumentation/logback/mdc/v1_0/AbstractLogbackTest.groovy b/instrumentation/logback/logback-mdc-1.0/testing/src/main/groovy/io/opentelemetry/instrumentation/logback/mdc/v1_0/AbstractLogbackTest.groovy index 791f916332c1..0062ea09bc67 100644 --- a/instrumentation/logback/logback-mdc-1.0/testing/src/main/groovy/io/opentelemetry/instrumentation/logback/mdc/v1_0/AbstractLogbackTest.groovy +++ b/instrumentation/logback/logback-mdc-1.0/testing/src/main/groovy/io/opentelemetry/instrumentation/logback/mdc/v1_0/AbstractLogbackTest.groovy @@ -7,6 +7,7 @@ package io.opentelemetry.instrumentation.logback.mdc.v1_0 import ch.qos.logback.classic.spi.ILoggingEvent import ch.qos.logback.core.read.ListAppender +import io.opentelemetry.api.baggage.Baggage import io.opentelemetry.api.trace.Span import io.opentelemetry.instrumentation.test.InstrumentationSpecification import org.slf4j.Logger @@ -39,8 +40,12 @@ abstract class AbstractLogbackTest extends InstrumentationSpecification { def "no ids when no span"() { when: - logger.info("log message 1") - logger.info("log message 2") + Baggage baggage = Baggage.empty().toBuilder().put("baggage_key", "baggage_value").build() + + runWithBaggage(baggage) { + AbstractLogbackTest.logger.info("log message 1") + AbstractLogbackTest.logger.info("log message 2") + } def events = listAppender.list @@ -50,25 +55,27 @@ abstract class AbstractLogbackTest extends InstrumentationSpecification { events[0].getMDCPropertyMap().get("trace_id") == null events[0].getMDCPropertyMap().get("span_id") == null events[0].getMDCPropertyMap().get("trace_flags") == null + events[0].getMDCPropertyMap().get("baggage_key") == (expectBaggage() ? "baggage.baggage_value" : null) events[1].message == "log message 2" events[1].getMDCPropertyMap().get("trace_id") == null events[1].getMDCPropertyMap().get("span_id") == null events[1].getMDCPropertyMap().get("trace_flags") == null + events[1].getMDCPropertyMap().get("baggage_key") == (expectBaggage() ? "baggage.baggage_value" : null) } def "ids when span"() { when: - Span span1 = runWithSpan("test") { + Baggage baggage = Baggage.empty().toBuilder().put("baggage_key", "baggage_value").build() + + Span span1 = runWithSpanAndBaggage("test", baggage) { AbstractLogbackTest.logger.info("log message 1") - Span.current() } logger.info("log message 2") - Span span2 = runWithSpan("test 2") { + Span span2 = runWithSpanAndBaggage("test 2", baggage) { AbstractLogbackTest.logger.info("log message 3") - Span.current() } def events = listAppender.list @@ -79,15 +86,35 @@ abstract class AbstractLogbackTest extends InstrumentationSpecification { events[0].getMDCPropertyMap().get("trace_id") == span1.spanContext.traceId events[0].getMDCPropertyMap().get("span_id") == span1.spanContext.spanId events[0].getMDCPropertyMap().get("trace_flags") == "01" + events[0].getMDCPropertyMap().get("baggage_key") == (expectBaggage() ? "baggage.baggage_value" : null) events[1].message == "log message 2" events[1].getMDCPropertyMap().get("trace_id") == null events[1].getMDCPropertyMap().get("span_id") == null events[1].getMDCPropertyMap().get("trace_flags") == null + events[1].getMDCPropertyMap().get("baggage_key") == null events[2].message == "log message 3" events[2].getMDCPropertyMap().get("trace_id") == span2.spanContext.traceId events[2].getMDCPropertyMap().get("span_id") == span2.spanContext.spanId events[2].getMDCPropertyMap().get("trace_flags") == "01" + events[2].getMDCPropertyMap().get("baggage_key") == (expectBaggage() ? "baggage.baggage_value" : null) + } + + Span runWithSpanAndBaggage(String spanName, Baggage baggage, Closure callback) { + return runWithSpan(spanName) { + runWithBaggage(baggage, callback) + Span.current() + } + } + + void runWithBaggage(Baggage baggage, Closure callback) { + try (var unusedScope = baggage.makeCurrent()) { + callback.call() + } + } + + boolean expectBaggage() { + return false } } diff --git a/instrumentation/logback/logback-mdc-1.0/testing/src/main/groovy/io/opentelemetry/instrumentation/logback/mdc/v1_0/AbstractLogbackWithBaggageTest.groovy b/instrumentation/logback/logback-mdc-1.0/testing/src/main/groovy/io/opentelemetry/instrumentation/logback/mdc/v1_0/AbstractLogbackWithBaggageTest.groovy new file mode 100644 index 000000000000..66762efeac06 --- /dev/null +++ b/instrumentation/logback/logback-mdc-1.0/testing/src/main/groovy/io/opentelemetry/instrumentation/logback/mdc/v1_0/AbstractLogbackWithBaggageTest.groovy @@ -0,0 +1,13 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.logback.mdc.v1_0 + +abstract class AbstractLogbackWithBaggageTest extends AbstractLogbackTest { + @Override + boolean expectBaggage() { + return true + } +} diff --git a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/bootstrap/Java8BytecodeBridge.java b/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/bootstrap/Java8BytecodeBridge.java index 52ed8541de37..588b48e23916 100644 --- a/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/bootstrap/Java8BytecodeBridge.java +++ b/javaagent-extension-api/src/main/java/io/opentelemetry/javaagent/bootstrap/Java8BytecodeBridge.java @@ -5,6 +5,7 @@ package io.opentelemetry.javaagent.bootstrap; +import io.opentelemetry.api.baggage.Baggage; import io.opentelemetry.api.trace.Span; import io.opentelemetry.context.Context; @@ -37,5 +38,9 @@ public static Span spanFromContext(Context context) { return Span.fromContext(context); } + public static Baggage baggageFromContext(Context context) { + return Baggage.fromContext(context); + } + private Java8BytecodeBridge() {} }