From cad266652246ae92e57f48539e9ddf7f8d890313 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Wed, 30 Sep 2015 16:53:13 +0100 Subject: [PATCH] Add support for LOG_LEVEL_PATTERN replacing the default level pattern For logback we also support logging.pattern.level as a synonym. Fixes gh-4062 --- .../appendix-application-properties.adoc | 1 + .../main/asciidoc/spring-boot-features.adoc | 26 ++++++++++++++ .../logback/DefaultLogbackConfiguration.java | 6 ++-- .../logging/logback/LogbackLoggingSystem.java | 35 +++++++++++-------- .../boot/logging/log4j/log4j-file.properties | 3 +- .../boot/logging/log4j/log4j.properties | 3 +- .../boot/logging/log4j2/log4j2-file.xml | 3 +- .../boot/logging/log4j2/log4j2.xml | 3 +- .../boot/logging/logback/defaults.xml | 4 +-- .../logback/LogbackLoggingSystemTests.java | 33 +++++++++++------ 10 files changed, 83 insertions(+), 34 deletions(-) diff --git a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index 99b8fd4ebd..6f74096607 100644 --- a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -63,6 +63,7 @@ content into your application; rather pick only the properties that you need. logging.path=/var/log logging.pattern.console= # appender pattern for output to the console (only supported with the default logback setup) logging.pattern.file= # appender pattern for output to the file (only supported with the default logback setup) + logging.pattern.level= # appender pattern for the log level (default %5p, only supported with the default logback setup) # IDENTITY ({sc-spring-boot}/context/ContextIdApplicationContextInitializer.{sc-ext}[ContextIdApplicationContextInitializer]) spring.application.name= diff --git a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc index 172c61da47..d908c574a6 100644 --- a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc +++ b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc @@ -1085,6 +1085,18 @@ To help with the customization some other properties are transferred from the Sp |`LOG_PATH` |Used in default log configuration if defined. +|`logging.pattern.console` +|`CONSOLE_LOG_PATTERN` +|The log pattern to use on the console (stdout). (Not supported with JDK logger.) + +|`logging.pattern.file` +|`FILE_LOG_PATTERN` +|The log pattern to use in a file (if LOG_FILE enabled). (Not supported with JDK logger.) + +|`logging.pattern.level` +|`LOG_LEVEL_PATTERN` +|The format to use to render the log level (default `%5p`). (The `logging.pattern.level` form is only supported by Logback.) + |`PID` |`PID` |The current process ID (discovered if possible and when not already defined as an OS @@ -1095,6 +1107,20 @@ To help with the customization some other properties are transferred from the Sp All the logging systems supported can consult System properties when parsing their configuration files. See the default configurations in `spring-boot.jar` for examples. +[TIP] +==== + +You can add MDC and other ad-hoc content to log lines by overriding +only the `LOG_LEVEL_PATTERN` (or `logging.pattern.level` with +Logback). For example, if you use `logging.pattern.level=user:%X{user} +%5p` then the default log format will contain an MDC entry for "user" +if it exists, e.g. + +---- +2015-09-30 12:30:04.031 user:juergen INFO 22174 --- [ nio-8080-exec-0] demo.Controller Handling authenticated request +---- +==== + [[boot-features-logback-extensions]] diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/logback/DefaultLogbackConfiguration.java b/spring-boot/src/main/java/org/springframework/boot/logging/logback/DefaultLogbackConfiguration.java index 4c95ab4b7e..72f01c406d 100644 --- a/spring-boot/src/main/java/org/springframework/boot/logging/logback/DefaultLogbackConfiguration.java +++ b/spring-boot/src/main/java/org/springframework/boot/logging/logback/DefaultLogbackConfiguration.java @@ -46,12 +46,12 @@ import ch.qos.logback.core.util.OptionHelper; class DefaultLogbackConfiguration { private static final String CONSOLE_LOG_PATTERN = "%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} " - + "%clr(%5p) %clr(${PID:- }){magenta} %clr(---){faint} " + + "%clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} " + "%clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} " + "%clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%rEx}"; - private static final String FILE_LOG_PATTERN = "%d{yyyy-MM-dd HH:mm:ss.SSS} %5p " - + "${PID:- } --- [%t] %-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%rEx}"; + private static final String FILE_LOG_PATTERN = "%d{yyyy-MM-dd HH:mm:ss.SSS} " + + "${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%t] %-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%rEx}"; private static final Charset UTF8 = Charset.forName("UTF-8"); diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/logback/LogbackLoggingSystem.java b/spring-boot/src/main/java/org/springframework/boot/logging/logback/LogbackLoggingSystem.java index 1a12ef8674..8d81ab6a0c 100644 --- a/spring-boot/src/main/java/org/springframework/boot/logging/logback/LogbackLoggingSystem.java +++ b/spring-boot/src/main/java/org/springframework/boot/logging/logback/LogbackLoggingSystem.java @@ -57,6 +57,7 @@ import ch.qos.logback.core.status.Status; public class LogbackLoggingSystem extends Slf4JLoggingSystem { private static final Map LEVELS; + static { Map levels = new HashMap(); levels.put(LogLevel.TRACE, Level.TRACE); @@ -85,8 +86,8 @@ public class LogbackLoggingSystem extends Slf4JLoggingSystem { @Override protected String[] getStandardConfigLocations() { - return new String[] { "logback-test.groovy", "logback-test.xml", - "logback.groovy", "logback.xml" }; + return new String[] { "logback-test.groovy", "logback-test.xml", "logback.groovy", + "logback.xml" }; } @Override @@ -109,6 +110,9 @@ public class LogbackLoggingSystem extends Slf4JLoggingSystem { LoggerContext context = getLoggerContext(); stopAndReset(context); LogbackConfigurator configurator = new LogbackConfigurator(context); + context.putProperty("LOG_LEVEL_PATTERN", + initializationContext.getEnvironment().resolvePlaceholders( + "${logging.pattern.level:${LOG_LEVEL_PATTERN:%5p}}")); new DefaultLogbackConfiguration(initializationContext, logFile) .apply(configurator); } @@ -127,8 +131,8 @@ public class LogbackLoggingSystem extends Slf4JLoggingSystem { ResourceUtils.getURL(location)); } catch (Exception ex) { - throw new IllegalStateException("Could not initialize Logback logging from " - + location, ex); + throw new IllegalStateException( + "Could not initialize Logback logging from " + location, ex); } List statuses = loggerContext.getStatusManager().getCopyOfStatusList(); StringBuilder errors = new StringBuilder(); @@ -139,8 +143,8 @@ public class LogbackLoggingSystem extends Slf4JLoggingSystem { } } if (errors.length() > 0) { - throw new IllegalStateException("Logback configuration error " - + "detected: \n" + errors); + throw new IllegalStateException( + "Logback configuration error " + "detected: \n" + errors); } } @@ -197,20 +201,21 @@ public class LogbackLoggingSystem extends Slf4JLoggingSystem { private ch.qos.logback.classic.Logger getLogger(String name) { LoggerContext factory = getLoggerContext(); - return factory.getLogger(StringUtils.isEmpty(name) ? Logger.ROOT_LOGGER_NAME - : name); + return factory + .getLogger(StringUtils.isEmpty(name) ? Logger.ROOT_LOGGER_NAME : name); } private LoggerContext getLoggerContext() { ILoggerFactory factory = StaticLoggerBinder.getSingleton().getLoggerFactory(); - Assert.isInstanceOf(LoggerContext.class, factory, String.format( - "LoggerFactory is not a Logback LoggerContext but Logback is on " - + "the classpath. Either remove Logback or the competing " - + "implementation (%s loaded from %s). If you are using " - + "Weblogic you will need to add 'org.slf4j' to " - + "prefer-application-packages in WEB-INF/weblogic.xml", - factory.getClass(), getLocation(factory))); + Assert.isInstanceOf(LoggerContext.class, factory, + String.format( + "LoggerFactory is not a Logback LoggerContext but Logback is on " + + "the classpath. Either remove Logback or the competing " + + "implementation (%s loaded from %s). If you are using " + + "Weblogic you will need to add 'org.slf4j' to " + + "prefer-application-packages in WEB-INF/weblogic.xml", + factory.getClass(), getLocation(factory))); return (LoggerContext) factory; } diff --git a/spring-boot/src/main/resources/org/springframework/boot/logging/log4j/log4j-file.properties b/spring-boot/src/main/resources/org/springframework/boot/logging/log4j/log4j-file.properties index ca1f0ec327..5242f3d128 100644 --- a/spring-boot/src/main/resources/org/springframework/boot/logging/log4j/log4j-file.properties +++ b/spring-boot/src/main/resources/org/springframework/boot/logging/log4j/log4j-file.properties @@ -3,7 +3,8 @@ log4j.rootCategory=INFO, CONSOLE, FILE PID=???? LOG_PATH=${java.io.tmpdir} LOG_FILE=${LOG_PATH}/spring.log -LOG_PATTERN=[%d{yyyy-MM-dd HH:mm:ss.SSS}] boot%X{context} - ${PID} %5p [%t] --- %c{1}: %m%n +LOG_LEVEL_PATTERN=%5p +LOG_PATTERN=[%d{yyyy-MM-dd HH:mm:ss.SSS}] boot%X{context} - ${PID} ${LOG_LEVEL_PATTERN} [%t] --- %c{1}: %m%n # CONSOLE is set to be a ConsoleAppender using a PatternLayout. log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender diff --git a/spring-boot/src/main/resources/org/springframework/boot/logging/log4j/log4j.properties b/spring-boot/src/main/resources/org/springframework/boot/logging/log4j/log4j.properties index 4b4d9f291e..a8769ee7a9 100644 --- a/spring-boot/src/main/resources/org/springframework/boot/logging/log4j/log4j.properties +++ b/spring-boot/src/main/resources/org/springframework/boot/logging/log4j/log4j.properties @@ -1,7 +1,8 @@ log4j.rootCategory=INFO, CONSOLE PID=???? -LOG_PATTERN=[%d{yyyy-MM-dd HH:mm:ss.SSS}] boot%X{context} - ${PID} %5p [%t] --- %c{1}: %m%n +LOG_LEVEL_PATTERN=%5p +LOG_PATTERN=[%d{yyyy-MM-dd HH:mm:ss.SSS}] boot%X{context} - ${PID} ${LOG_LEVEL_PATTERN} [%t] --- %c{1}: %m%n # CONSOLE is set to be a ConsoleAppender using a PatternLayout. log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender diff --git a/spring-boot/src/main/resources/org/springframework/boot/logging/log4j2/log4j2-file.xml b/spring-boot/src/main/resources/org/springframework/boot/logging/log4j2/log4j2-file.xml index 948991c554..e6eaab5e3f 100644 --- a/spring-boot/src/main/resources/org/springframework/boot/logging/log4j2/log4j2-file.xml +++ b/spring-boot/src/main/resources/org/springframework/boot/logging/log4j2/log4j2-file.xml @@ -3,7 +3,8 @@ ???? %rEx - %d{yyyy-MM-dd HH:mm:ss.SSS} %5p ${sys:PID} --- [%t] %-40.40c{1.} : %m%n${sys:LOG_EXCEPTION_CONVERSION_WORD} + %5p + %d{yyyy-MM-dd HH:mm:ss.SSS} ${LOG_LEVEL_PATTERN} ${sys:PID} --- [%t] %-40.40c{1.} : %m%n${sys:LOG_EXCEPTION_CONVERSION_WORD} diff --git a/spring-boot/src/main/resources/org/springframework/boot/logging/log4j2/log4j2.xml b/spring-boot/src/main/resources/org/springframework/boot/logging/log4j2/log4j2.xml index d0a04a3910..7781ec0a40 100644 --- a/spring-boot/src/main/resources/org/springframework/boot/logging/log4j2/log4j2.xml +++ b/spring-boot/src/main/resources/org/springframework/boot/logging/log4j2/log4j2.xml @@ -3,7 +3,8 @@ ???? %rEx - %clr{%d{yyyy-MM-dd HH:mm:ss.SSS}}{faint} %clr{%5p} %clr{${sys:PID}}{magenta} %clr{---}{faint} %clr{[%15.15t]}{faint} %clr{%-40.40c{1.}}{cyan} %clr{:}{faint} %m%n${sys:LOG_EXCEPTION_CONVERSION_WORD} + %5p + %clr{%d{yyyy-MM-dd HH:mm:ss.SSS}}{faint} %clr{${LOG_LEVEL_PATTERN}} %clr{${sys:PID}}{magenta} %clr{---}{faint} %clr{[%15.15t]}{faint} %clr{%-40.40c{1.}}{cyan} %clr{:}{faint} %m%n${sys:LOG_EXCEPTION_CONVERSION_WORD} diff --git a/spring-boot/src/main/resources/org/springframework/boot/logging/logback/defaults.xml b/spring-boot/src/main/resources/org/springframework/boot/logging/logback/defaults.xml index 7654847b2e..7cf9716557 100644 --- a/spring-boot/src/main/resources/org/springframework/boot/logging/logback/defaults.xml +++ b/spring-boot/src/main/resources/org/springframework/boot/logging/logback/defaults.xml @@ -9,8 +9,8 @@ initialization performed by Boot - - + + org.springframework.boot diff --git a/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java b/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java index 1db49aece1..ea5737cb4d 100644 --- a/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java +++ b/spring-boot/src/test/java/org/springframework/boot/logging/logback/LogbackLoggingSystemTests.java @@ -16,6 +16,16 @@ package org.springframework.boot.logging.logback; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + import java.io.File; import java.io.FileReader; import java.util.logging.Handler; @@ -44,16 +54,6 @@ import org.springframework.util.StringUtils; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.LoggerContext; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.not; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; - /** * Tests for {@link LogbackLoggingSystem}. * @@ -237,6 +237,19 @@ public class LogbackLoggingSystemTests extends AbstractLoggingSystemTests { getLineWithText(output, "Hello world").contains("INFO")); } + @Test + public void testLevelPatternProperty() { + MockEnvironment environment = new MockEnvironment(); + environment.setProperty("logging.pattern.level", "X%clr(%p)X"); + LoggingInitializationContext loggingInitializationContext = new LoggingInitializationContext( + environment); + this.loggingSystem.initialize(loggingInitializationContext, null, null); + this.logger.info("Hello world"); + String output = this.output.toString().trim(); + assertTrue("Wrong output pattern:\n" + output, + getLineWithText(output, "Hello world").contains("XINFOX")); + } + @Test public void testFilePatternProperty() throws Exception { MockEnvironment environment = new MockEnvironment();