See http://projects.lidalia.org.uk/slf4j-test for details.
mahoney / slf4j-test Goto Github PK
View Code? Open in Web Editor NEWImplementation of SLF4J which allows easy access to logging events in tests
Implementation of SLF4J which allows easy access to logging events in tests
See http://projects.lidalia.org.uk/slf4j-test for details.
Hi there, I recently found this library and think it would be very useful for testing some projects I'm working on. Unfortunately I haven't seen a license listed anywhere in the repo or on the project website. Would you consider adding an open source license?
Although LoggingEvent#getFormattedMessage()
isn't public (#28), its code is still usable as a reference. However, it doesn't present null
values the same way as SLF4J, due to un-matched wrapping/unwrapping of Optional
:
LoggingEvent e = new LoggingEvent(Level.INFO, "Content: {}, {}", null, "value");
System.out.println(MessageFormatter.arrayFormat(e.getMessage(), e.getArguments().toArray()).getMessage());
Produces:
Content: Optional.absent(), value
Rather than the expected:
Content: null, value
It looks like I'm only able to use this in Java 7; is there any way you can modify it to work with Java 6 as well (or accept patches)? I know Java6 is now EOL, but (at least where I work) there is a substantial amount of older code around that won't be validated with Java 7 anytime too soon.
I'd just give you a pull request, but I don't quite understand how the dependency page is being generated. It might be something done automatically for you, in which case, this can just remain as documentation.
The dependency page provides SBT instructions that would be appropriate for a scala library, but this being a java library, it makes sbt look for a dependency that doesn't exist.
the double percentage (%%) it suggests will try to append your version of scala to the dependency resolver, as Scala bytecode is not compatible between major versions. A java library should use a single percentage instead, and then the library will be found just fine.
The SLF4J FAQ states:
As of SLF4J version 1.5.3, logger instances survive serialization. Thus, serialization of the host class no longer requires any special action, even when loggers are declared as instance variables.
This is true of the official bindings, but not slf4j-test.
Test case (can be added to TestLoggerTests.java
):
import java.io.ByteArrayOutputStream;
import java.io.ByteArrayInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.io.IOException;
public class TestLoggerTests {
// ...
@Test
public void serializable() throws IOException, ClassNotFoundException {
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
new ObjectOutputStream(outStream).writeObject(testLogger);
ByteArrayInputStream inStream = new ByteArrayInputStream(outStream.toByteArray());
TestLogger deserializedLogger = (TestLogger) new ObjectInputStream(inStream).readObject();
deserializedLogger.info(message);
assertEquals(asList(info(mdcValues, message)), deserializedLogger.getLoggingEvents());
}
}
Test output:
serializable(uk.org.lidalia.slf4jtest.TestLoggerTests) Time elapsed: 0.095 sec <<< ERROR!
java.io.NotSerializableException: uk.org.lidalia.slf4jtest.TestLogger
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at uk.org.lidalia.slf4jtest.TestLoggerTests.serializable(TestLoggerTests.java:313)
Current workarounds include:
lazy transient
(By the way, thanks for the great library!)
Raising here, similar to the issue I've raised at logfellow/logstash-logback-encoder#526.
When using the sample project at https://gitlab.com/jamietanna/logstash-boot-slf4j-test/-/tree/main/complete, we see the following exception:
ch/qos/logback/classic/joran/JoranConfigurator
java.lang.NoClassDefFoundError: ch/qos/logback/classic/joran/JoranConfigurator
at org.springframework.boot.logging.logback.LogbackLoggingSystem$Factory.getLoggingSystem(LogbackLoggingSystem.java:352)
at org.springframework.boot.logging.DelegatingLoggingSystemFactory.getLoggingSystem(DelegatingLoggingSystemFactory.java:44)
at org.springframework.boot.logging.LoggingSystem.get(LoggingSystem.java:159)
at org.springframework.boot.context.logging.LoggingApplicationListener.onApplicationStartingEvent(LoggingApplicationListener.java:231)
at org.springframework.boot.context.logging.LoggingApplicationListener.onApplicationEvent(LoggingApplicationListener.java:213)
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:176)
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:169)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:143)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:131)
at org.springframework.boot.context.event.EventPublishingRunListener.starting(EventPublishingRunListener.java:76)
at org.springframework.boot.SpringApplicationRunListeners.lambda$starting$0(SpringApplicationRunListeners.java:53)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.springframework.boot.SpringApplicationRunListeners.doWithListeners(SpringApplicationRunListeners.java:117)
at org.springframework.boot.SpringApplicationRunListeners.starting(SpringApplicationRunListeners.java:53)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:317)
at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:123)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124)
at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:124)
at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:190)
at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:132)
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:244)
at org.springframework.test.context.junit.jupiter.SpringExtension.postProcessTestInstance(SpringExtension.java:138)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$6(ClassBasedTestDescriptor.java:350)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.executeAndMaskThrowable(ClassBasedTestDescriptor.java:355)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$7(ClassBasedTestDescriptor.java:350)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:177)
at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1655)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
at java.base/java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:312)
at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:735)
at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:734)
at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:658)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeTestInstancePostProcessors(ClassBasedTestDescriptor.java:349)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$instantiateAndPostProcessTestInstance$4(ClassBasedTestDescriptor.java:270)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.instantiateAndPostProcessTestInstance(ClassBasedTestDescriptor.java:269)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$2(ClassBasedTestDescriptor.java:259)
at java.base/java.util.Optional.orElseGet(Optional.java:369)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$3(ClassBasedTestDescriptor.java:258)
at org.junit.jupiter.engine.execution.TestInstancesProvider.getTestInstances(TestInstancesProvider.java:31)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$prepare$0(TestMethodTestDescriptor.java:101)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:100)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:65)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$prepare$1(NodeTestTask.java:111)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.prepare(NodeTestTask.java:111)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:79)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:143)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:129)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:248)
at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$5(DefaultLauncher.java:211)
at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:226)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:199)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:132)
at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:99)
at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:79)
at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:75)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:61)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
at com.sun.proxy.$Proxy2.stop(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.stop(TestWorker.java:133)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:182)
at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:164)
at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:414)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: java.lang.ClassNotFoundException: ch.qos.logback.classic.joran.JoranConfigurator
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
Similar to #21.
I wonder if this is something to fix upstream in Spring, where Spring should look for a class in logback-classic
, not a class in logback-core
The solution as described in the documentation, with surefire classpathDependencyExcludes, does not work when run through IntelliJ.
This causes this logging framework to work correctly on commandline, but still cause logging framework conflicts when run in IntelliJ.
Is there a way to make this framework compatible with IntelliJ?
Hi,
We have a spring boot application, set up using Gradle.
The test for the logger works.
We added another test, not for the logger, using SpringRunner:
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
This test fail due to problem with multiple binding of classes.
I tried many options to exclude different libraries from the gradle build, but no success.
Here's our dependencies in the build.gradle file:
(without exclusions. the "original" build file)
compile(
'com.google.code.gson:gson:2.8.5',
'com.squareup.okhttp3:okhttp:3.11.0',
'commons-io:commons-io:2.6',
'com.datadoghq:java-dogstatsd-client:2.4',
'commons-io:commons-io:2.6',
'net.logstash.logback:logstash-logback-encoder:5.+',
'org.projectlombok:lombok:1.18.4',
'org.springframework.boot:spring-boot-configuration-processor',
'org.springframework.boot:spring-boot-starter-actuator',
'org.springframework.boot:spring-boot-starter-data-cassandra',
'org.springframework.boot:spring-boot-starter-integration',
'org.springframework.boot:spring-boot-starter-web',
)
testCompile 'uk.org.lidalia:slf4j-test:1.1.0'
testCompile 'org.springframework.boot:spring-boot-starter-test'
testCompile 'com.google.guava:guava:27.0-jre'
testCompile 'org.springframework.boot:spring-boot-starter-test'
Here's the exception we get:
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/Users/eyalgo/.gradle/caches/modules-2/files-2.1/uk.org.lidalia/slf4j-test/1.1.0/f4f523049e041dea673bd421d7b0d61fb5e49548/slf4j-test-1.1.0.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/Users/eyalgo/.gradle/caches/modules-2/files-2.1/ch.qos.logback/logback-classic/1.2.3/7c4f3c474fb2c041d8028740440937705ebb473a/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 [uk.org.lidalia.slf4jtest.TestLoggerFactory]
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:125)
at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:108)
at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:190)
at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:132)
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:246)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:227)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:289)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:291)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:246)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: java.lang.IllegalArgumentException: LoggerFactory is not a Logback LoggerContext but Logback is on the classpath. Either remove Logback or the competing implementation (class uk.org.lidalia.slf4jtest.TestLoggerFactory loaded from file:/Users/eyalgo/.gradle/caches/modules-2/files-2.1/uk.org.lidalia/slf4j-test/1.1.0/f4f523049e041dea673bd421d7b0d61fb5e49548/slf4j-test-1.1.0.jar). If you are using WebLogic you will need to add 'org.slf4j' to prefer-application-packages in WEB-INF/weblogic.xml: uk.org.lidalia.slf4jtest.TestLoggerFactory
at org.springframework.util.Assert.instanceCheckFailed(Assert.java:655)
at org.springframework.util.Assert.isInstanceOf(Assert.java:555)
at org.springframework.boot.logging.logback.LogbackLoggingSystem.getLoggerContext(LogbackLoggingSystem.java:286)
at org.springframework.boot.logging.logback.LogbackLoggingSystem.beforeInitialize(LogbackLoggingSystem.java:102)
at org.springframework.boot.context.logging.LoggingApplicationListener.onApplicationStartingEvent(LoggingApplicationListener.java:217)
at org.springframework.boot.context.logging.LoggingApplicationListener.onApplicationEvent(LoggingApplicationListener.java:196)
at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172)
at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139)
at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:127)
at org.springframework.boot.context.event.EventPublishingRunListener.starting(EventPublishingRunListener.java:69)
at org.springframework.boot.SpringApplicationRunListeners.starting(SpringApplicationRunListeners.java:48)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:302)
at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:127)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:99)
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:117)
... 24 more
First of all, thank you for maintaining this very useful utility!
Unfortunately, starting with SLF4J 2.0, it doesn't work anymore. SLF4J 2.0 changed the way it discovers implementations. Quoting from their documentation:
Instead of "bindings" now org.slf4j.LoggerFactory searches for "providers".
Although I'm not an expert on SLF4J, I managed to implement such a provider. If you're open to such a contribution, I can create a PR for it.
public class Slf4jUser {
private static final Logger logger = LoggerFactory.getLogger(Slf4jUser.class);
public void aMethodThatLogs() {
logger.info("Hello World!");
}
}
public class Slf4jUserTest {
Slf4jUser slf4jUser = new Slf4jUser();
TestLogger logger = TestLoggerFactory.getTestLogger(Slf4jUser.class);
@Test
public void aMethodThatLogsLogsAsExpected() {
slf4jUser.aMethodThatLogs();
assertThat(logger.getLoggingEvents(), is(asList(info("Hello World!"))));
}
@After
public void clearLoggers() {
TestLoggerFactory.clear();
}
}
This test case throws
java.lang.AssertionError:
Expected: is <[LoggingEvent[level=INFO,mdc={},marker=Optional.absent(),throwable=Optional.absent(),message=Hello World!,arguments=[]]]>
but: was <[]>
at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)
at org.junit.Assert.assertThat(Assert.java:956)
at org.junit.Assert.assertThat(Assert.java:923)
at dozerExp.Slf4jUserTest.aMethodThatLogsLogsAsExpected(Slf4jUserTest.java:22)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
These look equal to me, here's my source (obviously the commented out line is not when I run it)
Expected :uk.org.lidalia.slf4jtest.LoggingEvent<LoggingEvent[level=TRACE,mdc={},marker=Optional.absent(),throwable=Optional.absent(),message=test {},arguments=[1]]>
Actual :uk.org.lidalia.slf4jtest.LoggingEvent<LoggingEvent[level=TRACE,mdc={},marker=Optional.absent(),throwable=Optional.absent(),message=test {},arguments=[1]]>
I did a little investigation, this is where it fails
&& fieldsOfThis.allMatch(hasEqualValueIn(that));
Other Slf4j implementations accept a null for the message when logging. I believe the slf4j API also doesn't label the message as @nonnull.
Currently LoggingEvent has a checkNotNull check on message. Which causes my existing code, that does log a null, to get an NPE. I think it was probably done to be like the checks on the other Optional fields.
In tests, I want to make assertions on the message that is generated in the logging system, not the template with positional args with "{}".
It could therefore be hard to test arguments, as it should need creating real instance of objects passed as args to logging, not just text really logged
in slf2.0
it uses LoggerFactory.findServiceProviders(...) to load slf implement module
which needs to implement
org.slf4j.spi.SLF4JServiceProvider interface.
we could fix it by add TestLogbackServiceProvider.java and add service definition file in
META-INF/services/org.slf4j.spi.SLF4JServiceProvider
like
private MDCAdapter mdcAdapter;
private ILoggerFactory loggerFactory;
private IMarkerFactory markerFactory;
@Override
public ILoggerFactory getLoggerFactory() {
return loggerFactory;
}
@Override
public IMarkerFactory getMarkerFactory() {
return markerFactory;
}
@Override
public MDCAdapter getMDCAdapter() {
return mdcAdapter;
}
@Override
public String getRequestedApiVersion() {
return "2.0.1";
}
@Override
public void initialize() {
mdcAdapter = StaticMDCBinder.SINGLETON.getMDCA();
loggerFactory = StaticLoggerBinder.getSingleton().getLoggerFactory();
markerFactory = StaticMarkerBinder.SINGLETON.getMarkerFactory();
}
}`
and
META-INF/services/org.slf4j.spi.SLF4JServiceProvider
content is
$packagename.TestLogbackServiceProvider
I'm finding the following useful:
public static Matcher<LoggingEvent> debugContaining(String substring) {
return formattedMessageMatches(Level.DEBUG, substring);
}
/// etc.
private static Matcher<LoggingEvent> formattedMessageMatches(
final Level debug, final String substring) {
return new BaseMatcher<LoggingEvent>() {
@Override
public boolean matches(Object item) {
if (item instanceof LoggingEvent) {
final LoggingEvent le = (LoggingEvent)item;
return le.getLevel().equals(debug) &&
getFormattedMessage(le).indexOf(substring) >= 0;
}
return false;
}
@Override
public void describeTo(Description description) {
description
.appendText("LoggingEvent with level ")
.appendValue(debug)
.appendText(" and a formatted message containing '")
.appendText(substring)
.appendText("'");
}
};
}
private static String getFormattedMessage(LoggingEvent event) {
return MessageFormatter.arrayFormat(
event.getMessage(), event.getArguments().toArray()).getMessage();
}
So I can say
assertThat(logger.getAllLoggingEvents(),
contains(
debugContaining("first message"),
debugContaining("the next message")));
Perhaps there should be a LoggingEventMatchers?
Hi,
I tried the code based on the sample given. But it was always throwing there is no events in the logger.
I have pasted below the code, log, error for your review. Could you please clarify and help?
Thanks
Raja
package com.charter.aesd.tvgo.app.quartz;
import java.text.ParseException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.concurrent.Immutable;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.quartz.CronScheduleBuilder;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.matchers.GroupMatcher;
import uk.org.lidalia.slf4jtest.LoggingEvent;
import uk.org.lidalia.slf4jtest.TestLogger;
import uk.org.lidalia.slf4jtest.TestLoggerFactory;
import ch.qos.logback.core.AppenderBase;
import com.charter.aesd.tvgo.transform.conf.LocalS3ClientModule;
import com.charter.aesd.tvgo.transform.conf.LocalTransformationModule;
import com.google.common.collect.ImmutableList;
import com.google.inject.Guice;
import com.google.inject.Injector;
import static uk.org.lidalia.slf4jtest.LoggingEvent.info;
import static java.util.Arrays.asList;
import static org.junit.Assert.assertThat;
import static org.hamcrest.Matchers.is;
public class TvGoSchedulerTest {
/* SLF4J logger with logback */
TestLogger logger = TestLoggerFactory.getTestLogger(TvGoFeedSchedulerListener.class);
@Test
public void schedulerTest() throws ParseException, SchedulerException, InterruptedException {
//schedule it
Injector injector = Guice.createInjector(new LocalTransformationModule(), new LocalS3ClientModule());
TvGoFeedJob job = injector.getInstance(TvGoFeedJob.class);
JobKey jobKeyDummy = new JobKey("jobDummy", "group1");
JobDetail jobDummy = JobBuilder.newJob(job.getClass())
.withIdentity(jobKeyDummy).build();
Trigger triggerDummy = TriggerBuilder
.newTrigger()
.withIdentity("triggerDummy", "trgGroup1")
.withSchedule(
CronScheduleBuilder.cronSchedule("0 0/1 * 1/1 * ? *"))
.build();
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
scheduler.getListenerManager().addSchedulerListener(new TvGoFeedSchedulerListener());
//assertThat(logger.getLoggingEvents(), is(asList(info("Job jobDummy from group group1 is Added to scheduler"))));
scheduler.start();
assertThat(logger.getLoggingEvents(), is(asList(info("Scheduler is Started"))));
scheduler.pauseJob(jobKeyDummy);
scheduler.resumeJob(jobKeyDummy);
scheduler.addJob(jobDummy,true);
scheduler.pauseTrigger(triggerDummy.getKey());
scheduler.resumeTrigger(triggerDummy.getKey());
GroupMatcher<JobKey> group = GroupMatcher.groupContains("group");
scheduler.pauseJobs(group);
scheduler.resumeJobs(group);
scheduler.clear();
scheduler.deleteJob(jobDummy.getKey());
scheduler.standby();
scheduler.scheduleJob(jobDummy, triggerDummy);
scheduler.shutdown();
}
@After
public void clearLog(){
TestLoggerFactory.clear();
}
}
Actual Log
21:18:23.564 [main] INFO c.c.a.t.a.q.TvGoFeedSchedulerListener - Scheduler is Started
21:18:23.564 [main] INFO c.c.a.t.a.q.TvGoFeedSchedulerListener - Job jobDummy from group group1 is Paused
21:18:23.568 [main] INFO c.c.a.t.a.q.TvGoFeedSchedulerListener - Job jobDummy from group group1 is Resumed
21:18:23.568 [main] INFO c.c.a.t.a.q.TvGoFeedSchedulerListener - Job jobDummy from group group1 is Added to scheduler
21:18:23.568 [main] INFO c.c.a.t.a.q.TvGoFeedSchedulerListener - Trigger triggerDummy of group trgGroup1 is Paused
21:18:23.568 [main] INFO c.c.a.t.a.q.TvGoFeedSchedulerListener - Trigger triggerDummy of group trgGroup1 is Resumed
21:18:23.578 [main] INFO c.c.a.t.a.q.TvGoFeedSchedulerListener - Jobs in Group group1 Paused
21:18:23.578 [main] INFO c.c.a.t.a.q.TvGoFeedSchedulerListener - Jobs in Group group1 Resumed
21:18:23.578 [main] INFO c.c.a.t.a.q.TvGoFeedSchedulerListener - Scheduling Data Cleared
21:18:23.578 [main] INFO o.q.c.QuartzScheduler - Scheduler DefaultQuartzScheduler_$NON_CLUSTERED paused.
21:18:23.579 [main] INFO c.c.a.t.a.q.TvGoFeedSchedulerListener - Job Scheduler in StandByMode
21:18:23.585 [main] INFO c.c.a.t.a.q.TvGoFeedSchedulerListener - Job jobDummy from group group1 is Added to scheduler
21:18:23.585 [main] INFO c.c.a.t.a.q.TvGoFeedSchedulerListener - Job jobDummy of group group1 is Scheduled to start at Thu May 01 21:18:23 IST 2014
21:18:23.588 [main] INFO o.q.c.QuartzScheduler - Scheduler DefaultQuartzScheduler$NON_CLUSTERED shutting down.
21:18:23.588 [main] INFO o.q.c.QuartzScheduler - Scheduler DefaultQuartzScheduler$NON_CLUSTERED paused.
21:18:23.588 [main] INFO c.c.a.t.a.q.TvGoFeedSchedulerListener - Job Scheduler in StandByMode
21:18:23.588 [main] INFO c.c.a.t.a.q.TvGoFeedSchedulerListener - Scheduler is Shutting Down
21:18:23.588 [main] INFO c.c.a.t.a.q.TvGoFeedSchedulerListener - Scheduler ShutDown
21:18:23.588 [main] INFO o.q.c.QuartzScheduler - Scheduler DefaultQuartzScheduler$_NON_CLUSTERED shutdown complete.
Error Trace
java.lang.AssertionError:
Expected: is <[LoggingEvent[level=INFO,mdc={},marker=Optional.absent(),throwable=Optional.absent(),message=Scheduler is Started,arguments=[]]]>
but: was <[]>
at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)
at org.junit.Assert.assertThat(Assert.java:865)
at org.junit.Assert.assertThat(Assert.java:832)
at com.charter.aesd.tvgo.app.quartz.TvGoSchedulerTest.schedulerTest(TvGoSchedulerTest.java:72)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:467)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:683)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:390)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:197)
First of all - this is a great little library, thanks for sharing.
It would be useful if the dependencies on other third-party libraries could be removed. For example, I was a little surprised to discover I was now dependent on Guava. The biggest problem here is more likely to be version clashes, hence thinking it may be preferential to remove these dependencies.
Just like it is hard to create a logging event with a specific timestamp to compare against, it is also hard to create a specific throwable to compare against. The problem is that Throwable
relies on Object.equals
which does a shallow comparison, i.e. requires objects to be the same in order to match. So for example, if the two throwables do not refer to the exact same StackTrace
object, the comparison fails.
For example, I get this message from junit:
org.opentest4j.AssertionFailedError: log messages ==> iterable contents differ at index [2], expected: uk.org.lidalia.slf4jtest.LoggingEvent@6aa61224<LoggingEvent[level=ERROR,mdc={},marker=Optional.absent(),throwable=Optional.of(java.io.IOException: Broken pipe),message=Failed to write request,arguments=[]]> but was: uk.org.lidalia.slf4jtest.LoggingEvent@30bce90b<LoggingEvent[level=ERROR,mdc={},marker=Optional.absent(),throwable=Optional.of(java.io.IOException: Broken pipe),message=Failed to write request,arguments=[]]>
where the expected value was created by
LoggingEvent.error(new IOException("Broken pipe"),"Failed to write request")
The comparison of the Throwable
could for example be changed to require that the just class and message was the same.
My team's code uses markers and MDC, and it would be helpful in our functional tests if the LoggingEvent.print()
function could include these on Stdout.
I will post a pull request in a bit...
It would be beneficial if we can continue using slf4j test, even when we migrate to SLF4J 2.0.x.
I am aware that this is a major undertaking, and it appears that you no longer actively maintain slf4j-test. I would like to volunteer for that work, either in this project, or I could fork the project to my own user and continue the work from there. In any case, the goal would be to produce a version 2 of slf4j-test that works with SLF4J 2.0.x.
SLF4j has special handling when an throwable passed as the last element of a varags log method, see http://www.slf4j.org/faq.html#paramException.
slf4j-test should do the same.
Hey,
I can't find slf4j-test in the maven central repository. Please push it to sonatype and add the artifact coordinates to the readme.md
Null pointer exception is not getting handled correctly, because npe.getMessage() is itself null.
@slf4j
public class Demo {
public static void doIt(){
Map<String, String> map = new ConcurrentHashMap<>();
try {
map.put(null, null);
} catch (NullPointerException npe) {
log.error(npe.getMessage(), npe);
}
}
}
class DemoTest extends Specification {
def "DoIt"() {
given:
TestLogger logger = TestLoggerFactory.getTestLogger(Demo.class);
Demo.doIt();
expect:
logger.getAllLoggingEvents().size() > 0;
}
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.