log-captor: 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
About this issue
- Original URL
- State: closed
- Created 3 years ago
- Comments: 15 (7 by maintainers)
Aha #2, removing the exclusion we tried adding for one of the above problems finally fixed it. So in summary, import the library before other log4j dependencies.
Amazing, you found the solution. Interesting to see that the order of declaring the dependency in this specific use-case worked for you! I will note this solution down. Good luck asserting your log messages!
By the way I published here Java tutorials - LogCaptor isolated examples with different logging frameworks include this example and the adjustments to the pom which are included to get it working.
Update 14-07-2021 I noticed that you are using
spring-boot-starter-log4j2
. This library has some transitive dependencies which indeed requires adjustments to your pom. I have added an example for this use case, see the example project here: https://github.com/Hakky54/java-tutorials/tree/main/log-captor-examples/log-captor-with-spring-boot-starter-log4j2So you basically need to add the following configuration to your pom:
This will exclude
org.apache.logging.log4j:log4j-slf4j-impl
from the classpath only during the test phase I mentioned this solution also at the beginning of our conversation, see here: https://github.com/Hakky54/log-captor/issues/6#issuecomment-770237736Hi Derek,
Thank you for this detailed issue report! This is a typical SLF4J error message that is preventing to have more than one slf4j binding to be present at the classpath.
The
log4j-to-slf4j
dependency within this library makes it possible to capture logs from apache when the end user is using apachelog4j-core
while not using slf4j api/slf4j bridge/slf4j binder at all. This makes it convenient for those end-users to start without the need for additional configuration. Even if this library didn’t includedlog4j-to-slf4j
you would still have a similar error. You can try it by excluding the specific library with the following snippet:The main reason why this is happening is that your classpath already has a slf4j implementation and LogCaptor too. This is also mentioned here LogCaptor - Using Log Captor alongside with other logging libraries. So what you need to do is just exclude your slf4j implementation during the test phase. If you are using maven surefire or failsafe plugin you can add the following snippet to your pom:
This configuration is excluding your
log4j-slf4j-impl
during the test-phase only. Can you give it a try and share your results here? I think excludinglog4j-to-slf4j
from LogCaptor would not be needed.I need to think about it. Thank you for the suggestion!