Skip to content

Commit

Permalink
Merge pull request #13 from dm-drogeriemarkt/readme-3.4.0
Browse files Browse the repository at this point in the history
preparation for 3.4.0
  • Loading branch information
cleaning-agent authored Feb 25, 2022
2 parents e19136c + ec3f213 commit b0dbcba
Show file tree
Hide file tree
Showing 8 changed files with 310 additions and 111 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build-and-deploy.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Build with Maven and deploy to Sonatype OSS repo
name: Build and deploy to Sonatype OSS repo

on:
push:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Build with Maven
name: Build

on:
push:
Expand Down
215 changes: 163 additions & 52 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,50 +1,63 @@
# log-capture

[<img src="https://opensourcelogos.aws.dmtech.cloud/dmTECH_opensource_logo.svg" height="20" width="130">](https://dmtech.de/)
[![Build Status](https://travis-ci.org/dm-drogeriemarkt/log-capture.svg?branch=master)](https://travis-ci.org/dm-drogeriemarkt/log-capture)
[![Apache License 2](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
[![Build Status](https://github.com/dm-drogeriemarkt/log-capture/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/dm-drogeriemarkt/log-capture/actions?query=branch%3Amaster)
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/de.dm.infrastructure/log-capture/badge.svg)](https://maven-badges.herokuapp.com/maven-central/de.dm.infrastructure/log-capture)

Simple assertions for log messages. See [Examples](#examples).

**Note that** log-capture asserts evaluated log statements. That means it depends on a logging implementation (*logback*), but works with any logging facade (*slf4j* and others)

```java
var name="world";
log.info("hello {}", name);
log.warn("bye {}", name);

logCapture.assertLogged(
info("hello world"),
warn("bye world")
);
info("hello world"),
warn("bye world")
);
```

It's even simple when there's more than just the message and level to assert:

```java
logCapture.assertLogged(
info("hello world",
logger("com.acme.helloworld.WorldGreeter")
)
warn("bye world",
exception().expectedType(WorldNotFoundException.class)
)
);
logCapture.assertLoggedInOrder(
info("hello world",
logger("com.acme.helloworld.WorldGreeter"))
warn("bye world",
exception().expectedType(WorldNotFoundException.class))
);
```

**Table of Contents**

* [Usage](#usage)
* [Maven](#maven)
* [Examples](#examples)
* [Unit Test Example:](#unit-test-example)
* [Integration Test Example:](#integration-test-example)
* [Example with MDC](#example-with-mdc)
* [More Examples](#more-examples)
* [Maven](#maven)
* [Additional matchers](#additional-matchers)
* [MDC content](#mdc-content)
* [Exceptions](#exceptions)
* [Markers](#markers)
* [Logger name](#logger-name)
* [Examples](#examples)
* [Unit Test Example:](#unit-test-example)
* [Integration Test Example:](#integration-test-example)
* [Example with additional MDC matcher](#example-with-additional-mdc-matcher)
* [More Examples](#more-examples)
* [Usage outside of JUnit 5 (Cucumber example)](#usage-outside-of-junit-5-cucumber-example)
* [Cucumber example](#cucumber-example)
* [Cucumber feature file](#cucumber-feature-file)
* [Cucumber example](#cucumber-example)
* [Cucumber feature file](#cucumber-feature-file)
* [Changes](#changes)
* [3.3.0](#330)
* [3.2.1](#321)
* [3.2.0](#320)
* [3.1.0](#310)
* [3.0.0](#300)
* [2.0.1](#201)
* [Updating from Version 1.x.x to 2.x.x](#updating-from-version-1xx-to-2xx)
* [3.4.0](#340)
* [3.3.0](#330)
* [3.2.1](#321)
* [3.2.0](#320)
* [3.1.0](#310)
* [3.0.0](#300)
* [2.0.1](#201)
* [Updating from Version 1.x.x to 2.x.x](#updating-from-version-1xx-to-2xx)
* [Updating from Version 3.2.x or lower to Version 3.3.x or higher](#updating-from-version-32x-or-lower-to-version-33x-or-higher)

## Usage

Expand All @@ -53,15 +66,62 @@ logCapture.assertLogged(
Add log-capture as a test dependency to your project. If you use Maven, add this to your pom.xml:

```xml

<dependency>
<groupId>de.dm.infrastructure</groupId>
<artifactId>log-capture</artifactId>
<version>3.3.0</version>
<version>3.4.0</version>
<scope>test</scope>
</dependency>
```

### Additional matchers

The default matchers can match level and/or message. But there are additional matchers for log messages. See also [the detailed example below](#example-with-additional-mdc-matcher)

#### MDC content

```java
MDC.put("key", "value");
log.info("did something");

logCapture.info("did something", mdc("key", "value"));`
```

#### Exceptions

```java
log.warn("oh no!",
new IllegalArgumentException("shame on you!",
new NullPointerException("never use null")));

logCapture.assertLogged(
warn("oh no!",
exception()
.expectedType(IllegalArgumentException.class)
.expectedCause(exception()
.expectedType(NullPointerException.class)
.expectedMessageRegex("never use null")
.build())
.build()
));
```

#### Markers

```java
log.info(MarkerFactory.getMarker("my-marker"), "hello with marker");

logCapture.info("hello with marker", marker("my-marker"));
```

#### Logger name

```java
log.info("did something");

logCapture.info("did something", logger("com.acme.foo"));
```

### Examples

#### Unit Test Example:
Expand All @@ -88,21 +148,21 @@ public class MyUnitTest {
//assert that the messages have been logged
//expected log message is a regular expression
logCapture.assertLogged(
info("^something interesting$"),
error("terrible")
info("^something interesting$"),
error("terrible")
);

//is the same assertion, but also asserts the order of these log messages
logCapture.assertLoggedInOrder(
info("^something interesting$"),
error("terrible")
info("^something interesting$"),
error("terrible")
);

//assert that no log message containing "something unwanted" with any log level exists
//and that no log message with level DEBUG exists
logCapture.assertNotLogged(
any("something unwanted"),
debug()
any("something unwanted"),
debug()
);
}
}
Expand Down Expand Up @@ -135,14 +195,14 @@ public class MyIntegrationTest {
log.error("something terrible");

logCapture.assertLogged(
info("^something interesting$"),
info("^start of info from utility.that.logs"),
error("terrible"));
info("^something interesting$"),
info("^start of info from utility.that.logs"),
error("terrible"));
}
}
```

#### Example with MDC
#### Example with additional MDC matcher

```Java
package my.company.application;
Expand All @@ -168,26 +228,25 @@ public class MyUnitTest {

// asserts my_mdc_key for both message and other_mdc_key for the second one
logCapture
.with(mdc("my_mdc_key", "^this is the MDC value$"))
.assertLoggedInOrder(
info("information attached"),
info("additional MDC information attached",
mdc("other_mdc_key", "^this is the other MDC value$")));
.with(mdc("my_mdc_key", "^this is the MDC value$"))
.assertLoggedInOrder(
info("information attached"),
info("additional MDC information attached",
mdc("other_mdc_key", "^this is the other MDC value$")));
}
}
```

If assertLogged fails because the message is correct, but the MDC value is wrong, the assertion error will contain the
actual MDC values of the last captured log event where the log message and level matched.
If assertLogged fails because the message is correct, but the MDC value is wrong, the assertion error will contain the actual MDC values of the last captured log event where the log message and level matched.

This can be useful for debugging purposes. For example, this test code:

```java
MDC.put("my_mdc_key","this is the wrong MDC value");
MDC.put("other_mdc_key","this is the other MDC value");
log.info("this message has some MDC information attached");
MDC.put("other_mdc_key","this is the other MDC value");
log.info("this message has some MDC information attached");

logCapture.assertLogged().info("information attached",
logCapture.assertLogged().info("information attached",
mdc("my_mdc_key","^something expected that never occurs$"),
mdc("other_mdc_key","^this is the other MDC value$"));
```
Expand All @@ -208,8 +267,7 @@ See [ReadableApiTest.java](src/test/java/com/example/app/ReadableApiTest.java) f

## Usage outside of JUnit 5 (Cucumber example)

If you intend to use LogCapture outside of a JUnit test, you cannot rely on JUnit's `@RegisterExtension` annotation and
must call LogCapture's `addAppenderAndSetLogLevelToTrace()` and `removeAppenderAndResetLogLevel()` methods manually.
If you intend to use LogCapture outside of a JUnit test, you cannot rely on JUnit's `@RegisterExtension` annotation and must call LogCapture's `addAppenderAndSetLogLevelToTrace()` and `removeAppenderAndResetLogLevel()` methods manually.

Be aware that this will still cause JUnit to be a dependency.

Expand All @@ -231,12 +289,18 @@ And with MDC logging context

## Changes

### 3.4.0

* Added `assertNotLogged(...)` for asserting that no matching log message has been logged
* Added more factory methods for `warn(...)`, `error(...)` as a shortcut to ignore the message when matching
* Added `any(...)` factory method for matching any log message regardless of level

### 3.3.0

* Introduced a new fluent API with
* better readability
* extensible log message assertions (to assert attached Exceptions, Markers and LoggerName beyond MDC content)
* Deprecated the old API (will be removed in 4.0)
* Deprecated the old API (will be removed in 4.0, [how to update](#updating-from-version-32x-or-lower-to-version-33x-or-higher))

### 3.2.1

Expand Down Expand Up @@ -269,3 +333,50 @@ Fixed a bug where multiline log messages (for example Messages that contain a st
* `LogCapture.forIntegrationTest(...)` has been replaced with `LogCapture.forPackages(...)`
* `logCapture.addAppender()` has been replaced with `logCapture.addAppenderAndSetLogLevelToTrace()`
* `logCapture.removeAppender()` has been replaced with `logCapture.removeAppenderAndResetLogLevel()`

### Updating from Version 3.2.x or lower to Version 3.3.x or higher

Since the old API has been deprecated in 3.3.0 in preparation for 4.0, existing assertions should be replaced. So for example:

```java
...

import static ch.qos.logback.classic.Level.INFO;
import static de.dm.infrastructure.logcapture.ExpectedMdcEntry.withMdc;

...

// plain assertion
logCapture.assertLogged(INFO, "Something happened.");
// assertion with MDC
logCapture.assertLogged(INFO, "Something with MDC content",
withMdc("bookingNumber", "1234"));
// in-order assertion
logCapture
.assertLogged(INFO, "Step 1")
.thenLogged(INFO, "Step 2");

```

needs to be replaced with:

```java
...

import static de.dm.infrastructure.logcapture.ExpectedMdcEntry.mdc;
import static de.dm.infrastructure.logcapture.LogExpectation.info;

...

// plain assertion
logCapture.assertLogged(info("Something happened."));
// assertion with MDC
logCapture.assertLogged(info("Something with MDC content",
mdc("rabattUpdate", "CapturableHeadline")));
// in-order assertion
ogCapture.assertLoggedInOrder(
info("Step 1")
info("Step 2")
);

```
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<version>${project.version}</version>

<name>Log Capture</name>
<description>Makes it possible to capture logs and assert if things have been logged</description>
<description>assertions for logging with logback</description>
<url>https://github.com/dm-drogeriemarkt/log-capture</url>

<licenses>
Expand All @@ -29,7 +29,7 @@
</developers>

<properties>
<project.version>3.3.0-SNAPSHOT</project.version>
<project.version>3.4.0-SNAPSHOT</project.version>

<java.version>1.8</java.version>
<lombok.version>1.18.20</lombok.version>
Expand Down
Loading

0 comments on commit b0dbcba

Please sign in to comment.