Skip to content

Commit 4815f1e

Browse files
cuichenlitrask
andauthored
Add support for jboss-logmanager (open-telemetry#5737)
* initialize the package Signed-off-by: Cuichen Li <[email protected]> * add jboss-logmanger 2.1 instrumentation Signed-off-by: Cuichen Li <[email protected]> * rename the test Signed-off-by: Cuichen Li <[email protected]> * clean comment Signed-off-by: Cuichen Li <[email protected]> * Revert "Add JBoss java.util.logging support (open-telemetry#5498)" This reverts commit 8b26cef. * Remove extra directory * Remove old jboss log manager test * Ensure no cross interference * Change base version to 1.1 * fix styles Signed-off-by: Cuichen Li <[email protected]> * run spotless apply Signed-off-by: Cuichen Li <[email protected]> * fix codenarc Signed-off-by: Cuichen Li <[email protected]> * change the package version and additional module name Signed-off-by: Cuichen Li <[email protected]> Co-authored-by: Trask Stalnaker <[email protected]>
1 parent 5b0d9e3 commit 4815f1e

File tree

10 files changed

+323
-73
lines changed

10 files changed

+323
-73
lines changed

instrumentation/java-util-logging/javaagent/build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ dependencies {
77

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

10+
// ensure no cross interference
11+
testInstrumentation(project(":instrumentation:jboss-logmanager-1.1:javaagent"))
12+
1013
testImplementation("org.awaitility:awaitility")
1114
}
1215

instrumentation/java-util-logging/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/jul/JavaUtilLoggingInstrumentation.java

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,6 @@ public void transform(TypeTransformer transformer) {
3838
.and(takesArguments(1))
3939
.and(takesArgument(0, named("java.util.logging.LogRecord"))),
4040
JavaUtilLoggingInstrumentation.class.getName() + "$LogAdvice");
41-
transformer.applyAdviceToMethod(
42-
isMethod()
43-
.and(isPublic())
44-
.and(named("logRaw"))
45-
.and(takesArguments(1))
46-
.and(takesArgument(0, named("org.jboss.logmanager.ExtLogRecord"))),
47-
JavaUtilLoggingInstrumentation.class.getName() + "$LogAdvice");
4841
}
4942

5043
@SuppressWarnings("unused")

instrumentation/java-util-logging/javaagent/src/test/groovy/JavaUtilLoggingTest.groovy

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import io.opentelemetry.instrumentation.test.AgentInstrumentationSpecification
77
import io.opentelemetry.sdk.logs.data.Severity
88
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
9-
import spock.lang.Shared
109
import spock.lang.Unroll
1110

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

1817
class JavaUtilLoggingTest extends AgentInstrumentationSpecification {
1918

20-
@Shared
21-
private final Object logger = createLogger("abc")
22-
23-
Object createLogger(String name) {
24-
Logger.getLogger(name)
25-
}
19+
private static final Logger logger = Logger.getLogger("abc")
2620

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

instrumentation/java-util-logging/jboss-testing/build.gradle.kts

Lines changed: 0 additions & 25 deletions
This file was deleted.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
plugins {
2+
id("otel.javaagent-instrumentation")
3+
}
4+
5+
muzzle {
6+
pass {
7+
group.set("org.jboss.logmanager")
8+
module.set("jboss-logmanager")
9+
versions.set("[1.1.0.GA,)")
10+
assertInverse.set(true)
11+
}
12+
}
13+
14+
dependencies {
15+
library("org.jboss.logmanager:jboss-logmanager:1.1.0.GA")
16+
17+
compileOnly(project(":instrumentation-appender-api-internal"))
18+
19+
// ensure no cross interference
20+
testInstrumentation(project(":instrumentation:java-util-logging:javaagent"))
21+
22+
testImplementation("org.awaitility:awaitility")
23+
}
24+
25+
tasks.withType<Test>().configureEach {
26+
// TODO run tests both with and without experimental log attributes
27+
jvmArgs("-Dotel.instrumentation.jboss-logmanager.experimental.capture-mdc-attributes=*")
28+
jvmArgs("-Dotel.instrumentation.jboss-logmanager.experimental-log-attributes=true")
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.jbosslogmanager.v1_1;
7+
8+
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
9+
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
10+
import static net.bytebuddy.matcher.ElementMatchers.named;
11+
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
12+
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;
13+
14+
import io.opentelemetry.instrumentation.api.appender.internal.LogEmitterProvider;
15+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
16+
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
17+
import io.opentelemetry.javaagent.instrumentation.api.CallDepth;
18+
import net.bytebuddy.asm.Advice;
19+
import net.bytebuddy.description.type.TypeDescription;
20+
import net.bytebuddy.matcher.ElementMatcher;
21+
import org.jboss.logmanager.ExtLogRecord;
22+
import org.jboss.logmanager.Logger;
23+
24+
public class JbossLogmanagerInstrumentation implements TypeInstrumentation {
25+
@Override
26+
public ElementMatcher<TypeDescription> typeMatcher() {
27+
return named("org.jboss.logmanager.Logger");
28+
}
29+
30+
@Override
31+
public void transform(TypeTransformer transformer) {
32+
transformer.applyAdviceToMethod(
33+
isMethod()
34+
.and(isPublic())
35+
.and(named("logRaw"))
36+
.and(takesArguments(1))
37+
.and(takesArgument(0, named("org.jboss.logmanager.ExtLogRecord"))),
38+
JbossLogmanagerInstrumentation.class.getName() + "$CallLogRawAdvice");
39+
}
40+
41+
@SuppressWarnings("unused")
42+
public static class CallLogRawAdvice {
43+
@Advice.OnMethodEnter(suppress = Throwable.class)
44+
public static void methodEnter(
45+
@Advice.This() Logger logger,
46+
@Advice.Argument(0) ExtLogRecord record,
47+
@Advice.Local("otelCallDepth") CallDepth callDepth) {
48+
// need to track call depth across all loggers in order to avoid double capture when one
49+
// logging framework delegates to another
50+
callDepth = CallDepth.forClass(LogEmitterProvider.class);
51+
if (callDepth.getAndIncrement() == 0) {
52+
LoggingEventMapper.INSTANCE.capture(logger, record);
53+
}
54+
}
55+
56+
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
57+
public static void methodExit(@Advice.Local("otelCallDepth") CallDepth callDepth) {
58+
callDepth.decrementAndGet();
59+
}
60+
}
61+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.jbosslogmanager.v1_1;
7+
8+
import static java.util.Collections.singletonList;
9+
10+
import com.google.auto.service.AutoService;
11+
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
12+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
13+
import java.util.List;
14+
15+
@AutoService(InstrumentationModule.class)
16+
public class JbossLogmanagerInstrumentationModule extends InstrumentationModule {
17+
18+
public JbossLogmanagerInstrumentationModule() {
19+
super("jboss-logmanager", "jboss-logmanager-1.1");
20+
}
21+
22+
@Override
23+
public List<TypeInstrumentation> typeInstrumentations() {
24+
return singletonList(new JbossLogmanagerInstrumentation());
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.jbosslogmanager.v1_1;
7+
8+
import static java.util.Collections.emptyList;
9+
10+
import io.opentelemetry.api.common.AttributeKey;
11+
import io.opentelemetry.api.common.Attributes;
12+
import io.opentelemetry.api.common.AttributesBuilder;
13+
import io.opentelemetry.context.Context;
14+
import io.opentelemetry.instrumentation.api.appender.internal.LogBuilder;
15+
import io.opentelemetry.instrumentation.api.appender.internal.Severity;
16+
import io.opentelemetry.instrumentation.api.cache.Cache;
17+
import io.opentelemetry.instrumentation.api.config.Config;
18+
import io.opentelemetry.javaagent.instrumentation.api.appender.internal.AgentLogEmitterProvider;
19+
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
20+
import java.io.PrintWriter;
21+
import java.io.StringWriter;
22+
import java.util.List;
23+
import java.util.Map;
24+
import org.jboss.logmanager.ExtLogRecord;
25+
import org.jboss.logmanager.Level;
26+
import org.jboss.logmanager.Logger;
27+
import org.jboss.logmanager.MDC;
28+
29+
public final class LoggingEventMapper {
30+
31+
public static final LoggingEventMapper INSTANCE = new LoggingEventMapper();
32+
33+
private static final Cache<String, AttributeKey<String>> mdcAttributeKeys = Cache.bounded(100);
34+
35+
private final List<String> captureMdcAttributes;
36+
37+
private static final boolean captureExperimentalAttributes =
38+
Config.get()
39+
.getBoolean("otel.instrumentation.jboss-logmanager.experimental-log-attributes", false);
40+
41+
// cached as an optimization
42+
private final boolean captureAllMdcAttributes;
43+
44+
private LoggingEventMapper() {
45+
this.captureMdcAttributes =
46+
Config.get()
47+
.getList(
48+
"otel.instrumentation.jboss-logmanager.experimental.capture-mdc-attributes",
49+
emptyList());
50+
this.captureAllMdcAttributes =
51+
captureMdcAttributes.size() == 1 && captureMdcAttributes.get(0).equals("*");
52+
}
53+
54+
public void capture(Logger logger, ExtLogRecord record) {
55+
String instrumentationName = logger.getName();
56+
if (instrumentationName == null || instrumentationName.isEmpty()) {
57+
instrumentationName = "ROOT";
58+
}
59+
60+
LogBuilder builder =
61+
AgentLogEmitterProvider.get().logEmitterBuilder(instrumentationName).build().logBuilder();
62+
63+
String message = record.getFormattedMessage();
64+
if (message != null) {
65+
builder.setBody(message);
66+
}
67+
68+
java.util.logging.Level level = record.getLevel();
69+
if (level != null) {
70+
builder.setSeverity(levelToSeverity(level));
71+
builder.setSeverityText(level.toString());
72+
}
73+
74+
AttributesBuilder attributes = Attributes.builder();
75+
76+
Throwable throwable = record.getThrown();
77+
if (throwable != null) {
78+
// TODO (trask) extract method for recording exception into
79+
// instrumentation-appender-api-internal
80+
attributes.put(SemanticAttributes.EXCEPTION_TYPE, throwable.getClass().getName());
81+
attributes.put(SemanticAttributes.EXCEPTION_MESSAGE, throwable.getMessage());
82+
StringWriter writer = new StringWriter();
83+
throwable.printStackTrace(new PrintWriter(writer));
84+
attributes.put(SemanticAttributes.EXCEPTION_STACKTRACE, writer.toString());
85+
}
86+
captureMdcAttributes(attributes);
87+
88+
if (captureExperimentalAttributes) {
89+
Thread currentThread = Thread.currentThread();
90+
attributes.put(SemanticAttributes.THREAD_NAME, currentThread.getName());
91+
attributes.put(SemanticAttributes.THREAD_ID, currentThread.getId());
92+
}
93+
94+
builder.setAttributes(attributes.build());
95+
96+
builder.setContext(Context.current());
97+
98+
builder.emit();
99+
}
100+
101+
private void captureMdcAttributes(AttributesBuilder attributes) {
102+
103+
Map<String, String> context = MDC.copy();
104+
105+
if (captureAllMdcAttributes) {
106+
if (context != null) {
107+
for (Map.Entry<String, String> entry : context.entrySet()) {
108+
attributes.put(
109+
getMdcAttributeKey(String.valueOf(entry.getKey())), String.valueOf(entry.getValue()));
110+
}
111+
}
112+
return;
113+
}
114+
115+
for (String key : captureMdcAttributes) {
116+
Object value = context.get(key);
117+
if (value != null) {
118+
attributes.put(key, value.toString());
119+
}
120+
}
121+
}
122+
123+
public static AttributeKey<String> getMdcAttributeKey(String key) {
124+
return mdcAttributeKeys.computeIfAbsent(
125+
key, k -> AttributeKey.stringKey("jboss-logmanager.mdc." + k));
126+
}
127+
128+
private static Severity levelToSeverity(java.util.logging.Level level) {
129+
int levelInt = level.intValue();
130+
if (levelInt >= Level.FATAL.intValue()) {
131+
return Severity.FATAL;
132+
} else if (levelInt >= Level.ERROR.intValue()) {
133+
return Severity.ERROR;
134+
} else if (levelInt >= Level.WARNING.intValue()) {
135+
return Severity.WARN;
136+
} else if (levelInt >= Level.INFO.intValue()) {
137+
return Severity.INFO;
138+
} else if (levelInt >= Level.DEBUG.intValue()) {
139+
return Severity.DEBUG;
140+
} else if (levelInt >= Level.TRACE.intValue()) {
141+
return Severity.TRACE;
142+
}
143+
return Severity.UNDEFINED_SEVERITY_NUMBER;
144+
}
145+
}

0 commit comments

Comments
 (0)