From 25a1ead0f867a3577e0e17b130f2bcba47117d9b Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Thu, 1 Oct 2026 11:30:23 +0200 Subject: [PATCH 1/6] feat(logs-intake): add shared agentless log event submission --- .../log/AgentlessLogSubmission.java | 136 ++++++++++++++++++ .../log/AgentlessLogSubmissionTest.java | 122 ++++++++++++++++ 2 files changed, 258 insertions(+) create mode 100644 dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/log/AgentlessLogSubmission.java create mode 100644 dd-java-agent/agent-bootstrap/src/test/java/datadog/trace/bootstrap/instrumentation/log/AgentlessLogSubmissionTest.java diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/log/AgentlessLogSubmission.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/log/AgentlessLogSubmission.java new file mode 100644 index 00000000000..8d6141c1266 --- /dev/null +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/log/AgentlessLogSubmission.java @@ -0,0 +1,136 @@ +package datadog.trace.bootstrap.instrumentation.log; + +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.traceConfig; + +import datadog.trace.api.Config; +import datadog.trace.api.CorrelationIdentifier; +import datadog.trace.api.logging.intake.LogsIntake; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; + +/** + * Submits events captured by logging backend instrumentations to the agentless logs intake. + * + *

Events use the payload shape of Log4j's JSON layout, which the logs backend already parses. + * Callers must capture events on the producer thread, so that the correlation identifiers match the + * active span. + */ +public final class AgentlessLogSubmission { + + public static final String TRACE = "TRACE"; + public static final String DEBUG = "DEBUG"; + public static final String INFO = "INFO"; + public static final String WARN = "WARN"; + public static final String ERROR = "ERROR"; + public static final String FATAL = "FATAL"; + + private static final int MAX_STACKTRACE_STRING_LENGTH = 16 * 1_024; + + private static final int SUBMISSION_SEVERITY = + severity(Config.get().getAgentlessLogSubmissionLevel()); + + private AgentlessLogSubmission() {} + + /** Returns whether events of the given level reach the configured submission level. */ + public static boolean isLevelEnabled(String level) { + return severity(level) >= SUBMISSION_SEVERITY; + } + + /** Converts a {@link java.util.logging.Level} integer value to a level name of this class. */ + public static String julLevel(int value) { + if (value >= 1100) { + return FATAL; + } else if (value >= 1000) { + return ERROR; + } else if (value >= 900) { + return WARN; + } else if (value >= 800) { + return INFO; + } else if (value >= 500) { + return DEBUG; + } + return TRACE; + } + + /** + * Submits an event. + * + * @param level a level name of this class + * @param context the user context of the event, such as the MDC, or {@code null} + */ + public static void submit( + String thread, + String level, + String loggerName, + String message, + Throwable thrown, + long timestamp, + Map context) { + Map log = new HashMap<>(); + log.put("thread", thread); + log.put("level", level); + log.put("loggerName", loggerName); + log.put("message", message); + log.put("timestamp", timestamp); + if (thrown != null) { + log.put("thrown", thrown(thrown)); + } + Map contextMap = new HashMap<>(); + if (traceConfig().isLogsInjectionEnabled()) { + addCorrelationIds(contextMap); + } + if (context != null) { + contextMap.putAll(context); + } + log.put("contextMap", contextMap); + LogsIntake.log(log); + } + + private static Map thrown(Throwable thrown) { + Map thrownLog = new HashMap<>(); + thrownLog.put("message", thrown.getMessage()); + thrownLog.put("name", thrown.getClass().getCanonicalName()); + StringWriter stringWriter = new StringWriter(); + thrown.printStackTrace(new PrintWriter(stringWriter)); + StringBuffer stackTrace = stringWriter.getBuffer(); + thrownLog.put( + "extendedStackTrace", + stackTrace.substring(0, Math.min(stackTrace.length(), MAX_STACKTRACE_STRING_LENGTH))); + return thrownLog; + } + + private static void addCorrelationIds(Map context) { + String traceId = CorrelationIdentifier.getTraceId(); + if (traceId != null && !traceId.equals("0")) { + context.put(CorrelationIdentifier.getTraceIdKey(), traceId); + } + String spanId = CorrelationIdentifier.getSpanId(); + if (spanId != null && !spanId.equals("0")) { + context.put(CorrelationIdentifier.getSpanIdKey(), spanId); + } + } + + static int severity(String level) { + switch (level == null ? INFO : level.toUpperCase(Locale.ROOT)) { + case "ALL": + return 0; + case TRACE: + return 1; + case DEBUG: + return 2; + case WARN: + return 4; + case ERROR: + return 5; + case FATAL: + return 6; + case "OFF": + return 7; + default: + return 3; + } + } +} diff --git a/dd-java-agent/agent-bootstrap/src/test/java/datadog/trace/bootstrap/instrumentation/log/AgentlessLogSubmissionTest.java b/dd-java-agent/agent-bootstrap/src/test/java/datadog/trace/bootstrap/instrumentation/log/AgentlessLogSubmissionTest.java new file mode 100644 index 00000000000..8355972846d --- /dev/null +++ b/dd-java-agent/agent-bootstrap/src/test/java/datadog/trace/bootstrap/instrumentation/log/AgentlessLogSubmissionTest.java @@ -0,0 +1,122 @@ +package datadog.trace.bootstrap.instrumentation.log; + +import static datadog.trace.bootstrap.instrumentation.log.AgentlessLogSubmission.isLevelEnabled; +import static datadog.trace.bootstrap.instrumentation.log.AgentlessLogSubmission.julLevel; +import static datadog.trace.bootstrap.instrumentation.log.AgentlessLogSubmission.severity; +import static datadog.trace.bootstrap.instrumentation.log.AgentlessLogSubmission.submit; +import static java.util.Collections.singletonMap; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import datadog.trace.api.logging.intake.LogsIntake; +import datadog.trace.api.logging.intake.LogsWriter; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.tabletest.junit.TableTest; + +class AgentlessLogSubmissionTest { + + private final List> messages = new ArrayList<>(); + + @BeforeEach + void registerWriter() { + LogsIntake.registerWriter( + new LogsWriter() { + @Override + public void log(Map message) { + messages.add(message); + } + + @Override + public void start() {} + + @Override + public void shutdown() {} + }); + } + + @AfterEach + void unregisterWriter() { + LogsIntake.registerWriter(null); + } + + @TableTest({ + "scenario | value | level", + "OFF | 2000 | FATAL", + "JBoss FATAL | 1100 | FATAL", + "SEVERE | 1000 | ERROR", + "WARNING | 900 | WARN ", + "INFO | 800 | INFO ", + "CONFIG | 700 | DEBUG", + "FINE | 500 | DEBUG", + "FINER | 400 | TRACE", + "FINEST | 300 | TRACE" + }) + void mapsJulLevels(int value, String level) { + assertEquals(level, julLevel(value)); + } + + @TableTest({ + "scenario | lower | higher", + "ALL and TRACE | ALL | TRACE ", + "TRACE, DEBUG | trace | DEBUG ", + "DEBUG, INFO | DEBUG | INFO ", + "INFO, WARN | INFO | WARN ", + "WARN, ERROR | WARN | ERROR ", + "ERROR, FATAL | ERROR | FATAL ", + "FATAL, OFF | FATAL | OFF " + }) + void ordersLevels(String lower, String higher) { + assertTrue(severity(lower) < severity(higher)); + } + + @Test + void defaultsToInfo() { + assertFalse(isLevelEnabled("DEBUG")); + assertTrue(isLevelEnabled("INFO")); + assertTrue(isLevelEnabled("unknown")); + } + + @Test + void submitsEvent() { + submit( + "main", + "ERROR", + "logger", + "message", + new IllegalStateException("failure"), + 42L, + singletonMap("custom", "value")); + + assertEquals(1, messages.size()); + Map message = messages.get(0); + assertEquals("main", message.get("thread")); + assertEquals("ERROR", message.get("level")); + assertEquals("logger", message.get("loggerName")); + assertEquals("message", message.get("message")); + assertEquals(42L, message.get("timestamp")); + assertEquals(singletonMap("custom", "value"), message.get("contextMap")); + Map thrown = (Map) message.get("thrown"); + assertEquals(IllegalStateException.class.getName(), thrown.get("name")); + assertEquals("failure", thrown.get("message")); + assertTrue( + thrown + .get("extendedStackTrace") + .toString() + .startsWith("java.lang.IllegalStateException: failure")); + } + + @Test + void submitsEventWithoutOptionalFields() { + submit("main", "INFO", "logger", "message", null, 42L, null); + + assertEquals(1, messages.size()); + assertFalse(messages.get(0).containsKey("thrown")); + assertTrue(((Map) messages.get(0).get("contextMap")).isEmpty()); + } +} From 59c031c0f97bbd70ac2c204c2adb92d86d03cdc1 Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Thu, 1 Oct 2026 11:30:25 +0200 Subject: [PATCH 2/6] feat(jboss-logmanager): support agentless log submission --- .../jboss/jboss-logmanager-1.1/build.gradle | 16 ++ .../jboss-logmanager-1.1/gradle.lockfile | 183 +++++++------ .../ExtLogRecordInstrumentation.java | 2 +- .../LoggerNodeInstrumentation.java | 43 ++- .../groovy/JBossLogsIntakeForkedTest.groovy | 250 ++++++++++++++++++ .../smoketest/LogInjectionSmokeTest.groovy | 7 +- 6 files changed, 411 insertions(+), 90 deletions(-) create mode 100644 dd-java-agent/instrumentation/jboss/jboss-logmanager-1.1/src/test/groovy/JBossLogsIntakeForkedTest.groovy diff --git a/dd-java-agent/instrumentation/jboss/jboss-logmanager-1.1/build.gradle b/dd-java-agent/instrumentation/jboss/jboss-logmanager-1.1/build.gradle index 8a6ac89d6ce..20b9e239bc2 100644 --- a/dd-java-agent/instrumentation/jboss/jboss-logmanager-1.1/build.gradle +++ b/dd-java-agent/instrumentation/jboss/jboss-logmanager-1.1/build.gradle @@ -11,6 +11,22 @@ muzzle { } } +addTestSuiteForDir('latestDepTest', 'test') +addTestSuiteExtendingForDir('latestDepForkedTest', 'latestDepTest', 'test') + +["latestDepTest", "latestDepForkedTest"].each { + tasks.named(it, Test) { + testJvmConstraints { + // jboss-logmanager 3.x requires Java 11 + minJavaVersion = JavaVersion.VERSION_11 + } + } +} + dependencies { compileOnly group: 'org.jboss.logmanager', name: 'jboss-logmanager', version: '1.1.0.GA' + + // LoggerNode.publish(ExtLogRecord), used by the instrumentation, was introduced in 1.2.0.GA + testImplementation group: 'org.jboss.logmanager', name: 'jboss-logmanager', version: '1.2.0.GA' + latestDepTestImplementation group: 'org.jboss.logmanager', name: 'jboss-logmanager', version: '+' } diff --git a/dd-java-agent/instrumentation/jboss/jboss-logmanager-1.1/gradle.lockfile b/dd-java-agent/instrumentation/jboss/jboss-logmanager-1.1/gradle.lockfile index 3c939ee397c..ab3738e57ff 100644 --- a/dd-java-agent/instrumentation/jboss/jboss-logmanager-1.1/gradle.lockfile +++ b/dd-java-agent/instrumentation/jboss/jboss-logmanager-1.1/gradle.lockfile @@ -2,63 +2,71 @@ # Manual edits can break the build and are not advised. # This file is expected to be part of source control. # To regenerate this file, run: ./gradlew :dd-java-agent:instrumentation:jboss:jboss-logmanager-1.1:dependencies --write-locks -cafe.cryptography:curve25519-elisabeth:0.1.0=testRuntimeClasspath -cafe.cryptography:ed25519-elisabeth:0.1.0=testRuntimeClasspath -ch.qos.logback:logback-classic:1.2.13=testCompileClasspath,testRuntimeClasspath -ch.qos.logback:logback-core:1.2.13=testCompileClasspath,testRuntimeClasspath -com.blogspot.mydailyjava:weak-lock-free:0.17=buildTimeInstrumentationPlugin,compileClasspath,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.datadoghq.okhttp3:okhttp:3.12.15=testCompileClasspath,testRuntimeClasspath -com.datadoghq.okio:okio:1.17.6=testCompileClasspath,testRuntimeClasspath -com.datadoghq:dd-instrument-java:0.0.6=buildTimeInstrumentationPlugin,compileClasspath,muzzleBootstrap,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.datadoghq:dd-javac-plugin-client:0.2.2=buildTimeInstrumentationPlugin,compileClasspath,muzzleBootstrap,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -com.datadoghq:java-dogstatsd-client:4.4.5=testRuntimeClasspath -com.datadoghq:sketches-java:0.8.3=testRuntimeClasspath +cafe.cryptography:curve25519-elisabeth:0.1.0=latestDepForkedTestRuntimeClasspath,latestDepTestRuntimeClasspath,testRuntimeClasspath +cafe.cryptography:ed25519-elisabeth:0.1.0=latestDepForkedTestRuntimeClasspath,latestDepTestRuntimeClasspath,testRuntimeClasspath +ch.qos.logback:logback-classic:1.2.13=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +ch.qos.logback:logback-core:1.2.13=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +com.blogspot.mydailyjava:weak-lock-free:0.17=buildTimeInstrumentationPlugin,compileClasspath,latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.datadoghq.okhttp3:okhttp:3.12.15=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +com.datadoghq.okio:okio:1.17.6=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +com.datadoghq:dd-instrument-java:0.0.6=buildTimeInstrumentationPlugin,compileClasspath,latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,muzzleBootstrap,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.datadoghq:dd-javac-plugin-client:0.2.2=buildTimeInstrumentationPlugin,compileClasspath,latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,muzzleBootstrap,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +com.datadoghq:java-dogstatsd-client:4.4.5=latestDepForkedTestRuntimeClasspath,latestDepTestRuntimeClasspath,testRuntimeClasspath +com.datadoghq:sketches-java:0.8.3=latestDepForkedTestRuntimeClasspath,latestDepTestRuntimeClasspath,testRuntimeClasspath com.github.javaparser:javaparser-core:3.25.6=codenarc -com.github.jnr:jffi:1.3.15=testRuntimeClasspath -com.github.jnr:jnr-a64asm:1.0.0=testRuntimeClasspath -com.github.jnr:jnr-constants:0.10.4=testRuntimeClasspath -com.github.jnr:jnr-enxio:0.32.20=testRuntimeClasspath -com.github.jnr:jnr-ffi:2.2.19=testRuntimeClasspath -com.github.jnr:jnr-posix:3.1.22=testRuntimeClasspath -com.github.jnr:jnr-unixsocket:0.38.25=testRuntimeClasspath -com.github.jnr:jnr-x86asm:1.0.2=testRuntimeClasspath +com.github.jnr:jffi:1.3.15=latestDepForkedTestRuntimeClasspath,latestDepTestRuntimeClasspath,testRuntimeClasspath +com.github.jnr:jnr-a64asm:1.0.0=latestDepForkedTestRuntimeClasspath,latestDepTestRuntimeClasspath,testRuntimeClasspath +com.github.jnr:jnr-constants:0.10.4=latestDepForkedTestRuntimeClasspath,latestDepTestRuntimeClasspath,testRuntimeClasspath +com.github.jnr:jnr-enxio:0.32.20=latestDepForkedTestRuntimeClasspath,latestDepTestRuntimeClasspath,testRuntimeClasspath +com.github.jnr:jnr-ffi:2.2.19=latestDepForkedTestRuntimeClasspath,latestDepTestRuntimeClasspath,testRuntimeClasspath +com.github.jnr:jnr-posix:3.1.22=latestDepForkedTestRuntimeClasspath,latestDepTestRuntimeClasspath,testRuntimeClasspath +com.github.jnr:jnr-unixsocket:0.38.25=latestDepForkedTestRuntimeClasspath,latestDepTestRuntimeClasspath,testRuntimeClasspath +com.github.jnr:jnr-x86asm:1.0.2=latestDepForkedTestRuntimeClasspath,latestDepTestRuntimeClasspath,testRuntimeClasspath com.github.spotbugs:spotbugs-annotations:4.10.4=compileClasspath,spotbugs com.github.spotbugs:spotbugs:4.10.4=spotbugs com.github.stephenc.jcip:jcip-annotations:1.0-1=spotbugs -com.google.auto.service:auto-service-annotations:1.1.1=annotationProcessor,compileClasspath,testAnnotationProcessor,testCompileClasspath -com.google.auto.service:auto-service:1.1.1=annotationProcessor,testAnnotationProcessor -com.google.auto:auto-common:1.2.1=annotationProcessor,testAnnotationProcessor -com.google.code.findbugs:jsr305:3.0.2=annotationProcessor,compileClasspath,spotbugs,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath +com.google.auto.service:auto-service-annotations:1.1.1=annotationProcessor,compileClasspath,latestDepForkedTestAnnotationProcessor,latestDepForkedTestCompileClasspath,latestDepTestAnnotationProcessor,latestDepTestCompileClasspath,testAnnotationProcessor,testCompileClasspath +com.google.auto.service:auto-service:1.1.1=annotationProcessor,latestDepForkedTestAnnotationProcessor,latestDepTestAnnotationProcessor,testAnnotationProcessor +com.google.auto:auto-common:1.2.1=annotationProcessor,latestDepForkedTestAnnotationProcessor,latestDepTestAnnotationProcessor,testAnnotationProcessor +com.google.code.findbugs:jsr305:3.0.2=annotationProcessor,compileClasspath,latestDepForkedTestAnnotationProcessor,latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestAnnotationProcessor,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,spotbugs,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath com.google.code.gson:gson:2.14.0=spotbugs -com.google.errorprone:error_prone_annotations:2.18.0=annotationProcessor,testAnnotationProcessor -com.google.errorprone:error_prone_annotations:2.47.0=testCompileClasspath,testRuntimeClasspath +com.google.errorprone:error_prone_annotations:2.18.0=annotationProcessor,latestDepForkedTestAnnotationProcessor,latestDepTestAnnotationProcessor,testAnnotationProcessor +com.google.errorprone:error_prone_annotations:2.47.0=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath com.google.errorprone:error_prone_annotations:2.48.0=spotbugs -com.google.guava:failureaccess:1.0.1=annotationProcessor,testAnnotationProcessor -com.google.guava:failureaccess:1.0.3=testCompileClasspath,testRuntimeClasspath -com.google.guava:guava:32.0.1-jre=annotationProcessor,testAnnotationProcessor -com.google.guava:guava:33.6.0-jre=testCompileClasspath,testRuntimeClasspath -com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=annotationProcessor,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath -com.google.j2objc:j2objc-annotations:2.8=annotationProcessor,testAnnotationProcessor -com.google.j2objc:j2objc-annotations:3.1=testCompileClasspath,testRuntimeClasspath -com.google.re2j:re2j:1.8=testRuntimeClasspath -com.squareup.moshi:moshi:1.11.0=testCompileClasspath,testRuntimeClasspath -com.squareup.okhttp3:logging-interceptor:3.12.12=testCompileClasspath,testRuntimeClasspath -com.squareup.okhttp3:okhttp:3.12.12=testCompileClasspath,testRuntimeClasspath -com.squareup.okio:okio:1.17.5=testCompileClasspath,testRuntimeClasspath +com.google.guava:failureaccess:1.0.1=annotationProcessor,latestDepForkedTestAnnotationProcessor,latestDepTestAnnotationProcessor,testAnnotationProcessor +com.google.guava:failureaccess:1.0.3=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.guava:guava:32.0.1-jre=annotationProcessor,latestDepForkedTestAnnotationProcessor,latestDepTestAnnotationProcessor,testAnnotationProcessor +com.google.guava:guava:33.6.0-jre=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=annotationProcessor,latestDepForkedTestAnnotationProcessor,latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestAnnotationProcessor,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testAnnotationProcessor,testCompileClasspath,testRuntimeClasspath +com.google.j2objc:j2objc-annotations:2.8=annotationProcessor,latestDepForkedTestAnnotationProcessor,latestDepTestAnnotationProcessor,testAnnotationProcessor +com.google.j2objc:j2objc-annotations:3.1=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +com.google.re2j:re2j:1.8=latestDepForkedTestRuntimeClasspath,latestDepTestRuntimeClasspath,testRuntimeClasspath +com.squareup.moshi:moshi:1.11.0=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +com.squareup.okhttp3:logging-interceptor:3.12.12=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +com.squareup.okhttp3:okhttp:3.12.12=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +com.squareup.okio:okio:1.17.5=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath com.thoughtworks.qdox:qdox:1.12.1=codenarc -commons-fileupload:commons-fileupload:1.5=testCompileClasspath,testRuntimeClasspath -commons-io:commons-io:2.11.0=testCompileClasspath,testRuntimeClasspath +commons-fileupload:commons-fileupload:1.5=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +commons-io:commons-io:2.11.0=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath commons-io:commons-io:2.21.0=spotbugs -de.thetaphi:forbiddenapis:3.11=compileClasspath,testCompileClasspath,testRuntimeClasspath -io.leangen.geantyref:geantyref:1.3.16=testRuntimeClasspath -io.sqreen:libsqreen:17.5.0=testRuntimeClasspath -javax.servlet:javax.servlet-api:3.1.0=testCompileClasspath,testRuntimeClasspath +de.thetaphi:forbiddenapis:3.11=compileClasspath,latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +io.leangen.geantyref:geantyref:1.3.16=latestDepForkedTestRuntimeClasspath,latestDepTestRuntimeClasspath,testRuntimeClasspath +io.smallrye.common:smallrye-common-constraint:2.15.0=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath +io.smallrye.common:smallrye-common-cpu:2.15.0=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath +io.smallrye.common:smallrye-common-expression:2.15.0=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath +io.smallrye.common:smallrye-common-function:2.15.0=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath +io.smallrye.common:smallrye-common-net:2.15.0=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath +io.smallrye.common:smallrye-common-os:2.15.0=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath +io.smallrye.common:smallrye-common-ref:2.15.0=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath +io.sqreen:libsqreen:17.5.0=latestDepForkedTestRuntimeClasspath,latestDepTestRuntimeClasspath,testRuntimeClasspath +jakarta.json:jakarta.json-api:2.1.3=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath +javax.servlet:javax.servlet-api:3.1.0=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath jaxen:jaxen:2.0.6=spotbugs -junit:junit:4.13.2=testRuntimeClasspath -net.bytebuddy:byte-buddy-agent:1.18.12=buildTimeInstrumentationPlugin,compileClasspath,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -net.bytebuddy:byte-buddy:1.18.12=buildTimeInstrumentationPlugin,compileClasspath,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath -net.java.dev.jna:jna-platform:5.8.0=testRuntimeClasspath -net.java.dev.jna:jna:5.8.0=testRuntimeClasspath +junit:junit:4.13.2=latestDepForkedTestRuntimeClasspath,latestDepTestRuntimeClasspath,testRuntimeClasspath +net.bytebuddy:byte-buddy-agent:1.18.12=buildTimeInstrumentationPlugin,compileClasspath,latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +net.bytebuddy:byte-buddy:1.18.12=buildTimeInstrumentationPlugin,compileClasspath,latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,muzzleTooling,runtimeClasspath,testCompileClasspath,testRuntimeClasspath +net.java.dev.jna:jna-platform:5.8.0=latestDepForkedTestRuntimeClasspath,latestDepTestRuntimeClasspath,testRuntimeClasspath +net.java.dev.jna:jna:5.8.0=latestDepForkedTestRuntimeClasspath,latestDepTestRuntimeClasspath,testRuntimeClasspath net.sf.saxon:Saxon-HE:12.10=spotbugs org.apache.ant:ant-antlr:1.10.14=codenarc org.apache.ant:ant-junit:1.10.14=codenarc @@ -67,59 +75,64 @@ org.apache.commons:commons-lang3:3.20.0=spotbugs org.apache.commons:commons-text:1.15.0=spotbugs org.apache.logging.log4j:log4j-api:2.26.1=spotbugs org.apache.logging.log4j:log4j-core:2.26.1=spotbugs -org.apiguardian:apiguardian-api:1.1.2=testCompileClasspath -org.checkerframework:checker-qual:3.33.0=annotationProcessor,testAnnotationProcessor +org.apiguardian:apiguardian-api:1.1.2=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.checkerframework:checker-qual:3.33.0=annotationProcessor,latestDepForkedTestAnnotationProcessor,latestDepTestAnnotationProcessor,testAnnotationProcessor org.codehaus.groovy:groovy-ant:3.0.23=codenarc org.codehaus.groovy:groovy-docgenerator:3.0.23=codenarc org.codehaus.groovy:groovy-groovydoc:3.0.23=codenarc org.codehaus.groovy:groovy-json:3.0.23=codenarc -org.codehaus.groovy:groovy-json:3.0.25=testCompileClasspath,testRuntimeClasspath +org.codehaus.groovy:groovy-json:3.0.25=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath org.codehaus.groovy:groovy-templates:3.0.23=codenarc org.codehaus.groovy:groovy-xml:3.0.23=codenarc org.codehaus.groovy:groovy:3.0.23=codenarc -org.codehaus.groovy:groovy:3.0.25=testCompileClasspath,testRuntimeClasspath +org.codehaus.groovy:groovy:3.0.25=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath org.codenarc:CodeNarc:3.7.0=codenarc org.dom4j:dom4j:2.2.0=spotbugs +org.eclipse.parsson:parsson:1.1.7=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath org.gmetrics:GMetrics:2.1.0=codenarc -org.hamcrest:hamcrest-core:1.3=testRuntimeClasspath -org.hamcrest:hamcrest:3.0=testCompileClasspath,testRuntimeClasspath +org.hamcrest:hamcrest-core:1.3=latestDepForkedTestRuntimeClasspath,latestDepTestRuntimeClasspath,testRuntimeClasspath +org.hamcrest:hamcrest:3.0=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.jboss.logging:jboss-logging:3.6.2.Final=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath org.jboss.logmanager:jboss-logmanager:1.1.0.GA=compileClasspath -org.jctools:jctools-core-jdk11:4.0.6=testRuntimeClasspath -org.jctools:jctools-core:4.0.6=testRuntimeClasspath -org.jspecify:jspecify:1.0.0=testCompileClasspath,testRuntimeClasspath -org.junit.jupiter:junit-jupiter-api:5.14.1=testCompileClasspath,testRuntimeClasspath -org.junit.jupiter:junit-jupiter-engine:5.14.1=testRuntimeClasspath -org.junit.jupiter:junit-jupiter-params:5.14.1=testCompileClasspath,testRuntimeClasspath -org.junit.jupiter:junit-jupiter:5.14.1=testCompileClasspath,testRuntimeClasspath -org.junit.platform:junit-platform-commons:1.14.1=testCompileClasspath,testRuntimeClasspath -org.junit.platform:junit-platform-engine:1.14.1=testCompileClasspath,testRuntimeClasspath -org.junit.platform:junit-platform-launcher:1.14.1=testRuntimeClasspath -org.junit.platform:junit-platform-runner:1.14.1=testRuntimeClasspath -org.junit.platform:junit-platform-suite-api:1.14.1=testRuntimeClasspath -org.junit.platform:junit-platform-suite-commons:1.14.1=testRuntimeClasspath -org.junit:junit-bom:5.14.1=testCompileClasspath,testRuntimeClasspath -org.mockito:mockito-core:4.4.0=testRuntimeClasspath -org.objenesis:objenesis:3.3=testCompileClasspath,testRuntimeClasspath -org.opentest4j:opentest4j:1.3.0=testCompileClasspath,testRuntimeClasspath +org.jboss.logmanager:jboss-logmanager:1.2.0.GA=testCompileClasspath,testRuntimeClasspath +org.jboss.logmanager:jboss-logmanager:3.2.2.Final=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath +org.jboss.modules:jboss-modules:2.3.0=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath +org.jctools:jctools-core-jdk11:4.0.6=latestDepForkedTestRuntimeClasspath,latestDepTestRuntimeClasspath,testRuntimeClasspath +org.jctools:jctools-core:4.0.6=latestDepForkedTestRuntimeClasspath,latestDepTestRuntimeClasspath,testRuntimeClasspath +org.jspecify:jspecify:1.0.0=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.junit.jupiter:junit-jupiter-api:5.14.1=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.junit.jupiter:junit-jupiter-engine:5.14.1=latestDepForkedTestRuntimeClasspath,latestDepTestRuntimeClasspath,testRuntimeClasspath +org.junit.jupiter:junit-jupiter-params:5.14.1=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.junit.jupiter:junit-jupiter:5.14.1=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.junit.platform:junit-platform-commons:1.14.1=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.junit.platform:junit-platform-engine:1.14.1=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.junit.platform:junit-platform-launcher:1.14.1=latestDepForkedTestRuntimeClasspath,latestDepTestRuntimeClasspath,testRuntimeClasspath +org.junit.platform:junit-platform-runner:1.14.1=latestDepForkedTestRuntimeClasspath,latestDepTestRuntimeClasspath,testRuntimeClasspath +org.junit.platform:junit-platform-suite-api:1.14.1=latestDepForkedTestRuntimeClasspath,latestDepTestRuntimeClasspath,testRuntimeClasspath +org.junit.platform:junit-platform-suite-commons:1.14.1=latestDepForkedTestRuntimeClasspath,latestDepTestRuntimeClasspath,testRuntimeClasspath +org.junit:junit-bom:5.14.1=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.mockito:mockito-core:4.4.0=latestDepForkedTestRuntimeClasspath,latestDepTestRuntimeClasspath,testRuntimeClasspath +org.objenesis:objenesis:3.3=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.opentest4j:opentest4j:1.3.0=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath org.ow2.asm:asm-analysis:9.10.1=spotbugs -org.ow2.asm:asm-analysis:9.7.1=testRuntimeClasspath -org.ow2.asm:asm-commons:9.10.1=spotbugs,testRuntimeClasspath -org.ow2.asm:asm-tree:9.10.1=spotbugs,testRuntimeClasspath +org.ow2.asm:asm-analysis:9.7.1=latestDepForkedTestRuntimeClasspath,latestDepTestRuntimeClasspath,testRuntimeClasspath +org.ow2.asm:asm-commons:9.10.1=latestDepForkedTestRuntimeClasspath,latestDepTestRuntimeClasspath,spotbugs,testRuntimeClasspath +org.ow2.asm:asm-tree:9.10.1=latestDepForkedTestRuntimeClasspath,latestDepTestRuntimeClasspath,spotbugs,testRuntimeClasspath org.ow2.asm:asm-util:9.10.1=spotbugs -org.ow2.asm:asm-util:9.7.1=testRuntimeClasspath -org.ow2.asm:asm:9.10.1=buildTimeInstrumentationPlugin,compileClasspath,muzzleTooling,runtimeClasspath,spotbugs,testCompileClasspath,testRuntimeClasspath -org.slf4j:jcl-over-slf4j:1.7.30=testCompileClasspath,testRuntimeClasspath -org.slf4j:jul-to-slf4j:1.7.30=testCompileClasspath,testRuntimeClasspath -org.slf4j:log4j-over-slf4j:1.7.30=testCompileClasspath,testRuntimeClasspath +org.ow2.asm:asm-util:9.7.1=latestDepForkedTestRuntimeClasspath,latestDepTestRuntimeClasspath,testRuntimeClasspath +org.ow2.asm:asm:9.10.1=buildTimeInstrumentationPlugin,compileClasspath,latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,muzzleTooling,runtimeClasspath,spotbugs,testCompileClasspath,testRuntimeClasspath +org.slf4j:jcl-over-slf4j:1.7.30=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.slf4j:jul-to-slf4j:1.7.30=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.slf4j:log4j-over-slf4j:1.7.30=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:1.7.30=buildTimeInstrumentationPlugin,compileClasspath,muzzleBootstrap,muzzleTooling,runtimeClasspath -org.slf4j:slf4j-api:1.7.32=testCompileClasspath,testRuntimeClasspath +org.slf4j:slf4j-api:1.7.32=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath org.slf4j:slf4j-api:2.0.17=spotbugsSlf4j org.slf4j:slf4j-api:2.0.18=spotbugs org.slf4j:slf4j-simple:2.0.17=spotbugsSlf4j -org.snakeyaml:snakeyaml-engine:2.9=buildTimeInstrumentationPlugin,muzzleTooling,runtimeClasspath,testRuntimeClasspath -org.spockframework:spock-bom:2.4-groovy-3.0=testCompileClasspath,testRuntimeClasspath -org.spockframework:spock-core:2.4-groovy-3.0=testCompileClasspath,testRuntimeClasspath -org.tabletest:tabletest-junit:1.2.2=testCompileClasspath,testRuntimeClasspath -org.tabletest:tabletest-parser:1.2.1=testCompileClasspath,testRuntimeClasspath +org.snakeyaml:snakeyaml-engine:2.9=buildTimeInstrumentationPlugin,latestDepForkedTestRuntimeClasspath,latestDepTestRuntimeClasspath,muzzleTooling,runtimeClasspath,testRuntimeClasspath +org.spockframework:spock-bom:2.4-groovy-3.0=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.spockframework:spock-core:2.4-groovy-3.0=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.tabletest:tabletest-junit:1.2.2=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath +org.tabletest:tabletest-parser:1.2.1=latestDepForkedTestCompileClasspath,latestDepForkedTestRuntimeClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath org.xmlresolver:xmlresolver:5.3.3=spotbugs empty=spotbugsPlugins diff --git a/dd-java-agent/instrumentation/jboss/jboss-logmanager-1.1/src/main/java/datadog/trace/instrumentation/jbosslogmanager/ExtLogRecordInstrumentation.java b/dd-java-agent/instrumentation/jboss/jboss-logmanager-1.1/src/main/java/datadog/trace/instrumentation/jbosslogmanager/ExtLogRecordInstrumentation.java index 982c26a34bd..d98e9f2a5f5 100644 --- a/dd-java-agent/instrumentation/jboss/jboss-logmanager-1.1/src/main/java/datadog/trace/instrumentation/jbosslogmanager/ExtLogRecordInstrumentation.java +++ b/dd-java-agent/instrumentation/jboss/jboss-logmanager-1.1/src/main/java/datadog/trace/instrumentation/jbosslogmanager/ExtLogRecordInstrumentation.java @@ -28,7 +28,7 @@ import org.jboss.logmanager.ExtLogRecord; @AutoService(InstrumenterModule.class) -public class ExtLogRecordInstrumentation extends InstrumenterModule.Tracing +public class ExtLogRecordInstrumentation extends InstrumenterModule.ContextTracking implements Instrumenter.ForTypeHierarchy, Instrumenter.HasMethodAdvice { public ExtLogRecordInstrumentation() { super("jboss-logmanager"); diff --git a/dd-java-agent/instrumentation/jboss/jboss-logmanager-1.1/src/main/java/datadog/trace/instrumentation/jbosslogmanager/LoggerNodeInstrumentation.java b/dd-java-agent/instrumentation/jboss/jboss-logmanager-1.1/src/main/java/datadog/trace/instrumentation/jbosslogmanager/LoggerNodeInstrumentation.java index 4e8cc81ef37..74d1801c24c 100644 --- a/dd-java-agent/instrumentation/jboss/jboss-logmanager-1.1/src/main/java/datadog/trace/instrumentation/jbosslogmanager/LoggerNodeInstrumentation.java +++ b/dd-java-agent/instrumentation/jboss/jboss-logmanager-1.1/src/main/java/datadog/trace/instrumentation/jbosslogmanager/LoggerNodeInstrumentation.java @@ -10,16 +10,19 @@ import com.google.auto.service.AutoService; import datadog.trace.agent.tooling.Instrumenter; import datadog.trace.agent.tooling.InstrumenterModule; +import datadog.trace.api.InstrumenterConfig; import datadog.trace.bootstrap.CallDepthThreadLocalMap; import datadog.trace.bootstrap.InstrumentationContext; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.api.AgentSpanContext; +import datadog.trace.bootstrap.instrumentation.log.AgentlessLogSubmission; import java.util.Map; import net.bytebuddy.asm.Advice; import org.jboss.logmanager.ExtLogRecord; +import org.jboss.logmanager.MDC; @AutoService(InstrumenterModule.class) -public class LoggerNodeInstrumentation extends InstrumenterModule.Tracing +public class LoggerNodeInstrumentation extends InstrumenterModule.ContextTracking implements Instrumenter.ForSingleType, Instrumenter.HasMethodAdvice { public LoggerNodeInstrumentation() { super("jboss-logmanager"); @@ -42,6 +45,13 @@ public void methodAdvice(MethodTransformer transformer) { .and(named("publish")) .and(takesArgument(0, named("org.jboss.logmanager.ExtLogRecord"))), LoggerNodeInstrumentation.class.getName() + "$AttachContextAdvice"); + if (InstrumenterConfig.get().isAgentlessLogSubmissionEnabled()) { + transformer.applyAdvice( + isMethod() + .and(named("publish")) + .and(takesArgument(0, named("org.jboss.logmanager.ExtLogRecord"))), + LoggerNodeInstrumentation.class.getName() + "$SubmitLogAdvice"); + } } public static class AttachContextAdvice { @@ -69,4 +79,35 @@ public static void resetDepth(@Advice.Enter boolean shouldReset) { } } } + + public static class SubmitLogAdvice { + @Advice.OnMethodEnter(suppress = Throwable.class) + public static boolean submit(@Advice.Argument(0) ExtLogRecord record) { + if (CallDepthThreadLocalMap.incrementCallDepth(AgentlessLogSubmission.class) > 0) { + return false; + } + try { + String level = AgentlessLogSubmission.julLevel(record.getLevel().intValue()); + if (AgentlessLogSubmission.isLevelEnabled(level)) { + AgentlessLogSubmission.submit( + record.getThreadName(), + level, + record.getLoggerName(), + record.getFormattedMessage(), + record.getThrown(), + record.getMillis(), + MDC.copy()); + } + } catch (Throwable ignored) { + } + return true; + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void resetDepth(@Advice.Enter boolean shouldReset) { + if (shouldReset) { + CallDepthThreadLocalMap.reset(AgentlessLogSubmission.class); + } + } + } } diff --git a/dd-java-agent/instrumentation/jboss/jboss-logmanager-1.1/src/test/groovy/JBossLogsIntakeForkedTest.groovy b/dd-java-agent/instrumentation/jboss/jboss-logmanager-1.1/src/test/groovy/JBossLogsIntakeForkedTest.groovy new file mode 100644 index 00000000000..5f35c0d31d8 --- /dev/null +++ b/dd-java-agent/instrumentation/jboss/jboss-logmanager-1.1/src/test/groovy/JBossLogsIntakeForkedTest.groovy @@ -0,0 +1,250 @@ +import datadog.trace.agent.test.InstrumentationSpecification +import datadog.trace.api.CorrelationIdentifier +import datadog.trace.api.logging.intake.LogsIntake +import datadog.trace.api.logging.intake.LogsWriter +import datadog.trace.bootstrap.instrumentation.api.AgentTracer +import java.util.concurrent.CopyOnWriteArrayList +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit +import java.util.logging.Handler +import java.util.logging.LogRecord +import org.jboss.logmanager.ExtLogRecord +import org.jboss.logmanager.Level +import org.jboss.logmanager.LogContext +import org.jboss.logmanager.Logger +import org.jboss.logmanager.MDC +import org.jboss.logmanager.handlers.AsyncHandler + +abstract class JBossLogsIntakeTestBase extends InstrumentationSpecification { + static final String LOGGER_NAME = "test.jboss.intake" + static final List> MESSAGES = new CopyOnWriteArrayList<>() + + static final LogContext LOG_CONTEXT = LogContext.create() + + Logger parent = LOG_CONTEXT.getLogger(LOGGER_NAME) + Logger logger = LOG_CONTEXT.getLogger(LOGGER_NAME + ".child") + ContextHandler handler = new ContextHandler() + + boolean agentless() { + true + } + + boolean injection() { + true + } + + @Override + void configurePreAgent() { + super.configurePreAgent() + injectSysConfig("agentless.log.submission.enabled", agentless().toString()) + injectSysConfig("logs.injection.enabled", injection().toString()) + injectSysConfig("agentless.log.submission.level", "WARN") + } + + def setupSpec() { + LogsIntake.registerWriter(new LogsWriter() { + void log(Map message) { + if (message.loggerName.toString().startsWith(LOGGER_NAME)) { + MESSAGES.add(message) + } + } + + void start() {} + + void shutdown() {} + }) + } + + def cleanupSpec() { + LogsIntake.registerWriter(null) + } + + def setup() { + MESSAGES.clear() + MDC.clear() + parent.setUseParentHandlers(false) + parent.setLevel(Level.TRACE) + logger.setLevel(Level.TRACE) + logger.addHandler(handler) + } + + def cleanup() { + MDC.clear() + logger.removeHandler(handler) + parent.clearHandlers() + } + + def "submits filtered events with mapped levels and exceptions"() { + when: + logger.log(Level.DEBUG, "debug") + logger.log(Level.INFO, "info") + logger.log(Level.WARN, "warn") + logger.log(java.util.logging.Level.WARNING, "jul {0}", "warning") + logger.log(Level.ERROR, "error", new IllegalStateException("failure")) + logger.log(Level.FATAL, "fatal") + + then: + handler.contexts.size() == 6 + if (agentless()) { + assert MESSAGES*.message == ["warn", "jul warning", "error", "fatal"] + assert MESSAGES*.level == ["WARN", "WARN", "ERROR", "FATAL"] + assert MESSAGES.every { + it.loggerName == logger.name && it.thread == Thread.currentThread().name && it.timestamp instanceof Long + } + def thrown = MESSAGES[2].thrown + assert thrown.name == IllegalStateException.name + assert thrown.message == "failure" + assert thrown.extendedStackTrace.contains("IllegalStateException: failure") + assert !MESSAGES[0].containsKey("thrown") + } else { + assert MESSAGES.empty + } + } + + def "submits producer context and preserves user MDC"() { + setup: + def span = AgentTracer.startSpan("test", "jboss-producer") + def scope = AgentTracer.activateSpan(span) + def traceId = CorrelationIdentifier.traceId + def spanId = CorrelationIdentifier.spanId + MDC.put("custom", "value") + + when: + logger.log(Level.WARN, "active") + def child = AgentTracer.startSpan("test", "jboss-child") + def childScope = AgentTracer.activateSpan(child) + def childSpanId = CorrelationIdentifier.spanId + logger.log(Level.WARN, "child") + childScope.close() + child.finish() + MDC.put("dd.trace_id", "user-trace") + logger.log(Level.WARN, "collision") + MDC.clear() + scope.close() + span.finish() + logger.log(Level.WARN, "outside") + + then: + handler.contexts[0]["dd.trace_id"] == (injection() ? traceId : null) + handler.contexts[0]["dd.span_id"] == (injection() ? spanId : null) + handler.contexts[0]["custom"] == "value" + if (agentless()) { + assert MESSAGES*.message == ["active", "child", "collision", "outside"] + def contexts = MESSAGES*.contextMap + assert contexts[0] == (injection() ? ["dd.trace_id": traceId, "dd.span_id": spanId, custom: "value"] : [custom: "value"]) + assert contexts[1]["dd.span_id"] == (injection() ? childSpanId : null) + assert contexts[1]["dd.trace_id"] == (injection() ? traceId : null) + assert contexts[2]["dd.trace_id"] == "user-trace" + assert contexts[3] == [:] + } else { + assert MESSAGES.empty + } + } + + def "submits once when the event reaches parent handlers"() { + setup: + def parentHandler = new ContextHandler() + parent.addHandler(parentHandler) + + when: + logger.log(Level.WARN, "propagated") + + then: + handler.contexts.size() == 1 + parentHandler.contexts.size() == 1 + MESSAGES*.message == (agentless() ? ["propagated"] : []) + } + + def "submits once with producer context when delivery is asynchronous"() { + setup: + def delivered = new CountDownLatch(1) + def release = new CountDownLatch(1) + def asyncTarget = new ContextHandler() { + @Override + void publish(LogRecord record) { + release.await(5, TimeUnit.SECONDS) + super.publish(record) + delivered.countDown() + } + } + def async = new AsyncHandler() + async.addHandler(asyncTarget) + logger.removeHandler(handler) + logger.addHandler(async) + def span = AgentTracer.startSpan("test", "jboss-async") + def scope = AgentTracer.activateSpan(span) + def traceId = CorrelationIdentifier.traceId + def spanId = CorrelationIdentifier.spanId + + when: + logger.log(Level.WARN, "async") + scope.close() + span.finish() + release.countDown() + + then: + delivered.await(5, TimeUnit.SECONDS) + asyncTarget.contexts[0]["dd.span_id"] == (injection() ? spanId : null) + if (agentless()) { + assert MESSAGES.size() == 1 + assert MESSAGES[0].contextMap["dd.trace_id"] == (injection() ? traceId : null) + assert MESSAGES[0].contextMap["dd.span_id"] == (injection() ? spanId : null) + } else { + assert MESSAGES.empty + } + + cleanup: + logger.removeHandler(async) + async.close() + } + + static class ContextHandler extends Handler { + final List> contexts = new CopyOnWriteArrayList<>() + + @Override + void publish(LogRecord record) { + def extRecord = (ExtLogRecord) record + contexts.add([ + "dd.trace_id": extRecord.getMdc("dd.trace_id"), + "dd.span_id" : extRecord.getMdc("dd.span_id"), + "custom" : extRecord.getMdc("custom") + ]) + } + + @Override + void flush() {} + + @Override + void close() {} + } +} + +class JBossLogsIntakeForkedTest extends JBossLogsIntakeTestBase {} + +class JBossNoSubmissionForkedTest extends JBossLogsIntakeTestBase { + boolean agentless() { + false + } +} + +class JBossNoInjectionForkedTest extends JBossLogsIntakeTestBase { + boolean injection() { + false + } +} + +class JBossCiAgentlessForkedTest extends JBossLogsIntakeTestBase { + @Override + void configurePreAgent() { + super.configurePreAgent() + injectSysConfig("trace.enabled", "false") + } +} + +class JBossCiCorrelationForkedTest extends JBossNoSubmissionForkedTest { + @Override + void configurePreAgent() { + super.configurePreAgent() + injectSysConfig("trace.enabled", "false") + } +} diff --git a/dd-smoke-tests/log-injection/src/test/groovy/datadog/smoketest/LogInjectionSmokeTest.groovy b/dd-smoke-tests/log-injection/src/test/groovy/datadog/smoketest/LogInjectionSmokeTest.groovy index f6a65371ec5..33739de2775 100644 --- a/dd-smoke-tests/log-injection/src/test/groovy/datadog/smoketest/LogInjectionSmokeTest.groovy +++ b/dd-smoke-tests/log-injection/src/test/groovy/datadog/smoketest/LogInjectionSmokeTest.groovy @@ -32,7 +32,8 @@ abstract class LogInjectionSmokeTest extends AbstractSmokeTest { static final String LOG4J2_BACKEND = "Log4j2" static final String LOGBACK_BACKEND = "Logback" - static final Set DIRECT_LOG_SUBMISSION_BACKENDS = [LOG4J2_BACKEND, LOGBACK_BACKEND] as Set + static final String JBOSS_BACKEND = "JBoss" + static final Set DIRECT_LOG_SUBMISSION_BACKENDS = [LOG4J2_BACKEND, LOGBACK_BACKEND, JBOSS_BACKEND] as Set @Shared File outputLogFile @@ -514,7 +515,7 @@ class JULInterfaceLog4j2LatestBackend extends JULInterfaceLog4j2Backend {} class JULInterfaceJBossBackend extends LogInjectionSmokeTest { def backend() { - "JBoss" + JBOSS_BACKEND } def supportsJson() { false @@ -762,7 +763,7 @@ class Log4j2InterfaceSlf4jToLogbackLatestBackend extends Log4j2InterfaceSlf4jToL class JBossInterfaceJBossBackend extends LogInjectionSmokeTest { def backend() { - "JBoss" + JBOSS_BACKEND } def supportsJson() { false From 2eeb111aa7ba40ab3a3291627d7af61f68c4a5c6 Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Fri, 2 Oct 2026 11:34:17 +0200 Subject: [PATCH 3/6] test(jboss-logmanager): migrate agentless log submission tests to JUnit 5 --- .../groovy/JBossLogsIntakeForkedTest.groovy | 250 --------------- .../test/java/JBossLogsIntakeForkedTest.java | 290 ++++++++++++++++++ 2 files changed, 290 insertions(+), 250 deletions(-) delete mode 100644 dd-java-agent/instrumentation/jboss/jboss-logmanager-1.1/src/test/groovy/JBossLogsIntakeForkedTest.groovy create mode 100644 dd-java-agent/instrumentation/jboss/jboss-logmanager-1.1/src/test/java/JBossLogsIntakeForkedTest.java diff --git a/dd-java-agent/instrumentation/jboss/jboss-logmanager-1.1/src/test/groovy/JBossLogsIntakeForkedTest.groovy b/dd-java-agent/instrumentation/jboss/jboss-logmanager-1.1/src/test/groovy/JBossLogsIntakeForkedTest.groovy deleted file mode 100644 index 5f35c0d31d8..00000000000 --- a/dd-java-agent/instrumentation/jboss/jboss-logmanager-1.1/src/test/groovy/JBossLogsIntakeForkedTest.groovy +++ /dev/null @@ -1,250 +0,0 @@ -import datadog.trace.agent.test.InstrumentationSpecification -import datadog.trace.api.CorrelationIdentifier -import datadog.trace.api.logging.intake.LogsIntake -import datadog.trace.api.logging.intake.LogsWriter -import datadog.trace.bootstrap.instrumentation.api.AgentTracer -import java.util.concurrent.CopyOnWriteArrayList -import java.util.concurrent.CountDownLatch -import java.util.concurrent.TimeUnit -import java.util.logging.Handler -import java.util.logging.LogRecord -import org.jboss.logmanager.ExtLogRecord -import org.jboss.logmanager.Level -import org.jboss.logmanager.LogContext -import org.jboss.logmanager.Logger -import org.jboss.logmanager.MDC -import org.jboss.logmanager.handlers.AsyncHandler - -abstract class JBossLogsIntakeTestBase extends InstrumentationSpecification { - static final String LOGGER_NAME = "test.jboss.intake" - static final List> MESSAGES = new CopyOnWriteArrayList<>() - - static final LogContext LOG_CONTEXT = LogContext.create() - - Logger parent = LOG_CONTEXT.getLogger(LOGGER_NAME) - Logger logger = LOG_CONTEXT.getLogger(LOGGER_NAME + ".child") - ContextHandler handler = new ContextHandler() - - boolean agentless() { - true - } - - boolean injection() { - true - } - - @Override - void configurePreAgent() { - super.configurePreAgent() - injectSysConfig("agentless.log.submission.enabled", agentless().toString()) - injectSysConfig("logs.injection.enabled", injection().toString()) - injectSysConfig("agentless.log.submission.level", "WARN") - } - - def setupSpec() { - LogsIntake.registerWriter(new LogsWriter() { - void log(Map message) { - if (message.loggerName.toString().startsWith(LOGGER_NAME)) { - MESSAGES.add(message) - } - } - - void start() {} - - void shutdown() {} - }) - } - - def cleanupSpec() { - LogsIntake.registerWriter(null) - } - - def setup() { - MESSAGES.clear() - MDC.clear() - parent.setUseParentHandlers(false) - parent.setLevel(Level.TRACE) - logger.setLevel(Level.TRACE) - logger.addHandler(handler) - } - - def cleanup() { - MDC.clear() - logger.removeHandler(handler) - parent.clearHandlers() - } - - def "submits filtered events with mapped levels and exceptions"() { - when: - logger.log(Level.DEBUG, "debug") - logger.log(Level.INFO, "info") - logger.log(Level.WARN, "warn") - logger.log(java.util.logging.Level.WARNING, "jul {0}", "warning") - logger.log(Level.ERROR, "error", new IllegalStateException("failure")) - logger.log(Level.FATAL, "fatal") - - then: - handler.contexts.size() == 6 - if (agentless()) { - assert MESSAGES*.message == ["warn", "jul warning", "error", "fatal"] - assert MESSAGES*.level == ["WARN", "WARN", "ERROR", "FATAL"] - assert MESSAGES.every { - it.loggerName == logger.name && it.thread == Thread.currentThread().name && it.timestamp instanceof Long - } - def thrown = MESSAGES[2].thrown - assert thrown.name == IllegalStateException.name - assert thrown.message == "failure" - assert thrown.extendedStackTrace.contains("IllegalStateException: failure") - assert !MESSAGES[0].containsKey("thrown") - } else { - assert MESSAGES.empty - } - } - - def "submits producer context and preserves user MDC"() { - setup: - def span = AgentTracer.startSpan("test", "jboss-producer") - def scope = AgentTracer.activateSpan(span) - def traceId = CorrelationIdentifier.traceId - def spanId = CorrelationIdentifier.spanId - MDC.put("custom", "value") - - when: - logger.log(Level.WARN, "active") - def child = AgentTracer.startSpan("test", "jboss-child") - def childScope = AgentTracer.activateSpan(child) - def childSpanId = CorrelationIdentifier.spanId - logger.log(Level.WARN, "child") - childScope.close() - child.finish() - MDC.put("dd.trace_id", "user-trace") - logger.log(Level.WARN, "collision") - MDC.clear() - scope.close() - span.finish() - logger.log(Level.WARN, "outside") - - then: - handler.contexts[0]["dd.trace_id"] == (injection() ? traceId : null) - handler.contexts[0]["dd.span_id"] == (injection() ? spanId : null) - handler.contexts[0]["custom"] == "value" - if (agentless()) { - assert MESSAGES*.message == ["active", "child", "collision", "outside"] - def contexts = MESSAGES*.contextMap - assert contexts[0] == (injection() ? ["dd.trace_id": traceId, "dd.span_id": spanId, custom: "value"] : [custom: "value"]) - assert contexts[1]["dd.span_id"] == (injection() ? childSpanId : null) - assert contexts[1]["dd.trace_id"] == (injection() ? traceId : null) - assert contexts[2]["dd.trace_id"] == "user-trace" - assert contexts[3] == [:] - } else { - assert MESSAGES.empty - } - } - - def "submits once when the event reaches parent handlers"() { - setup: - def parentHandler = new ContextHandler() - parent.addHandler(parentHandler) - - when: - logger.log(Level.WARN, "propagated") - - then: - handler.contexts.size() == 1 - parentHandler.contexts.size() == 1 - MESSAGES*.message == (agentless() ? ["propagated"] : []) - } - - def "submits once with producer context when delivery is asynchronous"() { - setup: - def delivered = new CountDownLatch(1) - def release = new CountDownLatch(1) - def asyncTarget = new ContextHandler() { - @Override - void publish(LogRecord record) { - release.await(5, TimeUnit.SECONDS) - super.publish(record) - delivered.countDown() - } - } - def async = new AsyncHandler() - async.addHandler(asyncTarget) - logger.removeHandler(handler) - logger.addHandler(async) - def span = AgentTracer.startSpan("test", "jboss-async") - def scope = AgentTracer.activateSpan(span) - def traceId = CorrelationIdentifier.traceId - def spanId = CorrelationIdentifier.spanId - - when: - logger.log(Level.WARN, "async") - scope.close() - span.finish() - release.countDown() - - then: - delivered.await(5, TimeUnit.SECONDS) - asyncTarget.contexts[0]["dd.span_id"] == (injection() ? spanId : null) - if (agentless()) { - assert MESSAGES.size() == 1 - assert MESSAGES[0].contextMap["dd.trace_id"] == (injection() ? traceId : null) - assert MESSAGES[0].contextMap["dd.span_id"] == (injection() ? spanId : null) - } else { - assert MESSAGES.empty - } - - cleanup: - logger.removeHandler(async) - async.close() - } - - static class ContextHandler extends Handler { - final List> contexts = new CopyOnWriteArrayList<>() - - @Override - void publish(LogRecord record) { - def extRecord = (ExtLogRecord) record - contexts.add([ - "dd.trace_id": extRecord.getMdc("dd.trace_id"), - "dd.span_id" : extRecord.getMdc("dd.span_id"), - "custom" : extRecord.getMdc("custom") - ]) - } - - @Override - void flush() {} - - @Override - void close() {} - } -} - -class JBossLogsIntakeForkedTest extends JBossLogsIntakeTestBase {} - -class JBossNoSubmissionForkedTest extends JBossLogsIntakeTestBase { - boolean agentless() { - false - } -} - -class JBossNoInjectionForkedTest extends JBossLogsIntakeTestBase { - boolean injection() { - false - } -} - -class JBossCiAgentlessForkedTest extends JBossLogsIntakeTestBase { - @Override - void configurePreAgent() { - super.configurePreAgent() - injectSysConfig("trace.enabled", "false") - } -} - -class JBossCiCorrelationForkedTest extends JBossNoSubmissionForkedTest { - @Override - void configurePreAgent() { - super.configurePreAgent() - injectSysConfig("trace.enabled", "false") - } -} diff --git a/dd-java-agent/instrumentation/jboss/jboss-logmanager-1.1/src/test/java/JBossLogsIntakeForkedTest.java b/dd-java-agent/instrumentation/jboss/jboss-logmanager-1.1/src/test/java/JBossLogsIntakeForkedTest.java new file mode 100644 index 00000000000..61a121a5857 --- /dev/null +++ b/dd-java-agent/instrumentation/jboss/jboss-logmanager-1.1/src/test/java/JBossLogsIntakeForkedTest.java @@ -0,0 +1,290 @@ +import static datadog.trace.api.config.GeneralConfig.AGENTLESS_LOG_SUBMISSION_ENABLED; +import static datadog.trace.api.config.GeneralConfig.AGENTLESS_LOG_SUBMISSION_LEVEL; +import static datadog.trace.api.config.TraceInstrumentationConfig.LOGS_INJECTION_ENABLED; +import static datadog.trace.api.config.TraceInstrumentationConfig.TRACE_ENABLED; +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.emptyMap; +import static java.util.Collections.singletonList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import datadog.context.ContextScope; +import datadog.trace.agent.test.AbstractInstrumentationTest; +import datadog.trace.api.Config; +import datadog.trace.api.CorrelationIdentifier; +import datadog.trace.api.InstrumenterConfig; +import datadog.trace.api.logging.intake.LogsIntake; +import datadog.trace.api.logging.intake.LogsWriter; +import datadog.trace.bootstrap.instrumentation.api.AgentSpan; +import datadog.trace.bootstrap.instrumentation.api.AgentTracer; +import datadog.trace.test.junit.utils.config.WithConfig; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.logging.Handler; +import java.util.logging.LogRecord; +import java.util.stream.Collectors; +import org.jboss.logmanager.ExtLogRecord; +import org.jboss.logmanager.Level; +import org.jboss.logmanager.LogContext; +import org.jboss.logmanager.Logger; +import org.jboss.logmanager.MDC; +import org.jboss.logmanager.handlers.AsyncHandler; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** Each subclass runs in its own JVM with the agent installed for its configuration. */ +@WithConfig(key = AGENTLESS_LOG_SUBMISSION_ENABLED, value = "true") +@WithConfig(key = LOGS_INJECTION_ENABLED, value = "true") +@WithConfig(key = AGENTLESS_LOG_SUBMISSION_LEVEL, value = "WARN") +abstract class AbstractJBossLogsIntakeForkedTest extends AbstractInstrumentationTest { + static final String LOGGER_NAME = "test.jboss.intake"; + static final List> MESSAGES = new CopyOnWriteArrayList<>(); + // The JBoss LogManager is not the JUL log manager of the test JVM + static final LogContext LOG_CONTEXT = LogContext.create(); + + final Logger parent = LOG_CONTEXT.getLogger(LOGGER_NAME); + final Logger logger = LOG_CONTEXT.getLogger(LOGGER_NAME + ".child"); + final ContextHandler handler = new ContextHandler(); + + @BeforeAll + static void registerWriter() { + LogsIntake.registerWriter(new CapturingLogsWriter()); + } + + @AfterAll + static void unregisterWriter() { + LogsIntake.registerWriter(null); + } + + @BeforeEach + void setupLoggers() { + MESSAGES.clear(); + MDC.clear(); + parent.setUseParentHandlers(false); + parent.setLevel(Level.TRACE); + logger.setLevel(Level.TRACE); + logger.addHandler(handler); + } + + @AfterEach + void cleanupLoggers() { + MDC.clear(); + logger.removeHandler(handler); + parent.clearHandlers(); + } + + static boolean agentless() { + return InstrumenterConfig.get().isAgentlessLogSubmissionEnabled(); + } + + static boolean injection() { + return Config.get().isLogsInjectionEnabled(); + } + + @Test + void submitsFilteredEventsWithMappedLevelsAndExceptions() { + logger.log(Level.DEBUG, "debug"); + logger.log(Level.INFO, "info"); + logger.log(Level.WARN, "warn"); + logger.log(java.util.logging.Level.WARNING, "jul {0}", "warning"); + logger.log(Level.ERROR, "error", new IllegalStateException("failure")); + logger.log(Level.FATAL, "fatal"); + + assertEquals(6, handler.contexts.size()); + if (!agentless()) { + assertTrue(MESSAGES.isEmpty()); + return; + } + assertEquals(asList("warn", "jul warning", "error", "fatal"), values("message")); + assertEquals(asList("WARN", "WARN", "ERROR", "FATAL"), values("level")); + for (Map message : MESSAGES) { + assertEquals(logger.getName(), message.get("loggerName")); + assertEquals(Thread.currentThread().getName(), message.get("thread")); + assertInstanceOf(Long.class, message.get("timestamp")); + } + Map thrown = (Map) MESSAGES.get(2).get("thrown"); + assertEquals(IllegalStateException.class.getName(), thrown.get("name")); + assertEquals("failure", thrown.get("message")); + assertTrue( + thrown.get("extendedStackTrace").toString().contains("IllegalStateException: failure")); + assertFalse(MESSAGES.get(0).containsKey("thrown")); + } + + @Test + void submitsProducerContextAndPreservesUserMdc() { + AgentSpan span = AgentTracer.startSpan("test", "jboss-producer"); + String traceId; + String spanId; + String childSpanId; + try (ContextScope scope = AgentTracer.activateSpan(span)) { + traceId = CorrelationIdentifier.getTraceId(); + spanId = CorrelationIdentifier.getSpanId(); + MDC.put("custom", "value"); + logger.log(Level.WARN, "active"); + AgentSpan child = AgentTracer.startSpan("test", "jboss-child"); + try (ContextScope childScope = AgentTracer.activateSpan(child)) { + childSpanId = CorrelationIdentifier.getSpanId(); + logger.log(Level.WARN, "child"); + } finally { + child.finish(); + } + MDC.put("dd.trace_id", "user-trace"); + logger.log(Level.WARN, "collision"); + MDC.clear(); + } finally { + span.finish(); + } + logger.log(Level.WARN, "outside"); + + Map active = handler.contexts.get(0); + assertEquals(injection() ? traceId : null, active.get("dd.trace_id")); + assertEquals(injection() ? spanId : null, active.get("dd.span_id")); + assertEquals("value", active.get("custom")); + if (!agentless()) { + assertTrue(MESSAGES.isEmpty()); + return; + } + assertEquals(asList("active", "child", "collision", "outside"), values("message")); + Map expected = new HashMap<>(); + if (injection()) { + expected.put("dd.trace_id", traceId); + expected.put("dd.span_id", spanId); + } + expected.put("custom", "value"); + assertEquals(expected, contextMap(0)); + assertEquals(injection() ? traceId : null, contextMap(1).get("dd.trace_id")); + assertEquals(injection() ? childSpanId : null, contextMap(1).get("dd.span_id")); + assertEquals("user-trace", contextMap(2).get("dd.trace_id")); + assertEquals(emptyMap(), contextMap(3)); + } + + @Test + void submitsOnceWhenTheEventReachesParentHandlers() { + ContextHandler parentHandler = new ContextHandler(); + parent.addHandler(parentHandler); + + logger.log(Level.WARN, "propagated"); + + assertEquals(1, handler.contexts.size()); + assertEquals(1, parentHandler.contexts.size()); + assertEquals(agentless() ? singletonList("propagated") : emptyList(), values("message")); + } + + @Test + void submitsOnceWithProducerContextWhenDeliveryIsAsynchronous() throws Exception { + CountDownLatch release = new CountDownLatch(1); + CountDownLatch delivered = new CountDownLatch(1); + ContextHandler asyncTarget = + new ContextHandler() { + @Override + public void publish(LogRecord record) { + try { + release.await(5, TimeUnit.SECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + super.publish(record); + delivered.countDown(); + } + }; + AsyncHandler async = new AsyncHandler(); + async.addHandler(asyncTarget); + logger.removeHandler(handler); + logger.addHandler(async); + try { + AgentSpan span = AgentTracer.startSpan("test", "jboss-async"); + String traceId; + String spanId; + try (ContextScope scope = AgentTracer.activateSpan(span)) { + traceId = CorrelationIdentifier.getTraceId(); + spanId = CorrelationIdentifier.getSpanId(); + logger.log(Level.WARN, "async"); + } finally { + span.finish(); + } + release.countDown(); + + assertTrue(delivered.await(5, TimeUnit.SECONDS)); + assertEquals(injection() ? spanId : null, asyncTarget.contexts.get(0).get("dd.span_id")); + if (agentless()) { + assertEquals(1, MESSAGES.size()); + assertEquals(injection() ? traceId : null, contextMap(0).get("dd.trace_id")); + assertEquals(injection() ? spanId : null, contextMap(0).get("dd.span_id")); + } else { + assertTrue(MESSAGES.isEmpty()); + } + } finally { + logger.removeHandler(async); + async.close(); + } + } + + static List values(String key) { + return MESSAGES.stream().map(message -> message.get(key)).collect(Collectors.toList()); + } + + static Map contextMap(int index) { + return (Map) MESSAGES.get(index).get("contextMap"); + } + + /** Records the correlation context seen by JBoss handlers. */ + static class ContextHandler extends Handler { + final List> contexts = new CopyOnWriteArrayList<>(); + + @Override + public void publish(LogRecord record) { + ExtLogRecord extRecord = (ExtLogRecord) record; + Map context = new HashMap<>(); + context.put("dd.trace_id", extRecord.getMdc("dd.trace_id")); + context.put("dd.span_id", extRecord.getMdc("dd.span_id")); + context.put("custom", extRecord.getMdc("custom")); + contexts.add(context); + } + + @Override + public void flush() {} + + @Override + public void close() {} + } + + static class CapturingLogsWriter implements LogsWriter { + @Override + public void log(Map message) { + if (message.get("loggerName").toString().startsWith(LOGGER_NAME)) { + MESSAGES.add(message); + } + } + + @Override + public void start() {} + + @Override + public void shutdown() {} + } +} + +class JBossLogsIntakeForkedTest extends AbstractJBossLogsIntakeForkedTest {} + +@WithConfig(key = AGENTLESS_LOG_SUBMISSION_ENABLED, value = "false") +class JBossNoSubmissionForkedTest extends AbstractJBossLogsIntakeForkedTest {} + +@WithConfig(key = LOGS_INJECTION_ENABLED, value = "false") +class JBossNoInjectionForkedTest extends AbstractJBossLogsIntakeForkedTest {} + +@WithConfig(key = TRACE_ENABLED, value = "false") +class JBossCiAgentlessForkedTest extends AbstractJBossLogsIntakeForkedTest {} + +@WithConfig(key = AGENTLESS_LOG_SUBMISSION_ENABLED, value = "false") +@WithConfig(key = TRACE_ENABLED, value = "false") +class JBossCiCorrelationForkedTest extends AbstractJBossLogsIntakeForkedTest {} From f42a40ebf4c82939d5685c2bf191e643515a149d Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Fri, 2 Oct 2026 12:45:34 +0200 Subject: [PATCH 4/6] fix(logs-intake): create the intake client when the first log is sent --- .../trace/logging/intake/LogsWriterImpl.java | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/dd-java-agent/agent-logs-intake/src/main/java/datadog/trace/logging/intake/LogsWriterImpl.java b/dd-java-agent/agent-logs-intake/src/main/java/datadog/trace/logging/intake/LogsWriterImpl.java index 6d88d0e0a59..d4f6544bda9 100644 --- a/dd-java-agent/agent-logs-intake/src/main/java/datadog/trace/logging/intake/LogsWriterImpl.java +++ b/dd-java-agent/agent-logs-intake/src/main/java/datadog/trace/logging/intake/LogsWriterImpl.java @@ -89,14 +89,16 @@ public void log(Map message) { } private void logPollingLoop() { - BackendApi backendApi = apiFactory.createBackendApi(intake); - LogsDispatcher logsDispatcher = new LogsDispatcher(backendApi); + LogsDispatcher logsDispatcher = null; while (!Thread.currentThread().isInterrupted()) { try { List> batch = new ArrayList<>(); batch.add(messageQueue.take()); messageQueue.drainTo(batch); + if (logsDispatcher == null) { + logsDispatcher = createDispatcher(); + } logsDispatcher.dispatch(batch); } catch (InterruptedException e) { @@ -107,7 +109,19 @@ private void logPollingLoop() { List> batch = new ArrayList<>(); messageQueue.drainTo(batch); if (!batch.isEmpty()) { + if (logsDispatcher == null) { + logsDispatcher = createDispatcher(); + } logsDispatcher.dispatch(batch); } } + + /** + * Creating the HTTP client initializes TLS, which can initialize the JUL {@code LogManager}. It + * is created for the first log, after the application has configured its own log manager. + */ + private LogsDispatcher createDispatcher() { + BackendApi backendApi = apiFactory.createBackendApi(intake); + return new LogsDispatcher(backendApi); + } } From c9d08f1cc0c0b51eafb46a2ef5818d50d967b228 Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Tue, 6 Oct 2026 13:16:06 +0200 Subject: [PATCH 5/6] feat(logs-intake): add service, env and version to submitted events --- .../log/AgentlessLogSubmission.java | 15 +++++++++++-- .../test/java/JBossLogsIntakeForkedTest.java | 22 ++++++++++++++++--- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/log/AgentlessLogSubmission.java b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/log/AgentlessLogSubmission.java index 8d6141c1266..303d1455a4d 100644 --- a/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/log/AgentlessLogSubmission.java +++ b/dd-java-agent/agent-bootstrap/src/main/java/datadog/trace/bootstrap/instrumentation/log/AgentlessLogSubmission.java @@ -5,6 +5,7 @@ import datadog.trace.api.Config; import datadog.trace.api.CorrelationIdentifier; import datadog.trace.api.logging.intake.LogsIntake; +import datadog.trace.bootstrap.instrumentation.api.Tags; import java.io.PrintWriter; import java.io.StringWriter; import java.util.HashMap; @@ -80,7 +81,7 @@ public static void submit( } Map contextMap = new HashMap<>(); if (traceConfig().isLogsInjectionEnabled()) { - addCorrelationIds(contextMap); + addCorrelationValues(contextMap); } if (context != null) { contextMap.putAll(context); @@ -102,7 +103,11 @@ private static Map thrown(Throwable thrown) { return thrownLog; } - private static void addCorrelationIds(Map context) { + private static void addCorrelationValues(Map context) { + Config config = Config.get(); + putIfNotEmpty(context, Tags.DD_SERVICE, config.getServiceName()); + putIfNotEmpty(context, Tags.DD_ENV, config.getEnv()); + putIfNotEmpty(context, Tags.DD_VERSION, config.getVersion()); String traceId = CorrelationIdentifier.getTraceId(); if (traceId != null && !traceId.equals("0")) { context.put(CorrelationIdentifier.getTraceIdKey(), traceId); @@ -113,6 +118,12 @@ private static void addCorrelationIds(Map context) { } } + private static void putIfNotEmpty(Map context, String key, String value) { + if (value != null && !value.isEmpty()) { + context.put(key, value); + } + } + static int severity(String level) { switch (level == null ? INFO : level.toUpperCase(Locale.ROOT)) { case "ALL": diff --git a/dd-java-agent/instrumentation/jboss/jboss-logmanager-1.1/src/test/java/JBossLogsIntakeForkedTest.java b/dd-java-agent/instrumentation/jboss/jboss-logmanager-1.1/src/test/java/JBossLogsIntakeForkedTest.java index 61a121a5857..1c147b6c10d 100644 --- a/dd-java-agent/instrumentation/jboss/jboss-logmanager-1.1/src/test/java/JBossLogsIntakeForkedTest.java +++ b/dd-java-agent/instrumentation/jboss/jboss-logmanager-1.1/src/test/java/JBossLogsIntakeForkedTest.java @@ -1,10 +1,12 @@ import static datadog.trace.api.config.GeneralConfig.AGENTLESS_LOG_SUBMISSION_ENABLED; import static datadog.trace.api.config.GeneralConfig.AGENTLESS_LOG_SUBMISSION_LEVEL; +import static datadog.trace.api.config.GeneralConfig.ENV; +import static datadog.trace.api.config.GeneralConfig.SERVICE_NAME; +import static datadog.trace.api.config.GeneralConfig.VERSION; import static datadog.trace.api.config.TraceInstrumentationConfig.LOGS_INJECTION_ENABLED; import static datadog.trace.api.config.TraceInstrumentationConfig.TRACE_ENABLED; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; -import static java.util.Collections.emptyMap; import static java.util.Collections.singletonList; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -20,6 +22,7 @@ import datadog.trace.api.logging.intake.LogsWriter; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; import datadog.trace.bootstrap.instrumentation.api.AgentTracer; +import datadog.trace.bootstrap.instrumentation.api.Tags; import datadog.trace.test.junit.utils.config.WithConfig; import java.util.HashMap; import java.util.List; @@ -46,6 +49,9 @@ @WithConfig(key = AGENTLESS_LOG_SUBMISSION_ENABLED, value = "true") @WithConfig(key = LOGS_INJECTION_ENABLED, value = "true") @WithConfig(key = AGENTLESS_LOG_SUBMISSION_LEVEL, value = "WARN") +@WithConfig(key = SERVICE_NAME, value = "jboss-service") +@WithConfig(key = ENV, value = "jboss-env") +@WithConfig(key = VERSION, value = "1.0") abstract class AbstractJBossLogsIntakeForkedTest extends AbstractInstrumentationTest { static final String LOGGER_NAME = "test.jboss.intake"; static final List> MESSAGES = new CopyOnWriteArrayList<>(); @@ -91,6 +97,16 @@ static boolean injection() { return Config.get().isLogsInjectionEnabled(); } + static Map serviceTags() { + Map tags = new HashMap<>(); + if (injection()) { + tags.put(Tags.DD_SERVICE, "jboss-service"); + tags.put(Tags.DD_ENV, "jboss-env"); + tags.put(Tags.DD_VERSION, "1.0"); + } + return tags; + } + @Test void submitsFilteredEventsWithMappedLevelsAndExceptions() { logger.log(Level.DEBUG, "debug"); @@ -155,7 +171,7 @@ void submitsProducerContextAndPreservesUserMdc() { return; } assertEquals(asList("active", "child", "collision", "outside"), values("message")); - Map expected = new HashMap<>(); + Map expected = serviceTags(); if (injection()) { expected.put("dd.trace_id", traceId); expected.put("dd.span_id", spanId); @@ -165,7 +181,7 @@ void submitsProducerContextAndPreservesUserMdc() { assertEquals(injection() ? traceId : null, contextMap(1).get("dd.trace_id")); assertEquals(injection() ? childSpanId : null, contextMap(1).get("dd.span_id")); assertEquals("user-trace", contextMap(2).get("dd.trace_id")); - assertEquals(emptyMap(), contextMap(3)); + assertEquals(serviceTags(), contextMap(3)); } @Test From bef2ad6141b7dc52c1eafe1060fffffdc77fe8e9 Mon Sep 17 00:00:00 2001 From: Daniel Mohedano Date: Thu, 15 Oct 2026 14:07:15 +0200 Subject: [PATCH 6/6] fix(jboss-logmanager): run latest dependency tests on Java 17 --- .../instrumentation/jboss/jboss-logmanager-1.1/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dd-java-agent/instrumentation/jboss/jboss-logmanager-1.1/build.gradle b/dd-java-agent/instrumentation/jboss/jboss-logmanager-1.1/build.gradle index 20b9e239bc2..45203a1f4d7 100644 --- a/dd-java-agent/instrumentation/jboss/jboss-logmanager-1.1/build.gradle +++ b/dd-java-agent/instrumentation/jboss/jboss-logmanager-1.1/build.gradle @@ -17,8 +17,8 @@ addTestSuiteExtendingForDir('latestDepForkedTest', 'latestDepTest', 'test') ["latestDepTest", "latestDepForkedTest"].each { tasks.named(it, Test) { testJvmConstraints { - // jboss-logmanager 3.x requires Java 11 - minJavaVersion = JavaVersion.VERSION_11 + // jboss-logmanager 3.2 requires Java 17 + minJavaVersion = JavaVersion.VERSION_17 } } }