Skip to content

Commit

Permalink
Implement MDC auto-instrumentation for log4j2
Browse files Browse the repository at this point in the history
  • Loading branch information
mateuszrzeszutek committed Sep 14, 2020
1 parent 221deb1 commit e860520
Show file tree
Hide file tree
Showing 11 changed files with 175 additions and 10 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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+ |
Expand Down
18 changes: 18 additions & 0 deletions instrumentation/log4j/log4j-2.13.2/auto/log4j-2.13.2-auto.gradle
Original file line number Diff line number Diff line change
@@ -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')
}
Original file line number Diff line number Diff line change
@@ -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<ClassLoader> classLoaderMatcher() {
return hasClassesNamed("org.apache.logging.log4j.core.util.ContextDataProvider");
}

@Override
public ElementMatcher<? super TypeDescription> 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<? extends ElementMatcher<? super MethodDescription>, 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<ContextDataProvider> providers) {
// check if already instrumented
for (ContextDataProvider provider : providers) {
if (provider instanceof OpenTelemetryContextDataProvider) {
return;
}
}

List<ContextDataProvider> instrumentedProviders = new ArrayList<>(providers.size() + 1);
instrumentedProviders.addAll(providers);
instrumentedProviders.add(new OpenTelemetryContextDataProvider());
providers = instrumentedProviders;
}
}
}
Original file line number Diff line number Diff line change
@@ -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 {
}
Original file line number Diff line number Diff line change
Expand Up @@ -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')
}
Original file line number Diff line number Diff line change
@@ -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 {
}
Original file line number Diff line number Diff line change
@@ -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'
}
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -52,6 +50,9 @@ class Log4j2Test extends Specification {
}

def "ids when span"() {
given:
def logger = LogManager.getLogger("TestLogger")

when:
Span span1
TraceUtils.runUnderTrace("test") {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public static ListAppender get() {

private static final ListAppender INSTANCE = new ListAppender();

private final List<LogEvent> events = Collections.synchronizedList(new ArrayList<>());
private final List<LogEvent> events = Collections.synchronizedList(new ArrayList<LogEvent>());

public ListAppender() {
super("ListAppender", null, null, true, Property.EMPTY_ARRAY);
Expand Down
2 changes: 2 additions & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down

0 comments on commit e860520

Please sign in to comment.