Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add jboss-logmanager mdc support #5842

Merged
merged 7 commits into from
Apr 19, 2022
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
plugins {
id("otel.javaagent-instrumentation")
}

muzzle {
pass {
group.set("org.jboss.logmanager")
module.set("jboss-logmanager")
versions.set("[1.1.0.GA,)")
assertInverse.set(true)
}
}

dependencies {
library("org.jboss.logmanager:jboss-logmanager:1.1.0.GA")

compileOnly(project(":instrumentation-appender-api-internal"))
mateuszrzeszutek marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.jbosslogmanager.v1_1;

import static io.opentelemetry.instrumentation.api.log.LoggingContextConstants.SPAN_ID;
import static io.opentelemetry.instrumentation.api.log.LoggingContextConstants.TRACE_FLAGS;
import static io.opentelemetry.instrumentation.api.log.LoggingContextConstants.TRACE_ID;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;

import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.SpanContext;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.field.VirtualField;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import java.util.Hashtable;
import java.util.Map;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import org.jboss.logmanager.ExtLogRecord;
import org.jboss.logmanager.MDC;

public class JbossExtLogRecordInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("org.jboss.logmanager.ExtLogRecord");
}

@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isMethod()
.and(isPublic())
.and(named("getMdc"))
.and(takesArguments(1))
.and(takesArgument(0, String.class)),
JbossExtLogRecordInstrumentation.class.getName() + "$GetMdcAdvice");

transformer.applyAdviceToMethod(
isMethod().and(isPublic()).and(named("copyMdc")).and(takesArguments(0)),
JbossExtLogRecordInstrumentation.class.getName() + "$GetMdcCopyAdvice");
mateuszrzeszutek marked this conversation as resolved.
Show resolved Hide resolved
}

@SuppressWarnings("unused")
public static class GetMdcAdvice {

@Advice.OnMethodExit(suppress = Throwable.class)
public static void onExit(
@Advice.This ExtLogRecord record,
@Advice.Argument(0) String key,
@Advice.Return(readOnly = false) String value) {
if (TRACE_ID.equals(key) || SPAN_ID.equals(key) || TRACE_FLAGS.equals(key)) {
if (value != null) {
// Assume already instrumented event if traceId/spanId/sampled is present.
return;
}

Context context = VirtualField.find(ExtLogRecord.class, Context.class).get(record);
SpanContext spanContext = Java8BytecodeBridge.spanFromContext(context).getSpanContext();
mateuszrzeszutek marked this conversation as resolved.
Show resolved Hide resolved

switch (key) {
case TRACE_ID:
value = spanContext.getTraceId();
break;
case SPAN_ID:
value = spanContext.getSpanId();
break;
case TRACE_FLAGS:
value = spanContext.getTraceFlags().asHex();
break;
default:
// do nothing
}
}
}
}

@SuppressWarnings("unused")
public static class GetMdcCopyAdvice {

@SuppressWarnings({"unchecked", "rawtypes"})
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(
@Advice.This ExtLogRecord record,
@Advice.FieldValue(value = "mdcCopy", readOnly = false) Map<String, String> mdcCopy) {
// this advice basically replaces the original method

Hashtable mdc = new Hashtable(MDC.copy());

// Assume already instrumented event if traceId is present.
if (!mdc.containsKey(TRACE_ID)) {
Span span = VirtualField.find(ExtLogRecord.class, Span.class).get(record);
if (span != null && span.getSpanContext().isValid()) {
SpanContext spanContext = span.getSpanContext();
mdc.put(TRACE_ID, spanContext.getTraceId());
mdc.put(SPAN_ID, spanContext.getSpanId());
mdc.put(TRACE_FLAGS, spanContext.getTraceFlags().asHex());
}
}

mdcCopy = mdc;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.jbosslogmanager.v1_1;

import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;

import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.field.VirtualField;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import org.jboss.logmanager.ExtLogRecord;

public class JbossLoggerInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("org.jboss.logmanager.Logger");
}

@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isMethod()
.and(isPublic())
.and(named("logRaw"))
.and(takesArguments(1))
.and(takesArgument(0, named("org.jboss.logmanager.ExtLogRecord"))),
JbossLoggerInstrumentation.class.getName() + "$CallAppendersAdvice");
}

@SuppressWarnings("unused")
public static class CallAppendersAdvice {

@Advice.OnMethodEnter(suppress = Throwable.class)
public static void onEnter(@Advice.Argument(0) ExtLogRecord record) {
VirtualField.find(ExtLogRecord.class, Context.class)
.set(record, Java8BytecodeBridge.currentContext());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.jbosslogmanager.v1_1;

import static java.util.Arrays.asList;

import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import java.util.List;

@AutoService(InstrumentationModule.class)
public class JbossLogmanagerInstrumentationModule extends InstrumentationModule {

public JbossLogmanagerInstrumentationModule() {
super("jboss-logmanager-mdc", "jboss-logmanager-mdc-1.1");
}

@Override
public List<TypeInstrumentation> typeInstrumentations() {
return asList(new JbossLoggerInstrumentation(), new JbossExtLogRecordInstrumentation());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

import io.opentelemetry.api.trace.Span
import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification
import org.jboss.logmanager.ExtLogRecord
import org.jboss.logmanager.Level
import org.jboss.logmanager.LogContext

import java.util.logging.Handler
import java.util.logging.LogRecord

class JbossLogmanagerMdcTest extends AgentInstrumentationSpecification {
class LogHandler extends Handler {
public List<ExtLogRecord> logRecords

LogHandler(LinkedList<ExtLogRecord> logRecords) {
this.logRecords = logRecords
}
@Override
void publish(LogRecord record) {
logRecords.push(record as ExtLogRecord)
}

@Override
void flush() {

}

@Override
void close() throws SecurityException {
}
}
def "no ids when no span"() {
given:
LinkedList<ExtLogRecord> logRecords = []
def logger = LogContext.getLogContext().getLogger('TestLogger')
logger.setLevel(Level.INFO)
logger.addHandler(new LogHandler(logRecords))
trask marked this conversation as resolved.
Show resolved Hide resolved

when:
logger.info("log message 1")

then:
logRecords.size() == 1
logRecords.get(0).message == "log message 1"
logRecords.get(0).getMdc("trace_id") == "00000000000000000000000000000000"
logRecords.get(0).getMdc("span_id") == "0000000000000000"
logRecords.get(0).getMdc("trace_flags") == "00"

cleanup:
logRecords.clear()
}

def "ids when span"() {
given:
def logger = LogContext.getLogContext().getLogger('TestLogger')
logger.setLevel(Level.DEBUG)
LinkedList<ExtLogRecord> logRecords = []
logger.addHandler(new LogHandler(logRecords))
when:
def span1 = runWithSpan("test") {
logger.info("log message 1")
Span.current()
}

logger.info("log message 2")

def span2 = runWithSpan("test 2") {
logger.info("log message 3")
Span.current()
}

then:

logRecords.size() == 3
logRecords.get(2).message == "log message 1"
logRecords.get(2).getMdc("trace_id") == span1.spanContext.traceId
logRecords.get(2).getMdc("span_id") == span1.spanContext.spanId
logRecords.get(2).getMdc("trace_flags") == "01"

logRecords.get(1).message == "log message 2"
logRecords.get(1).getMdc("trace_id") == "00000000000000000000000000000000"
logRecords.get(1).getMdc("span_id") == "0000000000000000"
logRecords.get(1).getMdc("trace_flags") == "00"
mateuszrzeszutek marked this conversation as resolved.
Show resolved Hide resolved

logRecords.get(0).message == "log message 3"
// this explicit getMdcCopy() call here is to make sure that whole instrumentation is tested
logRecords.get(0).copyMdc()
logRecords.get(0).getMdc("trace_id") == span2.spanContext.traceId
logRecords.get(0).getMdc("span_id") == span2.spanContext.spanId
logRecords.get(0).getMdc("trace_flags") == "01"

cleanup:
logRecords.clear()
}
}
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ include(":instrumentation:jaxws:jaxws-2.0-wildfly-testing")
include(":instrumentation:jaxws:jaxws-common:library")
include(":instrumentation:jaxws:jaxws-jws-api-1.1:javaagent")
include(":instrumentation:jboss-logmanager-1.1:javaagent")
include(":instrumentation:jboss-logmanager-mdc-1.1:javaagent")
include(":instrumentation:jdbc:javaagent")
include(":instrumentation:jdbc:library")
include(":instrumentation:jdbc:testing")
Expand Down