diff --git a/dd-java-agent/instrumentation/logback-1.0/src/main/java/datadog/trace/instrumentation/logback/LogbackLoggerInstrumentation.java b/dd-java-agent/instrumentation/logback-1.0/src/main/java/datadog/trace/instrumentation/logback/LogbackLoggerInstrumentation.java index 51fb2870370..66a860b7875 100644 --- a/dd-java-agent/instrumentation/logback-1.0/src/main/java/datadog/trace/instrumentation/logback/LogbackLoggerInstrumentation.java +++ b/dd-java-agent/instrumentation/logback-1.0/src/main/java/datadog/trace/instrumentation/logback/LogbackLoggerInstrumentation.java @@ -9,8 +9,10 @@ import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeSpan; import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.traceConfig; import static java.util.Collections.singletonMap; +import static net.bytebuddy.matcher.ElementMatchers.isBootstrapClassLoader; import static net.bytebuddy.matcher.ElementMatchers.isMethod; import static net.bytebuddy.matcher.ElementMatchers.isPublic; +import static net.bytebuddy.matcher.ElementMatchers.not; import static net.bytebuddy.matcher.ElementMatchers.takesArgument; import static net.bytebuddy.matcher.ElementMatchers.takesArguments; @@ -24,6 +26,7 @@ import datadog.trace.bootstrap.instrumentation.api.AgentSpanContext; import java.util.Map; import net.bytebuddy.asm.Advice; +import net.bytebuddy.matcher.ElementMatcher; @AutoService(InstrumenterModule.class) public class LogbackLoggerInstrumentation extends InstrumenterModule.ContextTracking @@ -33,6 +36,11 @@ public LogbackLoggerInstrumentation() { super("logback", "logs-intake", "logs-intake-logback"); } + @Override + public ElementMatcher.Junction classLoaderMatcher() { + return not(isBootstrapClassLoader()); + } + @Override public String instrumentedType() { return "ch.qos.logback.classic.Logger"; @@ -60,7 +68,8 @@ public void methodAdvice(MethodTransformer transformer) { LogbackLoggerInstrumentation.class.getName() + "$CallAppendersAdvice"); // this can be moved into a always on instrumenter module if one day context tracking can be // deactivated by config - if (InstrumenterConfig.get().isAppLogsCollectionEnabled()) { + if (InstrumenterConfig.get().isAppLogsCollectionEnabled() + || InstrumenterConfig.get().isAgentlessLogSubmissionEnabled()) { transformer.applyAdvice( isMethod() .and(isPublic()) diff --git a/dd-java-agent/instrumentation/logback-1.0/src/main/java/datadog/trace/instrumentation/logback/LoggingEventInstrumentation.java b/dd-java-agent/instrumentation/logback-1.0/src/main/java/datadog/trace/instrumentation/logback/LoggingEventInstrumentation.java index 2d78ccb8336..18ac0837924 100644 --- a/dd-java-agent/instrumentation/logback-1.0/src/main/java/datadog/trace/instrumentation/logback/LoggingEventInstrumentation.java +++ b/dd-java-agent/instrumentation/logback-1.0/src/main/java/datadog/trace/instrumentation/logback/LoggingEventInstrumentation.java @@ -27,7 +27,7 @@ import net.bytebuddy.matcher.ElementMatcher; @AutoService(InstrumenterModule.class) -public class LoggingEventInstrumentation extends InstrumenterModule.Tracing +public class LoggingEventInstrumentation extends InstrumenterModule.ContextTracking implements Instrumenter.ForTypeHierarchy, Instrumenter.HasMethodAdvice { public LoggingEventInstrumentation() { super("logback"); diff --git a/dd-java-agent/instrumentation/logback-1.0/src/main/java/datadog/trace/instrumentation/logback/LogsIntakeHelper.java b/dd-java-agent/instrumentation/logback-1.0/src/main/java/datadog/trace/instrumentation/logback/LogsIntakeHelper.java index 593acd82508..f75f388e6a6 100644 --- a/dd-java-agent/instrumentation/logback-1.0/src/main/java/datadog/trace/instrumentation/logback/LogsIntakeHelper.java +++ b/dd-java-agent/instrumentation/logback-1.0/src/main/java/datadog/trace/instrumentation/logback/LogsIntakeHelper.java @@ -1,7 +1,11 @@ package datadog.trace.instrumentation.logback; +import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.traceConfig; + +import ch.qos.logback.classic.Level; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.classic.spi.StackTraceElementProxy; +import datadog.trace.api.Config; import datadog.trace.api.CorrelationIdentifier; import datadog.trace.api.logging.intake.LogsIntake; import java.util.Arrays; @@ -11,8 +15,16 @@ public class LogsIntakeHelper { + private static final boolean APP_LOGS_COLLECTION = Config.get().isAppLogsCollectionEnabled(); + private static final Level SUBMISSION_LEVEL = + submissionLevel(Config.get().getAgentlessLogSubmissionLevel()); + public static void log(ILoggingEvent event) { - LogsIntake.log(map(event)); + // App-log collection keeps its existing payload and framework-level filtering, even when + // both collection flags are enabled. + if (APP_LOGS_COLLECTION || event.getLevel().isGreaterOrEqual(SUBMISSION_LEVEL)) { + LogsIntake.log(map(event)); + } } private static Map map(ILoggingEvent event) { @@ -32,6 +44,31 @@ private static Map map(ILoggingEvent event) { thrownLog.put("extendedStackTrace", stackTraceString); log.put("thrown", thrownLog); } + if (!APP_LOGS_COLLECTION) { + Map context = new HashMap<>(); + // Capture on the producer thread, independently of the ordering of the two entry advices. + // Injection-disabled logs retain user MDC but do not gain automatic correlation IDs. + if (traceConfig().isLogsInjectionEnabled()) { + addCorrelationIds(context); + } + Map mdc = event.getMDCPropertyMap(); + if (mdc != null) { + context.putAll(mdc); + } + log.put("contextMap", context); + log.put("timestamp", event.getTimeStamp()); + } else { + addCorrelationIds(log); + } + return log; + } + + /** Logback has no FATAL level, and ERROR is below a FATAL threshold. */ + private static Level submissionLevel(String level) { + return "FATAL".equalsIgnoreCase(level) ? Level.OFF : Level.toLevel(level, Level.INFO); + } + + private static void addCorrelationIds(Map log) { String traceId = CorrelationIdentifier.getTraceId(); if (traceId != null && !traceId.equals("0")) { log.put("dd.trace_id", traceId); @@ -40,6 +77,5 @@ private static Map map(ILoggingEvent event) { if (spanId != null && !spanId.equals("0")) { log.put("dd.span_id", spanId); } - return log; } } diff --git a/dd-java-agent/instrumentation/logback-1.0/src/test/java/LogbackApplication.java b/dd-java-agent/instrumentation/logback-1.0/src/test/java/LogbackApplication.java new file mode 100644 index 00000000000..70bfbb405e4 --- /dev/null +++ b/dd-java-agent/instrumentation/logback-1.0/src/test/java/LogbackApplication.java @@ -0,0 +1,19 @@ +import java.util.List; +import java.util.Map; + +/** Logback test application loaded apart from the logging backend of the test harness. */ +public interface LogbackApplication { + List> getContexts(); + + void log(String level, String message); + + void put(String key, String value); + + void clear(); + + void startAsync(); + + Map finishAsync() throws InterruptedException; + + void jul(); +} diff --git a/dd-java-agent/instrumentation/logback-1.0/src/test/java/LogbackLogsIntakeForkedTest.java b/dd-java-agent/instrumentation/logback-1.0/src/test/java/LogbackLogsIntakeForkedTest.java new file mode 100644 index 00000000000..7e219e574a4 --- /dev/null +++ b/dd-java-agent/instrumentation/logback-1.0/src/test/java/LogbackLogsIntakeForkedTest.java @@ -0,0 +1,248 @@ +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.APP_LOGS_COLLECTION_ENABLED; +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.singletonList; +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.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.List; +import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.stream.Collectors; +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 = APP_LOGS_COLLECTION_ENABLED, value = "false") +@WithConfig(key = LOGS_INJECTION_ENABLED, value = "true") +@WithConfig(key = "integration.logback.enabled", value = "true") +@WithConfig(key = AGENTLESS_LOG_SUBMISSION_LEVEL, value = "WARN") +abstract class AbstractLogbackLogsIntakeForkedTest extends AbstractInstrumentationTest { + static final String LOGGER_NAME = "test.logback.intake"; + static final List> MESSAGES = new CopyOnWriteArrayList<>(); + + static LogbackApplication app; + + @BeforeAll + static void setupApplication() throws Exception { + app = + (LogbackApplication) + new LogbackTestClassLoader(AbstractLogbackLogsIntakeForkedTest.class.getClassLoader()) + .loadClass("LogbackTestApplication") + .getDeclaredConstructor() + .newInstance(); + LogsIntake.registerWriter(new CapturingLogsWriter()); + } + + @AfterAll + static void unregisterWriter() { + LogsIntake.registerWriter(null); + } + + @BeforeEach + void clearState() { + MESSAGES.clear(); + app.clear(); + app.getContexts().clear(); + } + + @AfterEach + void clearMdc() { + app.clear(); + } + + static boolean agentless() { + return InstrumenterConfig.get().isAgentlessLogSubmissionEnabled(); + } + + static boolean appLogs() { + return InstrumenterConfig.get().isAppLogsCollectionEnabled(); + } + + static boolean injection() { + return Config.get().isLogsInjectionEnabled(); + } + + static boolean integration() { + return InstrumenterConfig.get().isIntegrationEnabled(singletonList("logback"), true); + } + + static boolean fatalLevel() { + return "FATAL".equals(Config.get().getAgentlessLogSubmissionLevel()); + } + + static boolean collected() { + return integration() && (appLogs() || (agentless() && !fatalLevel())); + } + + @Test + void flagsPreserveOptInAppLogPrecedenceAndFrameworkFiltering() { + app.log("DEBUG", "debug"); + app.log("INFO", "info"); + app.log("WARN", "formatted message"); + app.log("ERROR", "error"); + + List expected; + if (!collected()) { + expected = emptyList(); + } else if (appLogs()) { + expected = asList("debug", "info", "formatted message", "error"); + } else { + expected = asList("formatted message", "error"); + } + assertEquals(expected, messages()); + if (!MESSAGES.isEmpty()) { + Map thrown = (Map) MESSAGES.get(MESSAGES.size() - 1).get("thrown"); + assertEquals(IllegalStateException.class.getName(), thrown.get("name")); + assertEquals("failure", thrown.get("message")); + assertFalse(thrown.get("extendedStackTrace").toString().isEmpty()); + for (Map message : MESSAGES) { + assertEquals(!appLogs(), message.containsKey("contextMap")); + assertEquals(!appLogs(), message.containsKey("timestamp")); + } + } + } + + @Test + void producerCorrelationHonorsInjectionSwitchAndSnapshotsMdcBeforeWrapperCleanup() { + AgentSpan span = AgentTracer.startSpan("test", "logback-producer"); + String traceId; + String spanId; + try (ContextScope scope = AgentTracer.activateSpan(span)) { + traceId = CorrelationIdentifier.getTraceId(); + spanId = CorrelationIdentifier.getSpanId(); + app.put("custom", "before-clear"); + app.log("WARN", "active"); + app.put("dd.trace_id", "user-trace"); + app.log("WARN", "collision"); + app.clear(); + } finally { + span.finish(); + } + app.log("WARN", "outside"); + + List> contexts = app.getContexts(); + assertEquals(3, contexts.size()); + boolean injected = integration() && injection(); + assertEquals(injected ? traceId : null, contexts.get(0).get("dd.trace_id")); + assertEquals(injected ? spanId : null, contexts.get(0).get("dd.span_id")); + assertEquals("user-trace", contexts.get(1).get("dd.trace_id")); + assertFalse(contexts.get(2).containsKey("dd.trace_id")); + if (!collected()) { + assertTrue(MESSAGES.isEmpty()); + return; + } + assertEquals(3, MESSAGES.size()); + if (appLogs()) { + assertEquals(traceId, MESSAGES.get(0).get("dd.trace_id")); + assertEquals(spanId, MESSAGES.get(0).get("dd.span_id")); + assertEquals(traceId, MESSAGES.get(1).get("dd.trace_id")); + assertFalse(MESSAGES.get(2).containsKey("dd.trace_id")); + } else { + Map first = contextMap(0); + assertEquals(injection() ? traceId : null, first.get("dd.trace_id")); + assertEquals(injection() ? spanId : null, first.get("dd.span_id")); + assertEquals("before-clear", first.get("custom")); + assertEquals("user-trace", contextMap(1).get("dd.trace_id")); + assertFalse(contextMap(2).containsKey("dd.trace_id")); + } + } + + static List messages() { + return MESSAGES.stream().map(m -> (String) m.get("message")).collect(Collectors.toList()); + } + + static Map contextMap(int index) { + return (Map) MESSAGES.get(index).get("contextMap"); + } + + static class CapturingLogsWriter implements LogsWriter { + @Override + public void log(Map message) { + if (LOGGER_NAME.equals(message.get("loggerName"))) { + MESSAGES.add(message); + } + } + + @Override + public void start() {} + + @Override + public void shutdown() {} + } +} + +class LogbackLogsIntakeForkedTest extends AbstractLogbackLogsIntakeForkedTest { + @Test + void asyncDeliveryAfterScopeClosesRetainsProducerIdsExactlyOnce() throws Exception { + app.startAsync(); + AgentSpan span = AgentTracer.startSpan("test", "async-producer"); + String traceId; + String spanId; + try (ContextScope scope = AgentTracer.activateSpan(span)) { + traceId = CorrelationIdentifier.getTraceId(); + spanId = CorrelationIdentifier.getSpanId(); + app.log("WARN", "async"); + } finally { + span.finish(); + } + Map deliveredContext = app.finishAsync(); + + assertEquals(1, MESSAGES.size()); + assertEquals(traceId, contextMap(0).get("dd.trace_id")); + assertEquals(spanId, contextMap(0).get("dd.span_id")); + assertEquals(spanId, deliveredContext.get("dd.span_id")); + } + + @Test + void julBridgeReachesTheSameBackendHookOnce() { + app.jul(); + + assertEquals(singletonList("bridged"), messages()); + } +} + +@WithConfig(key = AGENTLESS_LOG_SUBMISSION_ENABLED, value = "false") +class LogbackNoSubmissionForkedTest extends AbstractLogbackLogsIntakeForkedTest {} + +@WithConfig(key = AGENTLESS_LOG_SUBMISSION_ENABLED, value = "false") +@WithConfig(key = APP_LOGS_COLLECTION_ENABLED, value = "true") +class LogbackAppLogsForkedTest extends AbstractLogbackLogsIntakeForkedTest {} + +@WithConfig(key = APP_LOGS_COLLECTION_ENABLED, value = "true") +class LogbackBothFlagsForkedTest extends AbstractLogbackLogsIntakeForkedTest {} + +@WithConfig(key = AGENTLESS_LOG_SUBMISSION_LEVEL, value = "FATAL") +class LogbackFatalLevelForkedTest extends AbstractLogbackLogsIntakeForkedTest {} + +@WithConfig(key = LOGS_INJECTION_ENABLED, value = "false") +class LogbackNoInjectionForkedTest extends AbstractLogbackLogsIntakeForkedTest {} + +@WithConfig(key = "integration.logback.enabled", value = "false") +class LogbackDisabledForkedTest extends AbstractLogbackLogsIntakeForkedTest {} + +@WithConfig(key = TRACE_ENABLED, value = "false") +class LogbackCiAgentlessForkedTest extends AbstractLogbackLogsIntakeForkedTest {} + +@WithConfig(key = AGENTLESS_LOG_SUBMISSION_ENABLED, value = "false") +@WithConfig(key = TRACE_ENABLED, value = "false") +class LogbackCiCorrelationForkedTest extends AbstractLogbackLogsIntakeForkedTest {} diff --git a/dd-java-agent/instrumentation/logback-1.0/src/test/java/LogbackTestApplication.java b/dd-java-agent/instrumentation/logback-1.0/src/test/java/LogbackTestApplication.java new file mode 100644 index 00000000000..886f578603e --- /dev/null +++ b/dd-java-agent/instrumentation/logback-1.0/src/test/java/LogbackTestApplication.java @@ -0,0 +1,124 @@ +import ch.qos.logback.classic.AsyncAppender; +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; +import org.slf4j.bridge.SLF4JBridgeHandler; + +public class LogbackTestApplication implements LogbackApplication { + private final Logger logger = (Logger) LoggerFactory.getLogger("test.logback.intake"); + private final CountDownLatch release = new CountDownLatch(1); + private final CountDownLatch delivered = new CountDownLatch(1); + private Map deliveredContext; + private AsyncAppender async; + private final List> contexts = new ArrayList<>(); + + public LogbackTestApplication() { + logger.setLevel(Level.DEBUG); + AppenderBase sink = + new AppenderBase() { + @Override + protected void append(ILoggingEvent event) { + contexts.add(new HashMap<>(event.getMDCPropertyMap())); + } + }; + sink.setContext(logger.getLoggerContext()); + sink.start(); + logger.addAppender(sink); + } + + @Override + public List> getContexts() { + return contexts; + } + + @Override + public void log(String level, String message) { + switch (level) { + case "DEBUG": + logger.debug(message); + break; + case "INFO": + logger.info(message); + break; + case "ERROR": + logger.error(message, new IllegalStateException("failure")); + break; + default: + logger.warn("{}", message); + } + } + + @Override + public void put(String key, String value) { + MDC.put(key, value); + } + + @Override + public void clear() { + MDC.clear(); + } + + @Override + public void startAsync() { + AppenderBase sink = + new AppenderBase() { + @Override + protected void append(ILoggingEvent event) { + try { + if (release.await(5, TimeUnit.SECONDS)) { + deliveredContext = new HashMap<>(event.getMDCPropertyMap()); + delivered.countDown(); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + }; + sink.setContext(logger.getLoggerContext()); + sink.start(); + async = new AsyncAppender(); + async.setContext(logger.getLoggerContext()); + async.addAppender(sink); + async.start(); + logger.addAppender(async); + } + + @Override + public Map finishAsync() throws InterruptedException { + release.countDown(); + try { + if (!delivered.await(5, TimeUnit.SECONDS)) { + throw new AssertionError("AsyncAppender did not deliver"); + } + return deliveredContext; + } finally { + logger.detachAppender(async); + async.stop(); + } + } + + @Override + public void jul() { + java.util.logging.Logger jul = java.util.logging.Logger.getLogger(logger.getName()); + SLF4JBridgeHandler bridge = new SLF4JBridgeHandler(); + boolean parentHandlers = jul.getUseParentHandlers(); + jul.setUseParentHandlers(false); + jul.addHandler(bridge); + try { + jul.warning("bridged"); + } finally { + jul.removeHandler(bridge); + jul.setUseParentHandlers(parentHandlers); + bridge.close(); + } + } +} diff --git a/dd-java-agent/instrumentation/logback-1.0/src/test/java/LogbackTestClassLoader.java b/dd-java-agent/instrumentation/logback-1.0/src/test/java/LogbackTestClassLoader.java new file mode 100644 index 00000000000..ff59bcb761d --- /dev/null +++ b/dd-java-agent/instrumentation/logback-1.0/src/test/java/LogbackTestClassLoader.java @@ -0,0 +1,45 @@ +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +/** The harness boot-loads its logger; loads a separate application Logback and SLF4J. */ +public class LogbackTestClassLoader extends ClassLoader { + public LogbackTestClassLoader(ClassLoader parent) { + super(parent); + } + + @Override + protected synchronized Class loadClass(String name, boolean resolve) + throws ClassNotFoundException { + if (!name.startsWith("ch.qos.logback.") + && !name.startsWith("org.slf4j.") + && !name.startsWith("LogbackTestApplication")) { + return super.loadClass(name, resolve); + } + Class loaded = findLoadedClass(name); + if (loaded == null) { + byte[] bytes = readClass(name); + loaded = defineClass(name, bytes, 0, bytes.length); + } + if (resolve) { + resolveClass(loaded); + } + return loaded; + } + + private byte[] readClass(String name) throws ClassNotFoundException { + try (InputStream input = getResourceAsStream(name.replace('.', '/') + ".class")) { + if (input == null) { + throw new ClassNotFoundException(name); + } + ByteArrayOutputStream output = new ByteArrayOutputStream(); + byte[] buffer = new byte[8192]; + for (int read; (read = input.read(buffer)) != -1; ) { + output.write(buffer, 0, read); + } + return output.toByteArray(); + } catch (IOException e) { + throw new ClassNotFoundException(name, e); + } + } +} 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 f409301b9b2..f6a65371ec5 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 @@ -31,6 +31,8 @@ abstract class LogInjectionSmokeTest extends AbstractSmokeTest { static final int TIMEOUT_SECS = 30 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 @Shared File outputLogFile @@ -185,7 +187,7 @@ abstract class LogInjectionSmokeTest extends AbstractSmokeTest { } def supportsDirectLogSubmission() { - return backend() == LOG4J2_BACKEND + return backend() in DIRECT_LOG_SUBMISSION_BACKENDS } def supportsAppLogCollection() { @@ -596,7 +598,7 @@ class Log4j2InterfaceLog4j2LatestBackend extends Log4j2InterfaceLog4j2Backend {} class Slf4jInterfaceLogbackBackend extends LogInjectionSmokeTest { def backend() { - "Logback" + LOGBACK_BACKEND } } @@ -703,7 +705,7 @@ class JULInterfaceSlf4jToLogbackBackend extends LogInjectionSmokeTest { def propertiesFile = File.createTempFile("julConfig", ".properties") def backend() { - "Logback" + LOGBACK_BACKEND } def setupSpec() { @@ -730,7 +732,7 @@ class JULInterfaceSlf4jToLogbackLatestBackend extends JULInterfaceSlf4jToLogback class JCLInterfaceSlf4jToLogbackBackend extends LogInjectionSmokeTest { def backend() { - "Logback" + LOGBACK_BACKEND } } @@ -740,7 +742,7 @@ class JCLInterfaceSlf4jToLogbackLatestBackend extends JCLInterfaceSlf4jToLogback class Log4j1InterfaceSlf4jToLogbackBackend extends LogInjectionSmokeTest { def backend() { - "Logback" + LOGBACK_BACKEND } } @@ -750,7 +752,7 @@ class Log4j1InterfaceSlf4jToLogbackLatestBackend extends Log4j1InterfaceSlf4jToL class Log4j2InterfaceSlf4jToLogbackBackend extends LogInjectionSmokeTest { def backend() { - "Logback" + LOGBACK_BACKEND } } @@ -800,7 +802,7 @@ class JBossInterfaceLog4j2LatestBackend extends JBossInterfaceLog4j2Backend {} class JBossInterfaceSlf4jToLogbackBackend extends LogInjectionSmokeTest { def backend() { - "Logback" + LOGBACK_BACKEND } } @@ -814,7 +816,7 @@ class FloggerInterfaceJULBackend extends JULBackend {} class FloggerInterfaceSlf4jToLogbackBackend extends LogInjectionSmokeTest { def backend() { - "Logback" + LOGBACK_BACKEND } List additionalArguments() {