Skip to content
This repository was archived by the owner on Apr 23, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

<logback.version>1.1.1</logback.version>
<slf4j.version>1.7.6</slf4j.version>
<hamcrest.version>1.3</hamcrest.version>

<!-- JDK properties that can be overridden from the command line
to build under a different JDKs (continuous integration) -->
Expand All @@ -115,6 +117,12 @@
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<version>${hamcrest.version}</version>
<scope>test</scope>
</dependency>

</dependencies>

Expand Down
60 changes: 60 additions & 0 deletions spring/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,26 @@
<optional>true</optional>
</dependency>

<!-- Testing -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${slf4j.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<version>${hamcrest.version}</version>
<scope>test</scope>
</dependency>

</dependencies>

<build>
Expand All @@ -90,6 +110,46 @@
</execution>
</executions>
</plugin>

<plugin>
<groupId>com.mycila</groupId>
<artifactId>license-maven-plugin</artifactId>
<version>2.6</version>
<executions>
<execution>
<id>default</id>
<goals>
<goal>check</goal>
</goals>
<configuration>
<excludes>
<exclude>**/README*</exclude>
<exclude>**/LICENSE*</exclude>
<exclude>**/AUTHORS*</exclude>
<exclude>src/test/resources/**</exclude>
<exclude>src/main/resources/**</exclude>
<exclude>src/test/java/**/MissingConfigurationTest*</exclude>
</excludes>
</configuration>
</execution>
<execution>
<id>mitre-license</id>
<goals>
<goal>check</goal>
</goals>
<configuration>
<properties>
<owner>The MITRE Corporation</owner>
<project.inceptionYear>2016</project.inceptionYear>
<email>jgibson@mitre.org</email>
</properties>
<includes>
<include>src/test/java/**/MissingConfigurationTest*</include>
</includes>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;

import org.slf4j.impl.StaticLoggerBinder;
Expand Down Expand Up @@ -62,13 +64,29 @@ private LogbackConfigurer() {
* (e.g. "classpath:logback.xml"), an absolute file URL
* (e.g. "file:C:/logback.xml), or a plain absolute path in the file system
* (e.g. "C:/logback.xml")
* @throws java.io.FileNotFoundException if the location specifies an invalid file path
* @throws java.io.FileNotFoundException if the location is not found or if the location specifies an invalid file path
* @throws ch.qos.logback.core.joran.spi.JoranException
* Thrown
*/
public static void initLogging(String location) throws FileNotFoundException, JoranException {
String resolvedLocation = SystemPropertyUtils.resolvePlaceholders(location);
URL url = ResourceUtils.getURL(resolvedLocation);
InputStream check = null;
try {
check = url.openStream();
} catch (FileNotFoundException e) {
throw e;
} catch (IOException e) {
// Ignore and let the configuration continue in case Logback can handle it successfully
} finally {
if (check != null) {
try {
check.close();
} catch (IOException e) {
// We can probably eat this safely and let Logback trip the error
}
}
}
LoggerContext loggerContext = (LoggerContext)StaticLoggerBinder.getSingleton().getLoggerFactory();

// in the current version logback automatically configures at startup the context, so we have to reset it
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
/**
* Copyright (C) 2016 The MITRE Corporation (jgibson@mitre.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ch.qos.logback.ext.spring;

import ch.qos.logback.classic.BasicConfigurator;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.UnsynchronizedAppenderBase;
import ch.qos.logback.core.joran.spi.JoranException;
import ch.qos.logback.ext.spring.web.WebLogbackConfigurer;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Random;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.rules.TestRule;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.impl.StaticLoggerBinder;
import org.springframework.mock.web.MockServletContext;
import static org.junit.Assert.*;

/**
* Tests the behavior of the logging configuration if the configuration files are missing.
* @author John Gibson
*/
public class MissingConfigurationTest {
/**
* Parameter specifying the location of the logback config file
*/
private static final String CONFIG_LOCATION_PARAM = "logbackConfigLocation";

private static Random rng = new Random();

@ClassRule
public static final TemporaryFolder PLAYGROUND = new TemporaryFolder();

private LogSensitiveMockServletContext context = new LogSensitiveMockServletContext();

@Before
public void clearFakeLogs() {
FakeAppender.logs.clear();
}

/**
* Aside from printing the test name this also ensures that the logging system is in the known default state.
*/
@Rule
public TestRule ensureLoggingInitialized = new TestWatcher() {
@Override
protected void starting(Description description) {
LoggerContext loggerContext = doCleanup();
BasicConfigurator.configure(loggerContext);
Logger log = LoggerFactory.getLogger(MissingConfigurationTest.class);
log.info("Starting test: " + description.getMethodName());
}
};

private LoggerContext doCleanup() {
LoggerContext loggerContext = (LoggerContext) StaticLoggerBinder.getSingleton().getLoggerFactory();
loggerContext.reset();
return loggerContext;
}

@After
public void cleanupLogging() {
doCleanup();
}

@Test(timeout = 3000L)
public void testMissingFileConfiguration() throws Exception {
File missingConfig = null;
do {
missingConfig = new File("missingConfigTest" + rng.nextInt() + ".xml");
} while (missingConfig.exists());

context.addInitParameter(CONFIG_LOCATION_PARAM, missingConfig.toURI().toURL().toString());

WebLogbackConfigurer.initLogging(context);
assertThat("Missing configuration file wasn't seen.", context.messages,
Matchers.hasItem((Matchers.containsString(missingConfig.getName()))));
}

@Test
public void testMissingClasspathConfiguration() throws Exception {
final String fakeEntry = "classpath:ch/qos/logback/ext/spring/does/not/exist/" + rng.nextInt() + ".xml";
context.addInitParameter(CONFIG_LOCATION_PARAM, fakeEntry);
WebLogbackConfigurer.initLogging(context);
assertThat("Missing configuration classpath wasn't seen.", context.messages,
Matchers.hasItem((Matchers.containsString(fakeEntry))));
}

@Test
public void testInvalidConfiguration() throws Exception {
File empty = PLAYGROUND.newFile("empty.xml");
context.addInitParameter(CONFIG_LOCATION_PARAM, empty.toURI().toURL().toString());
try {
WebLogbackConfigurer.initLogging(context);
fail("Error expected.");
} catch (RuntimeException e) {
assertEquals("Unexpected error while configuring logback", e.getMessage());
assertTrue("Expected a RuntimeException wrapping a JoranException, but it was a different cause: " + e.getCause(), e.getCause() instanceof JoranException);
}
}

@Test(timeout = 3000L)
public void testMissingFollowedByNormalConfiguration() throws Exception {
File missingConfig = null;
do {
missingConfig = new File("missingConfigTest" + rng.nextInt() + ".xml");
} while (missingConfig.exists());

File fileConfig = PLAYGROUND.newFile("fakeLogger.xml");
URL u = getClass().getResource("fakeLogger.xml");
InputStream is = null;
FileOutputStream os = null;
try {
is = u.openStream();
os = new FileOutputStream(fileConfig);
byte[] buff = new byte[2048];
for (int read = is.read(buff); read != -1; read = is.read(buff)) {
os.write(buff, 0, read);
}
} finally {
if (is != null) {
is.close();
}
if (os != null) {
os.close();
}
}

context.addInitParameter(CONFIG_LOCATION_PARAM, missingConfig.toURI().toURL().toString() + "," +
fileConfig.toURI().toURL().toString());
WebLogbackConfigurer.initLogging(context);
assertThat("Missing configuration file wasn't seen.", context.messages,
Matchers.hasItem((Matchers.containsString(missingConfig.getName()))));
assertThat("Actual configuration file wasn't seen.", context.messages,
Matchers.hasItem((Matchers.containsString(fileConfig.toURI().toURL().toString()))));

Logger log = LoggerFactory.getLogger(MissingConfigurationTest.class);
String key = "missingConfig" + rng.nextInt();
log.error(key);
ArrayList<ILoggingEvent> logs;
synchronized (FakeAppender.class) {
logs = new ArrayList<ILoggingEvent>(FakeAppender.logs);
}
assertThat(logs, Matchers.hasItem(new LoggingEventMatcher(key)));
}

@Test(timeout = 3000L)
public void testMissingFollowedByNormalClasspathConfiguration() throws Exception {
File missingConfig = null;
do {
missingConfig = new File("missingConfigTest" + rng.nextInt() + ".xml");
} while (missingConfig.exists());

String cpConfig = "classpath:ch/qos/logback/ext/spring/fakeLogger.xml";
context.addInitParameter(CONFIG_LOCATION_PARAM, missingConfig.toURI().toURL().toString() + "," +
cpConfig);
WebLogbackConfigurer.initLogging(context);
assertThat("Missing configuration file wasn't seen.", context.messages,
Matchers.hasItem((Matchers.containsString(missingConfig.getName()))));
assertThat("Actual configuration file wasn't seen.", context.messages,
Matchers.hasItem((Matchers.containsString(cpConfig))));

Logger log = LoggerFactory.getLogger(MissingConfigurationTest.class);
String key = "missingConfig" + rng.nextInt();
log.error(key);
ArrayList<ILoggingEvent> logs;
synchronized (FakeAppender.class) {
logs = new ArrayList<ILoggingEvent>(FakeAppender.logs);
}
assertThat(logs, Matchers.hasItem(new LoggingEventMatcher(key)));
}

public static class FakeAppender extends UnsynchronizedAppenderBase<ILoggingEvent> {
private static final ArrayList<ILoggingEvent> logs = new ArrayList<ILoggingEvent>();

@Override
protected void append(ILoggingEvent eventObject) {
synchronized(FakeAppender.class) {
logs.add(eventObject);
}
}
}

private static class LoggingEventMatcher extends BaseMatcher<ILoggingEvent> {
private final String message;

public LoggingEventMatcher(String message) {
this.message = message;
}

@Override
public boolean matches(Object item) {
if (!(item instanceof ILoggingEvent)) {
return false;
}
return message.equals(((ILoggingEvent) item).getMessage());
}

@Override
public void describeTo(org.hamcrest.Description description) {
description.appendText("Logging event with message").appendValue(message);
}
}

/**
* Spring's MockServletContext class uses a logger to write messages. This interferes with testing the actual
* logging system. In addition it makes it difficult to capture messages that we should expect to be logged to the
* servlet.
*/
private static class LogSensitiveMockServletContext extends MockServletContext {
public final ArrayList<String> messages = new ArrayList<String>();
private static final String PREFIX = "MockServlet: ";

@Override
public void log(String message) {
System.out.println(PREFIX + message);
messages.add(message);
}

@Override
public void log(Exception ex, String message) {
log(message, ex);
}

@Override
public void log(String message, Throwable ex) {
System.err.println(PREFIX + message);
messages.add(message);
System.err.println(PREFIX + ex.toString());
messages.add(ex.toString());
ex.printStackTrace();
}
}
}
Loading