diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 9f5843e..0b914a4 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -7,4 +7,7 @@ jobs:
build:
uses: ./.github/workflows/run-with-maven.yml
with:
- COMMAND: mvn --batch-mode -Dmaven.compiler.showDeprecation=true -Dmaven.compiler.showWarnings=true clean verify
+ COMMAND: >
+ mvn --batch-mode -Dmaven.compiler.showDeprecation=true -Dmaven.compiler.showWarnings=true -Dproject.version=0.0.0-SNAPSHOT clean install
+ && cd project-without-logstash
+ && mvn --batch-mode -Dlog-capture.version=0.0.0-SNAPSHOT clean verify
diff --git a/README.md b/README.md
index a6b6314..d66f95f 100644
--- a/README.md
+++ b/README.md
@@ -139,6 +139,26 @@ log.info("did something");
logCapture.info("did something", logger("com.acme.foo"));
```
+#### Key-Value (from Logstash)
+
+**Note that** this will only work if logstash-logback-encoder is in your classpath - log-capture does not depend on it, so you need to add it manually if you intend to use it.
+
+```java
+import static de.dm.infrastructure.logcapture.ExpectedKeyValue.keyValue;
+
+...
+
+log.info("hello",
+ StructuredArguments.keyValue("myString", "hello"),
+ StructuredArguments.keyValue("myNumber", 42)
+);
+
+logCapture.assertLogged(info("hello",
+ keyValue("myString", "hello"),
+ keyValue("myNumber", 42))
+);
+```
+
### Examples
#### Unit Test Example:
@@ -306,6 +326,12 @@ And with MDC logging context
## Changes
+### 3.5.0
+
+* Added new Log Event Matcher: `ExpectedKeyValue.keyValue(...)` to assert `StructuredArguments.keyValue(...)` from Logstash
+* Improved readability for assertion errors when using `assertNotLogged(...)`
+* Updated dependencies
+
### 3.4.1
* Improved Javadoc for deprecated methods
diff --git a/pom.xml b/pom.xml
index 60a64f2..8dabf98 100644
--- a/pom.xml
+++ b/pom.xml
@@ -29,15 +29,17 @@
- 3.4.1-SNAPSHOT
+ 3.5.0-SNAPSHOT
1.8
- 1.18.20
- 1.2.5
- 5.7.2
+ 1.18.22
+ 1.2.11
+ 7.0.1
+ 5.8.2
UTF-8
- 3.20.1
+ 0.23.1
+ 3.22.0
8.45.1
0.8.7
2.22.2
@@ -47,16 +49,11 @@
3.9.0
2.5.3
3.0.1
+ 4.4.0
1.6.8
-
- org.projectlombok
- lombok
- ${lombok.version}
- provided
-
ch.qos.logback
logback-classic
@@ -72,12 +69,34 @@
junit-jupiter-engine
${junit.version}
+
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+ provided
+
+
+ net.logstash.logback
+ logstash-logback-encoder
+ ${logstash-logback-encoder.version}
+ provided
+
+
+
org.assertj
assertj-core
${assertj-core.version}
test
+
+ com.tngtech.archunit
+ archunit-junit5
+ ${archunit.version}
+ test
+
diff --git a/project-without-logstash/.gitignore b/project-without-logstash/.gitignore
new file mode 100644
index 0000000..84d100c
--- /dev/null
+++ b/project-without-logstash/.gitignore
@@ -0,0 +1,9 @@
+/target/
+
+### NetBeans ###
+/nbproject/private/
+/build/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
diff --git a/project-without-logstash/pom.xml b/project-without-logstash/pom.xml
new file mode 100644
index 0000000..4813890
--- /dev/null
+++ b/project-without-logstash/pom.xml
@@ -0,0 +1,87 @@
+
+
+ 4.0.0
+
+ de.acme
+ test-project-without-logstash
+ 0.0.1-SNAPSHOT
+
+
+
+ MIT
+ https://opensource.org/licenses/MIT
+ repo
+
+
+
+
+ 1.8
+ UTF-8
+ 3.4.1-SNAPSHOT
+
+ 1.18.22
+ 1.2.11
+ 5.8.2
+ 3.22.0
+
+ 2.22.2
+ 3.9.0
+
+
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+ provided
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ ${junit.version}
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ ${junit.version}
+ test
+
+
+ de.dm.infrastructure
+ log-capture
+ ${log-capture.version}
+
+
+ org.assertj
+ assertj-core
+ ${assertj-core.version}
+ test
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${maven-compiler-plugin.version}
+
+
+ ${java.version}
+ ${encoding}
+ true
+ true
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ ${maven-surefire-plugin.version}
+
+
+
+
diff --git a/project-without-logstash/src/test/java/com/acme/LogCaptureTest.java b/project-without-logstash/src/test/java/com/acme/LogCaptureTest.java
new file mode 100644
index 0000000..3416887
--- /dev/null
+++ b/project-without-logstash/src/test/java/com/acme/LogCaptureTest.java
@@ -0,0 +1,34 @@
+package com.acme;
+
+import de.dm.infrastructure.logcapture.LogCapture;
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import static de.dm.infrastructure.logcapture.ExpectedKeyValue.keyValue;
+import static de.dm.infrastructure.logcapture.LogExpectation.info;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+@Slf4j
+public class LogCaptureTest {
+ @RegisterExtension
+ LogCapture logCapture = LogCapture.forCurrentPackage();
+
+ @Test
+ void assertionWorksDespiteLogstashNotBeingInTheClasspath() {
+ log.info("hello");
+ logCapture.assertLogged(info("hello"));
+ }
+
+ @Test
+ void errorMessageForKeyValueAssertion() {
+ log.info("hello");
+
+ IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () ->
+ logCapture.assertLogged(info("hello", keyValue("key", "value"))));
+
+ assertThat(thrown).hasMessage("keyValue cannot be used for log assertions if logstash-logback-encoder " +
+ "that provides StructuredArguments.keyValue(...) is not in the classpath.");
+ }
+}
diff --git a/src/main/java/de/dm/infrastructure/logcapture/CapturingAppender.java b/src/main/java/de/dm/infrastructure/logcapture/CapturingAppender.java
index 84daf89..2c99ea4 100644
--- a/src/main/java/de/dm/infrastructure/logcapture/CapturingAppender.java
+++ b/src/main/java/de/dm/infrastructure/logcapture/CapturingAppender.java
@@ -43,6 +43,7 @@ public synchronized void doAppend(ILoggingEvent loggingEvent) {
.mdcData(loggingEvent.getMDCPropertyMap())
.loggedException(getLoggedException(loggingEvent.getThrowableProxy()))
.marker(loggingEvent.getMarker())
+ .argumentArray(loggingEvent.getArgumentArray())
.build());
}
}
diff --git a/src/main/java/de/dm/infrastructure/logcapture/ExpectedException.java b/src/main/java/de/dm/infrastructure/logcapture/ExpectedException.java
index 8ab23dc..03f1f69 100644
--- a/src/main/java/de/dm/infrastructure/logcapture/ExpectedException.java
+++ b/src/main/java/de/dm/infrastructure/logcapture/ExpectedException.java
@@ -51,8 +51,8 @@ public String getNonMatchingErrorMessage(LoggedEvent loggedEvent) {
}
@Override
- public String getMatchingErrorMessage() {
- return format("not expected exception was found: %s", this);
+ public String getMatcherDetailDescription() {
+ return format("Exception: %s", this);
}
private static String loggedExceptionToString(Optional optionalException) {
@@ -70,7 +70,7 @@ private static String loggedExceptionToString(Optional argument instanceof SingleFieldAppendingMarker ? Stream.of((SingleFieldAppendingMarker) argument) : Stream.empty())
+ .anyMatch(marker -> expectedValue.equals(marker.getFieldValue()) && marker.getFieldName().equals(expectedKey));
+ }
+
+ static String getNonMatchingErrorMessage(LoggedEvent loggedEvent, String expectedKey, String expectedValue) {
+ String actualKeyValue = Arrays.stream(loggedEvent.getArgumentArray())
+ .flatMap(argument -> argument instanceof SingleFieldAppendingMarker ? Stream.of((SingleFieldAppendingMarker) argument) : Stream.empty())
+ .map(marker -> format(" key: \"%s\", value: \"%s\"", marker.getFieldName(), marker.getFieldValue()))
+ .collect(Collectors.joining(lineSeparator()));
+
+ return format(" expected key-value content: key: \"%s\", value: \"%s\"", expectedKey, expectedValue) +
+ lineSeparator() +
+ (actualKeyValue.isEmpty() ? " but no key-value content was found" : " actual key-value content:" + lineSeparator() + actualKeyValue);
+
+ }
+
+}
diff --git a/src/main/java/de/dm/infrastructure/logcapture/ExpectedLoggerName.java b/src/main/java/de/dm/infrastructure/logcapture/ExpectedLoggerName.java
index 7ad92f1..3b860f3 100644
--- a/src/main/java/de/dm/infrastructure/logcapture/ExpectedLoggerName.java
+++ b/src/main/java/de/dm/infrastructure/logcapture/ExpectedLoggerName.java
@@ -30,13 +30,13 @@ public String getNonMatchingErrorMessage(LoggedEvent loggedEvent) {
}
@Override
- public String getMatcherDescription() {
+ public String getMatcherTypeDescription() {
return "logger name";
}
@Override
- public String getMatchingErrorMessage() {
- return format("not expected logger name (regex) was found: \"%s\"", inputRegex);
+ public String getMatcherDetailDescription() {
+ return format("logger name (regex): \"%s\"", inputRegex);
}
/**
diff --git a/src/main/java/de/dm/infrastructure/logcapture/ExpectedMarker.java b/src/main/java/de/dm/infrastructure/logcapture/ExpectedMarker.java
index 6e5a7e6..f032fce 100644
--- a/src/main/java/de/dm/infrastructure/logcapture/ExpectedMarker.java
+++ b/src/main/java/de/dm/infrastructure/logcapture/ExpectedMarker.java
@@ -29,13 +29,13 @@ public String getNonMatchingErrorMessage(LoggedEvent loggedEvent) {
}
@Override
- public String getMatcherDescription() {
+ public String getMatcherTypeDescription() {
return "marker name";
}
@Override
- public String getMatchingErrorMessage() {
- return format("not expected marker name: \"%s\" was found", expectedName);
+ public String getMatcherDetailDescription() {
+ return format("marker name: \"%s\"", expectedName);
}
/**
diff --git a/src/main/java/de/dm/infrastructure/logcapture/ExpectedMdcEntry.java b/src/main/java/de/dm/infrastructure/logcapture/ExpectedMdcEntry.java
index ce8ef8a..14b86ce 100644
--- a/src/main/java/de/dm/infrastructure/logcapture/ExpectedMdcEntry.java
+++ b/src/main/java/de/dm/infrastructure/logcapture/ExpectedMdcEntry.java
@@ -70,14 +70,13 @@ public String getNonMatchingErrorMessage(LoggedEvent loggedEvent) {
}
@Override
- public String getMatcherDescription() {
+ public String getMatcherTypeDescription() {
return "MDC value";
}
@Override
- public String getMatchingErrorMessage() {
-
- return format("not expected MDCValue with key was found: \"%s\"", key);
+ public String getMatcherDetailDescription() {
+ return format("MDCValue with key: \"%s\"", key);
}
/**
diff --git a/src/main/java/de/dm/infrastructure/logcapture/LogAsserter.java b/src/main/java/de/dm/infrastructure/logcapture/LogAsserter.java
index 92211dd..e9f79a0 100644
--- a/src/main/java/de/dm/infrastructure/logcapture/LogAsserter.java
+++ b/src/main/java/de/dm/infrastructure/logcapture/LogAsserter.java
@@ -49,8 +49,8 @@ public NothingElseLoggedAsserter assertLoggedInAnyOrder(LogExpectation... logExp
"Imprecise matching: Two log expectations have matched the same message. " +
"Use more precise matching or in-order matching. " +
"(First match: %s | Second match: %s",
- getLevelAndRegexExpectation(previousMatch.level, previousMatch.regex, previousMatch.logEventMatchers),
- getLevelAndRegexExpectation(assertion.level, assertion.regex, assertion.logEventMatchers)));
+ getDescriptionForUnwantedLogMessage(previousMatch.level, previousMatch.regex, previousMatch.logEventMatchers),
+ getDescriptionForUnwantedLogMessage(assertion.level, assertion.regex, assertion.logEventMatchers)));
}
matches.put(lastCapturedLogEvent.lastAssertedLogMessageIndex, assertion);
}
@@ -180,7 +180,7 @@ Integer assertCapturedNext(Optional level, Optional regex, int st
if (eventMatchingWithoutAdditionalMatchers != null) {
throwAssertionForPartiallyMatchingLoggedEvent(level, regex, eventMatchingWithoutAdditionalMatchers, logEventMatchers);
}
- throw new AssertionError(format("Expected log message has not occurred: %s", getLevelAndRegexExpectation(level, regex, logEventMatchers)));
+ throw new AssertionError(format("Expected log message has not occurred: %s", getDescriptionForExpectedMessage(level, regex)));
}
void assertNotCaptured(Optional level, Optional regex, List logEventMatchers) {
@@ -189,7 +189,7 @@ void assertNotCaptured(Optional level, Optional regex, List level, Optional regex) {
+ private static String getDescriptionForExpectedMessage(Optional level, Optional regex) {
if (!level.isPresent() && !regex.isPresent()) {
return "";
@@ -241,10 +241,11 @@ private static String getLevelAndRegexExpectation(Optional level, Optiona
(regex.map(s -> "Regex: \"" + s + "\"").orElse(""));
}
- private static String getLevelAndRegexExpectation(Optional level, Optional regex, List matchers) {
+ private static String getDescriptionForUnwantedLogMessage(Optional level, Optional regex, List matchers) {
String matchersText = "";
if (!matchers.isEmpty()) {
- matchersText = ", with matchers: " + matchers.stream().map(LogEventMatcher::getMatchingErrorMessage).collect(Collectors.joining(", "));
+ matchersText = ", with matchers:" + lineSeparator() + " " + matchers.stream().map(LogEventMatcher::getMatcherDetailDescription)
+ .collect(Collectors.joining(lineSeparator() + " "));
}
if (!level.isPresent() && !regex.isPresent()) {
return "" + matchersText;
diff --git a/src/main/java/de/dm/infrastructure/logcapture/LogEventMatcher.java b/src/main/java/de/dm/infrastructure/logcapture/LogEventMatcher.java
index fb17635..8e842f8 100644
--- a/src/main/java/de/dm/infrastructure/logcapture/LogEventMatcher.java
+++ b/src/main/java/de/dm/infrastructure/logcapture/LogEventMatcher.java
@@ -27,14 +27,14 @@ public interface LogEventMatcher {
*
* @return matched aspect
*/
- String getMatcherDescription();
+ String getMatcherTypeDescription();
/**
- * returns an error message describing why a logged event does match, even of it shouldn't.
+ * returns an error message describing the concrete matcher, including as much of its expectations as possible
*
* @return matched aspect
*/
- String getMatchingErrorMessage();
+ String getMatcherDetailDescription();
}
diff --git a/src/main/java/de/dm/infrastructure/logcapture/LoggedEvent.java b/src/main/java/de/dm/infrastructure/logcapture/LoggedEvent.java
index 6510870..662688b 100644
--- a/src/main/java/de/dm/infrastructure/logcapture/LoggedEvent.java
+++ b/src/main/java/de/dm/infrastructure/logcapture/LoggedEvent.java
@@ -21,6 +21,7 @@ class LoggedEvent {
private final Optional loggedException;
private final String loggerName;
private final Marker marker;
+ private final Object[] argumentArray;
@AllArgsConstructor(access = PRIVATE)
@Builder
diff --git a/src/test/java/com/example/app/LogstashKeyValueTest.java b/src/test/java/com/example/app/LogstashKeyValueTest.java
new file mode 100644
index 0000000..d48a480
--- /dev/null
+++ b/src/test/java/com/example/app/LogstashKeyValueTest.java
@@ -0,0 +1,130 @@
+package com.example.app;
+
+import de.dm.infrastructure.logcapture.LogCapture;
+import lombok.extern.slf4j.Slf4j;
+import net.logstash.logback.argument.StructuredArguments;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import static de.dm.infrastructure.logcapture.ExpectedKeyValue.keyValue;
+import static de.dm.infrastructure.logcapture.LogExpectation.info;
+import static java.lang.System.lineSeparator;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+@SuppressWarnings("java:S5778") //this rule does not increase the clarity of these tests
+@Slf4j
+class LogstashKeyValueTest {
+ @RegisterExtension
+ LogCapture logCapture = LogCapture.forCurrentPackage();
+
+ @Test
+ void worksWithString() {
+ log.info("hello", StructuredArguments.keyValue("myKey", "myValue"));
+
+ logCapture.assertLogged(info("hello", keyValue("myKey", "myValue")));
+ }
+
+ @Test
+ void failsWithString() {
+ log.info("hello", StructuredArguments.keyValue("myKey", "actualValue"));
+
+ AssertionError assertionError = assertThrows(AssertionError.class, () ->
+ logCapture.assertLogged(info("hello", keyValue("myKey", "expectedValue"))));
+
+ assertThat(assertionError).hasMessageFindingMatch(
+ "Expected log message has occurred, but never with the expected key-value content: Level: INFO, Regex: \"hello\"" + ".*" +
+ " expected key-value content: key: \"myKey\", value: \"expectedValue\"" + ".*" +
+ " actual key-value content:" + ".*" +
+ " key: \"myKey\", value: \"actualValue\"");
+ }
+
+ @Test
+ void worksWithInteger() {
+ log.info("hello", StructuredArguments.keyValue("myKey", 100000));
+
+ logCapture.assertLogged(info("hello", keyValue("myKey", 100000)));
+ }
+
+ @Test
+ void failsWithInteger() {
+ log.info("hello", StructuredArguments.keyValue("myKey", 100001));
+
+ AssertionError assertionError = assertThrows(AssertionError.class, () ->
+ logCapture.assertLogged(info("hello", keyValue("myKey", 100000))));
+
+ assertThat(assertionError).hasMessageFindingMatch(
+ "Expected log message has occurred, but never with the expected key-value content: Level: INFO, Regex: \"hello\"" + ".*" +
+ " expected key-value content: key: \"myKey\", value: \"100000\"" + ".*" +
+ " actual key-value content:" + ".*" +
+ " key: \"myKey\", value: \"100001\"");
+ }
+
+ @Test
+ void failsWithIntegerThatIsNoInteger() {
+ log.info("hello", StructuredArguments.keyValue("myKey", "not an int"));
+
+ AssertionError assertionError = assertThrows(AssertionError.class, () ->
+ logCapture.assertLogged(info("hello", keyValue("myKey", 100000))));
+
+ assertThat(assertionError).hasMessageFindingMatch(
+ "Expected log message has occurred, but never with the expected key-value content: Level: INFO, Regex: \"hello\"" + ".*" +
+ " expected key-value content: key: \"myKey\", value: \"100000\"" + ".*" +
+ " actual key-value content:" + ".*" +
+ " key: \"myKey\", value: \"not an int\"");
+ }
+
+
+ @Test
+ void worksWithLong() {
+ log.info("hello", StructuredArguments.keyValue("myKey", 1000000000000L));
+
+ logCapture.assertLogged(info("hello", keyValue("myKey", 1000000000000L)));
+ }
+
+ @Test
+ void failsWithLong() {
+ log.info("hello", StructuredArguments.keyValue("myKey", 1000000000001L));
+
+ AssertionError assertionError = assertThrows(AssertionError.class, () ->
+ logCapture.assertLogged(info("hello", keyValue("myKey", 1000000000000L))));
+
+ assertThat(assertionError).hasMessageFindingMatch(
+ "Expected log message has occurred, but never with the expected key-value content: Level: INFO, Regex: \"hello\"" + ".*" +
+ " expected key-value content: key: \"myKey\", value: \"1000000000000\"" + ".*" +
+ " actual key-value content:" + ".*" +
+ " key: \"myKey\", value: \"1000000000001\"");
+ }
+
+ @Test
+ void failsWithLongThatIsNoLong() {
+ log.info("hello", StructuredArguments.keyValue("myKey", "not a long"));
+
+ AssertionError assertionError = assertThrows(AssertionError.class, () ->
+ logCapture.assertLogged(info("hello", keyValue("myKey", 1000000000000L))));
+
+ assertThat(assertionError).hasMessageFindingMatch(
+ "Expected log message has occurred, but never with the expected key-value content: Level: INFO, Regex: \"hello\"" + ".*" +
+ " expected key-value content: key: \"myKey\", value: \"1000000000000\"" + ".*" +
+ " actual key-value content:" + ".*" +
+ " key: \"myKey\", value: \"not a long\"");
+ }
+
+ @Test
+ void assertNotLoggedSucceeds() {
+ log.info("info", StructuredArguments.keyValue("key", "actualValue"));
+
+ logCapture.assertNotLogged(info("info", keyValue("key", "forbiddenValue")));
+ }
+
+ @Test
+ void assertNotLoggedFailsWithProperMessage() {
+ log.info("info", StructuredArguments.keyValue("key", "forbiddenValue"));
+
+ AssertionError actual = assertThrows(AssertionError.class, () ->
+ logCapture.assertNotLogged(info("info", keyValue("key", "forbiddenValue"))));
+
+ assertThat(actual).hasMessage("Found a log message that should not be logged: Level: INFO, Regex: \"info\", with matchers:" +
+ lineSeparator() + " keyValue content with key: \"key\" and value: \"forbiddenValue\"");
+ }
+}
diff --git a/src/test/java/com/example/app/ReadableApiTest.java b/src/test/java/com/example/app/ReadableApiTest.java
index a9818a4..ea52e63 100644
--- a/src/test/java/com/example/app/ReadableApiTest.java
+++ b/src/test/java/com/example/app/ReadableApiTest.java
@@ -179,16 +179,16 @@ void assertNotLoggedFails() {
log.info("testlogmessage");
final AssertionError exceptionAny = assertThrows(AssertionError.class, () -> logCapture.assertNotLogged(any()));
- assertThat(exceptionAny).hasMessage("Expected log message should not occur: ");
+ assertThat(exceptionAny).hasMessage("Found a log message that should not be logged: ");
final AssertionError exceptionWithLevel = assertThrows(AssertionError.class, () -> logCapture.assertNotLogged(info()));
- assertThat(exceptionWithLevel).hasMessage("Expected log message should not occur: Level: INFO");
+ assertThat(exceptionWithLevel).hasMessage("Found a log message that should not be logged: Level: INFO");
final AssertionError exceptionWithRegex = assertThrows(AssertionError.class, () -> logCapture.assertNotLogged(any("testlogmessage")));
- assertThat(exceptionWithRegex).hasMessage("Expected log message should not occur: Regex: \"testlogmessage\"");
+ assertThat(exceptionWithRegex).hasMessage("Found a log message that should not be logged: Regex: \"testlogmessage\"");
final AssertionError exceptionWithRegexAndLevel = assertThrows(AssertionError.class, () -> logCapture.assertNotLogged(info("testlogmessage")));
- assertThat(exceptionWithRegexAndLevel).hasMessage("Expected log message should not occur: Level: INFO, Regex: \"testlogmessage\"");
+ assertThat(exceptionWithRegexAndLevel).hasMessage("Found a log message that should not be logged: Level: INFO, Regex: \"testlogmessage\"");
}
}
@@ -277,7 +277,8 @@ void markerWithAssertNotLogged() {
final AssertionError assertionError = assertThrows(AssertionError.class, () -> logCapture.assertNotLogged(
info("hello with marker",
marker("expected"))));
- assertThat(assertionError).hasMessage("Expected log message should not occur: Level: INFO, Regex: \"hello with marker\", with matchers: not expected marker name: \"expected\" was found");
+ assertThat(assertionError).hasMessage("Found a log message that should not be logged: Level: INFO, Regex: \"hello with marker\", with matchers:" +
+ lineSeparator() + " marker name: \"expected\"");
}
}
@@ -332,14 +333,15 @@ void loggerWithAssertNotLogged() {
log.info("hello on this logger");
logCapture.assertNotLogged(
- info("ello on this logger",
+ info("hello on this logger",
logger("wrongLogger")));
final AssertionError assertionError = assertThrows(AssertionError.class, () -> logCapture.assertNotLogged(
info("hello on this logger",
logger("ReadableApiTest$"))));
assertThat(assertionError)
- .hasMessage("Expected log message should not occur: Level: INFO, Regex: \"hello on this logger\", with matchers: not expected logger name (regex) was found: \"ReadableApiTest$\"");
+ .hasMessage("Found a log message that should not be logged: Level: INFO, Regex: \"hello on this logger\", with matchers:" +
+ lineSeparator() + " logger name (regex): \"ReadableApiTest$\"");
}
}
@@ -516,12 +518,13 @@ void notLoggedWithMdc() {
logCapture.assertNotLogged(info("helloWorld", mdc("thirdKey", "value")));
final AssertionError oneKeyMatches = assertThrows(AssertionError.class, () -> logCapture.assertNotLogged(info("hello world", mdc("key", "value"))));
- assertThat(oneKeyMatches).hasMessage("Expected log message should not occur: Level: INFO, Regex: \"hello world\", with matchers: not expected MDCValue with key was found: \"key\"");
+ assertThat(oneKeyMatches).hasMessage("Found a log message that should not be logged: Level: INFO, Regex: \"hello world\", with matchers:" +
+ lineSeparator() + " MDCValue with key: \"key\"");
final AssertionError bothKeysMatches = assertThrows(AssertionError.class, () -> logCapture.assertNotLogged(info("hello world", mdc("key", "value"), mdc("another_key", "another_value"))));
- assertThat(bothKeysMatches).hasMessage("Expected log message should not occur: Level: INFO, Regex: \"hello world\", with matchers: not expected MDCValue with key was found: \"key\", not expected MDCValue with key was found: \"another_key\"");
-
-
+ assertThat(bothKeysMatches).hasMessage("Found a log message that should not be logged: Level: INFO, Regex: \"hello world\", with matchers:" +
+ lineSeparator() + " MDCValue with key: \"key\"" +
+ lineSeparator() + " MDCValue with key: \"another_key\"");
}
}
diff --git a/src/test/java/de/dm/infrastructure/logcapture/ExpectedKeyValueUnitTest.java b/src/test/java/de/dm/infrastructure/logcapture/ExpectedKeyValueUnitTest.java
new file mode 100644
index 0000000..236b9f3
--- /dev/null
+++ b/src/test/java/de/dm/infrastructure/logcapture/ExpectedKeyValueUnitTest.java
@@ -0,0 +1,16 @@
+package de.dm.infrastructure.logcapture;
+
+import net.logstash.logback.marker.SingleFieldAppendingMarker;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+class ExpectedKeyValueUnitTest {
+ @Test
+ @SuppressWarnings("squid:S3415")
+ // assertion arguments are in the right order, despite Sonar thinking otherwise
+ void logstashMarkerClassCanonicalNameIsCorrect() {
+ assertThat(ExpectedKeyValue.LOGSTASH_MARKER_CLASS).isEqualTo(SingleFieldAppendingMarker.class.getCanonicalName());
+ }
+
+}
diff --git a/src/test/java/de/dm/infrastructure/logcapture/LogCaptureArchTest.java b/src/test/java/de/dm/infrastructure/logcapture/LogCaptureArchTest.java
new file mode 100644
index 0000000..ddfabe4
--- /dev/null
+++ b/src/test/java/de/dm/infrastructure/logcapture/LogCaptureArchTest.java
@@ -0,0 +1,19 @@
+package de.dm.infrastructure.logcapture;
+
+import com.tngtech.archunit.core.importer.ImportOption.DoNotIncludeTests;
+import com.tngtech.archunit.junit.AnalyzeClasses;
+import com.tngtech.archunit.junit.ArchTest;
+import com.tngtech.archunit.lang.ArchRule;
+
+import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.classes;
+
+@AnalyzeClasses(packagesOf = LogCapture.class, importOptions = DoNotIncludeTests.class)
+public class LogCaptureArchTest {
+ @ArchTest
+ static ArchRule logstashIsOnlyUsedInDelegate = classes()
+ .that()
+ .doNotHaveFullyQualifiedName(ExpectedKeyValueLogstashDelegate.class.getCanonicalName())
+ .should()
+ .onlyDependOnClassesThat()
+ .resideOutsideOfPackage("net.logstash.logback..");
+}