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 support for jboss-logmanager #5737

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ dependencies {

compileOnly(project(":instrumentation-appender-api-internal"))

// ensure no cross interference
testInstrumentation(project(":instrumentation:jboss-logmanager-1.1:javaagent"))

testImplementation("org.awaitility:awaitility")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,6 @@ public void transform(TypeTransformer transformer) {
.and(takesArguments(1))
.and(takesArgument(0, named("java.util.logging.LogRecord"))),
JavaUtilLoggingInstrumentation.class.getName() + "$LogAdvice");
transformer.applyAdviceToMethod(
isMethod()
.and(isPublic())
.and(named("logRaw"))
.and(takesArguments(1))
.and(takesArgument(0, named("org.jboss.logmanager.ExtLogRecord"))),
JavaUtilLoggingInstrumentation.class.getName() + "$LogAdvice");
}

@SuppressWarnings("unused")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification
import io.opentelemetry.sdk.logs.data.Severity
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
import spock.lang.Shared
import spock.lang.Unroll

import java.util.logging.Level
Expand All @@ -17,12 +16,7 @@ import static org.awaitility.Awaitility.await

class JavaUtilLoggingTest extends AgentInstrumentationSpecification {

@Shared
private final Object logger = createLogger("abc")

Object createLogger(String name) {
Logger.getLogger(name)
}
private static final Logger logger = Logger.getLogger("abc")

@Unroll
def "test method=#testMethod with testArgs=#testArgs and parent=#parent"() {
Expand Down

This file was deleted.

29 changes: 29 additions & 0 deletions instrumentation/jboss-logmanager-1.1/javaagent/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
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"))

// ensure no cross interference
testInstrumentation(project(":instrumentation:java-util-logging:javaagent"))

testImplementation("org.awaitility:awaitility")
}

tasks.withType<Test>().configureEach {
// TODO run tests both with and without experimental log attributes
jvmArgs("-Dotel.instrumentation.jboss-logmanager.experimental.capture-mdc-attributes=*")
jvmArgs("-Dotel.instrumentation.jboss-logmanager.experimental-log-attributes=true")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* 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.instrumentation.api.appender.internal.LogEmitterProvider;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import io.opentelemetry.javaagent.instrumentation.api.CallDepth;
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.Logger;

public class JbossLogmanagerInstrumentation 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"))),
JbossLogmanagerInstrumentation.class.getName() + "$CallLogRawAdvice");
}

@SuppressWarnings("unused")
public static class CallLogRawAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void methodEnter(
@Advice.This() Logger logger,
@Advice.Argument(0) ExtLogRecord record,
@Advice.Local("otelCallDepth") CallDepth callDepth) {
// need to track call depth across all loggers in order to avoid double capture when one
// logging framework delegates to another
callDepth = CallDepth.forClass(LogEmitterProvider.class);
if (callDepth.getAndIncrement() == 0) {
LoggingEventMapper.INSTANCE.capture(logger, record);
}
}

@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void methodExit(@Advice.Local("otelCallDepth") CallDepth callDepth) {
callDepth.decrementAndGet();
}
}
}
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.Collections.singletonList;

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", "jboss-logmanager-1.1");
}

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

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

import static java.util.Collections.emptyList;

import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.context.Context;
import io.opentelemetry.instrumentation.api.appender.internal.LogBuilder;
import io.opentelemetry.instrumentation.api.appender.internal.Severity;
import io.opentelemetry.instrumentation.api.cache.Cache;
import io.opentelemetry.instrumentation.api.config.Config;
import io.opentelemetry.javaagent.instrumentation.api.appender.internal.AgentLogEmitterProvider;
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.List;
import java.util.Map;
import org.jboss.logmanager.ExtLogRecord;
import org.jboss.logmanager.Level;
import org.jboss.logmanager.Logger;
import org.jboss.logmanager.MDC;

public final class LoggingEventMapper {

public static final LoggingEventMapper INSTANCE = new LoggingEventMapper();

private static final Cache<String, AttributeKey<String>> mdcAttributeKeys = Cache.bounded(100);

private final List<String> captureMdcAttributes;

private static final boolean captureExperimentalAttributes =
Config.get()
.getBoolean("otel.instrumentation.jboss-logmanager.experimental-log-attributes", false);

// cached as an optimization
private final boolean captureAllMdcAttributes;

private LoggingEventMapper() {
this.captureMdcAttributes =
Config.get()
.getList(
"otel.instrumentation.jboss-logmanager.experimental.capture-mdc-attributes",
emptyList());
this.captureAllMdcAttributes =
captureMdcAttributes.size() == 1 && captureMdcAttributes.get(0).equals("*");
}

public void capture(Logger logger, ExtLogRecord record) {
String instrumentationName = logger.getName();
if (instrumentationName == null || instrumentationName.isEmpty()) {
instrumentationName = "ROOT";
}

LogBuilder builder =
AgentLogEmitterProvider.get().logEmitterBuilder(instrumentationName).build().logBuilder();

String message = record.getFormattedMessage();
if (message != null) {
builder.setBody(message);
}

java.util.logging.Level level = record.getLevel();
if (level != null) {
builder.setSeverity(levelToSeverity(level));
builder.setSeverityText(level.toString());
}

AttributesBuilder attributes = Attributes.builder();

Throwable throwable = record.getThrown();
if (throwable != null) {
// TODO (trask) extract method for recording exception into
// instrumentation-appender-api-internal
attributes.put(SemanticAttributes.EXCEPTION_TYPE, throwable.getClass().getName());
attributes.put(SemanticAttributes.EXCEPTION_MESSAGE, throwable.getMessage());
StringWriter writer = new StringWriter();
throwable.printStackTrace(new PrintWriter(writer));
attributes.put(SemanticAttributes.EXCEPTION_STACKTRACE, writer.toString());
}
captureMdcAttributes(attributes);

if (captureExperimentalAttributes) {
Thread currentThread = Thread.currentThread();
attributes.put(SemanticAttributes.THREAD_NAME, currentThread.getName());
attributes.put(SemanticAttributes.THREAD_ID, currentThread.getId());
}

builder.setAttributes(attributes.build());

builder.setContext(Context.current());

builder.emit();
}

private void captureMdcAttributes(AttributesBuilder attributes) {

Map<String, String> context = MDC.copy();

if (captureAllMdcAttributes) {
if (context != null) {
for (Map.Entry<String, String> entry : context.entrySet()) {
attributes.put(
getMdcAttributeKey(String.valueOf(entry.getKey())), String.valueOf(entry.getValue()));
}
}
return;
}

for (String key : captureMdcAttributes) {
Object value = context.get(key);
if (value != null) {
attributes.put(key, value.toString());
}
}
}

public static AttributeKey<String> getMdcAttributeKey(String key) {
return mdcAttributeKeys.computeIfAbsent(
key, k -> AttributeKey.stringKey("jboss-logmanager.mdc." + k));
}

private static Severity levelToSeverity(java.util.logging.Level level) {
int levelInt = level.intValue();
if (levelInt >= Level.FATAL.intValue()) {
return Severity.FATAL;
} else if (levelInt >= Level.ERROR.intValue()) {
return Severity.ERROR;
} else if (levelInt >= Level.WARNING.intValue()) {
return Severity.WARN;
} else if (levelInt >= Level.INFO.intValue()) {
return Severity.INFO;
} else if (levelInt >= Level.DEBUG.intValue()) {
return Severity.DEBUG;
} else if (levelInt >= Level.TRACE.intValue()) {
return Severity.TRACE;
}
return Severity.UNDEFINED_SEVERITY_NUMBER;
}
}
Loading