From 177ea459f1f825d0e7a39049d5294258fdb431f6 Mon Sep 17 00:00:00 2001 From: Rob Baily Date: Thu, 2 Jul 2015 09:53:09 -0400 Subject: [PATCH] Support log pattern properties with logback Update AbstractLoggingSystem to pass LoggingInitializationContext to loadDefaults() method to enable access to the environment. DefaultLogbackConfiguration now uses this to find log pattern overrides. Fixes gh-3367 Closes gh-3405 --- .../appendix-application-properties.adoc | 2 + .../boot/logging/AbstractLoggingSystem.java | 6 +- .../boot/logging/java/JavaLoggingSystem.java | 5 +- .../logging/log4j/Log4JLoggingSystem.java | 3 +- .../logging/log4j2/Log4J2LoggingSystem.java | 3 +- .../logback/DefaultLogbackConfiguration.java | 27 +++++++-- .../logging/logback/LogbackLoggingSystem.java | 7 ++- .../logback/LogbackLoggingSystemTests.java | 57 ++++++++++++++++++- 8 files changed, 96 insertions(+), 14 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 b19f5a8e8d..24234afe67 100644 --- a/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -57,6 +57,8 @@ content into your application; rather pick only the properties that you need. logging.file=myapp.log logging.config= # location of config file (default classpath:logback.xml for logback) logging.level.*= # levels for loggers, e.g. "logging.level.org.springframework=DEBUG" (TRACE, DEBUG, INFO, WARN, ERROR, FATAL, OFF) + 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) # IDENTITY ({sc-spring-boot}/context/ContextIdApplicationContextInitializer.{sc-ext}[ContextIdApplicationContextInitializer]) spring.application.name= diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/AbstractLoggingSystem.java b/spring-boot/src/main/java/org/springframework/boot/logging/AbstractLoggingSystem.java index 6e587ed2f7..ca69e50a5b 100644 --- a/spring-boot/src/main/java/org/springframework/boot/logging/AbstractLoggingSystem.java +++ b/spring-boot/src/main/java/org/springframework/boot/logging/AbstractLoggingSystem.java @@ -71,7 +71,7 @@ public abstract class AbstractLoggingSystem extends LoggingSystem { loadConfiguration(initializationContext, config, logFile); return; } - loadDefaults(logFile); + loadDefaults(initializationContext, logFile); } /** @@ -129,9 +129,11 @@ public abstract class AbstractLoggingSystem extends LoggingSystem { /** * Load sensible defaults for the logging system. + * @param initializationContext the logging initialization context * @param logFile the file to load or {@code null} if no log file is to be written */ - protected abstract void loadDefaults(LogFile logFile); + protected abstract void loadDefaults( + LoggingInitializationContext initializationContext, LogFile logFile); /** * Load a specific configuration. diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/java/JavaLoggingSystem.java b/spring-boot/src/main/java/org/springframework/boot/logging/java/JavaLoggingSystem.java index ff662ba5cb..fe172f0846 100644 --- a/spring-boot/src/main/java/org/springframework/boot/logging/java/JavaLoggingSystem.java +++ b/spring-boot/src/main/java/org/springframework/boot/logging/java/JavaLoggingSystem.java @@ -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. @@ -72,7 +72,8 @@ public class JavaLoggingSystem extends AbstractLoggingSystem { } @Override - protected void loadDefaults(LogFile logFile) { + protected void loadDefaults(LoggingInitializationContext initializationContext, + LogFile logFile) { if (logFile != null) { loadConfiguration(getPackagedConfigFile("logging-file.properties"), logFile); } diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/log4j/Log4JLoggingSystem.java b/spring-boot/src/main/java/org/springframework/boot/logging/log4j/Log4JLoggingSystem.java index 96af7c1a25..ab772113d6 100644 --- a/spring-boot/src/main/java/org/springframework/boot/logging/log4j/Log4JLoggingSystem.java +++ b/spring-boot/src/main/java/org/springframework/boot/logging/log4j/Log4JLoggingSystem.java @@ -70,7 +70,8 @@ public class Log4JLoggingSystem extends Slf4JLoggingSystem { } @Override - protected void loadDefaults(LogFile logFile) { + protected void loadDefaults(LoggingInitializationContext initializationContext, + LogFile logFile) { if (logFile != null) { loadConfiguration(getPackagedConfigFile("log4j-file.properties"), logFile); } diff --git a/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java b/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java index 57578be33a..72e1a448a9 100644 --- a/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java +++ b/spring-boot/src/main/java/org/springframework/boot/logging/log4j2/Log4J2LoggingSystem.java @@ -136,7 +136,8 @@ public class Log4J2LoggingSystem extends Slf4JLoggingSystem { } @Override - protected void loadDefaults(LogFile logFile) { + protected void loadDefaults(LoggingInitializationContext initializationContext, + LogFile logFile) { if (logFile != null) { loadConfiguration(getPackagedConfigFile("log4j2-file.xml"), logFile); } 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 24aeb38908..45f3d537b0 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 @@ -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. @@ -18,7 +18,12 @@ package org.springframework.boot.logging.logback; import java.nio.charset.Charset; +import org.springframework.boot.bind.RelaxedPropertyResolver; import org.springframework.boot.logging.LogFile; +import org.springframework.boot.logging.LoggingInitializationContext; +import org.springframework.core.env.Environment; +import org.springframework.core.env.PropertyResolver; +import org.springframework.core.env.PropertySourcesPropertyResolver; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.encoder.PatternLayoutEncoder; @@ -50,12 +55,23 @@ class DefaultLogbackConfiguration { private static final Charset UTF8 = Charset.forName("UTF-8"); + private final PropertyResolver patterns; + private final LogFile logFile; - public DefaultLogbackConfiguration(LogFile logFile) { + public DefaultLogbackConfiguration( + LoggingInitializationContext initializationContext, LogFile logFile) { + this.patterns = getPatternsResolver(initializationContext.getEnvironment()); this.logFile = logFile; } + private PropertyResolver getPatternsResolver(Environment environment) { + if (environment == null) { + return new PropertySourcesPropertyResolver(null); + } + return new RelaxedPropertyResolver(environment, "logging.pattern."); + } + @SuppressWarnings("unchecked") public void apply(LogbackConfigurator config) { synchronized (config.getConfigurationLock()) { @@ -99,8 +115,8 @@ class DefaultLogbackConfiguration { private Appender consoleAppender(LogbackConfigurator config) { ConsoleAppender appender = new ConsoleAppender(); PatternLayoutEncoder encoder = new PatternLayoutEncoder(); - encoder.setPattern(OptionHelper.substVars(CONSOLE_LOG_PATTERN, - config.getContext())); + String logPattern = this.patterns.getProperty("console", CONSOLE_LOG_PATTERN); + encoder.setPattern(OptionHelper.substVars(logPattern, config.getContext())); encoder.setCharset(UTF8); config.start(encoder); appender.setEncoder(encoder); @@ -112,7 +128,8 @@ class DefaultLogbackConfiguration { String logFile) { RollingFileAppender appender = new RollingFileAppender(); PatternLayoutEncoder encoder = new PatternLayoutEncoder(); - encoder.setPattern(OptionHelper.substVars(FILE_LOG_PATTERN, config.getContext())); + String logPattern = this.patterns.getProperty("file", FILE_LOG_PATTERN); + encoder.setPattern(OptionHelper.substVars(logPattern, config.getContext())); appender.setEncoder(encoder); config.start(encoder); 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 c4ccc1e660..5fe4adf48f 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 @@ -103,12 +103,14 @@ public class LogbackLoggingSystem extends Slf4JLoggingSystem { } @Override - protected void loadDefaults(LogFile logFile) { + protected void loadDefaults(LoggingInitializationContext initializationContext, + LogFile logFile) { LoggerContext context = getLoggerContext(); context.stop(); context.reset(); LogbackConfigurator configurator = new LogbackConfigurator(context); - new DefaultLogbackConfiguration(logFile).apply(configurator); + new DefaultLogbackConfiguration(initializationContext, logFile) + .apply(configurator); } @Override @@ -210,4 +212,5 @@ public class LogbackLoggingSystem extends Slf4JLoggingSystem { } return "unknown location"; } + } 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 6fe9c1fe86..358aa7676d 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 @@ -17,6 +17,7 @@ package org.springframework.boot.logging.logback; import java.io.File; +import java.io.FileReader; import java.util.logging.Handler; import java.util.logging.LogManager; @@ -30,10 +31,12 @@ import org.slf4j.ILoggerFactory; import org.slf4j.bridge.SLF4JBridgeHandler; import org.slf4j.impl.StaticLoggerBinder; import org.springframework.boot.logging.AbstractLoggingSystemTests; +import org.springframework.boot.logging.LogFile; import org.springframework.boot.logging.LogLevel; import org.springframework.boot.logging.LoggingInitializationContext; import org.springframework.boot.test.OutputCapture; import org.springframework.mock.env.MockEnvironment; +import org.springframework.util.FileCopyUtils; import org.springframework.util.StringUtils; import ch.qos.logback.classic.Logger; @@ -87,6 +90,8 @@ public class LogbackLoggingSystemTests extends AbstractLoggingSystemTests { String output = this.output.toString().trim(); assertTrue("Wrong output:\n" + output, output.contains("Hello world")); assertFalse("Output not hidden:\n" + output, output.contains("Hidden")); + assertTrue("Wrong output pattern:\n" + output, + getLineWithText(output, "Hello world").contains("INFO")); assertFalse(new File(tmpDir() + "/spring.log").exists()); } @@ -98,9 +103,14 @@ public class LogbackLoggingSystemTests extends AbstractLoggingSystemTests { getLogFile(null, tmpDir())); this.logger.info("Hello world"); String output = this.output.toString().trim(); + File file = new File(tmpDir() + "/spring.log"); assertTrue("Wrong output:\n" + output, output.contains("Hello world")); assertFalse("Output not hidden:\n" + output, output.contains("Hidden")); - assertTrue(new File(tmpDir() + "/spring.log").exists()); + assertTrue("Wrong console output pattern:\n" + output, + getLineWithText(output, "Hello world").contains("INFO")); + assertTrue(file.exists()); + assertTrue("Wrong file output pattern:\n" + output, + getLineWithText(file, "Hello world").contains("INFO")); } @Test @@ -197,4 +207,49 @@ public class LogbackLoggingSystemTests extends AbstractLoggingSystemTests { return false; } + @Test + public void testConsolePatternProperty() { + MockEnvironment environment = new MockEnvironment(); + environment.setProperty("logging.pattern.console", "%logger %msg"); + LoggingInitializationContext loggingInitializationContext = new LoggingInitializationContext( + environment); + this.loggingSystem.initialize(loggingInitializationContext, null, null); + this.logger.info("Hello world"); + String output = this.output.toString().trim(); + assertFalse("Wrong output pattern:\n" + output, + getLineWithText(output, "Hello world").contains("INFO")); + } + + @Test + public void testFilePatternProperty() throws Exception { + MockEnvironment environment = new MockEnvironment(); + environment.setProperty("logging.pattern.file", "%logger %msg"); + LoggingInitializationContext loggingInitializationContext = new LoggingInitializationContext( + environment); + File file = new File(tmpDir(), "logback-test.log"); + LogFile logFile = getLogFile(file.getPath(), null); + this.loggingSystem.initialize(loggingInitializationContext, null, logFile); + this.logger.info("Hello world"); + String output = this.output.toString().trim(); + assertTrue("Wrong console output pattern:\n" + output, + getLineWithText(output, "Hello world").contains("INFO")); + assertFalse("Wrong file output pattern:\n" + output, + getLineWithText(file, "Hello world").contains("INFO")); + } + + private String getLineWithText(File file, String outputSearch) throws Exception { + return getLineWithText(FileCopyUtils.copyToString(new FileReader(file)), + outputSearch); + } + + private String getLineWithText(String output, String outputSearch) { + String[] lines = output.split("\\r?\\n"); + for (String line : lines) { + if (line.contains(outputSearch)) { + return line; + } + } + return null; + } + }