Skip to content
Merged
9 changes: 5 additions & 4 deletions instrumentation/jboss-logmanager/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Settings for the JBoss Log Manager instrumentation

| System property | Type | Default | Description |
|-----------------------------------------------------------------------------|---------|---------|--------------------------------------------------------------------------------------------------------------|
| `otel.instrumentation.jboss-logmanager.experimental-log-attributes` | Boolean | `false` | Enable the capture of experimental log attributes `thread.name` and `thread.id`. |
| `otel.instrumentation.jboss-logmanager.experimental.capture-mdc-attributes` | String | | Comma separated list of MDC attributes to capture. Use the wildcard character `*` to capture all attributes. |
| System property | Type | Default | Description |
|-----------------------------------------------------------------------------|---------|---------|------------------------------------------------------------------------------------------------------------------------------------|
| `otel.instrumentation.jboss-logmanager.experimental-log-attributes` | Boolean | `false` | Enable the capture of experimental log attributes `thread.name` and `thread.id`. |
| `otel.instrumentation.jboss-logmanager.experimental.capture-mdc-attributes` | String | | Comma separated list of MDC attributes to capture. Use the wildcard character `*` to capture all attributes. |
| `otel.instrumentation.jboss-logmanager.experimental.capture-event-name` | Boolean | `false` | Enable moving the `event.name` attribute (captured by one of the other mechanisms of capturing attributes) to the log event name. |
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ if (latestDepTest) {
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.capture-event-name=true")
jvmArgs("-Dotel.instrumentation.jboss-logmanager.experimental-log-attributes=true")
jvmArgs("-Dotel.instrumentation.java-util-logging.experimental-log-attributes=true")
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.api.incubator.logs.ExtendedLogRecordBuilder;
import io.opentelemetry.api.logs.LogRecordBuilder;
import io.opentelemetry.api.logs.Severity;
Expand All @@ -32,6 +30,9 @@ public final class LoggingEventMapper {

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

// copied from EventIncubatingAttributes
private static final AttributeKey<String> EVENT_NAME = AttributeKey.stringKey("event.name");

private final List<String> captureMdcAttributes;

private static final boolean captureExperimentalAttributes =
Expand All @@ -41,6 +42,11 @@ public final class LoggingEventMapper {
// cached as an optimization
private final boolean captureAllMdcAttributes;

private final boolean captureEventName =
AgentInstrumentationConfig.get()
.getBoolean(
"otel.instrumentation.jboss-logmanager.experimental.capture-event-name", false);

private LoggingEventMapper() {
this.captureMdcAttributes =
AgentInstrumentationConfig.get()
Expand Down Expand Up @@ -75,55 +81,59 @@ public void capture(Logger logger, ExtLogRecord record) {
builder.setSeverityText(level.toString());
}

AttributesBuilder attributes = Attributes.builder();

Throwable throwable = record.getThrown();
if (throwable != null) {
// this cast is safe within java agent instrumentation
((ExtendedLogRecordBuilder) builder).setException(throwable);
}
captureMdcAttributes(attributes);
captureMdcAttributes(builder);

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

builder.setAllAttributes(attributes.build());

builder.setContext(Context.current());

builder.setTimestamp(record.getMillis(), MILLISECONDS);
builder.emit();
}

private void captureMdcAttributes(AttributesBuilder attributes) {
private void captureMdcAttributes(LogRecordBuilder builder) {

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()));
setAttributeOrEventName(builder, getMdcAttributeKey(entry.getKey()), entry.getValue());
}
}
return;
}

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

public static AttributeKey<String> getMdcAttributeKey(String key) {
return mdcAttributeKeys.computeIfAbsent(key, AttributeKey::stringKey);
}

private void setAttributeOrEventName(
LogRecordBuilder builder, AttributeKey<String> key, Object value) {
if (value != null) {
if (captureEventName && key.equals(EVENT_NAME)) {
builder.setEventName(value.toString());
} else {
builder.setAttribute(key, value.toString());
}
}
}

private static Severity levelToSeverity(java.util.logging.Level level) {
int levelInt = level.intValue();
if (levelInt >= Level.FATAL.intValue()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,11 +202,13 @@ private static void performLogging(
void testMdc() {
MDC.put("key1", "val1");
MDC.put("key2", "val2");
MDC.put("event.name", "MyEventName");
try {
logger.info("xyz");
} finally {
MDC.remove("key1");
MDC.remove("key2");
MDC.remove("event.name");
}

testing.waitAndAssertLogRecords(
Expand All @@ -216,6 +218,7 @@ void testMdc() {
.hasInstrumentationScope(InstrumentationScopeInfo.builder("abc").build())
.hasSeverity(Severity.INFO)
.hasSeverityText("INFO")
.hasEventName("MyEventName")
.hasAttributesSatisfyingExactly(
equalTo(AttributeKey.stringKey("key1"), "val1"),
equalTo(AttributeKey.stringKey("key2"), "val2"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
| `otel.instrumentation.log4j-appender.experimental-log-attributes` | Boolean | `false` | Enable the capture of experimental log attributes `thread.name` and `thread.id`. |
| `otel.instrumentation.log4j-appender.experimental.capture-code-attributes` | Boolean | `false` | Enable the capture of [source code attributes]. Note that capturing source code attributes at logging sites might add a performance overhead. |
| `otel.instrumentation.log4j-appender.experimental.capture-mdc-attributes` | String | | Comma separated list of context data attributes to capture. Use the wildcard character `*` to capture all attributes. |
| `otel.instrumentation.log4j-appender.experimental.capture-event-name` | Boolean | `false` | Enable moving the `event.name` attribute (captured by one of the other mechanisms of capturing attributes) to the log event name. |

[source code attributes]: https://github.com/open-telemetry/semantic-conventions/blob/main/docs/general/attributes.md#source-code-attributes
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ tasks.withType<Test>().configureEach {
// TODO run tests both with and without experimental log attributes
jvmArgs("-Dotel.instrumentation.log4j-appender.experimental.capture-mdc-attributes=*")
jvmArgs("-Dotel.instrumentation.log4j-appender.experimental.capture-code-attributes=true")
jvmArgs("-Dotel.instrumentation.log4j-appender.experimental-log-attributes=true")
jvmArgs("-Dotel.instrumentation.log4j-appender.experimental.capture-event-name=true")
jvmArgs("-Dotel.instrumentation.log4j-appender.experimental-log-attributes=true")
jvmArgs("-Dotel.instrumentation.common.experimental.controller-telemetry.enabled=true")
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.api.incubator.logs.ExtendedLogRecordBuilder;
import io.opentelemetry.api.logs.LogRecordBuilder;
import io.opentelemetry.api.logs.Severity;
Expand Down Expand Up @@ -44,6 +42,8 @@ public final class LogEventMapper {
private static final AttributeKey<Long> CODE_LINENO = AttributeKey.longKey("code.lineno");
private static final AttributeKey<String> CODE_NAMESPACE =
AttributeKey.stringKey("code.namespace");
// copied from EventIncubatingAttributes
private static final AttributeKey<String> EVENT_NAME = AttributeKey.stringKey("event.name");
// copied from org.apache.log4j.Level because it was only introduced in 1.2.12
private static final int TRACE_INT = 5000;

Expand All @@ -56,6 +56,10 @@ public final class LogEventMapper {
// cached as an optimization
private final boolean captureAllMdcAttributes;

private final boolean captureEventName =
AgentInstrumentationConfig.get()
.getBoolean("otel.instrumentation.log4j-appender.experimental.capture-event-name", false);

private LogEventMapper() {
List<String> captureMdcAttributes =
AgentInstrumentationConfig.get()
Expand Down Expand Up @@ -98,49 +102,47 @@ public void capture(
builder.setSeverityText(level.toString());
}

AttributesBuilder attributes = Attributes.builder();

// throwable
if (throwable != null) {
if (builder instanceof ExtendedLogRecordBuilder) {
((ExtendedLogRecordBuilder) builder).setException(throwable);
} else {
attributes.put(ExceptionAttributes.EXCEPTION_TYPE, throwable.getClass().getName());
attributes.put(ExceptionAttributes.EXCEPTION_MESSAGE, throwable.getMessage());
builder.setAttribute(ExceptionAttributes.EXCEPTION_TYPE, throwable.getClass().getName());
builder.setAttribute(ExceptionAttributes.EXCEPTION_MESSAGE, throwable.getMessage());
StringWriter writer = new StringWriter();
throwable.printStackTrace(new PrintWriter(writer));
attributes.put(ExceptionAttributes.EXCEPTION_STACKTRACE, writer.toString());
builder.setAttribute(ExceptionAttributes.EXCEPTION_STACKTRACE, writer.toString());
}
}

captureMdcAttributes(attributes);
captureMdcAttributes(builder);

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

if (captureCodeAttributes) {
LocationInfo locationInfo = new LocationInfo(new Throwable(), fqcn);
String fileName = locationInfo.getFileName();
if (fileName != null) {
if (SemconvStability.isEmitStableCodeSemconv()) {
attributes.put(CodeAttributes.CODE_FILE_PATH, fileName);
builder.setAttribute(CodeAttributes.CODE_FILE_PATH, fileName);
}
if (SemconvStability.isEmitOldCodeSemconv()) {
attributes.put(CODE_FILEPATH, fileName);
builder.setAttribute(CODE_FILEPATH, fileName);
}
}

if (SemconvStability.isEmitStableCodeSemconv()) {
attributes.put(
builder.setAttribute(
CodeAttributes.CODE_FUNCTION_NAME,
locationInfo.getClassName() + "." + locationInfo.getMethodName());
}
if (SemconvStability.isEmitOldCodeSemconv()) {
attributes.put(CODE_NAMESPACE, locationInfo.getClassName());
attributes.put(CODE_FUNCTION, locationInfo.getMethodName());
builder.setAttribute(CODE_NAMESPACE, locationInfo.getClassName());
builder.setAttribute(CODE_FUNCTION, locationInfo.getMethodName());
}

String lineNumber = locationInfo.getLineNumber();
Expand All @@ -154,49 +156,56 @@ public void capture(
}
if (codeLineNo >= 0) {
if (SemconvStability.isEmitStableCodeSemconv()) {
attributes.put(CodeAttributes.CODE_LINE_NUMBER, codeLineNo);
builder.setAttribute(CodeAttributes.CODE_LINE_NUMBER, (long) codeLineNo);
}
if (SemconvStability.isEmitOldCodeSemconv()) {
attributes.put(CODE_LINENO, codeLineNo);
builder.setAttribute(CODE_LINENO, (long) codeLineNo);
}
}
}

builder.setAllAttributes(attributes.build());

// span context
builder.setContext(Context.current());

builder.setTimestamp(Instant.now());
builder.emit();
}

private void captureMdcAttributes(AttributesBuilder attributes) {
private void captureMdcAttributes(LogRecordBuilder builder) {

Hashtable<?, ?> context = MDC.getContext();

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

for (Map.Entry<String, AttributeKey<String>> entry : captureMdcAttributes.entrySet()) {
Object value = context.get(entry.getKey());
if (value != null) {
attributes.put(entry.getValue(), value.toString());
}
setAttributeOrEventName(builder, entry.getValue(), value);
}
}

private static AttributeKey<String> getMdcAttributeKey(String key) {
return mdcAttributeKeys.computeIfAbsent(key, AttributeKey::stringKey);
}

private void setAttributeOrEventName(
LogRecordBuilder builder, AttributeKey<String> key, Object value) {
if (value != null) {
if (captureEventName && key.equals(EVENT_NAME)) {
builder.setEventName(value.toString());
} else {
builder.setAttribute(key, value.toString());
}
}
}

private static Severity levelToSeverity(Priority level) {
int lev = level.toInt();
if (lev <= TRACE_INT) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,11 +176,13 @@ private static void test(
void testMdc() {
MDC.put("key1", "val1");
MDC.put("key2", "val2");
MDC.put("event.name", "MyEventName");
try {
logger.info("xyz");
} finally {
MDC.remove("key1");
MDC.remove("key2");
MDC.remove("event.name");
}

List<AttributeAssertion> assertions =
Expand All @@ -195,6 +197,7 @@ void testMdc() {
logRecord ->
logRecord
.hasBody("xyz")
.hasEventName("MyEventName")
.hasInstrumentationScope(InstrumentationScopeInfo.builder("abc").build())
.hasSeverity(Severity.INFO)
.hasSeverityText("INFO")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@
| `otel.instrumentation.log4j-appender.experimental.capture-map-message-attributes` | Boolean | `false` | Enable the capture of `MapMessage` attributes. |
| `otel.instrumentation.log4j-appender.experimental.capture-marker-attribute` | Boolean | `false` | Enable the capture of Log4j markers as attributes. |
| `otel.instrumentation.log4j-appender.experimental.capture-mdc-attributes` | String | | Comma separated list of context data attributes to capture. Use the wildcard character `*` to capture all attributes. |
| `otel.instrumentation.log4j-appender.experimental.capture-event-name` | Boolean | `false` | Enable moving the `event.name` attribute (captured by one of the other mechanisms of capturing attributes) to the log event name. |

[source code attributes]: https://github.com/open-telemetry/semantic-conventions/blob/main/docs/general/attributes.md#source-code-attributes
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ public final class Log4jHelper {
List<String> captureContextDataAttributes =
config.getList(
"otel.instrumentation.log4j-appender.experimental.capture-mdc-attributes", emptyList());
boolean captureEventName =
config.getBoolean(
"otel.instrumentation.log4j-appender.experimental.capture-event-name", false);

mapper =
new LogEventMapper<>(
Expand All @@ -60,7 +63,8 @@ public final class Log4jHelper {
captureCodeAttributes,
captureMapMessageAttributes,
captureMarkerAttribute,
captureContextDataAttributes);
captureContextDataAttributes,
captureEventName);
}

public static void capture(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ The available settings are:
| `captureMapMessageAttributes` | Boolean | `false` | Enable the capture of `MapMessage` attributes. |
| `captureMarkerAttribute` | Boolean | `false` | Enable the capture of Log4j markers as attributes. |
| `captureContextDataAttributes` | String | | Comma separated list of context data attributes to capture. Use the wildcard character `*` to capture all attributes. |
| `captureEventName` | Boolean | `false` | Enable moving the `event.name` attribute (captured by one of the other mechanisms of capturing attributes) to the log event name. |
| `numLogsCapturedBeforeOtelInstall` | Integer | 1000 | Log telemetry is emitted after the initialization of the OpenTelemetry Log4j appender with an OpenTelemetry object. This setting allows you to modify the size of the cache used to replay the first logs. |

[source code attributes]: https://github.com/open-telemetry/semantic-conventions/blob/main/docs/general/attributes.md#source-code-attributes
Loading