diff --git a/README.md b/README.md index 89d6d9eaae1d..ccf7725d3bbf 100644 --- a/README.md +++ b/README.md @@ -204,7 +204,7 @@ provide the path to a JAR file including an SPI implementation using the system | [khttp](https://khttp.readthedocs.io) | 0.1+ | | [Kubernetes Client](https://github.com/kubernetes-client/java) | 7.0+ | | [Lettuce](https://github.com/lettuce-io/lettuce-core) | 4.0+ | -| [Log4j 2](https://logging.apache.org/log4j/2.x/) | 2.13.2+ | +| [Log4j 2](https://logging.apache.org/log4j/2.x/) | 2.7+ | | [Logback](http://logback.qos.ch/) | 1.0+ | | [MongoDB Drivers](https://mongodb.github.io/mongo-java-driver/) | 3.3+ | | [Netty](https://github.com/netty/netty) | 3.8+ | diff --git a/instrumentation/log4j/log4j-2-testing/src/main/groovy/Log4j2Test.groovy b/instrumentation/log4j/log4j-2-testing/src/main/groovy/Log4j2Test.groovy index 09f58cecb703..c98415b0bb3c 100644 --- a/instrumentation/log4j/log4j-2-testing/src/main/groovy/Log4j2Test.groovy +++ b/instrumentation/log4j/log4j-2-testing/src/main/groovy/Log4j2Test.groovy @@ -14,14 +14,14 @@ * limitations under the License. */ -import io.opentelemetry.auto.test.AgentTestRunner +import io.opentelemetry.auto.test.InstrumentationSpecification import io.opentelemetry.auto.test.utils.TraceUtils import io.opentelemetry.instrumentation.log4j.v2_13_2.ListAppender import io.opentelemetry.trace.Span import io.opentelemetry.trace.TracingContextUtils import org.apache.logging.log4j.LogManager -abstract class Log4j2Test extends AgentTestRunner { +abstract class Log4j2Test extends InstrumentationSpecification { def cleanup() { ListAppender.get().clearEvents() } diff --git a/instrumentation/log4j/log4j-2.13.2/auto/src/test/groovy/AutoLog4j2Test.groovy b/instrumentation/log4j/log4j-2.13.2/auto/src/test/groovy/AutoLog4j2Test.groovy index 978204ace48f..47da4221f5f3 100644 --- a/instrumentation/log4j/log4j-2.13.2/auto/src/test/groovy/AutoLog4j2Test.groovy +++ b/instrumentation/log4j/log4j-2.13.2/auto/src/test/groovy/AutoLog4j2Test.groovy @@ -14,5 +14,7 @@ * limitations under the License. */ -class AutoLog4j2Test extends Log4j2Test { +import io.opentelemetry.auto.test.AgentTestTrait + +class AutoLog4j2Test extends Log4j2Test implements AgentTestTrait { } diff --git a/instrumentation/log4j/log4j-2.13.2/library/src/test/groovy/LibraryLog4j2Test.groovy b/instrumentation/log4j/log4j-2.13.2/library/src/test/groovy/LibraryLog4j2Test.groovy index e7a4b30ec605..cf65e081fb9f 100644 --- a/instrumentation/log4j/log4j-2.13.2/library/src/test/groovy/LibraryLog4j2Test.groovy +++ b/instrumentation/log4j/log4j-2.13.2/library/src/test/groovy/LibraryLog4j2Test.groovy @@ -14,5 +14,7 @@ * limitations under the License. */ -class LibraryLog4j2Test extends Log4j2Test { +import io.opentelemetry.auto.test.InstrumentationTestTrait + +class LibraryLog4j2Test extends Log4j2Test implements InstrumentationTestTrait { } \ No newline at end of file diff --git a/instrumentation/log4j/log4j-2.7/src/main/java/io/opentelemetry/instrumentation/auto/log4j/v2_7/Log4j27MdcInstrumentation.java b/instrumentation/log4j/log4j-2.7/src/main/java/io/opentelemetry/instrumentation/auto/log4j/v2_7/Log4j27MdcInstrumentation.java index 1c9115e13bbb..eda1bccdc59b 100644 --- a/instrumentation/log4j/log4j-2.7/src/main/java/io/opentelemetry/instrumentation/auto/log4j/v2_7/Log4j27MdcInstrumentation.java +++ b/instrumentation/log4j/log4j-2.7/src/main/java/io/opentelemetry/instrumentation/auto/log4j/v2_7/Log4j27MdcInstrumentation.java @@ -16,20 +16,16 @@ package io.opentelemetry.instrumentation.auto.log4j.v2_7; -import static io.opentelemetry.instrumentation.api.log.LoggingContextConstants.SAMPLED; -import static io.opentelemetry.instrumentation.api.log.LoggingContextConstants.SPAN_ID; -import static io.opentelemetry.instrumentation.api.log.LoggingContextConstants.TRACE_ID; import static io.opentelemetry.javaagent.tooling.ClassLoaderMatcher.hasClassesNamed; -import static io.opentelemetry.javaagent.tooling.bytebuddy.matcher.AgentElementMatchers.implementsInterface; import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.isPublic; +import static net.bytebuddy.matcher.ElementMatchers.isStatic; import static net.bytebuddy.matcher.ElementMatchers.named; import static net.bytebuddy.matcher.ElementMatchers.not; import static net.bytebuddy.matcher.ElementMatchers.returns; import com.google.auto.service.AutoService; import io.opentelemetry.javaagent.tooling.Instrumenter; -import io.opentelemetry.trace.SpanContext; -import io.opentelemetry.trace.TracingContextUtils; import java.util.Collections; import java.util.Map; import net.bytebuddy.asm.Advice; @@ -37,8 +33,7 @@ import net.bytebuddy.description.type.TypeDescription; import net.bytebuddy.implementation.bytecode.assign.Assigner.Typing; import net.bytebuddy.matcher.ElementMatcher; -import org.apache.logging.log4j.util.SortedArrayStringMap; -import org.apache.logging.log4j.util.StringMap; +import org.apache.logging.log4j.core.ContextDataInjector; @AutoService(Instrumenter.class) public class Log4j27MdcInstrumentation extends Instrumenter.Default { @@ -46,6 +41,13 @@ public Log4j27MdcInstrumentation() { super("log4j2", "log4j", "log4j-2.7"); } + @Override + public String[] helperClassNames() { + return new String[] { + "io.opentelemetry.instrumentation.auto.log4j.v2_7.SpanDecoratingContextDataInjector" + }; + } + @Override public ElementMatcher classLoaderMatcher() { return not(hasClassesNamed("org.apache.logging.log4j.core.util.ContextDataProvider")); @@ -53,39 +55,25 @@ public ElementMatcher classLoaderMatcher() { @Override public ElementMatcher typeMatcher() { - return implementsInterface(named("org.apache.logging.log4j.core.ContextDataInjector")); + return named("org.apache.logging.log4j.core.impl.ContextDataInjectorFactory"); } @Override public Map, String> transformers() { return Collections.singletonMap( isMethod() - .and(named("injectContextData")) - .and(returns(named("org.apache.logging.log4j.util.StringMap"))), - Log4j27MdcInstrumentation.class.getName() + "$InjectContextDataAdvice"); + .and(isPublic()) + .and(isStatic()) + .and(named("createInjector")) + .and(returns(named("org.apache.logging.log4j.core.ContextDataInjector"))), + Log4j27MdcInstrumentation.class.getName() + "$CreateInjectorAdvice"); } - public static class InjectContextDataAdvice { + public static class CreateInjectorAdvice { @Advice.OnMethodExit(suppress = Throwable.class) public static void onExit( - @Advice.Return(typing = Typing.DYNAMIC, readOnly = false) StringMap contextData) { - SpanContext currentContext = TracingContextUtils.getCurrentSpan().getContext(); - if (!currentContext.isValid()) { - return; - } - - if (contextData.containsKey(TRACE_ID)) { - // Assume already instrumented event if traceId is present. - return; - } - - StringMap newContextData = new SortedArrayStringMap(contextData); - newContextData.putValue(TRACE_ID, currentContext.getTraceIdAsHexString()); - newContextData.putValue(SPAN_ID, currentContext.getSpanIdAsHexString()); - if (currentContext.isSampled()) { - newContextData.putValue(SAMPLED, "true"); - } - contextData = newContextData; + @Advice.Return(typing = Typing.DYNAMIC, readOnly = false) ContextDataInjector injector) { + injector = new SpanDecoratingContextDataInjector(injector); } } } diff --git a/instrumentation/log4j/log4j-2.7/src/main/java/io/opentelemetry/instrumentation/auto/log4j/v2_7/SpanDecoratingContextDataInjector.java b/instrumentation/log4j/log4j-2.7/src/main/java/io/opentelemetry/instrumentation/auto/log4j/v2_7/SpanDecoratingContextDataInjector.java new file mode 100644 index 000000000000..8f14488830db --- /dev/null +++ b/instrumentation/log4j/log4j-2.7/src/main/java/io/opentelemetry/instrumentation/auto/log4j/v2_7/SpanDecoratingContextDataInjector.java @@ -0,0 +1,66 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.opentelemetry.instrumentation.auto.log4j.v2_7; + +import static io.opentelemetry.instrumentation.api.log.LoggingContextConstants.SAMPLED; +import static io.opentelemetry.instrumentation.api.log.LoggingContextConstants.SPAN_ID; +import static io.opentelemetry.instrumentation.api.log.LoggingContextConstants.TRACE_ID; + +import io.opentelemetry.trace.SpanContext; +import io.opentelemetry.trace.TracingContextUtils; +import java.util.List; +import org.apache.logging.log4j.core.ContextDataInjector; +import org.apache.logging.log4j.core.config.Property; +import org.apache.logging.log4j.util.ReadOnlyStringMap; +import org.apache.logging.log4j.util.SortedArrayStringMap; +import org.apache.logging.log4j.util.StringMap; + +public final class SpanDecoratingContextDataInjector implements ContextDataInjector { + private final ContextDataInjector delegate; + + public SpanDecoratingContextDataInjector(ContextDataInjector delegate) { + this.delegate = delegate; + } + + @Override + public StringMap injectContextData(List list, StringMap stringMap) { + StringMap contextData = delegate.injectContextData(list, stringMap); + + if (contextData.containsKey(TRACE_ID)) { + // Assume already instrumented event if traceId is present. + return contextData; + } + + SpanContext currentContext = TracingContextUtils.getCurrentSpan().getContext(); + if (!currentContext.isValid()) { + return contextData; + } + + StringMap newContextData = new SortedArrayStringMap(contextData); + newContextData.putValue(TRACE_ID, currentContext.getTraceIdAsHexString()); + newContextData.putValue(SPAN_ID, currentContext.getSpanIdAsHexString()); + if (currentContext.isSampled()) { + newContextData.putValue(SAMPLED, "true"); + } + return newContextData; + } + + @Override + public ReadOnlyStringMap rawContextData() { + return delegate.rawContextData(); + } +} diff --git a/instrumentation/log4j/log4j-2.7/src/test/groovy/Log4j27Test.groovy b/instrumentation/log4j/log4j-2.7/src/test/groovy/Log4j27Test.groovy index 4761dd9bc276..71ee2f8db73f 100644 --- a/instrumentation/log4j/log4j-2.7/src/test/groovy/Log4j27Test.groovy +++ b/instrumentation/log4j/log4j-2.7/src/test/groovy/Log4j27Test.groovy @@ -14,5 +14,7 @@ * limitations under the License. */ -class Log4j27Test extends Log4j2Test { +import io.opentelemetry.auto.test.AgentTestTrait + +class Log4j27Test extends Log4j2Test implements AgentTestTrait { }