From e860520997f48c8529ba9458d55e5bdb8570bf8a Mon Sep 17 00:00:00 2001 From: mateuszrzeszutek Date: Mon, 14 Sep 2020 15:52:01 +0200 Subject: [PATCH] Implement MDC auto-instrumentation for log4j2 --- README.md | 1 + .../auto/log4j-2.13.2-auto.gradle | 18 ++++ .../log4j/v2_13_2/Log4j2Instrumentation.java | 92 +++++++++++++++++++ .../src/test/groovy/AutoLog4j2Test.groovy | 18 ++++ .../library/log4j-2.13.2-library.gradle | 2 +- .../src/test/groovy/LibraryLog4j2Test.groovy | 18 ++++ .../testing/log4j-2.13.2-testing.gradle | 15 +++ .../src/main/groovy}/Log4j2Test.groovy | 17 ++-- .../log4j/v2_13_2/ListAppender.java | 2 +- .../src/main}/resources/log4j2-test.xml | 0 settings.gradle | 2 + 11 files changed, 175 insertions(+), 10 deletions(-) create mode 100644 instrumentation/log4j/log4j-2.13.2/auto/log4j-2.13.2-auto.gradle create mode 100644 instrumentation/log4j/log4j-2.13.2/auto/src/main/java/io/opentelemetry/instrumentation/auto/log4j/v2_13_2/Log4j2Instrumentation.java create mode 100644 instrumentation/log4j/log4j-2.13.2/auto/src/test/groovy/AutoLog4j2Test.groovy create mode 100644 instrumentation/log4j/log4j-2.13.2/library/src/test/groovy/LibraryLog4j2Test.groovy create mode 100644 instrumentation/log4j/log4j-2.13.2/testing/log4j-2.13.2-testing.gradle rename instrumentation/log4j/log4j-2.13.2/{library/src/test/groovy/io/opentelemetry/instrumentation/log4j/v2_13_2 => testing/src/main/groovy}/Log4j2Test.groovy (90%) rename instrumentation/log4j/log4j-2.13.2/{library/src/test => testing/src/main}/java/io/opentelemetry/instrumentation/log4j/v2_13_2/ListAppender.java (98%) rename instrumentation/log4j/log4j-2.13.2/{library/src/test => testing/src/main}/resources/log4j2-test.xml (100%) diff --git a/README.md b/README.md index 9f701aad6101..89d6d9eaae1d 100644 --- a/README.md +++ b/README.md @@ -204,6 +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+ | | [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.13.2/auto/log4j-2.13.2-auto.gradle b/instrumentation/log4j/log4j-2.13.2/auto/log4j-2.13.2-auto.gradle new file mode 100644 index 000000000000..d63e3d94c063 --- /dev/null +++ b/instrumentation/log4j/log4j-2.13.2/auto/log4j-2.13.2-auto.gradle @@ -0,0 +1,18 @@ +apply from: "$rootDir/gradle/instrumentation.gradle" + +muzzle { + pass { + group = "org.apache.logging.log4j" + module = "log4j-core" + versions = "[2.13.2,)" + assertInverse = true + } +} + +dependencies { + library group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.13.2' + + implementation project(':instrumentation:log4j:log4j-2.13.2:library') + + testImplementation project(':instrumentation:log4j:log4j-2.13.2:testing') +} diff --git a/instrumentation/log4j/log4j-2.13.2/auto/src/main/java/io/opentelemetry/instrumentation/auto/log4j/v2_13_2/Log4j2Instrumentation.java b/instrumentation/log4j/log4j-2.13.2/auto/src/main/java/io/opentelemetry/instrumentation/auto/log4j/v2_13_2/Log4j2Instrumentation.java new file mode 100644 index 000000000000..75fa183a4510 --- /dev/null +++ b/instrumentation/log4j/log4j-2.13.2/auto/src/main/java/io/opentelemetry/instrumentation/auto/log4j/v2_13_2/Log4j2Instrumentation.java @@ -0,0 +1,92 @@ +/* + * 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_13_2; + +import static io.opentelemetry.javaagent.tooling.ClassLoaderMatcher.hasClassesNamed; +import static net.bytebuddy.matcher.ElementMatchers.isMethod; +import static net.bytebuddy.matcher.ElementMatchers.isPrivate; +import static net.bytebuddy.matcher.ElementMatchers.isStatic; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +import com.google.auto.service.AutoService; +import io.opentelemetry.instrumentation.log4j.v2_13_2.OpenTelemetryContextDataProvider; +import io.opentelemetry.javaagent.tooling.Instrumenter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.implementation.bytecode.assign.Assigner.Typing; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.logging.log4j.core.util.ContextDataProvider; + +@AutoService(Instrumenter.class) +public final class Log4j2Instrumentation extends Instrumenter.Default { + public Log4j2Instrumentation() { + super("log4j2", "log4j"); + } + + @Override + public ElementMatcher classLoaderMatcher() { + return hasClassesNamed("org.apache.logging.log4j.core.util.ContextDataProvider"); + } + + @Override + public ElementMatcher typeMatcher() { + return named("org.apache.logging.log4j.core.impl.ThreadContextDataInjector"); + } + + @Override + public String[] helperClassNames() { + return new String[] { + "io.opentelemetry.instrumentation.log4j.v2_13_2.OpenTelemetryContextDataProvider" + }; + } + + @Override + public Map, String> transformers() { + return Collections.singletonMap( + isMethod() + .and(isPrivate()) + .and(isStatic()) + .and(named("getProviders")) + .and(takesArguments(0)), + Log4j2Instrumentation.class.getName() + "$GetProvidersAdvice"); + } + + public static class GetProvidersAdvice { + @Advice.OnMethodExit(suppress = Throwable.class) + public static void onExit( + @Advice.Return(typing = Typing.DYNAMIC, readOnly = false) + List providers) { + // check if already instrumented + for (ContextDataProvider provider : providers) { + if (provider instanceof OpenTelemetryContextDataProvider) { + return; + } + } + + List instrumentedProviders = new ArrayList<>(providers.size() + 1); + instrumentedProviders.addAll(providers); + instrumentedProviders.add(new OpenTelemetryContextDataProvider()); + providers = instrumentedProviders; + } + } +} 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 new file mode 100644 index 000000000000..978204ace48f --- /dev/null +++ b/instrumentation/log4j/log4j-2.13.2/auto/src/test/groovy/AutoLog4j2Test.groovy @@ -0,0 +1,18 @@ +/* + * 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. + */ + +class AutoLog4j2Test extends Log4j2Test { +} diff --git a/instrumentation/log4j/log4j-2.13.2/library/log4j-2.13.2-library.gradle b/instrumentation/log4j/log4j-2.13.2/library/log4j-2.13.2-library.gradle index 3469e09f6135..12e2338d18e3 100644 --- a/instrumentation/log4j/log4j-2.13.2/library/log4j-2.13.2-library.gradle +++ b/instrumentation/log4j/log4j-2.13.2/library/log4j-2.13.2-library.gradle @@ -10,5 +10,5 @@ dependencies { annotationProcessor deps.autoservice compileOnly deps.autoservice - testAnnotationProcessor group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.13.2' + testImplementation project(':instrumentation:log4j:log4j-2.13.2:testing') } 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 new file mode 100644 index 000000000000..e7a4b30ec605 --- /dev/null +++ b/instrumentation/log4j/log4j-2.13.2/library/src/test/groovy/LibraryLog4j2Test.groovy @@ -0,0 +1,18 @@ +/* + * 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. + */ + +class LibraryLog4j2Test extends Log4j2Test { +} \ No newline at end of file diff --git a/instrumentation/log4j/log4j-2.13.2/testing/log4j-2.13.2-testing.gradle b/instrumentation/log4j/log4j-2.13.2/testing/log4j-2.13.2-testing.gradle new file mode 100644 index 000000000000..415b8b11b1c0 --- /dev/null +++ b/instrumentation/log4j/log4j-2.13.2/testing/log4j-2.13.2-testing.gradle @@ -0,0 +1,15 @@ +apply from: "$rootDir/gradle/java.gradle" + +dependencies { + api project(':testing-common') + + api group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.13.2' + + implementation deps.guava + + implementation deps.groovy + implementation deps.opentelemetryApi + implementation deps.spock + + annotationProcessor group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.13.2' +} diff --git a/instrumentation/log4j/log4j-2.13.2/library/src/test/groovy/io/opentelemetry/instrumentation/log4j/v2_13_2/Log4j2Test.groovy b/instrumentation/log4j/log4j-2.13.2/testing/src/main/groovy/Log4j2Test.groovy similarity index 90% rename from instrumentation/log4j/log4j-2.13.2/library/src/test/groovy/io/opentelemetry/instrumentation/log4j/v2_13_2/Log4j2Test.groovy rename to instrumentation/log4j/log4j-2.13.2/testing/src/main/groovy/Log4j2Test.groovy index 449a82b0f6e1..ae04c22853ca 100644 --- a/instrumentation/log4j/log4j-2.13.2/library/src/test/groovy/io/opentelemetry/instrumentation/log4j/v2_13_2/Log4j2Test.groovy +++ b/instrumentation/log4j/log4j-2.13.2/testing/src/main/groovy/Log4j2Test.groovy @@ -14,24 +14,22 @@ * limitations under the License. */ -package io.opentelemetry.instrumentation.log4j.v2_13_2 - +import io.opentelemetry.auto.test.AgentTestRunner 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 -import org.apache.logging.log4j.Logger -import spock.lang.Specification - -class Log4j2Test extends Specification { - - private static final Logger logger = LogManager.getLogger("TestLogger") +abstract class Log4j2Test extends AgentTestRunner { def cleanup() { ListAppender.get().clearEvents() } def "no ids when no span"() { + given: + def logger = LogManager.getLogger("TestLogger") + when: logger.info("log message 1") logger.info("log message 2") @@ -52,6 +50,9 @@ class Log4j2Test extends Specification { } def "ids when span"() { + given: + def logger = LogManager.getLogger("TestLogger") + when: Span span1 TraceUtils.runUnderTrace("test") { diff --git a/instrumentation/log4j/log4j-2.13.2/library/src/test/java/io/opentelemetry/instrumentation/log4j/v2_13_2/ListAppender.java b/instrumentation/log4j/log4j-2.13.2/testing/src/main/java/io/opentelemetry/instrumentation/log4j/v2_13_2/ListAppender.java similarity index 98% rename from instrumentation/log4j/log4j-2.13.2/library/src/test/java/io/opentelemetry/instrumentation/log4j/v2_13_2/ListAppender.java rename to instrumentation/log4j/log4j-2.13.2/testing/src/main/java/io/opentelemetry/instrumentation/log4j/v2_13_2/ListAppender.java index 3bf0772e9bb6..514e1404f219 100644 --- a/instrumentation/log4j/log4j-2.13.2/library/src/test/java/io/opentelemetry/instrumentation/log4j/v2_13_2/ListAppender.java +++ b/instrumentation/log4j/log4j-2.13.2/testing/src/main/java/io/opentelemetry/instrumentation/log4j/v2_13_2/ListAppender.java @@ -41,7 +41,7 @@ public static ListAppender get() { private static final ListAppender INSTANCE = new ListAppender(); - private final List events = Collections.synchronizedList(new ArrayList<>()); + private final List events = Collections.synchronizedList(new ArrayList()); public ListAppender() { super("ListAppender", null, null, true, Property.EMPTY_ARRAY); diff --git a/instrumentation/log4j/log4j-2.13.2/library/src/test/resources/log4j2-test.xml b/instrumentation/log4j/log4j-2.13.2/testing/src/main/resources/log4j2-test.xml similarity index 100% rename from instrumentation/log4j/log4j-2.13.2/library/src/test/resources/log4j2-test.xml rename to instrumentation/log4j/log4j-2.13.2/testing/src/main/resources/log4j2-test.xml diff --git a/settings.gradle b/settings.gradle index bd81ec7ea9ff..cd1dfc629d55 100644 --- a/settings.gradle +++ b/settings.gradle @@ -126,7 +126,9 @@ include ':instrumentation:kubernetes-client-7.0' include ':instrumentation:lettuce:lettuce-4.0' include ':instrumentation:lettuce:lettuce-5.0' include ':instrumentation:lettuce:lettuce-5.1' +include ':instrumentation:log4j:log4j-2.13.2:auto' include ':instrumentation:log4j:log4j-2.13.2:library' +include ':instrumentation:log4j:log4j-2.13.2:testing' include ':instrumentation:logback:logback-1.0.0:auto' include ':instrumentation:logback:logback-1.0.0:library' include ':instrumentation:logback:logback-1.0.0:testing'