diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1eeeb0c --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Created by .ignore support plugin (hsz.mobi) +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +.idea +*.iml + + +### Maven template +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties + +# Avoid ignoring Maven wrapper jar file (.jar files are usually ignored) +!/.mvn/wrapper/maven-wrapper.jar + + diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..f5c99a7 --- /dev/null +++ b/.travis.yml @@ -0,0 +1 @@ +language: java \ No newline at end of file diff --git a/java/logback-spike/LICENSE b/LICENSE similarity index 83% rename from java/logback-spike/LICENSE rename to LICENSE index f4a67ed..2625adb 100644 --- a/java/logback-spike/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2016 Patrick Kua +Copyright for portions of this project are held by Patrick Kua, 2016 as part of project thekua/Sample-Code. All other copyright for project schnatterer/logback-spike are held by Johannes Schnatterer, 2017. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md new file mode 100644 index 0000000..a2feddc --- /dev/null +++ b/README.md @@ -0,0 +1,47 @@ +# logback-spike +[![Build Status](https://travis-ci.org/schnatterer/logback-spike.svg?branch=master)](https://travis-ci.org/schnatterer/logback-spike) +[![JitPack](https://jitpack.io/v/schnatterer/logback-spike.svg)](https://jitpack.io/#schnatterer/logback-spike) +[![License](https://img.shields.io/github/license/schnatterer/logback-spike.svg)](LICENSE) + +Logback and SLF4j unit testing. + +Original idea from [thekua's blog](https://www.thekua.com/atwork/2011/11/testing-logging-with-logback/), forked from [his repo](https://github.com/thekua/Sample-Code/tree/master/java/logback-spike) in order to provide it conveniently with good reuse as maven dependency. + +## Usage + +Get it via [JitPack](https://jitpack.io/#schnatterer/logback-spike), for example using maven. + +Add the following maven repository to your POM.xml + +```xml + + + jitpack.io + https://jitpack.io + + +``` + +Then add the actual dependency + +```xml + + com.github.schnatterer + logback-spike + 1.0.0 + test + +``` + +From here it's really simple to use: +````java +// Given +LogbackCapturingAppender capturing = LogbackCapturingAppender.weaveInto(OurDomainWithLogger.LOG); + +// when +new OurDomainWithLogger().logInfo("This should be logged{}", "!"); + +// then +assertThat(capturing.getCapturedLogMessages().get(0), is("This should be logged!")); +```` +See [LogbackCapturingAppender's javadoc](src/main/java/com/thekua/spikes/LogbackCapturingAppender.java) and [its unit test](src/test/java/com/thekua/spikes/LogbackCapturingAppenderTest.java) for more insights. diff --git a/java/.gitignore b/java/.gitignore deleted file mode 100644 index 7df3665..0000000 --- a/java/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -*.class -*.ipr -*.iws -*.iml -target diff --git a/java/logback-spike/src/test/java/com/thekua/spikes/LogbackCapturingAppender.java b/java/logback-spike/src/test/java/com/thekua/spikes/LogbackCapturingAppender.java deleted file mode 100644 index 9af6810..0000000 --- a/java/logback-spike/src/test/java/com/thekua/spikes/LogbackCapturingAppender.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.thekua.spikes; - - -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.core.Appender; -import ch.qos.logback.core.AppenderBase; - -import java.util.ArrayList; -import java.util.List; - -public class LogbackCapturingAppender extends AppenderBase { - public static class Factory { - private static List ALL = new ArrayList(); - - public static LogbackCapturingAppender weaveInto(org.slf4j.Logger sl4jLogger) { - LogbackCapturingAppender appender = new LogbackCapturingAppender(sl4jLogger); - ALL.add(appender); - return appender; - } - - - public static void cleanUp() { - for (LogbackCapturingAppender appender : ALL) { - appender.cleanUp(); - } - } - - } - - private final Logger logger; - private ILoggingEvent captured; - - public LogbackCapturingAppender(org.slf4j.Logger sl4jLogger) { - this.logger = (Logger) sl4jLogger; - connect(logger); - detachDefaultConsoleAppender(); - } - - private void detachDefaultConsoleAppender() { - Logger rootLogger = getRootLogger(); - Appender consoleAppender = rootLogger.getAppender("console"); - rootLogger.detachAppender(consoleAppender); - } - - private Logger getRootLogger() { - return logger.getLoggerContext().getLogger("ROOT"); - } - - private void connect(Logger logger) { - logger.setLevel(Level.ALL); - logger.addAppender(this); - this.start(); - } - - public String getCapturedLogMessage() { - return captured.getMessage(); - } - - @Override - protected void append(ILoggingEvent iLoggingEvent) { - captured = iLoggingEvent; - } - - private void cleanUp() { - logger.detachAppender(this); - - } -} diff --git a/java/logback-spike/src/test/java/com/thekua/spikes/LogbackCapturingAppenderTest.java b/java/logback-spike/src/test/java/com/thekua/spikes/LogbackCapturingAppenderTest.java deleted file mode 100644 index 45b1d6b..0000000 --- a/java/logback-spike/src/test/java/com/thekua/spikes/LogbackCapturingAppenderTest.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.thekua.spikes; - - -import org.junit.After; -import org.junit.Test; - -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; - -public class LogbackCapturingAppenderTest { - @After - public void cleanUp() { - LogbackCapturingAppender.Factory.cleanUp(); - } - - @Test - public void shouldCaptureAGivenLog() throws Exception { - // Given - LogbackCapturingAppender capturing = LogbackCapturingAppender.Factory.weaveInto(OurDomainWithLogger.LOG); - OurDomainWithLogger domainClass = new OurDomainWithLogger(); - - // when - domainClass.logThis("This should be logged"); - - // then - assertThat(capturing.getCapturedLogMessage(), is("This should be logged")); - } - - @Test - public void shouldNotCaptureAGiveLogAfterCleanUp() throws Exception { - // Given - LogbackCapturingAppender capturing = LogbackCapturingAppender.Factory.weaveInto(OurDomainWithLogger.LOG); - OurDomainWithLogger domainClass = new OurDomainWithLogger(); - domainClass.logThis("This should be logged at info"); - LogbackCapturingAppender.Factory.cleanUp(); - - // when - domainClass.logThis("This should not be logged"); - - // then - assertThat(capturing.getCapturedLogMessage(), is("This should be logged at info")); - } -} diff --git a/java/logback-spike/pom.xml b/pom.xml similarity index 54% rename from java/logback-spike/pom.xml rename to pom.xml index f32f7fc..c63d9c4 100644 --- a/java/logback-spike/pom.xml +++ b/pom.xml @@ -1,40 +1,41 @@ - + 4.0.0 com.thekua.spikes logback-spike - 1.0-SNAPSHOT + 1.0.1-SNAPSHOT jar logback-spike - http://maven.apache.org UTF-8 + + scm:git:git@github.com:schnatterer/logback-spike.git + scm:git:git@github.com:schnatterer/logback-spike.git + https://github.com/schnatterer/logback-spike + HEAD + + - - org.slf4j - slf4j-api - 1.6.2 - ch.qos.logback logback-classic - 1.0.0 + 1.2.3 junit - junit-dep - 4.8.2 + junit + 4.12 test org.hamcrest hamcrest-all - 1.2 + 1.3 + test diff --git a/src/main/java/com/thekua/spikes/LogbackCapturingAppender.java b/src/main/java/com/thekua/spikes/LogbackCapturingAppender.java new file mode 100644 index 0000000..f7270ab --- /dev/null +++ b/src/main/java/com/thekua/spikes/LogbackCapturingAppender.java @@ -0,0 +1,100 @@ +package com.thekua.spikes; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.Appender; +import ch.qos.logback.core.AppenderBase; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +/** + * Logback appender that simply stores all logged messages of an {@link org.slf4j.Logger} in memory and provides methods + * to retrieve them, so they can be used in asserts of unit tests. + *
+ * Add a logger like so
+ * {@code LogbackCapturingAppender capturing = LogbackCapturingAppender.weaveInto(OurDomainWithLogger.LOG); } + *

+ * Retrieve any log messages using any of the following methods: + *
    + *
  • {@link #getCapturedEvents()}
  • + *
  • {@link #getCapturedLogMessages()}
  • + *
+ *
+ * If necessary remove appender instance or all {@link LogbackCapturingAppender}s using + * + *
    + *
  • {@link #cleanUp()}
  • + *
  • {@link #cleanUpAll()}
  • + *
+ */ +public class LogbackCapturingAppender extends AppenderBase { + private static final List ALL = new ArrayList(); + + private final Logger logger; + + private List capturedEvents = new LinkedList(); + + public static LogbackCapturingAppender weaveInto(org.slf4j.Logger sl4jLogger) { + LogbackCapturingAppender appender = new LogbackCapturingAppender(sl4jLogger); + ALL.add(appender); + return appender; + } + + public static void cleanUpAll() { + for (LogbackCapturingAppender appender : ALL) { + appender.cleanUp(); + } + } + + public void cleanUp() { + logger.detachAppender(this); + } + + /** + * @return whole event, including log level, unformatted message, etc. + */ + public List getCapturedEvents() { + return capturedEvents; + } + + /** + * @return formatted message strings + */ + public List getCapturedLogMessages() { + List capturedMessages = new LinkedList(); + for (ILoggingEvent capturedEvent : capturedEvents) { + capturedMessages.add(capturedEvent.getFormattedMessage()); + } + return capturedMessages; + } + + @Override + protected void append(ILoggingEvent iLoggingEvent) { + capturedEvents.add(iLoggingEvent); + } + + private LogbackCapturingAppender(org.slf4j.Logger sl4jLogger) { + this.logger = (Logger) sl4jLogger; + connect(logger); + detachDefaultConsoleAppender(); + } + + private void detachDefaultConsoleAppender() { + Logger rootLogger = getRootLogger(); + Appender consoleAppender = rootLogger.getAppender("console"); + rootLogger.detachAppender(consoleAppender); + } + + private Logger getRootLogger() { + return logger.getLoggerContext().getLogger("ROOT"); + } + + private void connect(Logger logger) { + logger.setLevel(Level.ALL); + logger.addAppender(this); + this.start(); + } +} diff --git a/src/test/java/com/thekua/spikes/LogbackCapturingAppenderTest.java b/src/test/java/com/thekua/spikes/LogbackCapturingAppenderTest.java new file mode 100644 index 0000000..0e7510f --- /dev/null +++ b/src/test/java/com/thekua/spikes/LogbackCapturingAppenderTest.java @@ -0,0 +1,76 @@ +package com.thekua.spikes; + + +import ch.qos.logback.classic.Level; +import org.junit.After; +import org.junit.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; + +public class LogbackCapturingAppenderTest { + @After + public void cleanUp() { + LogbackCapturingAppender.cleanUpAll(); + } + + @Test + public void shouldCaptureAGivenLogMessage() throws Exception { + // Given + LogbackCapturingAppender capturing = LogbackCapturingAppender.weaveInto(OurDomainWithLogger.LOG); + OurDomainWithLogger domainClass = new OurDomainWithLogger(); + + // when + domainClass.logInfo("This should be logged{}", "!"); + + // then + assertThat(capturing.getCapturedLogMessages().get(0), is("This should be logged!")); + } + + @Test + public void shouldCaptureMultipleLogMessages() throws Exception { + // Given, when + LogbackCapturingAppender capturing = sendMultipleFormattedMessages(); + + // then + assertThat(capturing.getCapturedLogMessages().get(0), is("This should be logged!")); + assertThat(capturing.getCapturedLogMessages().get(1), is("This should also be logged")); + } + + @Test + public void shouldCaptureMultipleLogEvents() throws Exception { + LogbackCapturingAppender capturing = sendMultipleFormattedMessages(); + + // then + assertThat(capturing.getCapturedEvents().get(0).getLevel(), is(Level.INFO)); + assertThat(capturing.getCapturedEvents().get(1).getLevel(), is(Level.WARN)); + } + + + @Test + public void shouldNotCaptureAGiveLogAfterCleanUp() throws Exception { + // Given + LogbackCapturingAppender capturing = LogbackCapturingAppender.weaveInto(OurDomainWithLogger.LOG); + OurDomainWithLogger domainClass = new OurDomainWithLogger(); + domainClass.logInfo("This should be logged at info"); + LogbackCapturingAppender.cleanUpAll(); + + // when + domainClass.logInfo("This should not be logged"); + + // then + assertThat(capturing.getCapturedLogMessages().get(0), is("This should be logged at info")); + } + + private LogbackCapturingAppender sendMultipleFormattedMessages() { + // Given + LogbackCapturingAppender capturing = LogbackCapturingAppender.weaveInto(OurDomainWithLogger.LOG); + OurDomainWithLogger domainClass = new OurDomainWithLogger(); + + // when + domainClass.logInfo("This should be logged{}", "!"); + domainClass.logWarn("This should also be logged"); + return capturing; + } + +} diff --git a/java/logback-spike/src/main/java/com/thekua/spikes/OurDomainWithLogger.java b/src/test/java/com/thekua/spikes/OurDomainWithLogger.java similarity index 52% rename from java/logback-spike/src/main/java/com/thekua/spikes/OurDomainWithLogger.java rename to src/test/java/com/thekua/spikes/OurDomainWithLogger.java index 785ac6b..6d52f76 100644 --- a/java/logback-spike/src/main/java/com/thekua/spikes/OurDomainWithLogger.java +++ b/src/test/java/com/thekua/spikes/OurDomainWithLogger.java @@ -8,8 +8,16 @@ public class OurDomainWithLogger { static Logger LOG = LoggerFactory.getLogger(OurDomainWithLogger.class); - public void logThis(String message) { + public void logInfo(String message) { LOG.info(message); } + public void logWarn(String message) { + LOG.warn(message); + } + + public void logInfo(String message, Object... args) { + LOG.info(message, args); + } + }