Giter VIP home page Giter VIP logo

log-captor's Introduction

Actions Status Quality Gate Status Coverage JDK Compatibility Kotlin Compatibility Scala Compatibility Android API Compatibility Reliability Rating Security Rating Vulnerabilities Apache2 license Maven Central javadoc FOSSA Status Join the chat at https://gitter.im/hakky54/logcaptor

SonarCloud

LogCaptor Tweet

Install library with:

Install with maven

<dependency>
    <groupId>io.github.hakky54</groupId>
    <artifactId>logcaptor</artifactId>
    <version>2.9.2</version>
    <scope>test</scope>
</dependency>

Install with Gradle

testImplementation 'io.github.hakky54:logcaptor:2.9.2'

Install with Scala SBT

libraryDependencies += "io.github.hakky54" % "logcaptor" % "2.9.2" % Test

Install with Apache Ivy

<dependency org="io.github.hakky54" name="logcaptor" rev="2.9.2" />

Table of contents

  1. Introduction
  2. Usage
  3. Known issues
  4. Contributing
  5. License

Introduction

Hey, hello there ๐Ÿ‘‹ Welcome. I hope you will like this library โค๏ธ

LogCaptor is a library which will enable you to easily capture logging entries for unit and integration testing purposes.

Do you want to capture the console output? Please have a look at ConsoleCaptor.

Advantages

  • No mocking required
  • No custom JUnit extension required
  • Plug & play

Supported Java versions

  • Java 8
  • Java 11+

Tested Logging libraries

  • SLFJ4
  • Logback
  • Java Util Logging
  • Apache Log4j
  • Apache Log4j2
  • Log4j with Lombok
  • Log4j2 with Lombok
  • SLFJ4 with Lombok
  • JBossLog with Lombok
  • Java Util Logging with Lombok
  • Spring Boot Starter Log4j2
  • Google Flogger

See the unit test LogCaptorShould for all the scenario's or checkout this project Java Tutorials which contains more isolated examples of the individual logging frameworks

Compatibility

LogCaptor SLF4J Java Kotlin Scala Android
2.9.x 2.x.x 8+ 1.5+ 2.11+ 24+
2.8.x 1.x.x 8+ 1.5+ 2.11+ 24+

Usage

Capture logs
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class FooService {

    private static final Logger LOGGER = LogManager.getLogger(FooService.class);

    public void sayHello() {
        LOGGER.info("Keyboard not responding. Press any key to continue...");
        LOGGER.warn("Congratulations, you are pregnant!");
    }

}
Unit test:
import static org.assertj.core.api.Assertions.assertThat;

import nl.altindag.log.LogCaptor;
import org.junit.jupiter.api.Test;

public class FooServiceShould {

    @Test
    public void logInfoAndWarnMessages() {
        LogCaptor logCaptor = LogCaptor.forClass(FooService.class);

        FooService fooService = new FooService();
        fooService.sayHello();

        // Get logs based on level
        assertThat(logCaptor.getInfoLogs()).containsExactly("Keyboard not responding. Press any key to continue...");
        assertThat(logCaptor.getWarnLogs()).containsExactly("Congratulations, you are pregnant!");

        // Get all logs
        assertThat(logCaptor.getLogs())
                .hasSize(2)
                .contains(
                    "Keyboard not responding. Press any key to continue...",
                    "Congratulations, you are pregnant!"
                );
    }
}
Initialize LogCaptor once and reuse it during multiple tests with clearLogs method within the afterEach method:
import nl.altindag.log.LogCaptor;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeAll;

public class FooServiceShould {

    private static LogCaptor logCaptor;
    private static final String EXPECTED_INFO_MESSAGE = "Keyboard not responding. Press any key to continue...";
    private static final String EXPECTED_WARN_MESSAGE = "Congratulations, you are pregnant!";
    
    @BeforeAll
    public static void setupLogCaptor() {
        logCaptor = LogCaptor.forClass(FooService.class);
    }

    @AfterEach
    public void clearLogs() {
        logCaptor.clearLogs();
    }
    
    @AfterAll
    public static void tearDown() {
        logCaptor.close();
    }

    @Test
    public void logInfoAndWarnMessagesAndGetWithEnum() {
        FooService service = new FooService();
        service.sayHello();

        assertThat(logCaptor.getInfoLogs()).containsExactly(EXPECTED_INFO_MESSAGE);
        assertThat(logCaptor.getWarnLogs()).containsExactly(EXPECTED_WARN_MESSAGE);

        assertThat(logCaptor.getLogs()).hasSize(2);
    }

    @Test
    public void logInfoAndWarnMessagesAndGetWithString() {
        FooService service = new FooService();
        service.sayHello();

        assertThat(logCaptor.getInfoLogs()).containsExactly(EXPECTED_INFO_MESSAGE);
        assertThat(logCaptor.getWarnLogs()).containsExactly(EXPECTED_WARN_MESSAGE);

        assertThat(logCaptor.getLogs()).hasSize(2);
    }

}
Class which will log events if specific log level has been set
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class FooService {

    private static final Logger LOGGER = LogManager.getLogger(FooService.class);

    public void sayHello() {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Keyboard not responding. Press any key to continue...");
        }
        LOGGER.info("Congratulations, you are pregnant!");
    }

}
Unit test:
import static org.assertj.core.api.Assertions.assertThat;

import nl.altindag.log.LogCaptor;
import org.junit.jupiter.api.Test;

public class FooServiceShould {

    @Test
    public void logInfoAndWarnMessages() {
        LogCaptor logCaptor = LogCaptor.forClass(FooService.class);
        logCaptor.setLogLevelToInfo();

        FooService fooService = new FooService();
        fooService.sayHello();

        assertThat(logCaptor.getInfoLogs()).contains("Congratulations, you are pregnant!");
        assertThat(logCaptor.getDebugLogs())
            .doesNotContain("Keyboard not responding. Press any key to continue...")
            .isEmpty();
    }
}
Class which will also log an exception
import nl.altindag.log.service.Service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;

public class FooService {

    private static final Logger LOGGER = LoggerFactory.getLogger(ZooService.class);

    @Override
    public void sayHello() {
        try {
            tryToSpeak();
        } catch (IOException e) {
            LOGGER.error("Caught unexpected exception", e);
        }
    }

    private void tryToSpeak() throws IOException {
        throw new IOException("KABOOM!");
    }
}
Unit test:
import static org.assertj.core.api.Assertions.assertThat;

import nl.altindag.log.LogCaptor;
import nl.altindag.log.model.LogEvent;
import org.junit.jupiter.api.Test;

public class FooServiceShould {

    @Test
    void captureLoggingEventsContainingException() {
        LogCaptor logCaptor = LogCaptor.forClass(ZooService.class);

        FooService service = new FooService();
        service.sayHello();

        List<LogEvent> logEvents = logCaptor.getLogEvents();
        assertThat(logEvents).hasSize(1);

        LogEvent logEvent = logEvents.get(0);
        assertThat(logEvent.getMessage()).isEqualTo("Caught unexpected exception");
        assertThat(logEvent.getLevel()).isEqualTo("ERROR");
        assertThat(logEvent.getThrowable()).isPresent();

        assertThat(logEvent.getThrowable().get())
                .hasMessage("KABOOM!")
                .isInstanceOf(IOException.class);
    }
}
Capture Managed Diagnostic Context (MDC)
import nl.altindag.log.service.LogMessage;
import nl.altindag.log.service.Service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;

public class FooService {

    private static final Logger LOGGER = LoggerFactory.getLogger(ServiceWithSlf4jAndMdcHeaders.class);

    public void sayHello() {
        try {
            MDC.put("my-mdc-key", "my-mdc-value");
            LOGGER.info(LogMessage.INFO.getMessage());
        } finally {
            MDC.clear();
        }

        LOGGER.info("Hello there!");
    }

}
Unit test:
import static org.assertj.core.api.Assertions.assertThat;

import nl.altindag.log.LogCaptor;
import nl.altindag.log.model.LogEvent;
import org.junit.jupiter.api.Test;

public class FooServiceShould {

   @Test
   void captureLoggingEventsContainingMdc() {
      LogCaptor logCaptor = LogCaptor.forClass(FooService.class);

      FooService service = new FooService();
      service.sayHello();

      List<LogEvent> logEvents = logCaptor.getLogEvents();

      assertThat(logEvents).hasSize(2);

      assertThat(logEvents.get(0).getDiagnosticContext())
              .hasSize(1)
              .extractingByKey("my-mdc-key")
              .isEqualTo("my-mdc-value");

      assertThat(logEvents.get(1).getDiagnosticContext()).isEmpty();
   }
}
Disable any logs for a specific class

In some use cases a unit test can generate too many logs by another class. This could be annoying as it will cause noise in your build logs. LogCaptor can disable those log messages with the following snippet:

import static org.assertj.core.api.Assertions.assertThat;

import nl.altindag.log.LogCaptor;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

public class FooServiceShould {

    private static LogCaptor logCaptorForSomeOtherService = LogCaptor.forClass(SomeService.class);

    @BeforeAll
    static void disableLogs() {
        logCaptorForSomeOtherService.disableLogs();
    }

    @AfterAll
    static void resetLogLevel() {
        logCaptorForSomeOtherService.resetLogLevel();
    }

    @Test
    void logInfoAndWarnMessages() {
        LogCaptor logCaptor = LogCaptor.forClass(FooService.class);

        FooService service = new FooService();
        service.sayHello();

        assertThat(logCaptor.getLogs())
                .hasSize(2)
                .contains(
                    "Keyboard not responding. Press any key to continue...",
                    "Congratulations, you are pregnant!"
                );
    }
}
Disable console output

The console output can be disabled with 2 different options:

  1. With the LogCaptor.disableConsoleOutput() method
  2. With a logback-test.xml file, see below for the details.

Add logback-test.xml to your test resources with the following content:

<configuration>
   <statusListener class="ch.qos.logback.core.status.NopStatusListener" />
</configuration>
Returnable values from LogCaptor
import nl.altindag.log.LogCaptor;
import nl.altindag.log.model.LogEvent;

import java.time.ZonedDateTime;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import org.junit.jupiter.api.Test;

class FooServiceShould {

   @Test
   void showCaseAllReturnableValues() {
        LogCaptor logCaptor = LogCaptor.forClass(FooService.class);

        List<String> logs = logCaptor.getLogs();
        List<String> infoLogs = logCaptor.getInfoLogs();
        List<String> debugLogs = logCaptor.getDebugLogs();
        List<String> warnLogs = logCaptor.getWarnLogs();
        List<String> errorLogs = logCaptor.getErrorLogs();
        List<String> traceLogs = logCaptor.getTraceLogs();

        LogEvent logEvent = logCaptor.getLogEvents().get(0);
        String message = logEvent.getMessage();
        String formattedMessage = logEvent.getFormattedMessage();
        String level = logEvent.getLevel();
        List<Object> arguments = logEvent.getArguments();
        String loggerName = logEvent.getLoggerName();
        String threadName = logEvent.getThreadName();
        ZonedDateTime timeStamp = logEvent.getTimeStamp();
        Map<String, String> diagnosticContext = logEvent.getDiagnosticContext();
        Optional<Throwable> throwable = logEvent.getThrowable();
    }
    
}

Known issues

Using Log Captor alongside with other logging libraries

When building your maven or gradle project it can complain that you are using multiple SLF4J implementations. Log Captor is using logback as SLF4J implementation and SLF4J doesn't allow you to use multiple implementations, therefore you need to explicitly specify which to use during which build phase if you are using multiple SLF4J implementations.

During the test execution it can give you the following warning:

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:~/.m2/repository/org/apache/logging/log4j/log4j-slf4j-impl/2.17.0/log4j-slf4j-impl-2.17.0.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:~/.m2/repository/ch/qos/logback/logback-classic/1.2.10/logback-classic-1.2.10.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.apache.logging.slf4j.Log4jLoggerFactory]

Because of this dependency issue the test may fail to capture the logs. You can fix that by excluding your main logging framework during the unit/integration test phase. Below is an example for Maven Failsafe and Maven Surefire. You can discover which dependency you need to exclude by anaylising the SLF4J warning displayed above.

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <configuration>
                <classpathDependencyExcludes>
                    <classpathDependencyExclude>org.apache.logging.log4j:log4j-slf4j-impl</classpathDependencyExclude>
                    <classpathDependencyExclude>org.apache.logging.log4j:log4j-slf4j2-impl</classpathDependencyExclude>
                </classpathDependencyExcludes>
            </configuration>
        </plugin>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-failsafe-plugin</artifactId>
            <configuration>
                <classpathDependencyExcludes>
                    <classpathDependencyExclude>org.apache.logging.log4j:log4j-slf4j-impl</classpathDependencyExclude>
                    <classpathDependencyExclude>org.apache.logging.log4j:log4j-slf4j2-impl</classpathDependencyExclude>
                </classpathDependencyExcludes>
            </configuration>
        </plugin>
    </plugins>
</build>

And for gradle:

configurations {
    testImplementation {
        exclude(group = "org.apache.logging.log4j", module = "log4j-slf4j-impl")
        exclude(group = "org.apache.logging.log4j", module = "log4j-slf4j2-impl")
    }
}

Capturing logs of static inner classes

LogCaptor successfully captures logs of static inner classes with LogCaptor.forClass(MyStaticInnerClass.class) construction when SLF4J, Log4J, JUL is used. This doesn't apply to static inner classes when Log4J2 is used, because it uses different method to for initializing the logger. It used Class.getCanonicalName() under the covers instead of Class.getName(). You should use LogCaptor.forName(StaticInnerClass.class.getCanonicalName()) or LogCaptor.forRoot()for to be able to capture logs for static inner classes while using Log4J2.

Mixing up different classloaders

It may occur that different classloaders are being used during your tests. LogCaptor works with different classloaders, but it will fail to set up if part of it has been created with a different classloader. This may occur for example while using @QuarkusTest annotation. The logger will be setup by the default JDK classloader [jdk.internal.loader.ClassLoaders$AppClassLoader] while LogCaptor will be setup by the other classloader, in this case Quarkus [io.quarkus.bootstrap.classloading.QuarkusClassLoader]. LogCaptor will try to cast an object during the preparation, but it will fail as it is not possible to cast an object created by a different classloader. You need to make sure the logger is using the same classloader as LogCaptor or the other way around.

There is also an easier alternative solution by sending all of your logs to the console and capture that with ConsoleCaptor. Add the following two dependencies to your project:

<dependencies>
   <dependency>
      <groupId>io.github.hakky54</groupId>
      <artifactId>consolecaptor</artifactId>
      <scope>test</scope>
   </dependency>

   <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <scope>test</scope>
   </dependency>

</dependencies>

Add the logback-test.xml configuration below to your test resources:

<configuration>

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
        </encoder>
    </appender>

    <root level="TRACE">
        <appender-ref ref="STDOUT" />
    </root>

</configuration>

Your target class:

@Path("/hello")
public class HelloResource {
    
    Logger logger = LoggerFactory.getLogger(GreetingResource.class);

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String hello() {
        logger.info("Hello");
        return "Hello";
    }
}

Your test class:

import io.quarkus.test.junit.QuarkusTest;
import nl.altindag.console.ConsoleCaptor;
import org.junit.jupiter.api.Test;

import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;

@QuarkusTest
class QuarkusTestTest {

    @Test
    void captureLogs() {
        try(ConsoleCaptor consoleCaptor = new ConsoleCaptor()) {
           HelloResource resource = new HelloResource();
           resource.hello();

           List<String> standardOutput = consoleCaptor.getStandardOutput();

           assertThat(standardOutput)
                   .hasSize(1)
                   .contains("Hello");
        }
    }

}

Contributing

There are plenty of ways to contribute to this project:

  • Give it a star
  • Share it with a Tweet
  • Join the Gitter room and leave a feedback or help with answering users questions
  • Submit a PR

License

FOSSA Status

log-captor's People

Contributors

akazver avatar allcontributors[bot] avatar andreynudko avatar dependabot[bot] avatar fossabot avatar hakky54 avatar rohannagar avatar sleepo581 avatar sullis avatar vorburger avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

log-captor's Issues

ConcurrentModificationException in getLogs()

We tried LogCaptor in some integration-style tests, where logging actually happens in background thread.
Occasionally getLogs() throws ConcurrentModificationException -
quick look into code shows that ListAppender.list is not thread-safe, so it's almost certainly a race between test and application threads. I think it's not a problem for actual logging since AppenderBase::doAppend is synchronized, but it is a problem when scanning the list.
One possible fix would be to wrap all access to list into synchronized(listAppender). Or, maybe even better, swap it for CopyOnWrite / Collections::synchronizedList.
Any views on that? Happy to submit PR as well.

ava.lang.NoClassDefFoundError: org/slf4j/spi/LoggingEventAware

Describe the bug
Using the LogCaptor 2.9.2 in a Java project which is based on OSGi/ODL frameworks. It was working perfectly fine but recent changes to address security issues in the parent projects causes the JUnit tests to fail. The failure is the usual
java.lang.ExceptionInInitializerError
Caused by: org.apache.logging.log4j.LoggingException: log4j-slf4j-impl cannot be present with log4j-to-slf4j

So, I added the exclusion as below

io.github.hakky54
logcaptor
2.9.2
test


org.apache.logging.log4j
log4j-to-slf4j


Now, the log4j-slf4j-impl cannot be present with log4j-to-slf4j issue is gone, but getting a different exception as below
java.lang.NoClassDefFoundError: org/slf4j/spi/LoggingEventAware
at com.fujitsu.fnc.mdsaladaptor.util.MDSalUtilTest.(MDSalUtilTest.java:49)
The line number (49 as an example in the above exception) is the line where the LogCaptor is initialized as below
private static final LogCaptor logCaptor = LogCaptor.forClass(MDSALUtil.class);

To Reproduce
As described above

Expected behavior
How to get past the NoClassDefFoundError: org/slf4j/spi/LoggingEventAware

Screenshots
If applicable, add screenshots to help explain your problem.

Environmental Data:

  • Java Version - openjdk version 11.0.21
  • Maven Version - Apache Maven 3.6.3
  • OS Linux

Additional context
mvn dependency tree output (without the exclusion)
com.fujitsu.fnc.mdsaladaptor:mdsaladaptor-impl:bundle:7.3.0-SNAPSHOT
+- com.fujitsu.fnc.model:sdn-model:jar:7.3.0-SNAPSHOT:compile
| +- org.opendaylight.mdsal:yang-binding:jar:2.0.17:compile
| +- org.opendaylight.yangtools:yang-common:jar:3.0.16:compile
| +- org.opendaylight.mdsal.binding.model.ietf:rfc6991-ietf-yang-types:jar:2.0.17:compile
| | - org.opendaylight.mdsal.model:ietf-type-util:jar:3.0.17:compile
| +- org.opendaylight.mdsal.binding.model.ietf:rfc6991-ietf-inet-types:jar:2.0.17:compile
| +- org.opendaylight.mdsal.model:ietf-topology-l3-unicast-igp:jar:2013.10.21.16.17:compile
| | - org.opendaylight.mdsal.model:ietf-topology:jar:2013.10.21.16.17:compile
| +- org.opendaylight.mdsal.model:yang-ext:jar:2013.09.07.16.17:compile
| +- com.google.guava:guava:jar:32.1.1-jre:compile
| | +- com.google.guava:failureaccess:jar:1.0.1:compile
| | +- com.google.guava:listenablefuture:jar:9999.0-empty-to-avoid-conflict-with-guava:compile
| | +- com.google.code.findbugs:jsr305:jar:3.0.2:compile
| | +- org.checkerframework:checker-qual:jar:3.33.0:compile
| | - com.google.j2objc:j2objc-annotations:jar:2.8:compile
| +- org.opendaylight.yangtools:concepts:jar:3.0.16:compile
| - com.fujitsu.fnc.thirdparty:openroadm-model:jar:7.3.0-SNAPSHOT:compile
| +- org.opendaylight.mdsal.binding.model.ietf:rfc8345-ietf-network:jar:2.0.17:compile
| +- org.opendaylight.mdsal.binding.model.ietf:rfc8345-ietf-network-topology:jar:2.0.17:compile
| +- org.opendaylight.mdsal.model:iana-afn-safi:jar:2013.07.04.16.17:compile
| +- org.opendaylight.netconf:ietf-netconf:jar:1.7.4:compile
| +- edu.umd.cs.findbugs:annotations:jar:1.0.0:compile
| - com.wordnik:swagger-annotations:jar:1.3.9:compile
+- com.fujitsu.fnc.sdnfw:kafka-provider-api:jar:7.3.0-SNAPSHOT:compile
| +- com.google.code.gson:gson:jar:2.10.1:compile
| - javax.xml.bind:jaxb-api:jar:2.3.1:compile
| - javax.activation:javax.activation-api:jar:1.2.0:compile
+- org.opendaylight.mdsal:mdsal-binding-api:jar:4.0.17:compile
| +- org.opendaylight.mdsal:mdsal-common-api:jar:4.0.17:compile
| - org.opendaylight.mdsal.model:general-entity:jar:2.0.17:compile
+- org.opendaylight.mdsal:mdsal-dom-api:jar:4.0.17:compile
+- org.opendaylight.mdsal:mdsal-common-util:jar:4.0.17:compile
+- org.opendaylight.controller:sal-clustering-commons:jar:1.10.4:compile
+- org.opendaylight.mdsal:mdsal-binding-spec-util:jar:2.0.17:compile
+- org.osgi:org.osgi.core:jar:6.0.0:compile
+- org.opendaylight.mdsal:mdsal-binding-dom-codec:jar:4.0.17:compile
| +- org.javassist:javassist:jar:3.26.0-GA:compile
| - org.opendaylight.yangtools:yang-data-impl:jar:3.0.16:compile
| +- org.opendaylight.yangtools:rfc7952-data-util:jar:3.0.16:compile
| +- org.opendaylight.yangtools:rfc8528-data-util:jar:3.0.16:compile
| - org.antlr:antlr4-runtime:jar:4.7.2:compile
+- org.opendaylight.mdsal:mdsal-binding-generator-api:jar:2.0.17:compile
+- org.opendaylight.mdsal:mdsal-binding-generator-impl:jar:2.0.17:compile
| - org.opendaylight.yangtools:yang-parser-impl:jar:3.0.16:compile
| +- org.opendaylight.yangtools:yang-parser-api:jar:3.0.16:compile
| +- org.opendaylight.yangtools:yang-parser-reactor:jar:3.0.16:compile
| +- org.opendaylight.yangtools:yang-parser-spi:jar:3.0.16:compile
| +- org.opendaylight.yangtools:yang-parser-rfc7950:jar:3.0.16:compile
| +- org.opendaylight.yangtools:odlext-model-api:jar:3.0.16:compile
| +- org.opendaylight.yangtools:odlext-parser-support:jar:3.0.16:compile
| +- org.opendaylight.yangtools:openconfig-model-api:jar:3.0.16:compile
| +- org.opendaylight.yangtools:openconfig-parser-support:jar:3.0.16:compile
| +- org.opendaylight.yangtools:rfc6241-model-api:jar:1.0.16:compile
| +- org.opendaylight.yangtools:rfc6241-parser-support:jar:1.0.16:compile
| +- org.opendaylight.yangtools:rfc6536-model-api:jar:3.0.16:compile
| +- org.opendaylight.yangtools:rfc6536-parser-support:jar:3.0.16:compile
| +- org.opendaylight.yangtools:rfc7952-model-api:jar:3.0.16:compile
| +- org.opendaylight.yangtools:rfc7952-parser-support:jar:3.0.16:compile
| +- org.opendaylight.yangtools:rfc8040-model-api:jar:3.0.16:compile
| +- org.opendaylight.yangtools:rfc8040-parser-support:jar:3.0.16:compile
| +- org.opendaylight.yangtools:rfc8528-model-api:jar:3.0.16:compile
| +- org.opendaylight.yangtools:rfc8528-parser-support:jar:3.0.16:compile
| - org.opendaylight.yangtools:yang-xpath-impl:jar:1.0.16:compile
+- org.opendaylight.mdsal:mdsal-binding-generator-util:jar:2.0.17:compile
| - org.opendaylight.yangtools:yang-model-util:jar:3.0.16:compile
+- org.opendaylight.yangtools:yang-data-api:jar:3.0.16:compile
| - org.opendaylight.yangtools:util:jar:3.0.16:compile
| - tech.pantheon.triemap:triemap:jar:1.1.0:compile
+- org.osgi:org.osgi.compendium:jar:5.0.0:compile
+- com.fujitsu.fnc.mdsaladaptor:mdsaladaptor-api:jar:7.3.0-SNAPSHOT:compile
| - org.json:json:jar:20231013:compile
+- de.danielbechler:java-object-diff:jar:0.92:compile
+- commons-io:commons-io:jar:2.13.0:compile
+- org.opendaylight.yangtools:yang-data-codec-gson:jar:3.0.16:compile
| - org.opendaylight.yangtools:yang-data-util:jar:3.0.16:compile
| - org.opendaylight.yangtools:rfc8528-data-api:jar:3.0.16:compile
+- org.opendaylight.yangtools:yang-data-codec-xml:jar:3.0.16:compile
| +- org.codehaus.woodstox:stax2-api:jar:4.2:compile
| - org.opendaylight.yangtools:rfc7952-data-api:jar:3.0.16:compile
+- org.opendaylight.netconf:netconf-api:jar:1.7.4:compile
| +- io.netty:netty-transport:jar:4.1.51.Final:compile
| | +- io.netty:netty-common:jar:4.1.51.Final:compile
| | +- io.netty:netty-buffer:jar:4.1.51.Final:compile
| | - io.netty:netty-resolver:jar:4.1.51.Final:compile
| +- org.opendaylight.netconf:ietf-netconf-monitoring:jar:1.7.4:compile
| +- org.opendaylight.netconf:ietf-netconf-monitoring-extension:jar:1.7.4:compile
| - org.opendaylight.mdsal.binding.model.ietf:rfc6991:jar:2.0.17:compile
+- org.opendaylight.yangtools:yang-model-api:jar:3.0.16:compile
| - org.opendaylight.yangtools:yang-xpath-api:jar:1.0.16:compile
+- org.apache.karaf.shell:org.apache.karaf.shell.console:jar:4.3.9:compile
| +- org.jline:jline:jar:3.21.0:compile
| +- org.fusesource.jansi:jansi:jar:2.4.0:compile
| +- org.apache.karaf.jaas:org.apache.karaf.jaas.modules:jar:4.3.9:compile
| | +- org.apache.karaf.jaas:org.apache.karaf.jaas.config:jar:4.3.9:compile
| | +- jakarta.xml.bind:jakarta.xml.bind-api:jar:2.3.3:compile
| | | - jakarta.activation:jakarta.activation-api:jar:1.2.2:compile
| | +- org.glassfish.jaxb:jaxb-runtime:jar:2.3.3:compile
| | | +- org.glassfish.jaxb:txw2:jar:2.3.3:compile
| | | +- com.sun.istack:istack-commons-runtime:jar:3.0.11:compile
| | | - com.sun.activation:jakarta.activation:jar:1.2.2:runtime
| | - javax.activation:activation:jar:1.1.1:compile
| +- org.apache.karaf.jaas:org.apache.karaf.jaas.boot:jar:4.3.9:compile
| +- org.apache.karaf.shell:org.apache.karaf.shell.core:jar:4.3.9:compile
| - org.apache.sshd:sshd-osgi:jar:2.9.2:compile
+- io.github.hakky54:logcaptor:jar:2.9.2:test
| +- ch.qos.logback:logback-classic:jar:1.3.14:test
| | - ch.qos.logback:logback-core:jar:1.3.14:test
| +- org.apache.logging.log4j:log4j-to-slf4j:jar:2.22.0:test
| - org.slf4j:jul-to-slf4j:jar:2.0.9:test
+- org.slf4j:slf4j-api:jar:1.7.25:compile
+- org.javatuples:javatuples:jar:1.2:compile
+- com.fasterxml.jackson.core:jackson-databind:jar:2.16.1:provided
| +- com.fasterxml.jackson.core:jackson-annotations:jar:2.16.1:provided
| - com.fasterxml.jackson.core:jackson-core:jar:2.16.1:provided
+- org.apache.kafka:kafka-clients:jar:3.2.0:compile
| +- com.github.luben:zstd-jni:jar:1.5.2-1:runtime
| +- org.lz4:lz4-java:jar:1.8.0:runtime
| - org.xerial.snappy:snappy-java:jar:1.1.8.4:runtime
+- org.mockito:mockito-inline:jar:3.9.0:test
+- org.robolectric:robolectric-utils:jar:3.1.2:test
| +- org.ow2.asm:asm:jar:5.0.1:test
| +- org.ow2.asm:asm-commons:jar:5.0.1:test
| | - org.ow2.asm:asm-tree:jar:5.0.1:test
| +- org.robolectric:robolectric-annotations:jar:3.1.2:test
| - com.google.android.apps.common.testing.accessibility.framework:accessibility-test-framework:jar:2.1:test
| +- org.hamcrest:hamcrest-library:jar:1.3:test
| - com.google.protobuf:protobuf-java:jar:2.6.1:test
+- com.theoryinpractise:googleformatter-maven-plugin:maven-plugin:1.0.6:compile
| +- org.apache.maven.plugin-tools:maven-plugin-annotations:jar:3.4:compile
| | - org.apache.maven:maven-artifact:jar:3.0:compile
| +- org.codehaus.plexus:plexus-utils:jar:2.0.5:compile
| +- org.apache.maven:maven-plugin-api:jar:3.3.9:compile
| | +- org.apache.maven:maven-model:jar:3.3.9:compile
| | - org.eclipse.sisu:org.eclipse.sisu.plexus:jar:0.3.2:compile
| | +- javax.enterprise:cdi-api:jar:1.0:compile
| | | - javax.annotation:jsr250-api:jar:1.0:compile
| | - org.eclipse.sisu:org.eclipse.sisu.inject:jar:0.3.2:compile
| +- org.apache.maven:maven-core:jar:3.3.9:compile
| | +- org.apache.maven:maven-settings:jar:3.3.9:compile
| | +- org.apache.maven:maven-settings-builder:jar:3.3.9:compile
| | | - org.apache.maven:maven-builder-support:jar:3.3.9:compile
| | +- org.apache.maven:maven-repository-metadata:jar:3.3.9:compile
| | +- org.apache.maven:maven-model-builder:jar:3.3.9:compile
| | +- org.apache.maven:maven-aether-provider:jar:3.3.9:compile
| | | - org.eclipse.aether:aether-spi:jar:1.0.2.v20150114:compile
| | +- org.eclipse.aether:aether-impl:jar:1.0.2.v20150114:compile
| | +- org.eclipse.aether:aether-api:jar:1.0.2.v20150114:compile
| | +- org.eclipse.aether:aether-util:jar:1.0.2.v20150114:compile
| | +- com.google.inject:guice:jar:no_aop:4.0:compile
| | | +- javax.inject:javax.inject:jar:1:compile
| | | - aopalliance:aopalliance:jar:1.0:compile
| | +- org.codehaus.plexus:plexus-interpolation:jar:1.21:compile
| | +- org.codehaus.plexus:plexus-classworlds:jar:2.5.2:compile
| | +- org.codehaus.plexus:plexus-component-annotations:jar:1.6:compile
| | - org.sonatype.plexus:plexus-sec-dispatcher:jar:1.3:compile
| | - org.sonatype.plexus:plexus-cipher:jar:1.4:compile
| +- org.codehaus.plexus:plexus-compiler-api:jar:2.3:compile
| +- org.apache.maven.scm:maven-scm-api:jar:1.9.4:compile
| +- org.apache.maven.scm:maven-scm-manager-plexus:jar:1.9.4:compile
| | - org.codehaus.plexus:plexus-container-default:jar:1.0-alpha-9:compile
| | - classworlds:classworlds:jar:1.1-alpha-2:compile
| +- org.apache.maven.scm:maven-scm-providers-standard:pom:1.9.4:compile
| | +- org.apache.maven.scm:maven-scm-provider-accurev:jar:1.9.4:runtime
| | +- org.apache.maven.scm:maven-scm-provider-bazaar:jar:1.9.4:runtime
| | +- org.apache.maven.scm:maven-scm-provider-clearcase:jar:1.9.4:runtime
| | +- org.apache.maven.scm:maven-scm-provider-cvsexe:jar:1.9.4:runtime
| | | - org.apache.maven.scm:maven-scm-provider-cvs-commons:jar:1.9.4:runtime
| | +- org.apache.maven.scm:maven-scm-provider-cvsjava:jar:1.9.4:runtime
| | | +- org.netbeans.lib:cvsclient:jar:20060125:runtime
| | | - ch.ethz.ganymed:ganymed-ssh2:jar:build210:runtime
| | +- org.apache.maven.scm:maven-scm-provider-gitexe:jar:1.9.4:runtime
| | | +- commons-lang:commons-lang:jar:2.6:runtime
| | | - org.apache.maven.scm:maven-scm-provider-git-commons:jar:1.9.4:runtime
| | +- org.apache.maven.scm:maven-scm-provider-hg:jar:1.9.4:runtime
| | +- org.apache.maven.scm:maven-scm-provider-perforce:jar:1.9.4:runtime
| | +- org.apache.maven.scm:maven-scm-provider-starteam:jar:1.9.4:runtime
| | +- org.apache.maven.scm:maven-scm-provider-svnexe:jar:1.9.4:runtime
| | | - org.apache.maven.scm:maven-scm-provider-svn-commons:jar:1.9.4:runtime
| | +- org.apache.maven.scm:maven-scm-provider-synergy:jar:1.9.4:runtime
| | +- org.apache.maven.scm:maven-scm-provider-vss:jar:1.9.4:runtime
| | +- org.apache.maven.scm:maven-scm-provider-tfs:jar:1.9.4:runtime
| | +- org.apache.maven.scm:maven-scm-provider-integrity:jar:1.9.4:runtime
| | | +- com.mks.api:mksapi-jar:jar:4.10.9049:runtime
| | | - org.codehaus.groovy:groovy-all:jar:1.7.6:runtime
| | - org.apache.maven.scm:maven-scm-provider-jazz:jar:1.9.4:runtime
| +- com.google.truth:truth:jar:0.28:compile
| +- com.google.googlejavaformat:google-java-format:jar:1.0:compile
| | +- com.beust:jcommander:jar:1.47:compile
| | - org.eclipse.jdt:org.eclipse.jdt.core:jar:3.10.0:compile
| | +- org.eclipse.core:org.eclipse.core.resources:jar:3.7.100:compile
| | | - org.eclipse.core:org.eclipse.core.expressions:jar:3.4.300:compile
| | +- org.eclipse.core:org.eclipse.core.runtime:jar:3.7.0:compile
| | | +- org.eclipse.osgi:org.eclipse.osgi:jar:3.7.1:compile
| | | +- org.eclipse.equinox:org.eclipse.equinox.common:jar:3.6.0:compile
| | | +- org.eclipse.core:org.eclipse.core.jobs:jar:3.5.100:compile
| | | +- org.eclipse.equinox:org.eclipse.equinox.registry:jar:3.5.101:compile
| | | +- org.eclipse.equinox:org.eclipse.equinox.preferences:jar:3.4.1:compile
| | | +- org.eclipse.core:org.eclipse.core.contenttype:jar:3.4.100:compile
| | | - org.eclipse.equinox:org.eclipse.equinox.app:jar:1.3.100:compile
| | +- org.eclipse.core:org.eclipse.core.filesystem:jar:1.3.100:compile
| | - org.eclipse.text:org.eclipse.text:jar:3.5.101:compile
| | - org.eclipse.core:org.eclipse.core.commands:jar:3.6.0:compile
| - com.google.errorprone:error_prone_annotations:jar:2.0.9:compile
+- au.com.acegi:xml-format-maven-plugin:maven-plugin:3.2.0:compile
| +- org.apache.commons:commons-lang3:jar:3.12.0:compile
| - org.dom4j:dom4j:jar:2.0.3:compile
+- org.mockito:mockito-core:jar:3.5.10:compile
| +- net.bytebuddy:byte-buddy:jar:1.10.13:compile
| +- net.bytebuddy:byte-buddy-agent:jar:1.10.13:compile
| - org.objenesis:objenesis:jar:3.1:compile
+- junit:junit:jar:4.13:compile
| - org.hamcrest:hamcrest-core:jar:1.3:compile
+- org.junit.jupiter:junit-jupiter:jar:5.7.2:test
| +- org.junit.jupiter:junit-jupiter-api:jar:5.7.2:test
| | +- org.apiguardian:apiguardian-api:jar:1.1.0:test
| | +- org.opentest4j:opentest4j:jar:1.2.0:test
| | - org.junit.platform:junit-platform-commons:jar:1.7.2:test
| +- org.junit.jupiter:junit-jupiter-params:jar:5.7.2:test
| - org.junit.jupiter:junit-jupiter-engine:jar:5.7.2:test
| - org.junit.platform:junit-platform-engine:jar:1.7.2:test
+- org.slf4j:log4j-over-slf4j:jar:1.7.25:compile
+- org.apache.logging.log4j:log4j-slf4j-impl:jar:2.20.0:compile
| - org.apache.logging.log4j:log4j-core:jar:2.20.0:runtime
+- org.slf4j:jcl-over-slf4j:jar:1.7.25:compile
+- org.apache.logging.log4j:log4j-api:jar:2.20.0:compile
+- commons-net:commons-net:jar:3.9.0:compile
+- org.powermock:powermock-api-mockito2:jar:2.0.7:test
| - org.powermock:powermock-api-support:jar:2.0.7:test
| +- org.powermock:powermock-reflect:jar:2.0.7:test
| - org.powermock:powermock-core:jar:2.0.7:test
+- org.powermock:powermock-module-junit4:jar:2.0.7:test
| - org.powermock:powermock-module-junit4-common:jar:2.0.7:test
+- org.apache.felix:org.apache.felix.scr.annotations:jar:1.9.6:compile
+- org.apache.felix:org.apache.felix.scr:jar:1.8.2:compile
| - org.codehaus.mojo:animal-sniffer-annotations:jar:1.9:compile
+- org.apache.felix:org.apache.felix.framework:jar:4.2.1:compile
- org.eclipse.jdt:org.eclipse.jdt.annotation:jar:2.2.100:compile

Get not only message but also cause

Hi.
I wanna get throwable class from logger.
I debugged and found throwable class name from listAppender.list.get(0).getThrowableProxy().getClassName().
It would be nice that LogCaptor#getLogs() returns a model that have message and throwable class name.
What do you think?

Allow to retrieve logs for custom log levels

** Feature description **
If I configured my e.g. Log4jLogger with custom log levels (e.g. LogLevel.forName("CUSTOM_LEVEL", 401)), the I would want to be able to call a method like LogCaptor#getLogs(CUSTOM_LEVEL); or LogCaptor#getLogs(CUSTOM_LEVEL.getName()); to get all the relevant logs for my custom LogLevel.

** Possible solution **
I haven't tried it, but changing the visibility of the LogCaptor#getLogs(Level level); method from private to public might already do the trick.

** Alternative solution **
An alternative currently is to call getLogs() and then filter the logs by myself. That's not really a problem, but the feature is easy to implement and quite useful, so why not implement it.

Are we supposed to close LogCaptor at the end of a test?

Describe the bug
This is more of a question. But colleague of mine noticed that LogCaptor implements AutoCloseable interface. So he asked if we should use try-with-resources or call close() method in the end of the test.

Because the simple example in the REAMDE file doesn't do that. It simply calls LogCaptor.forClass(...) stores it in the variable and that's it.

@Test
 public void logInfoAndWarnMessages() {
        LogCaptor logCaptor = LogCaptor.forClass(FooService.class);

        // just do some stuff
}

Is there any risk of not calling close() or not using the variation of reusing the captor between tests and using BeforeAll/AfterAll hooks?

Environmental Data:

  • LogCaptor version: 2.7.10
  • Java Version: 17
  • Gradle Version: 7.6
  • OS: Windows/Linux

Running the unit test directly from IDEA gives error

Describe the bug
when running test class via intellij idea : IntelliJ IDEA 2021.3.1
I get the following:

/usr/lib/jvm/java-1.11.0-openjdk-amd64/bin/java -ea -Didea.test.cyclic.buffer.size=1048576 -javaagent:/.local/share/JetBrains/Toolbox/apps/IDEA-U/ch-0/213.6461.79/lib/idea_rt.jar=43869:/.local/share/JetBrains/Toolbox/apps/IDEA-U/ch-0/213.6461.79/bin -Dfile.encoding=UTF-8 -classpath /.m2/repository/org/junit/platform/junit-platform-launcher/1.7.2/junit-platform-launcher-1.7.2.jar:/.local/share/JetBrains/Toolbox/apps/IDEA-U/ch-0/213.6461.79/lib/idea_rt.jar:/.local/share/JetBrains/Toolbox/apps/IDEA-U/ch-0/213.6461.79/plugins/junit/lib/junit5-rt.jar:/.local/share/JetBrains/Toolbox/apps/IDEA-U/ch-0/213.6461.79/plugins/junit/lib/junit-rt.jar:/.m2/repository/org/threeten/threetenbp/1.5.1/threetenbp-1.5.1.jar:/.m2/repository/com/google/guava/guava/31.0.1-jre/guava-31.0.1-jre.jar:/.m2/repository/com/google/guava/failureaccess/1.0.1/failureaccess-1.0.1.jar:/.m2/repository/com/google/guava/listenablefuture/9999.0-empty-to-avoid-conflict-with-guava/listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar:/.m2/repository/com/google/code/findbugs/jsr305/3.0.2/jsr305-3.0.2.jar:/.m2/repository/org/checkerframework/checker-qual/3.12.0/checker-qual-3.12.0.jar:/.m2/repository/com/google/errorprone/error_prone_annotations/2.7.1/error_prone_annotations-2.7.1.jar:/.m2/repository/com/google/j2objc/j2objc-annotations/1.3/j2objc-annotations-1.3.jar:/.m2/repository/org/slf4j/slf4j-log4j12/1.7.32/slf4j-log4j12-1.7.32.jar:/.m2/repository/org/slf4j/slf4j-api/1.7.32/slf4j-api-1.7.32.jar:/.m2/repository/log4j/log4j/1.2.17/log4j-1.2.17.jar:/.m2/repository/org/springframework/boot/spring-boot-starter-security/2.5.4/spring-boot-starter-security-2.5.4.jar:/.m2/repository/org/springframework/boot/spring-boot-starter/2.5.4/spring-boot-starter-2.5.4.jar:/.m2/repository/org/springframework/boot/spring-boot/2.5.4/spring-boot-2.5.4.jar:/.m2/repository/org/springframework/spring-core/5.3.9/spring-core-5.3.9.jar:/.m2/repository/org/springframework/spring-jcl/5.3.9/spring-jcl-5.3.9.jar:/.m2/repository/org/springframework/spring-context/5.3.9/spring-context-5.3.9.jar:/.m2/repository/org/springframework/spring-aop/5.3.9/spring-aop-5.3.9.jar:/.m2/repository/org/springframework/spring-beans/5.3.9/spring-beans-5.3.9.jar:/.m2/repository/org/springframework/spring-expression/5.3.9/spring-expression-5.3.9.jar:/.m2/repository/org/springframework/boot/spring-boot-autoconfigure/2.5.4/spring-boot-autoconfigure-2.5.4.jar:/.m2/repository/jakarta/annotation/jakarta.annotation-api/1.3.5/jakarta.annotation-api-1.3.5.jar:/.m2/repository/org/yaml/snakeyaml/1.28/snakeyaml-1.28.jar:/.m2/repository/org/springframework/security/spring-security-config/5.5.2/spring-security-config-5.5.2.jar:/.m2/repository/org/springframework/security/spring-security-core/5.5.2/spring-security-core-5.5.2.jar:/.m2/repository/org/springframework/security/spring-security-crypto/5.5.2/spring-security-crypto-5.5.2.jar:/.m2/repository/org/springframework/security/spring-security-web/5.5.2/spring-security-web-5.5.2.jar:/.m2/repository/org/springframework/spring-web/5.3.9/spring-web-5.3.9.jar:/.m2/repository/ch/aaap/harvest-client/1.1.1/harvest-client-1.1.1.jar:/.m2/repository/com/squareup/retrofit2/retrofit/2.3.0/retrofit-2.3.0.jar:/.m2/repository/com/squareup/okhttp3/okhttp/3.14.9/okhttp-3.14.9.jar:/.m2/repository/com/squareup/okio/okio/1.6.0/okio-1.6.0.jar:/.m2/repository/com/squareup/retrofit2/converter-gson/2.3.0/converter-gson-2.3.0.jar:/.m2/repository/com/google/code/gson/gson/2.8.7/gson-2.8.7.jar:/.m2/repository/com/squareup/okhttp3/logging-interceptor/3.14.9/logging-interceptor-3.14.9.jar:/.m2/repository/com/fatboyindustrial/gson-javatime-serialisers/gson-javatime-serialisers/1.1.1/gson-javatime-serialisers-1.1.1.jar:/.m2/repository/org/immutables/gson/2.5.6/gson-2.5.6.jar:/.m2/repository/com/typesafe/config/1.3.3/config-1.3.3.jar:/.m2/repository/io/gsonfire/gson-fire/1.8.5/gson-fire-1.8.5.jar:/.m2/repository/org/mapstruct/mapstruct/1.4.2.Final/mapstruct-1.4.2.Final.jar:/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jsr310/2.12.4/jackson-datatype-jsr310-2.12.4.jar:/.m2/repository/com/fasterxml/jackson/core/jackson-annotations/2.12.4/jackson-annotations-2.12.4.jar:/.m2/repository/com/fasterxml/jackson/core/jackson-core/2.12.4/jackson-core-2.12.4.jar:/.m2/repository/com/fasterxml/jackson/core/jackson-databind/2.12.4/jackson-databind-2.12.4.jar:/.m2/repository/org/springframework/boot/spring-boot-starter-websocket/2.5.4/spring-boot-starter-websocket-2.5.4.jar:/.m2/repository/org/springframework/boot/spring-boot-starter-web/2.5.4/spring-boot-starter-web-2.5.4.jar:/.m2/repository/org/springframework/boot/spring-boot-starter-json/2.5.4/spring-boot-starter-json-2.5.4.jar:/.m2/repository/com/fasterxml/jackson/datatype/jackson-datatype-jdk8/2.12.4/jackson-datatype-jdk8-2.12.4.jar:/.m2/repository/com/fasterxml/jackson/module/jackson-module-parameter-names/2.12.4/jackson-module-parameter-names-2.12.4.jar:/.m2/repository/org/springframework/boot/spring-boot-starter-tomcat/2.5.4/spring-boot-starter-tomcat-2.5.4.jar:/.m2/repository/org/apache/tomcat/embed/tomcat-embed-core/9.0.52/tomcat-embed-core-9.0.52.jar:/.m2/repository/org/apache/tomcat/embed/tomcat-embed-el/9.0.52/tomcat-embed-el-9.0.52.jar:/.m2/repository/org/apache/tomcat/embed/tomcat-embed-websocket/9.0.52/tomcat-embed-websocket-9.0.52.jar:/.m2/repository/org/springframework/spring-webmvc/5.3.9/spring-webmvc-5.3.9.jar:/.m2/repository/org/springframework/spring-messaging/5.3.9/spring-messaging-5.3.9.jar:/.m2/repository/org/springframework/spring-websocket/5.3.9/spring-websocket-5.3.9.jar:/.m2/repository/commons-validator/commons-validator/1.7/commons-validator-1.7.jar:/.m2/repository/commons-beanutils/commons-beanutils/1.9.4/commons-beanutils-1.9.4.jar:/.m2/repository/commons-logging/commons-logging/1.2/commons-logging-1.2.jar:/.m2/repository/commons-collections/commons-collections/3.2.2/commons-collections-3.2.2.jar:/.m2/repository/commons-digester/commons-digester/2.1/commons-digester-2.1.jar:/.m2/repository/org/apache/poi/poi/3.14/poi-3.14.jar:/.m2/repository/commons-codec/commons-codec/1.15/commons-codec-1.15.jar:/.m2/repository/org/passay/passay/1.6.1/passay-1.6.1.jar:/.m2/repository/commons-io/commons-io/2.11.0/commons-io-2.11.0.jar:/.m2/repository/javax/validation/validation-api/2.0.1.Final/validation-api-2.0.1.Final.jar:/.m2/repository/org/springframework/boot/spring-boot-starter-aop/2.5.4/spring-boot-starter-aop-2.5.4.jar:/.m2/repository/org/aspectj/aspectjweaver/1.9.7/aspectjweaver-1.9.7.jar:/.m2/repository/org/apache/commons/commons-lang3/3.12.0/commons-lang3-3.12.0.jar:/.m2/repository/org/apache/oltu/oauth2/org.apache.oltu.oauth2.client/1.0.1/org.apache.oltu.oauth2.client-1.0.1.jar:/.m2/repository/org/apache/oltu/oauth2/org.apache.oltu.oauth2.common/1.0.1/org.apache.oltu.oauth2.common-1.0.1.jar:/.m2/repository/org/json/json/20140107/json-20140107.jar:/.m2/repository/org/mockito/mockito-junit-jupiter/3.9.0/mockito-junit-jupiter-3.9.0.jar:/.m2/repository/org/mockito/mockito-core/4.0.0/mockito-core-4.0.0.jar:/.m2/repository/net/bytebuddy/byte-buddy/1.10.22/byte-buddy-1.10.22.jar:/.m2/repository/net/bytebuddy/byte-buddy-agent/1.10.22/byte-buddy-agent-1.10.22.jar:/.m2/repository/org/objenesis/objenesis/3.2/objenesis-3.2.jar:/.m2/repository/org/junit/jupiter/junit-jupiter-api/5.7.2/junit-jupiter-api-5.7.2.jar:/.m2/repository/org/apiguardian/apiguardian-api/1.1.0/apiguardian-api-1.1.0.jar:/.m2/repository/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar:/.m2/repository/org/junit/platform/junit-platform-commons/1.7.2/junit-platform-commons-1.7.2.jar:/.m2/repository/io/jsonwebtoken/jjwt-api/0.11.2/jjwt-api-0.11.2.jar:/.m2/repository/org/springframework/boot/spring-boot-starter-data-jpa/2.5.4/spring-boot-starter-data-jpa-2.5.4.jar:/.m2/repository/org/springframework/boot/spring-boot-starter-jdbc/2.5.4/spring-boot-starter-jdbc-2.5.4.jar:/.m2/repository/com/zaxxer/HikariCP/4.0.3/HikariCP-4.0.3.jar:/.m2/repository/org/springframework/spring-jdbc/5.3.9/spring-jdbc-5.3.9.jar:/.m2/repository/org/springframework/spring-tx/5.3.9/spring-tx-5.3.9.jar:/.m2/repository/jakarta/transaction/jakarta.transaction-api/1.3.3/jakarta.transaction-api-1.3.3.jar:/.m2/repository/jakarta/persistence/jakarta.persistence-api/2.2.3/jakarta.persistence-api-2.2.3.jar:/.m2/repository/org/hibernate/hibernate-core/5.4.32.Final/hibernate-core-5.4.32.Final.jar:/.m2/repository/org/jboss/logging/jboss-logging/3.4.2.Final/jboss-logging-3.4.2.Final.jar:/.m2/repository/org/javassist/javassist/3.27.0-GA/javassist-3.27.0-GA.jar:/.m2/repository/antlr/antlr/2.7.7/antlr-2.7.7.jar:/.m2/repository/org/jboss/jandex/2.2.3.Final/jandex-2.2.3.Final.jar:/.m2/repository/com/fasterxml/classmate/1.5.1/classmate-1.5.1.jar:/.m2/repository/org/dom4j/dom4j/2.1.3/dom4j-2.1.3.jar:/.m2/repository/org/hibernate/common/hibernate-commons-annotations/5.1.2.Final/hibernate-commons-annotations-5.1.2.Final.jar:/.m2/repository/org/glassfish/jaxb/jaxb-runtime/2.3.5/jaxb-runtime-2.3.5.jar:/.m2/repository/jakarta/xml/bind/jakarta.xml.bind-api/2.3.3/jakarta.xml.bind-api-2.3.3.jar:/.m2/repository/org/glassfish/jaxb/txw2/2.3.5/txw2-2.3.5.jar:/.m2/repository/com/sun/istack/istack-commons-runtime/3.0.12/istack-commons-runtime-3.0.12.jar:/.m2/repository/com/sun/activation/jakarta.activation/1.2.2/jakarta.activation-1.2.2.jar:/.m2/repository/org/springframework/data/spring-data-jpa/2.5.4/spring-data-jpa-2.5.4.jar:/.m2/repository/org/springframework/data/spring-data-commons/2.5.4/spring-data-commons-2.5.4.jar:/.m2/repository/org/springframework/spring-orm/5.3.9/spring-orm-5.3.9.jar:/.m2/repository/org/springframework/spring-aspects/5.3.9/spring-aspects-5.3.9.jar:/.m2/repository/org/springframework/security/spring-security-jwt/1.1.1.RELEASE/spring-security-jwt-1.1.1.RELEASE.jar:/.m2/repository/org/bouncycastle/bcpkix-jdk15on/1.64/bcpkix-jdk15on-1.64.jar:/.m2/repository/org/bouncycastle/bcprov-jdk15on/1.64/bcprov-jdk15on-1.64.jar:/.m2/repository/com/h2database/h2/1.4.200/h2-1.4.200.jar:/.m2/repository/org/apache/xmlbeans/xmlbeans/2.6.0/xmlbeans-2.6.0.jar:/.m2/repository/stax/stax-api/1.0.1/stax-api-1.0.1.jar:/.m2/repository/io/jsonwebtoken/jjwt-impl/0.11.2/jjwt-impl-0.11.2.jar:/.m2/repository/org/apache/poi/poi-ooxml/3.14/poi-ooxml-3.14.jar:/.m2/repository/org/apache/poi/poi-ooxml-schemas/3.14/poi-ooxml-schemas-3.14.jar:/.m2/repository/com/github/virtuald/curvesapi/1.03/curvesapi-1.03.jar:/.m2/repository/com/squareup/okhttp/logging-interceptor/2.7.5/logging-interceptor-2.7.5.jar:/.m2/repository/com/squareup/okhttp/okhttp/2.7.5/okhttp-2.7.5.jar:/.m2/repository/org/springframework/boot/spring-boot-starter-test/2.5.4/spring-boot-starter-test-2.5.4.jar:/.m2/repository/org/springframework/boot/spring-boot-test/2.5.4/spring-boot-test-2.5.4.jar:/.m2/repository/org/springframework/boot/spring-boot-test-autoconfigure/2.5.4/spring-boot-test-autoconfigure-2.5.4.jar:/.m2/repository/com/jayway/jsonpath/json-path/2.5.0/json-path-2.5.0.jar:/.m2/repository/net/minidev/json-smart/2.4.7/json-smart-2.4.7.jar:/.m2/repository/net/minidev/accessors-smart/2.4.7/accessors-smart-2.4.7.jar:/.m2/repository/org/ow2/asm/asm/9.1/asm-9.1.jar:/.m2/repository/org/assertj/assertj-core/3.19.0/assertj-core-3.19.0.jar:/.m2/repository/org/hamcrest/hamcrest/2.2/hamcrest-2.2.jar:/.m2/repository/org/junit/jupiter/junit-jupiter/5.7.2/junit-jupiter-5.7.2.jar:/.m2/repository/org/junit/jupiter/junit-jupiter-params/5.7.2/junit-jupiter-params-5.7.2.jar:/.m2/repository/org/junit/jupiter/junit-jupiter-engine/5.7.2/junit-jupiter-engine-5.7.2.jar:/.m2/repository/org/junit/platform/junit-platform-engine/1.7.2/junit-platform-engine-1.7.2.jar:/.m2/repository/org/skyscreamer/jsonassert/1.5.0/jsonassert-1.5.0.jar:/.m2/repository/com/vaadin/external/google/android-json/0.0.20131108.vaadin1/android-json-0.0.20131108.vaadin1.jar:/.m2/repository/org/springframework/spring-test/5.3.9/spring-test-5.3.9.jar:/.m2/repository/org/xmlunit/xmlunit-core/2.8.2/xmlunit-core-2.8.2.jar:/.m2/repository/org/mapstruct/mapstruct-processor/1.4.2.Final/mapstruct-processor-1.4.2.Final.jar:/.m2/repository/org/apache/poi/poi-scratchpad/3.14/poi-scratchpad-3.14.jar:/.m2/repository/io/jsonwebtoken/jjwt-jackson/0.11.2/jjwt-jackson-0.11.2.jar:/.m2/repository/org/springframework/boot/spring-boot-properties-migrator/2.5.4/spring-boot-properties-migrator-2.5.4.jar:/.m2/repository/org/springframework/boot/spring-boot-configuration-metadata/2.5.4/spring-boot-configuration-metadata-2.5.4.jar:/.m2/repository/mysql/mysql-connector-java/8.0.26/mysql-connector-java-8.0.26.jar:/.m2/repository/org/springframework/boot/spring-boot-configuration-processor/2.5.4/spring-boot-configuration-processor-2.5.4.jar:/.m2/repository/org/apache/poi/ooxml-schemas/1.3/ooxml-schemas-1.3.jar:/.m2/repository/io/springfox/springfox-swagger2/3.0.0/springfox-swagger2-3.0.0.jar:/.m2/repository/io/springfox/springfox-spi/3.0.0/springfox-spi-3.0.0.jar:/.m2/repository/io/springfox/springfox-core/3.0.0/springfox-core-3.0.0.jar:/.m2/repository/org/springframework/plugin/spring-plugin-core/2.0.0.RELEASE/spring-plugin-core-2.0.0.RELEASE.jar:/.m2/repository/org/springframework/plugin/spring-plugin-metadata/2.0.0.RELEASE/spring-plugin-metadata-2.0.0.RELEASE.jar:/.m2/repository/io/springfox/springfox-schema/3.0.0/springfox-schema-3.0.0.jar:/.m2/repository/io/springfox/springfox-swagger-common/3.0.0/springfox-swagger-common-3.0.0.jar:/.m2/repository/io/springfox/springfox-spring-web/3.0.0/springfox-spring-web-3.0.0.jar:/.m2/repository/io/github/classgraph/classgraph/4.8.83/classgraph-4.8.83.jar:/.m2/repository/io/springfox/springfox-spring-webmvc/3.0.0/springfox-spring-webmvc-3.0.0.jar:/.m2/repository/io/swagger/swagger-annotations/1.5.20/swagger-annotations-1.5.20.jar:/.m2/repository/io/swagger/swagger-models/1.5.20/swagger-models-1.5.20.jar:/.m2/repository/io/swagger/core/v3/swagger-annotations/2.1.2/swagger-annotations-2.1.2.jar:/.m2/repository/io/springfox/springfox-spring-webflux/3.0.0/springfox-spring-webflux-3.0.0.jar:/.m2/repository/org/springframework/security/spring-security-test/5.5.2/spring-security-test-5.5.2.jar:/.m2/repository/org/springframework/boot/spring-boot-maven-plugin/2.5.6/spring-boot-maven-plugin-2.5.6.jar:/.m2/repository/org/springframework/boot/spring-boot-buildpack-platform/2.5.4/spring-boot-buildpack-platform-2.5.4.jar:/.m2/repository/net/java/dev/jna/jna-platform/5.7.0/jna-platform-5.7.0.jar:/.m2/repository/net/java/dev/jna/jna/5.7.0/jna-5.7.0.jar:/.m2/repository/org/apache/commons/commons-compress/1.20/commons-compress-1.20.jar:/.m2/repository/org/apache/httpcomponents/httpclient/4.5.13/httpclient-4.5.13.jar:/.m2/repository/org/apache/httpcomponents/httpcore/4.4.14/httpcore-4.4.14.jar:/.m2/repository/org/tomlj/tomlj/1.0.0/tomlj-1.0.0.jar:/.m2/repository/org/antlr/antlr4-runtime/4.7.2/antlr4-runtime-4.7.2.jar:/.m2/repository/org/springframework/boot/spring-boot-loader-tools/2.5.4/spring-boot-loader-tools-2.5.4.jar:/.m2/repository/org/apache/maven/shared/maven-common-artifact-filters/3.2.0/maven-common-artifact-filters-3.2.0.jar:/.m2/repository/org/apache/maven/maven-artifact/3.1.1/maven-artifact-3.1.1.jar:/.m2/repository/org/codehaus/plexus/plexus-utils/3.2.1/plexus-utils-3.2.1.jar:/.m2/repository/org/apache/maven/maven-model/3.1.1/maven-model-3.1.1.jar:/.m2/repository/org/apache/maven/maven-core/3.1.1/maven-core-3.1.1.jar:/.m2/repository/org/apache/maven/maven-settings/3.1.1/maven-settings-3.1.1.jar:/.m2/repository/org/apache/maven/maven-settings-builder/3.1.1/maven-settings-builder-3.1.1.jar:/.m2/repository/org/codehaus/plexus/plexus-interpolation/1.19/plexus-interpolation-1.19.jar:/.m2/repository/org/codehaus/plexus/plexus-component-annotations/1.5.5/plexus-component-annotations-1.5.5.jar:/.m2/repository/org/sonatype/plexus/plexus-sec-dispatcher/1.3/plexus-sec-dispatcher-1.3.jar:/.m2/repository/org/sonatype/plexus/plexus-cipher/1.4/plexus-cipher-1.4.jar:/.m2/repository/org/apache/maven/maven-repository-metadata/3.1.1/maven-repository-metadata-3.1.1.jar:/.m2/repository/org/apache/maven/maven-plugin-api/3.6.3/maven-plugin-api-3.6.3.jar:/.m2/repository/org/eclipse/sisu/org.eclipse.sisu.plexus/0.0.0.M5/org.eclipse.sisu.plexus-0.0.0.M5.jar:/.m2/repository/javax/enterprise/cdi-api/1.0/cdi-api-1.0.jar:/.m2/repository/javax/annotation/jsr250-api/1.0/jsr250-api-1.0.jar:/.m2/repository/javax/inject/javax.inject/1/javax.inject-1.jar:/.m2/repository/org/sonatype/sisu/sisu-guice/3.1.0/sisu-guice-3.1.0-no_aop.jar:/.m2/repository/aopalliance/aopalliance/1.0/aopalliance-1.0.jar:/.m2/repository/org/eclipse/sisu/org.eclipse.sisu.inject/0.0.0.M5/org.eclipse.sisu.inject-0.0.0.M5.jar:/.m2/repository/org/codehaus/plexus/plexus-classworlds/2.6.0/plexus-classworlds-2.6.0.jar:/.m2/repository/org/apache/maven/maven-model-builder/3.1.1/maven-model-builder-3.1.1.jar:/.m2/repository/org/apache/maven/maven-aether-provider/3.1.1/maven-aether-provider-3.1.1.jar:/.m2/repository/org/eclipse/aether/aether-api/0.9.0.M2/aether-api-0.9.0.M2.jar:/.m2/repository/org/eclipse/aether/aether-spi/0.9.0.M2/aether-spi-0.9.0.M2.jar:/.m2/repository/org/eclipse/aether/aether-util/0.9.0.M2/aether-util-0.9.0.M2.jar:/.m2/repository/org/eclipse/aether/aether-impl/0.9.0.M2/aether-impl-0.9.0.M2.jar:/.m2/repository/org/apache/maven/shared/maven-shared-utils/3.3.3/maven-shared-utils-3.3.3.jar:/.m2/repository/org/sonatype/plexus/plexus-build-api/0.0.7/plexus-build-api-0.0.7.jar:/.m2/repository/org/liquibase/liquibase-core/4.6.1/liquibase-core-4.6.1.jar:/.m2/repository/javax/xml/bind/jaxb-api/2.3.1/jaxb-api-2.3.1.jar:/.m2/repository/javax/activation/javax.activation-api/1.2.0/javax.activation-api-1.2.0.jar:/.m2/repository/jakarta/activation/jakarta.activation-api/1.2.2/jakarta.activation-api-1.2.2.jar:/.m2/repository/io/github/hakky54/logcaptor/2.7.8/logcaptor-2.7.8.jar:/.m2/repository/ch/qos/logback/logback-classic/1.2.5/logback-classic-1.2.5.jar:/.m2/repository/ch/qos/logback/logback-core/1.2.5/logback-core-1.2.5.jar:/.m2/repository/org/apache/logging/log4j/log4j-to-slf4j/2.14.1/log4j-to-slf4j-2.14.1.jar:/.m2/repository/org/apache/logging/log4j/log4j-api/2.14.1/log4j-api-2.14.1.jar:/.m2/repository/org/slf4j/jul-to-slf4j/1.7.32/jul-to-slf4j-1.7.32.jar:/.m2/repository/com/samskivert/jmustache/1.15/jmustache-1.15.jar com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit5 org.me.MyTest
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/.m2/repository/org/slf4j/slf4j-log4j12/1.7.32/slf4j-log4j12-1.7.32.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/.m2/repository/ch/qos/logback/logback-classic/1.2.5/logback-classic-1.2.5.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.slf4j.impl.Log4jLoggerFactory]

java.lang.IllegalArgumentException: SLF4J Logger implementation should be of the type [ch.qos.logback.classic.Logger] but found [org.slf4j.impl.Log4jLoggerAdapter].

at nl.altindag.log.util.ValidationUtils.requireLoggerOfType(ValidationUtils.java:36)
at nl.altindag.log.LogCaptor.<init>(LogCaptor.java:55)
at nl.altindag.log.LogCaptor.forClass(LogCaptor.java:82)
at org.me.MyTest.<init>(MyKeysTest.java:11)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
at org.junit.platform.commons.util.ReflectionUtils.newInstance(ReflectionUtils.java:513)
at org.junit.jupiter.engine.execution.ConstructorInvocation.proceed(ConstructorInvocation.java:56)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
at org.junit.jupiter.api.extension.InvocationInterceptor.interceptTestClassConstructor(InvocationInterceptor.java:72)
at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:77)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeTestClassConstructor(ClassBasedTestDescriptor.java:342)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.instantiateTestClass(ClassBasedTestDescriptor.java:289)
at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.instantiateTestClass(ClassTestDescriptor.java:79)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.instantiateAndPostProcessTestInstance(ClassBasedTestDescriptor.java:267)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$2(ClassBasedTestDescriptor.java:259)

To Reproduce

  1. Simple spring boot 2.5.4

Expected behavior
to run without errors like in maven but with debugging

Screenshots

Environmental Data:

  • Java Version 11
  • Maven Version 3.+
  • OS (Linux)

Additional context
probebly more config error on idea side then code bug

There appears to be a classloading problem when using LogCaptor with `@QuarkusTest` test classes

Describe the bug
When using logcaptor with quarkus in a test annotated with @QuarkusTest, an exception is thrown when creating a LogCaptor instance using the factory method(s). I followed the steps to exclude the quarkus slf4j implementation per the documentation, but now the error is SLF4J Logger implementation should be of the type [ch.qos.logback.classic.Logger] but found [ch.qos.logback.classic.Logger]. from ValidationUtils.java.

To Reproduce

  1. I created a MRE at this repository https://github.com/SanderSmee/mre-logcaptor-quarkus . It contains 2 tests, one with and one without @QuarkusTest to identify the problem

Expected behavior
A correct instantiation of a LogCaptor instance, so logging can be captured.

Environmental Data:

  • Java Version : openjdk version "11.0.13" 2021-10-19 LTS
  • Maven Version
  • Gradle Version: 7.3.3
  • OS : MacOS 21.2, MacBook M1Pro

Additional context
I'm aware of the classloading things that quarkus does when using tests with @QuarkusTest. So it's possible the solution to this problem lies with quarkus.

org.apache.logging.log4j loggers not supported like test shows

I'm working with apache.logging.log4j on existing classes, and followed the example test as below.

//Basic example class
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class FooService {

    private static final Logger LOGGER = LogManager.getLogger(FooService.class);

    public void sayHello() {
        LOGGER.info("Keyboard not responding. Press any key to continue...");
        LOGGER.warn("Congratulations, you are pregnant!");
    }

}

//Unit test:
import nl.altindag.log.LogCaptor;

public class FooServiceShould {
    @Test
    public void logInfoAndWarnMessages() {
        String expectedInfoMessage = "Keyboard not responding. Press any key to continue...";
        String expectedWarnMessage = "Congratulations, you are pregnant!";

        LogCaptor logCaptor = LogCaptor.forClass(FooService.class);
        //...
    }
}

However, i get the error shown below log4j-slf4j-impl cannot be present with log4j-to-slf4j using the same libraries as the demo.

import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager; //all existing classes are already setup with these libs

public class Address {
    
    private static final Logger logger = LogManager.getLogger(Address.class);
    
    public Address(int a, int b, int c, int d) {
    	//other code
        logger.error("Address is malformed.");
    }
}

import nl.altindag.log.LogCaptor;

public class AddressTest {
    @Test
    public void testAddressValidation() {
        LogCaptor logCaptor = LogCaptor.forClass(Address.class);
        
        new Address(999,0,0,0);
        
        assertTrue(logCaptor.getErrorLogs().contains("Address is malformed."));
    }
}
org.apache.logging.log4j.LoggingException: log4j-slf4j-impl cannot be present with log4j-to-slf4j
	at org.apache.logging.slf4j.Log4jLoggerFactory.validateContext(Log4jLoggerFactory.java:49)
	at org.apache.logging.slf4j.Log4jLoggerFactory.newLogger(Log4jLoggerFactory.java:39)
	at org.apache.logging.slf4j.Log4jLoggerFactory.newLogger(Log4jLoggerFactory.java:30)
	at org.apache.logging.log4j.spi.AbstractLoggerAdapter.getLogger(AbstractLoggerAdapter.java:54)
	at org.apache.logging.slf4j.Log4jLoggerFactory.getLogger(Log4jLoggerFactory.java:30)
	at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:363)
	at nl.altindag.log.LogCaptor.<init>(LogCaptor.java:49)
	at nl.altindag.log.LogCaptor.forClass(LogCaptor.java:69)
	at ....AddressTest.testAddressValidation(AddressTest.java:20)

and the console output

SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:~/.m2/repository/org/apache/logging/log4j/log4j-slf4j-impl/2.13.3/log4j-slf4j-impl-2.13.3.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:~/.m2/repository/ch/qos/logback/logback-classic/1.2.3/logback-classic-1.2.3.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.apache.logging.slf4j.Log4jLoggerFactory]

I'm not sure if i'm applying the pom exclusions properly, but it seems like it might be easier to make a version that is compatible with org.apache.logging.log4j instead

Expose MDC logback property map in LogEvent

Is your feature request related to a problem? Please describe.
We need to test that we're correctly passing down the MDC context into executors and completable futures

Describe the solution you'd like
Expose the MDC property map in the LogEvent object so that we can extract the map in our test assertions

Describe alternatives you've considered
None so far ๐Ÿ˜ž

Additional context
The ILoggingEvent interface from logback exposed the MDC property map via a get method.

Map<String, String> getMDCPropertyMap();

Would be awesome if we could populate the LogEvent object with a copy of the MDC map from the original event ๐Ÿฅบ

Would love to get together a PR if I can help ๐Ÿ˜„

LogEvent should implement toString()

Is your feature request related to a problem? Please describe.
Currently LogEvent does not have a toString() method implementation. As such, when they are report in assertion failures they should the class name and reference only which isn't useful e.g.

Collection should have size 1 but has size 2. Values: [nl.altindag.log.model.LogEvent@56b58bd6, nl.altindag.log.model.LogEvent@8301464]

Describe the solution you'd like
For a toString() method to be implemented that outputs the contents of the LogLevel object

Describe alternatives you've considered
None as there are no sutiable alternatives

Additional context
Add any other context or screenshots about the feature request here.

Not compatible with log4j2 2.17.2

Describe the bug
It's not compatible with log4j2 2.17.2. It happen after log4j2 broke compatibility with slf4j binding.

To Reproduce

  1. Migrate to log4j2 2.17.2
  2. Exception at Junit5 testing:
    java.lang.ExceptionInInitializerError
    Caused by: java.lang.ClassCastException: org.apache.logging.slf4j.SLF4JLoggerContext cannot be cast to org.apache.logging.log4j.core.LoggerContext

Expected behavior
No exception

Screenshots
If applicable, add screenshots to help explain your problem.

Environmental Data:

  • Java Version: 8
  • Maven Version
  • OS (Windows/Linux/MacOS): wsl(Ubuntu)

Additional context
Log4j2 supplied with new sfl4j adapter to fix compatibility issue: log4j-slf4j-impl and log4j-slf4j18-impl, but replacing log4j-to-slf4j adapter with them didn't work.

Add support for name based creation

Sometimes we have to monitor name based loggers and would be great to have such new method available:

 public static LogCaptor<String> forName(String name) {
        return new LogCaptor<>(name);
 }

IllegalArgumentException while using logcaptor

Describe the bug
Logcaptor is not working as expected. Getting the IllegalArgumentException

To Reproduce

  1. Use log captor as mentioned in the readme
  2. Getting this error when using it java.lang.IllegalArgumentException: SLF4J Logger implementation should be of the type [ch.qos.logback.classic.Logger] but found [org.slf4j.impl.Slf4jLogger].

We are using quarkus and logger from org.jboss.logging.Logger which implements Slf4jLogger.

Is it forcing us to use logback logger only instead Slf4jlogger?

We excluded the mentioned configuration and after that it's giving the class loader error

<classpathDependencyExcludes> <classpathDependencyExclude>org.jboss.slf4j:slf4j-jboss-logmanager</classpathDependencyExclude> </classpathDependencyExcludes>

Suppressing Console Output

In my unit tests I have code that intentionally throws an exception to verify that it gets handled gracefully (message gets logged but execution continues in this case.) I am successfully using LogCaptor to verify that the logging happened but the console ALSO shows the messages (and their associated stack traces) when the unit tests run - this makes for really long output. While it is only an annoyance for me, it would be ideal to be able to suppress the console output of these messages since I am intentionally causing them and am using LogCaptor to verify that they happened.

In the closed issues section I see a brief discussion of such a feature in 2021 but it doesn't appear to have been merged back into the main code branch. Does that feature sound like it fits this use case and, if so, can it be merged into the main code branch?

Upgrade to 2.8.0

Since the upgrade from 2.7.10 to 2.8.0 I get the following exception on my test:

  java.lang.NoClassDefFoundError: org/slf4j/spi/LoggingEventAware

	at java.lang.ClassLoader.defineClass1(Native Method)
	at java.lang.ClassLoader.defineClass(ClassLoader.java:757)
	at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
	at java.net.URLClassLoader.defineClass(URLClassLoader.java:473)
	at java.net.URLClassLoader.access$100(URLClassLoader.java:74)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:369)
	at java.net.URLClassLoader$1.run(URLClassLoader.java:363)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(URLClassLoader.java:362)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:419)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:352)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:352)
	at nl.altindag.log.LogCaptor.<init>(LogCaptor.java:55)
	at nl.altindag.log.LogCaptor.forClass(LogCaptor.java:82)
	at nl.corp.online.lib.reqresp.client.exception.ClientInterceptorExceptionLoggerTest.<clinit>(ClientInterceptorExceptionLoggerTest.java:31)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at org.junit.platform.commons.util.ReflectionUtils.newInstance(ReflectionUtils.java:550)
	at org.junit.jupiter.engine.execution.ConstructorInvocation.proceed(ConstructorInvocation.java:56)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
	at org.junit.jupiter.api.extension.InvocationInterceptor.interceptTestClassConstructor(InvocationInterceptor.java:73)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
	at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
	at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:77)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeTestClassConstructor(ClassBasedTestDescriptor.java:355)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.instantiateTestClass(ClassBasedTestDescriptor.java:302)
	at org.junit.jupiter.engine.descriptor.ClassTestDescriptor.instantiateTestClass(ClassTestDescriptor.java:79)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.instantiateAndPostProcessTestInstance(ClassBasedTestDescriptor.java:280)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$4(ClassBasedTestDescriptor.java:272)
	at java.util.Optional.orElseGet(Optional.java:267)
	at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$5(ClassBasedTestDescriptor.java:271)
	at org.junit.jupiter.engine.execution.TestInstancesProvider.getTestInstances(TestInstancesProvider.java:31)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$prepare$0(TestMethodTestDescriptor.java:102)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:101)
	at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:66)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$prepare$2(NodeTestTask.java:123)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.prepare(NodeTestTask.java:123)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:90)
	at java.util.ArrayList.forEach(ArrayList.java:1259)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at java.util.ArrayList.forEach(ArrayList.java:1259)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
	at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
	at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
	at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
	at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
	at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
	at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
	at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
	at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
	at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53)
	at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:57)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
	at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
Caused by: java.lang.ClassNotFoundException: org.slf4j.spi.LoggingEventAware
	at java.net.URLClassLoader.findClass(URLClassLoader.java:387)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:419)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:352)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:352)
	... 84 more

The code:

   @ExtendWith(MockitoExtension.class)
   class ClientInterceptorExceptionLoggerTest {

    private static final LogCaptor logCaptor = LogCaptor.forClass(ClientInterceptorExceptionLogger.class);

    @AfterEach
    public void clearLogs() {
        logCaptor.clearLogs();
    }

    @AfterAll
    public static void tearDown() {
        logCaptor.close();
    }

....

public void somemethod(){
...
        final String warnLogLine = logCaptor.getWarnLogs().get(0);
...
}

Is this a known issue? How do I solve it?

Issue with custom levels

Describe the bug

I'm not sure if I am doing something wrong but just adding the dependencies is causing tests to fail. Let me explain.

I am getting an error in classes that log to a custom level.

java.lang.NullPointerException: Cannot invoke "org.apache.logging.log4j.Level.getStandardLevel()" because "level" is null

	at org.apache.logging.slf4j.SLF4JLogger.isEnabledFor(SLF4JLogger.java:209)

To Reproduce

My company uses log4j2 with custom levels in the log4j settings.

        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.19.0</version>
        </dependency>

        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.19.0</version>
        </dependency>

The custom levels are added in the configuration.

<Configuration status="debug" name="product" packages="com.product.backend" monitorInterval="900">
    <CustomLevels>
        <CustomLevel name="PRODUCT_FINE" intLevel="460" />
        <CustomLevel name="PRODUCT_DEBUG" intLevel="450" />
    </CustomLevels>
    ...

Once I add log-captor to the dependencies I get the error. No tests have been written yet using log-captor.

When I debug I can check the LEVELS property and see that it does not contain the custom levels. Also the forName method is never called to add the levels from the custom configs.

Expected behavior

Support for custom levels. Maybe there is a trick that I didn't see in the readme. Any help is appreciated.

Screenshots
If applicable, add screenshots to help explain your problem.

Environmental Data:

  • Java Version
  • Maven Version
  • OS (Windows/Linux/MacOS)

Additional context
Add any other context about the problem here.

Prevent all loggers from printing to stdout

Is your feature request related to a problem? Please describe.
When LogCaptor is launched, it makes all loggers log to the most verbose level, and they print to stdout. This is making my tests slow by an order of magnitude (from less than 1s to around 5s), because some libraries I invoke in the tests log very heavily.

More so, in some tests where the input data is very large and it happens to gets logged by an underlying library, the tests time out because the printing to stdout takes very long. I could work around this using .disableLogs, but that couples my test to a bunch of internal class names of the underlying library.

Describe the solution you'd like
I would like to have a way to configure LogCaptor to prevent all loggers from printing to stdout. This would not change how logging levels are currently handled by LogCaptor (as opposed to what .disableLogs does). The logged messages and objects would still be kept in memory in order to later assert against them with the current API.

This could even be the default behavior, although that might be a breaking change for some people.

In case it is not the default behaviour, it could be configured with an environment variable. What do you think?

Describe alternatives you've considered
An alternative is to manually disable all printing to stdout altogether. However, this would wipe out all other strings printed to stdout which are not logs (e.g. messages from the test runner or other testing libraries).

Another alternative is to add one .disableLogs call per verbose logging class somewhere before the test starts running, but that's fragile as it couples the tests to the logging implementation of each verbose logging library.

Does this project target Log4J API 1.7?

Describe the bug
When putting log-captor to my project, I immediately get the classic org.apache.logging.log4j.LoggingException: log4j-slf4j-impl cannot be present with log4j-to-slf4j exception.

BUT: excluding log4j-slf4j-impl from the test scope, turns thing around so that now NO logger implementation is found:
SLF4J: No SLF4J providers were found.
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See https://www.slf4j.org/codes.html#noProviders for further details.
SLF4J: Class path contains SLF4J bindings targeting slf4j-api versions 1.7.x or earlier.
SLF4J: Ignoring binding found at [jar:file:/Users/q163557/.gradle/caches/modules-2/files-2.1/ch.qos.logback/logback-classic/1.2.11/4741689214e9d1e8408b206506cbe76d1c6a7d60/logback-classic-1.2.11.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See https://www.slf4j.org/codes.html#ignoredBindings for an explanation.

SLF4J Logger implementation should be of the type [ch.qos.logback.classic.Logger] but found [org.slf4j.helpers.NOPLogger].

My project is now working with Log4J2 - could it be that this log-captor is still using the 1.7 API??

Not working with org.jacoco:jacoco-maven-plugin

Hi,
I just updated a large project from java 11 to 17 and introduced log-captor for unit testing.

Now all my test are fine in the IDE, but when running them in the CI/CD pipeline on the server or locally with
"./mvnw org.jacoco:jacoco-maven-plugin:prepare-agent test -Dmaven.test.failure=true"

I face failing tests depending on the environment these are different test. Always the log-captor seems to miss some logging.

This is how the pom looks like:

 <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <version>2.22.2</version>
                    <configuration>
                        <excludes>
                            <exclude>**/*IntegrationTest.java</exclude>
                            <exclude>**/*PerformanceTest.java</exclude>
                        </excludes>
                        <classpathDependencyExcludes>
                            <classpathDependencyExclude>org.apache.logging.log4j:log4j-slf4j-impl</classpathDependencyExclude>
                        </classpathDependencyExcludes>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-failsafe-plugin</artifactId>
                    <version>2.22.2</version>
                    <configuration>
                        <classpathDependencyExcludes>
                            <classpathDependencyExclude>org.apache.logging.log4j:log4j-slf4j-impl</classpathDependencyExclude>
                        </classpathDependencyExcludes>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.jacoco</groupId>
                    <artifactId>jacoco-maven-plugin</artifactId>
                    <configuration>
                        <excludes>
                            <exclude>**/*IntegrationTest.*</exclude>
                            <exclude>**/*PerformanceTest.*</exclude>
                        </excludes>
                    </configuration>
                </plugin>

Any ideas what might go wrong?

Seeing DEBUG level logs after adding logcaptor dependency

Describe the bug
I started seeing DEBUG level logging after adding the logcaptor dependency.
logger.isDebugEnabled is now returning true.

To Reproduce
I'm using slf4j and this is the log4j2.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="info">
    <Appenders>
        <Console name="CONSOLE" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{ABSOLUTE} %5p %t %c{1}:%M:%L - %m%n"/>
        </Console>
        <Async name="ASYNC" includeLocation="true">
            <AppenderRef ref="CONSOLE"/>
        </Async>
    </Appenders>
    <Loggers>
        <Root level="info">
            <AppenderRef ref="ASYNC"/>
        </Root>
        <Logger name="LoggerName" level="info" additivity="false">
            <AppenderRef ref="ASYNC"/>
        </Logger>
    </Loggers>
</Configuration>

Expected behavior
How can I keep the same level of logging as before adding the dependency?

Screenshots
If applicable, add screenshots to help explain your problem.

Environmental Data:

  • Java Version
  • Maven Version
  • OS (Windows/Linux/MacOS)

Additional context
Add any other context about the problem here.

Logcaptor error logs do not contain expected content when reusing instance

Describe the bug
I use logcaptor in a parameterized test method of a @SpringBootIntegrationTest class to check whether the expected error message was actually logged. I loosely followed the reuse example from the readme (see code snippets below).

For every parameterization, one error message is logged in the sut (verified that via debugger). When looking at the logcaptor.getErrorLogs though, this is not always correctly reflected there.

I tried out some things and it appears as if the following things make a difference:

  • Just running the test method vs. running the whole test class
  • Reusing the logcaptor instance or creating a new one every time
  • Having the logcaptor field as an instance variable vs. making it static

The possible results for the assertion whether the specific error message has been logged are:

  • Everything ok
  • Assertion fails only for the first parameterized method call
  • Assertion fails for all parameterizations
static field, reuse non-static field, reuse static field, no reuse non-static field, no reuse
ran test method all fail first fails ok ok
ran test class all fail ok ok ok

To Reproduce

@SpringBootIntegrationTest
class TestClass {
//    LogCaptor logcaptor;
//    static LogCaptor logcaptor;
//    LogCaptor logcaptor = LogCaptor.forClass(Sut.class);

    @Autowired
    Sut sut;
    
    @BeforeEach
    void beforeEach() {
//        logcaptor = LogCaptor.forClass(Sut.class);
//        logcaptor.clearLogs();
    }

    private static Stream<List<String>> provideParameters() {
        return Stream.of(
                null,
                List.of()
        );
    }

    @ParameterizedTest
    @MethodSource("provideParameters")
    void testMethod(List<String> parameters) {
        CompletableFuture<void> future = sut.doStuff(parameters);
        assertThrows(Exception.class, () -> future.get(10, TimeUnit.SECONDS));
        assertThat(logcaptor.getErrorLogs()).containsExactly("Expected error message");
    }
}

In Sut:

@Async
public CompletableFuture<Void> doStuff(List<String> params) {
    if (params == null || params.isEmpty()) {
        var errorMessage = "Expected error message";
        log.error(errorMessage);
        return CompletableFuture.failedFuture(new Exception(errorMessage));
    }

    try {
        // do stuff
    } catch (Exception e) {
        log.error(e.getMessage(), e);
        return CompletableFuture.failedFuture(e);
    }

    return CompletableFuture.completedFuture(null);
}

Expected behavior
I would have expected logCaptor.getErrorLogs to contain the expected error message no matter how the test method is run and no matter how the lagCaptor is defined and (re)used.

Environmental Data:

  • Logcaptor 2.7.0
  • junit 5.7.2
  • jul-to-slf4j 1.7.32
  • slf4j-api 1.7.32
  • log4j-to-slf4j 2.14.1
  • log4j-api 2.14.1
  • Java 16.0.2
  • Gradle 7.1.1
  • Windows 10

disableConsoleOutput() is throwing NPE

Describe the bug
disableConsoleOutput() is throwing NPE. The reason lies in the source code of LogCaptor.java. It is searching the console appender with "console" string which returns null. However, when in debugging mode I called the same method with "CONSOLE" string parameter and voila, it returned the appender successfully. This parameter is gathered from CONSOLE_APPENDER_NAME constant which is always "console".

In summary, getAppender method in LogCaptor.java should search case-insensitively or using both "console" and "CONSOLE" parameters, not just "console".

Expected behavior
It should not throw NPE and disable console output successfully.

Environmental Data:

  • Log-captor: 2.9.0
  • Spring Boot: 3.1.0
  • Log4j: 2.20.2
  • JUnit: 5.9.3
  • Mockito: 5.2.0
  • Java Version: 17
  • Maven Version: 3.9.3
  • OS: Windows 10

spring-boot-starter + log-captor: log4j-slf4j-impl cannot be present with log4j-to-slf4j

Describe the bug
Hi, running a test with Log-captor ends up with error: log4j-slf4j-impl cannot be present with log4j-to-slf4j.
I have spring-boot-starter as a dependency + I would like to use log4j2 for logging.

To Reproduce
Run the test in the sample project: https://github.com/ebarrier/logcaptor-test

I have tried excluding org.apache.logging.log4j:log4j-slf4j-impl from surefire/failsafe plugins, to no avail.

Excluding log4j-to-slf4j from Log-captor yields: SLF4J Logger implementation should be of the type [ch.qos.logback.classic.Logger] but found [org.apache.logging.slf4j.Log4jLogger]

If I also (in addition) exclude logback-classic from Log-captor, I then get: java.lang.NoClassDefFoundError: ch/qos/logback/core/Appender

I do not know what to do more. Would you have any hint what could be the problem?

Thanks in advance

Environmental Data:

  • Java Version 17.0.5
  • Maven Version 3.8.5
  • Linux Ubuntu

Documentation Request for Use in Quarkus; Problem with SLF4J

First of all, thank for your work. I am trying to use your library in a Quarkus project and am running into a multiple SLF4J implementation problem, nothing new for you I am sure.

SLF4J: Found provider [org.slf4j.impl.JBossSlf4jServiceProvider@10959ece]
SLF4J: Found provider [ch.qos.logback.classic.spi.LogbackServiceProvider@3a6bb9bf]

But in my case I cannot sort it out.

To replicate, I created a Quarkus project using the maven architype here:

mvn io.quarkus.platform:quarkus-maven-plugin:3.6.3:create
-DprojectGroupId=org.acme
-DprojectArtifactId=getting-started
-Dextensions='resteasy-reactive'

and duplicated your FooService and the FooServiceTest.

The dependencies from your project are:

  <groupId>io.github.hakky54</groupId>
  <artifactId>logcaptor</artifactId>
  <version>2.9.2</version>
  <scope>test</scope>

  <groupId>org.assertj</groupId>
  <artifactId>assertj-core</artifactId>
  <version>3.24.2</version>
  <scope>test</scope>

  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <version>1.18.30</version>

Thanks for your consideration!

consoleCaptor.getStandardOutput() doesn't work

Describe the bug
Hello, My issue is that with the same way as it is explained in the README I tried to use consoleCaptor.getStandardOutput() since I'm using @QuarkusTest but it gets nothing from the output, Even though the console is showing multiple json logs in the output.

@QuarkusTest
public class TestLog {

  ObjectMapper mapper = new ObjectMapper();

  @Test
  public void testLogIsInJsonFormat() {
    try (ConsoleCaptor consoleCaptor = new ConsoleCaptor()) {

      QueryClass query = new QueryClass();
      query.querySomeItem(null, null);
      List<String> standardOutPut = consoleCaptor.getStandardOutput(); // this list returns empty

      assertTrue(isValid(standardOutPut.get(1)));
    }
  }
}
{"timestamp":"2022-08-10T09:32:41.109+03:00","sequence":76,"loggerClassName":"org.jboss.logging.Logger","loggerName":"io.quarkus.amazon.lambda.runtime.AbstractLambdaPollLoop","level":"INFO","message":"Listening on: http://localhost:8081/_lambda_/2018-06-01/runtime/invocation/next","threadName":"Lambda Thread (TEST)","threadId":127,"mdc":{},"ndc":"","hostName":"my-laptop-name","processName":"JUnitStarter","processId":48118}

java.lang.IndexOutOfBoundsException: Index 1 out of bounds for length 0 // This means nothing was captured from the logs
    <dependency>
      <groupId>io.github.hakky54</groupId>
      <artifactId>logcaptor</artifactId>
      <version>2.7.10</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>io.github.hakky54</groupId>
      <artifactId>consolecaptor</artifactId>
      <version>1.0.2</version>
      <scope>test</scope>
    </dependency>

I suspect that maybe because I'm using @JbossLog annotation, but then how this can be used with that JBossLog ?

Environmental Data:

  • Java Version 17
  • Maven Version 3.8.5
  • OS (MacOS)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.