Improve support for alternative Log4j 2 configuration file formats

This commit adds support for configuring Log4j 2 with YAML. It also
improves the existing support for configuring Log4j 2 with JSON.

Previously, Log4J2LoggingSystem returned a hard-coded list of standard
config locations that includes both JSON and XML file suffixes. Log4j 
2’s support for JSON configuration files requires Jackson’s ObjectMapper
to be on the classpath so, in its absence, the standard config locations
were incorrect.

This commit updates Log4J2LoggingSystem to return an array of standard
config locations based on what’s on the classpath. It also updates the
documentation to describe the additional dependencies that are required
to enable YAML or JSON-based configuration.

Closes gh-2239
This commit is contained in:
Andy Wilkinson 2015-01-22 15:06:20 +00:00
parent f0421801fb
commit ea7b5c6e3c
4 changed files with 100 additions and 5 deletions

View File

@ -445,6 +445,11 @@
<artifactId>jackson-dataformat-xml</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-joda</artifactId>

View File

@ -1241,6 +1241,18 @@ samples for more detail and to see it in action.
[[howto-configure-log4j-for-logging-yaml-or-json-config]]
==== Use YAML or JSON to configure Log4j 2
In addition to its default XML configuration format, Log4j 2 also supports YAML and JSON
configuration files. To configure Log4j 2 to use an alternative configuration file format
all you need to do is add an appropriate dependency to the classpath. To use YAML, add a
dependency on `com.fasterxml.jackson.dataformat:jackson-dataformat-yaml` and Log4j 2 will
look for configuration files names `log4j2.yaml` or `log4j2.yml`. To use JSON, add a
dependency on `com.fasterxml.jackson.core:jackson-databind` and Log4j 2 will look for
configuration files named `log4j2.json` or `log4j2.jsn`
[[howto-data-access]]
== Data Access

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2014 the original author or authors.
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,8 +17,10 @@
package org.springframework.boot.logging.log4j2;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.logging.log4j.Level;
@ -38,6 +40,7 @@ import org.springframework.boot.logging.LogLevel;
import org.springframework.boot.logging.LoggingSystem;
import org.springframework.boot.logging.Slf4JLoggingSystem;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ResourceUtils;
/**
@ -95,7 +98,24 @@ public class Log4J2LoggingSystem extends Slf4JLoggingSystem {
@Override
protected String[] getStandardConfigLocations() {
return new String[] { "log4j2.json", "log4j2.jsn", "log4j2.xml" };
return getCurrentlySupportedConfigLocations();
}
private String[] getCurrentlySupportedConfigLocations() {
List<String> supportedConfigLocations = new ArrayList<String>();
if (isClassAvailable("com.fasterxml.jackson.dataformat.yaml.YAMLParser")) {
Collections.addAll(supportedConfigLocations, "log4j2.yaml", "log4j2.yml");
}
if (isClassAvailable("com.fasterxml.jackson.databind.ObjectMapper")) {
Collections.addAll(supportedConfigLocations, "log4j2.json", "log4j2.jsn");
}
supportedConfigLocations.add("log4j2.xml");
return supportedConfigLocations.toArray(new String[supportedConfigLocations
.size()]);
}
protected boolean isClassAvailable(String className) {
return ClassUtils.isPresent(className, getClassLoader());
}
@Override

View File

@ -1,5 +1,5 @@
/*
* Copyright 2012-2014 the original author or authors.
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -17,6 +17,9 @@
package org.springframework.boot.logging.log4j2;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -29,7 +32,11 @@ import org.springframework.boot.logging.LogLevel;
import org.springframework.boot.test.OutputCapture;
import org.springframework.util.StringUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import static org.hamcrest.Matchers.arrayContaining;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
@ -39,14 +46,14 @@ import static org.junit.Assert.assertTrue;
*
* @author Daniel Fullarton
* @author Phillip Webb
* @author Andy Wilkinson
*/
public class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
@Rule
public OutputCapture output = new OutputCapture();
private final Log4J2LoggingSystem loggingSystem = new Log4J2LoggingSystem(getClass()
.getClassLoader());
private final TestLog4J2LoggingSystem loggingSystem = new TestLog4J2LoggingSystem();
private Logger logger;
@ -120,4 +127,55 @@ public class Log4J2LoggingSystemTests extends AbstractLoggingSystemTests {
assertTrue("Wrong output:\n" + output, output.contains("Hello world"));
}
@Test
public void configLocationsWithNoExtraDependencies() {
assertThat(this.loggingSystem.getStandardConfigLocations(),
is(arrayContaining("log4j2.xml")));
}
@Test
public void configLocationsWithJacksonDatabind() {
this.loggingSystem.availableClasses(ObjectMapper.class.getName());
assertThat(this.loggingSystem.getStandardConfigLocations(),
is(arrayContaining("log4j2.json", "log4j2.jsn", "log4j2.xml")));
}
@Test
public void configLocationsWithJacksonDataformatYaml() {
this.loggingSystem
.availableClasses("com.fasterxml.jackson.dataformat.yaml.YAMLParser");
assertThat(this.loggingSystem.getStandardConfigLocations(),
is(arrayContaining("log4j2.yaml", "log4j2.yml", "log4j2.xml")));
}
@Test
public void configLocationsWithJacksonDatabindAndDataformatYaml() {
this.loggingSystem.availableClasses(
"com.fasterxml.jackson.dataformat.yaml.YAMLParser",
ObjectMapper.class.getName());
assertThat(
this.loggingSystem.getStandardConfigLocations(),
is(arrayContaining("log4j2.yaml", "log4j2.yml", "log4j2.json",
"log4j2.jsn", "log4j2.xml")));
}
private static class TestLog4J2LoggingSystem extends Log4J2LoggingSystem {
private List<String> availableClasses = new ArrayList<String>();
public TestLog4J2LoggingSystem() {
super(TestLog4J2LoggingSystem.class.getClassLoader());
}
@Override
protected boolean isClassAvailable(String className) {
return this.availableClasses.contains(className);
}
private void availableClasses(String... classNames) {
Collections.addAll(this.availableClasses, classNames);
}
}
}