Buffered Logging
Tue Apr 29 2025

In unit tests, it can be useful to suppress all logging for successful tests, but still log everything when tests fail.
In order to accomplish this, we need to buffer all logging before we know the result of the test, and then flush or clear the buffer once we know the outcome.

BufferedAppender

BufferedAppender is the logback appender that buffers all logging until it receives a CLEAR or FLUSH marker.
You can use directly in logback configuration. It requires a delegate appender for flushing, declared using an appender-ref.
logback-test.xml
<configuration packagingData="true">
  <appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
      <pattern>%highlight(%-5level) %cyan(%date{HH:mm:ss.SSS, ${LOGGING_TIMEZONE}}) %gray(\(%file:%line\)) [%white(%thread)] %blue(%marker) {%magenta(%mdc)} %green(%logger): %message%n%rootException</pattern>
    </encoder>
  </appender>
  <appender name="Buffered" class="io.liftwizard.logging.logback.appender.buffered.BufferedAppender">
    <appender-ref ref="Console"/>
  </appender>
  <root level="INFO">
    <appender-ref ref="Buffered"/>
  </root>
</configuration>
BufferedAppender lives in the liftwizard-logging-buffered-appender module.
<dependency>
    <groupId>io.liftwizard</groupId>
    <artifactId>liftwizard-logging-buffered-appender</artifactId>
    <scope>test</scope>
</dependency>

Log Markers

We must log CLEAR and FLUSH markers to instruct BufferedAppender to clear or flush its logs. If you are using JUnit 4 or 5, you can use the included Rule or Extension to log these markers automatically.
JUnit 4JUnit 5
LogMarkerTestExtension is a JUnit 5 Extension that clears the buffer before all tests and flushes the buffer after failed tests. It does this by logging CLEAR and FLUSH markers.
@ExtendWith(LogMarkerTestExtension.class)
public class ExampleTest
{
    @Test
    public void smokeTest()
    {
        // test code
    }
}
LogMarkerTestExtension lives in the liftwizard-junit-extension-log-marker module.
<dependency>
    <groupId>io.liftwizard</groupId>
    <artifactId>liftwizard-junit-extension-log-marker</artifactId>
    <scope>test</scope>
</dependency>

BufferedAppenderFactory

The BufferedAppenderFactory allows you to use an appender with the type buffered where you would otherwise use console in your Dropwizard configuration.
"logging": {
  "level": "DEBUG",
  "appenders": [
    {
      "type": "buffered",
      "timeZone": "${LOGGING_TIMEZONE:-system}",
      "logFormat": "%highlight(%-5level) %cyan(%date{HH:mm:ss.SSS, %dwTimeZone}) %gray(\\(%file:%line\\)) [%white(%thread)] %blue(%marker) {%magenta(%mdc)} %green(%logger): %message%n%rootException",
      "includeCallerData": true,
    },
  ]
}
BufferedAppenderFactory lives in the liftwizard-config-logging-buffered module.
<dependency>
    <groupId>io.liftwizard</groupId>
    <artifactId>liftwizard-config-logging-buffered</artifactId>
    <scope>test</scope>
</dependency>
Note:BufferedAppenderFactory is primarily useful for tests that use Dropwizard's JUnit 4 Rule DropwizardAppRule or Dropwizard's JUnit 5 Extension DropwizardAppExtension.
JUnit 4JUnit 5
LogMarkerTestExtension was used as an annotation in the previous example. When used together with DropwizardAppExtension, it ought to be a field to control execution order. Both extensions tear down logging, and LogMarkerTestExtension needs to perform its tear down first, to flush its contents to the console.
@RegisterExtension
final DropwizardAppExtension<MyAppConfiguration> dropwizardAppExtension = new DropwizardAppExtension<>(
        MyApplication.class,
        ResourceHelpers.resourceFilePath("test-example.json5"));

@RegisterExtension
final LogMarkerTestExtension logMarkerTestExtension = new LogMarkerTestExtension();