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
Mateusz Rzeszutek committed Sep 16, 2020
1 parent e323d3c commit 9d9b54d
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 37 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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+ |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,7 @@
* limitations under the License.
*/

class AutoLog4j2Test extends Log4j2Test {
import io.opentelemetry.auto.test.AgentTestTrait

class AutoLog4j2Test extends Log4j2Test implements AgentTestTrait {
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,7 @@
* limitations under the License.
*/

class LibraryLog4j2Test extends Log4j2Test {
import io.opentelemetry.auto.test.InstrumentationTestTrait

class LibraryLog4j2Test extends Log4j2Test implements InstrumentationTestTrait {
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,76 +16,64 @@

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;
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.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 {
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<ClassLoader> classLoaderMatcher() {
return not(hasClassesNamed("org.apache.logging.log4j.core.util.ContextDataProvider"));
}

@Override
public ElementMatcher<? super TypeDescription> typeMatcher() {
return implementsInterface(named("org.apache.logging.log4j.core.ContextDataInjector"));
return named("org.apache.logging.log4j.core.impl.ContextDataInjectorFactory");
}

@Override
public Map<? extends ElementMatcher<? super MethodDescription>, 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);
}
}
}
Original file line number Diff line number Diff line change
@@ -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<Property> 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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,7 @@
* limitations under the License.
*/

class Log4j27Test extends Log4j2Test {
import io.opentelemetry.auto.test.AgentTestTrait

class Log4j27Test extends Log4j2Test implements AgentTestTrait {
}

0 comments on commit 9d9b54d

Please sign in to comment.