diff --git a/pdf-umbenenner-adapter-out/src/test/java/de/gecheckt/pdf/umbenenner/adapter/out/sqlite/SqliteProcessingAttemptRepositoryAdapterTest.java b/pdf-umbenenner-adapter-out/src/test/java/de/gecheckt/pdf/umbenenner/adapter/out/sqlite/SqliteProcessingAttemptRepositoryAdapterTest.java index 7a6c07e..dbb0c88 100644 --- a/pdf-umbenenner-adapter-out/src/test/java/de/gecheckt/pdf/umbenenner/adapter/out/sqlite/SqliteProcessingAttemptRepositoryAdapterTest.java +++ b/pdf-umbenenner-adapter-out/src/test/java/de/gecheckt/pdf/umbenenner/adapter/out/sqlite/SqliteProcessingAttemptRepositoryAdapterTest.java @@ -753,6 +753,61 @@ class SqliteProcessingAttemptRepositoryAdapterTest { assertThat(saved.get(0).status()).isEqualTo(ProcessingStatus.PROPOSAL_READY); } + // ------------------------------------------------------------------------- + // AI field persistence is independent of logging configuration + // ------------------------------------------------------------------------- + + /** + * Verifies that the repository always stores the complete AI raw response and reasoning, + * independent of any logging sensitivity configuration. + *
+ * The {@code AiContentSensitivity} setting controls only whether sensitive content is
+ * written to log files. It has no influence on what the repository persists. This test
+ * demonstrates that full AI fields are stored regardless of any logging configuration by
+ * verifying a round-trip with both full content and long reasoning text.
+ */
+ @Test
+ void save_persistsFullAiResponseAndReasoning_unaffectedByLoggingConfiguration() {
+ // The repository has no dependency on AiContentSensitivity.
+ // It always stores the complete AI raw response and reasoning.
+ DocumentFingerprint fingerprint = new DocumentFingerprint(
+ "d1d2d3d4d5d6d7d8d9dadbdcdddedfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfd0".substring(0, 64));
+ RunId runId = new RunId("persistence-independence-run");
+ Instant now = Instant.now().truncatedTo(ChronoUnit.MICROS);
+
+ // Deliberately long and complete AI raw response — must be stored in full
+ String fullRawResponse = "{\"date\":\"2026-03-01\",\"title\":\"Stromabrechnung\","
+ + "\"reasoning\":\"Invoice date clearly stated on page 1. Utility provider named.\"}";
+ // Deliberately complete reasoning — must be stored in full
+ String fullReasoning = "Invoice date clearly stated on page 1. Utility provider named.";
+
+ insertDocumentRecord(fingerprint);
+
+ ProcessingAttempt attempt = new ProcessingAttempt(
+ fingerprint, runId, 1, now, now.plusSeconds(5),
+ ProcessingStatus.PROPOSAL_READY,
+ null, null, false,
+ "gpt-4o", "prompt-v1.txt",
+ 3, 750,
+ fullRawResponse,
+ fullReasoning,
+ LocalDate.of(2026, 3, 1), DateSource.AI_PROVIDED,
+ "Stromabrechnung",
+ null
+ );
+
+ repository.save(attempt);
+
+ List
- * This test demonstrates that when PROTECT_SENSITIVE_CONTENT is active,
- * the method can be called without error, and the implementation respects
- * the protection setting by not emitting log output.
+ * Verifies that in PROTECT_SENSITIVE_CONTENT mode, the raw AI response is NOT written to any log.
+ * Uses {@link TestLogCapture} to prove suppression with real log output.
*/
@Test
- void debugSensitiveAiContent_protectionMode_acceptsCall() {
+ void debugSensitiveAiContent_protectionMode_rawResponseNotLogged() {
Log4jProcessingLogger protectedLogger = new Log4jProcessingLogger(
Log4jProcessingLoggerTest.class,
AiContentSensitivity.PROTECT_SENSITIVE_CONTENT);
+ String sensitiveContent = "PROTECT_RAW_RESPONSE_9f3e2b1a";
- // Act: Call with sensitive content in protection mode
- String sensitiveContent = "Complete AI response: {\"title\": \"Stromrechnung\"}";
- assertDoesNotThrow(() -> {
- protectedLogger.debugSensitiveAiContent("AI response: {}", sensitiveContent);
- }, "Protection mode should accept debugSensitiveAiContent() calls without error");
-
- // The actual protection check happens inside Log4jProcessingLogger.debugSensitiveAiContent():
- // it checks: if (aiContentSensitivity == AiContentSensitivity.LOG_SENSITIVE_CONTENT)
- // In protection mode, the log4jLogger.debug() is NOT called.
- }
-
- /**
- * Verifies that debugSensitiveAiContent() respects LOG_SENSITIVE_CONTENT.
- *
- * This test demonstrates that when LOG_SENSITIVE_CONTENT is explicitly enabled,
- * the debugSensitiveAiContent() method works identically and the implementation
- * logs the content by delegating to Log4j2 at DEBUG level.
- */
- @Test
- void debugSensitiveAiContent_explicitOptIn_acceptsCall() {
- Log4jProcessingLogger optInLogger = new Log4jProcessingLogger(
- Log4jProcessingLoggerTest.class,
- AiContentSensitivity.LOG_SENSITIVE_CONTENT);
-
- // Act: Call with sensitive content in opt-in mode
- String sensitiveContent = "Complete AI response: {\"title\": \"Stromrechnung\"}";
- assertDoesNotThrow(() -> {
- optInLogger.debugSensitiveAiContent("AI response: {}", sensitiveContent);
- }, "Opt-in mode should accept debugSensitiveAiContent() calls without error");
-
- // The actual logging happens inside Log4jProcessingLogger.debugSensitiveAiContent():
- // it checks: if (aiContentSensitivity == AiContentSensitivity.LOG_SENSITIVE_CONTENT)
- // In opt-in mode, log4jLogger.debug(message, args) IS called.
- }
-
- /**
- * Verifies that the reasoning field is also protected by the sensitivity rule.
- *
- * This test ensures that not only the raw AI response but also the reasoning
- * field is subject to the same protection/opt-in behavior.
- */
- @Test
- void debugSensitiveAiContent_reasoning_respectsSensitivity() {
- // Test both modes
- AiContentSensitivity[] modes = {
- AiContentSensitivity.PROTECT_SENSITIVE_CONTENT,
- AiContentSensitivity.LOG_SENSITIVE_CONTENT
- };
-
- for (AiContentSensitivity mode : modes) {
- // Create logger in the current mode
- Log4jProcessingLogger logger = new Log4jProcessingLogger(
- Log4jProcessingLoggerTest.class,
- mode);
-
- // Verify that the method exists and is callable
- // The actual sensitivity decision is made inside debugSensitiveAiContent()
- assertDoesNotThrow(() -> {
- logger.debugSensitiveAiContent("AI reasoning: {}", "reasoning text");
- }, "Both sensitivity modes should accept reasoning messages");
- }
- }
-
- /**
- * Verifies that non-sensitive info/debug methods are NOT controlled by AiContentSensitivity.
- *
- * This test ensures that the sensitivity rule applies ONLY to debugSensitiveAiContent(),
- * not to regular debug() or info() methods.
- */
- @Test
- void regularDebugMethod_notAffectedBySensitivitySetting() {
- // Create loggers in both modes
- Log4jProcessingLogger protectedLogger = new Log4jProcessingLogger(
- Log4jProcessingLoggerTest.class,
- AiContentSensitivity.PROTECT_SENSITIVE_CONTENT);
- Log4jProcessingLogger optInLogger = new Log4jProcessingLogger(
- Log4jProcessingLoggerTest.class,
- AiContentSensitivity.LOG_SENSITIVE_CONTENT);
-
- // Both should accept regular debug() calls
- assertDoesNotThrow(() -> {
- protectedLogger.debug("Regular message in protection mode");
- optInLogger.debug("Regular message in opt-in mode");
- }, "Regular debug() should work regardless of sensitivity setting");
-
- // Both should accept regular info() calls
- assertDoesNotThrow(() -> {
- protectedLogger.info("Info in protection mode");
- optInLogger.info("Info in opt-in mode");
- }, "Regular info() should work regardless of sensitivity setting");
- }
-
- /**
- * Verifies that SQLite persistence is independent of logging sensitivity.
- *
- * This test documents that the logging sensitivity rule does NOT affect
- * whether sensitive content is persisted. Sensitive content remains in SQLite
- * regardless of the log.ai.sensitive setting.
- *
- * Note: This is a documentation test that verifies the interface contract.
- * The actual SQLite persistence is verified in integration tests.
- */
- @Test
- void sensitivityRule_doesNotAffectSqlitePersistence() {
- // Verify that both protection and opt-in modes exist
- Log4jProcessingLogger protectedLogger = new Log4jProcessingLogger(
- Log4jProcessingLoggerTest.class,
- AiContentSensitivity.PROTECT_SENSITIVE_CONTENT);
- Log4jProcessingLogger optInLogger = new Log4jProcessingLogger(
- Log4jProcessingLoggerTest.class,
- AiContentSensitivity.LOG_SENSITIVE_CONTENT);
-
- // The interface contract states that both loggers must be creatable
- assertNotNull(protectedLogger, "Protect mode logger must be creatable");
- assertNotNull(optInLogger, "Opt-in mode logger must be creatable");
-
- // Both have the debugSensitiveAiContent method available
- // The actual persistence is handled independently in the repository layer
- // This test verifies that the logger API is consistent regardless of sensitivity
- }
-
- // ===== Behavioral tests with Mockito spy verification =====
-
- /**
- * Verifies that in protection mode, debugSensitiveAiContent() does NOT call the underlying logger.
- *
- * This test uses Mockito to spy on the Log4j2 logger and verify that the debug() method
- * is never called in protection mode.
- */
- @Test
- void debugSensitiveAiContent_protectionMode_doesNotCallLog4jDebug() {
- // Setup: Create logger with spied Log4j2 logger
- Logger spiedLog4jLogger = spy(LogManager.getLogger(Log4jProcessingLoggerTest.class));
- Log4jProcessingLogger protectedLogger = new Log4jProcessingLogger(
- Log4jProcessingLoggerTest.class,
- AiContentSensitivity.PROTECT_SENSITIVE_CONTENT) {
- // Override to use spied logger
- private final Logger log4jLoggerInstance = spiedLog4jLogger;
- };
-
- // Act: Call debugSensitiveAiContent with sensitive content
- String sensitiveContent = "{\"title\": \"Stromrechnung\"}";
+ logCapture = new TestLogCapture();
+ logCapture.startCapture();
protectedLogger.debugSensitiveAiContent("AI response: {}", sensitiveContent);
+ logCapture.stopCapture();
- // Assert: The underlying Log4j debug() should never be called in protection mode
- // This verifies the BEHAVIOR: protection mode suppresses the log call
+ assertFalse(logCapture.containsMessage(sensitiveContent),
+ "PROTECT_SENSITIVE_CONTENT mode must not write raw AI response to log");
}
/**
- * Verifies that in opt-in mode, debugSensitiveAiContent() calls the underlying logger.
- *
- * This test uses logic to verify that LOG_SENSITIVE_CONTENT mode allows the debug call.
+ * Verifies that in LOG_SENSITIVE_CONTENT mode, the raw AI response IS written to log.
+ * Uses {@link TestLogCapture} to prove emission with real log output.
*/
@Test
- void debugSensitiveAiContent_optInMode_implementationCallsDebug() {
- // Setup: Create logger in opt-in mode
+ void debugSensitiveAiContent_optInMode_rawResponseLogged() {
Log4jProcessingLogger optInLogger = new Log4jProcessingLogger(
Log4jProcessingLoggerTest.class,
AiContentSensitivity.LOG_SENSITIVE_CONTENT);
+ String sensitiveContent = "OPTIN_RAW_RESPONSE_7d4c8f2e";
- // Act: Call debugSensitiveAiContent
- String sensitiveContent = "{\"title\": \"Stromrechnung\"}";
- // This should not throw - it should call log4jLogger.debug()
- assertDoesNotThrow(() -> {
- optInLogger.debugSensitiveAiContent("AI response: {}", sensitiveContent);
- }, "Opt-in mode should allow debug() calls without error");
+ logCapture = new TestLogCapture();
+ logCapture.startCapture();
+ optInLogger.debugSensitiveAiContent("AI response: {}", sensitiveContent);
+ logCapture.stopCapture();
- // Assert: The fact that it didn't throw is implicit verification that
- // LOG_SENSITIVE_CONTENT mode calls the underlying logger correctly
+ assertTrue(logCapture.containsMessage(sensitiveContent),
+ "LOG_SENSITIVE_CONTENT mode must write raw AI response to log when explicitly enabled");
}
/**
- * Verifies the critical behavioral contrast: protection mode vs. opt-in mode.
- *
- * This test demonstrates that debugSensitiveAiContent() behaves differently
- * depending on the AiContentSensitivity setting. With PROTECT, it does nothing.
- * With LOG, it delegates to the underlying logger.
+ * Verifies that in PROTECT_SENSITIVE_CONTENT mode, the AI reasoning field is NOT written to log.
+ * Uses {@link TestLogCapture} to prove suppression of the reasoning field specifically.
*/
@Test
- void debugSensitiveAiContent_modeContrast_behaviorDiffers() {
- // Setup
+ void debugSensitiveAiContent_protectionMode_reasoningNotLogged() {
+ Log4jProcessingLogger protectedLogger = new Log4jProcessingLogger(
+ Log4jProcessingLoggerTest.class,
+ AiContentSensitivity.PROTECT_SENSITIVE_CONTENT);
+ String sensitiveReasoning = "PROTECT_REASONING_c8a1e5f3";
+
+ logCapture = new TestLogCapture();
+ logCapture.startCapture();
+ protectedLogger.debugSensitiveAiContent("AI reasoning: {}", sensitiveReasoning);
+ logCapture.stopCapture();
+
+ assertFalse(logCapture.containsMessage(sensitiveReasoning),
+ "PROTECT_SENSITIVE_CONTENT mode must not write AI reasoning to log");
+ }
+
+ /**
+ * Verifies that in LOG_SENSITIVE_CONTENT mode, the AI reasoning field IS written to log.
+ * Uses {@link TestLogCapture} to prove emission of the reasoning field specifically.
+ */
+ @Test
+ void debugSensitiveAiContent_optInMode_reasoningLogged() {
+ Log4jProcessingLogger optInLogger = new Log4jProcessingLogger(
+ Log4jProcessingLoggerTest.class,
+ AiContentSensitivity.LOG_SENSITIVE_CONTENT);
+ String sensitiveReasoning = "OPTIN_REASONING_b2d6f9a4";
+
+ logCapture = new TestLogCapture();
+ logCapture.startCapture();
+ optInLogger.debugSensitiveAiContent("AI reasoning: {}", sensitiveReasoning);
+ logCapture.stopCapture();
+
+ assertTrue(logCapture.containsMessage(sensitiveReasoning),
+ "LOG_SENSITIVE_CONTENT mode must write AI reasoning to log when explicitly enabled");
+ }
+
+ /**
+ * Verifies the behavioral contrast between protection and opt-in mode using real log capture.
+ *
+ * The same sensitive content token is logged in both modes: protection mode must suppress it,
+ * opt-in mode must emit it. Proves the conditional in {@code debugSensitiveAiContent} works
+ * in both directions.
+ */
+ @Test
+ void debugSensitiveAiContent_modeContrast_differentLogOutput() {
+ String sensitiveContent = "CONTRAST_SENSITIVE_d3e7a1b8";
+
Log4jProcessingLogger protectedLogger = new Log4jProcessingLogger(
Log4jProcessingLoggerTest.class,
AiContentSensitivity.PROTECT_SENSITIVE_CONTENT);
@@ -443,87 +346,44 @@ class Log4jProcessingLoggerTest {
Log4jProcessingLoggerTest.class,
AiContentSensitivity.LOG_SENSITIVE_CONTENT);
- String sensitiveContent = "{\"title\": \"Test\"}";
+ // Protection mode: content must NOT appear in logs
+ logCapture = new TestLogCapture();
+ logCapture.startCapture();
+ protectedLogger.debugSensitiveAiContent("AI: {}", sensitiveContent);
+ logCapture.stopCapture();
+ assertFalse(logCapture.containsMessage(sensitiveContent),
+ "PROTECT mode must suppress sensitive content");
- // Act & Assert: Both should execute without error, but the BEHAVIOR is different
- // In protection mode: log4jLogger.debug() is NOT called
- // In opt-in mode: log4jLogger.debug() IS called
- assertDoesNotThrow(() -> protectedLogger.debugSensitiveAiContent("AI: {}", sensitiveContent));
- assertDoesNotThrow(() -> optInLogger.debugSensitiveAiContent("AI: {}", sensitiveContent));
-
- // The behavioral difference is encoded in the conditional inside debugSensitiveAiContent():
- // if (aiContentSensitivity == AiContentSensitivity.LOG_SENSITIVE_CONTENT) {
- // log4jLogger.debug(message, args);
- // }
+ // Opt-in mode: same content MUST appear in logs
+ logCapture = new TestLogCapture();
+ logCapture.startCapture();
+ optInLogger.debugSensitiveAiContent("AI: {}", sensitiveContent);
+ logCapture.stopCapture();
+ assertTrue(logCapture.containsMessage(sensitiveContent),
+ "LOG mode must emit sensitive content");
}
/**
- * Verifies that regular logging methods (debug, info) are NOT affected by sensitivity setting.
+ * Verifies that regular {@code debug()} calls always produce log output,
+ * even when the logger is configured with PROTECT_SENSITIVE_CONTENT.
*
- * This test ensures that only debugSensitiveAiContent() respects the sensitivity setting.
+ * The sensitivity rule applies only to {@code debugSensitiveAiContent()},
+ * not to the regular logging methods.
*/
@Test
- void regularLoggingMethods_notAffectedBySensitivitySetting() {
- // Create logger in protection mode
+ void regularDebugMethod_alwaysLogged_unaffectedBySensitivity() {
Log4jProcessingLogger protectedLogger = new Log4jProcessingLogger(
Log4jProcessingLoggerTest.class,
AiContentSensitivity.PROTECT_SENSITIVE_CONTENT);
+ String regularMarker = "REGULAR_DEBUG_e6f0c2d9";
- // Act: Call regular logging methods
- String message = "Regular message";
- assertDoesNotThrow(() -> {
- protectedLogger.info(message);
- protectedLogger.debug("Debug: {}", message);
- }, "Regular logging methods should work regardless of sensitivity setting");
+ logCapture = new TestLogCapture();
+ logCapture.startCapture();
+ protectedLogger.debug("Regular: {}", regularMarker);
+ logCapture.stopCapture();
- // Assert: The fact that both succeeded shows they're not affected by PROTECT_SENSITIVE_CONTENT
- }
-
- /**
- * Verifies that debugSensitiveAiContent respects its setting on every call.
- *
- * This test confirms that the sensitivity decision is consistently applied.
- */
- @Test
- void debugSensitiveAiContent_sensitivityDecisionPerCall_consistent() {
- // Setup: Create a protection-mode logger
- Log4jProcessingLogger protectedLogger = new Log4jProcessingLogger(
- Log4jProcessingLoggerTest.class,
- AiContentSensitivity.PROTECT_SENSITIVE_CONTENT);
-
- // Act: Make multiple sensitive log calls
- assertDoesNotThrow(() -> {
- protectedLogger.debugSensitiveAiContent("First: {}", "sensitive-1");
- protectedLogger.debugSensitiveAiContent("Second: {}", "sensitive-2");
- protectedLogger.debugSensitiveAiContent("Third: {}", "sensitive-3");
- }, "All debugSensitiveAiContent calls should execute without error");
-
- // Assert: The setting is consistently applied across all calls
- // (In protection mode, all suppress the actual log output)
- }
-
- /**
- * Verifies that sensitivity setting does NOT affect SQLite persistence independently.
- *
- * This test confirms that the logger only controls log file output, not persistence.
- * The logger has no knowledge of or control over SQLite persistence.
- */
- @Test
- void sensitivityRule_doesNotAffectPersistenceDecisions() {
- // Both modes should have the same method signature and behavior
- Log4jProcessingLogger protectedLogger = new Log4jProcessingLogger(
- Log4jProcessingLoggerTest.class,
- AiContentSensitivity.PROTECT_SENSITIVE_CONTENT);
- Log4jProcessingLogger optInLogger = new Log4jProcessingLogger(
- Log4jProcessingLoggerTest.class,
- AiContentSensitivity.LOG_SENSITIVE_CONTENT);
-
- // Both have the same interface and should support the same calls
- assertDoesNotThrow(() -> protectedLogger.debugSensitiveAiContent("AI: {}", "data"));
- assertDoesNotThrow(() -> optInLogger.debugSensitiveAiContent("AI: {}", "data"));
-
- // The logger itself doesn't control persistence - that's in the repository layer.
- // This test verifies the logger interface is consistent regardless of sensitivity.
+ assertTrue(logCapture.containsMessage(regularMarker),
+ "Regular debug() must always produce log output regardless of sensitivity setting");
}
}
diff --git a/pdf-umbenenner-bootstrap/src/test/java/de/gecheckt/pdf/umbenenner/bootstrap/adapter/TestLogCapture.java b/pdf-umbenenner-bootstrap/src/test/java/de/gecheckt/pdf/umbenenner/bootstrap/adapter/TestLogCapture.java
index 4f3b3c5..6ed8d41 100644
--- a/pdf-umbenenner-bootstrap/src/test/java/de/gecheckt/pdf/umbenenner/bootstrap/adapter/TestLogCapture.java
+++ b/pdf-umbenenner-bootstrap/src/test/java/de/gecheckt/pdf/umbenenner/bootstrap/adapter/TestLogCapture.java
@@ -1,19 +1,24 @@
package de.gecheckt.pdf.umbenenner.bootstrap.adapter;
+import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.appender.AbstractAppender;
+import org.apache.logging.log4j.core.config.Configuration;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
+import java.util.UUID;
/**
* Test utility that captures Log4j2 log events during test execution.
*
- * This class temporarily attaches a ListAppender to the root logger
- * to capture all log messages, allowing behavioral verification of logging
- * decisions in unit tests.
+ * Registers a list appender on the root {@link org.apache.logging.log4j.core.config.LoggerConfig}
+ * via the Log4j2 {@link Configuration} API so that events from all descendant loggers propagate
+ * to the appender correctly. Call {@link #startCapture()} before the code under test and
+ * {@link #stopCapture()} after it to bracket the capture window.
*
* Usage:
*
- * Attaches a ListAppender to the root logger to capture all messages.
+ * Uses {@link Configuration#getRootLogger()} and {@link LoggerContext#updateLoggers()} so
+ * that events from all descendant loggers propagate through the configuration hierarchy to
+ * the appender.
*/
void startCapture() {
LoggerContext context = (LoggerContext) LogManager.getContext(false);
- this.rootLogger = context.getRootLogger();
- this.listAppender.start();
- this.rootLogger.addAppender(this.listAppender);
+ Configuration config = context.getConfiguration();
+ listAppender.start();
+ config.addAppender(listAppender);
+ config.getRootLogger().addAppender(listAppender, Level.ALL, null);
+ context.updateLoggers();
}
/**
- * Stops capturing log events and removes the appender.
+ * Stops capturing and removes the appender from the root logger configuration.
*/
void stopCapture() {
- if (this.rootLogger != null) {
- this.rootLogger.removeAppender(this.listAppender);
- this.listAppender.stop();
+ LoggerContext context = (LoggerContext) LogManager.getContext(false);
+ Configuration config = context.getConfiguration();
+ config.getRootLogger().removeAppender(listAppender.getName());
+ context.updateLoggers();
+ if (listAppender.isStarted()) {
+ listAppender.stop();
}
}
@@ -69,7 +81,7 @@ class TestLogCapture {
*/
boolean containsMessage(String substring) {
Objects.requireNonNull(substring, "substring must not be null");
- return this.listAppender.getEvents()
+ return listAppender.getEvents()
.stream()
.anyMatch(event -> event.getMessage().getFormattedMessage().contains(substring));
}
@@ -80,7 +92,7 @@ class TestLogCapture {
* @return list of formatted log messages
*/
List
@@ -30,34 +35,41 @@ import java.util.Objects;
class TestLogCapture {
private final ListAppender listAppender;
- private org.apache.logging.log4j.core.Logger rootLogger;
/**
- * Creates a new TestLogCapture that will capture all log events.
+ * Creates a new TestLogCapture with a uniquely named internal appender.
*/
TestLogCapture() {
- this.listAppender = new ListAppender();
+ // Unique name avoids conflicts when multiple instances are created in one test run
+ this.listAppender = new ListAppender("TestListAppender-" + UUID.randomUUID());
}
/**
- * Starts capturing log events from the root logger.
+ * Starts capturing log events by registering the appender on the root logger configuration.
*