Commit 931add4a authored by Phillip Webb's avatar Phillip Webb

Programmatically configure logback defaults

Update LogbackLoggingSystem to programmatically configure logback
defaults rather than parsing XML. This change shaves about 100ms off
the start up time.

Fixes gh-1796
parent 36ffad2b
/*
* Copyright 2012-2014 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.
* 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 org.springframework.boot.logging.logback;
import java.nio.charset.Charset;
import org.springframework.util.StringUtils;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.ConsoleAppender;
import ch.qos.logback.core.rolling.FixedWindowRollingPolicy;
import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy;
import ch.qos.logback.core.util.OptionHelper;
/**
* Default logback configuration used by Spring Boot. Uses {@link LogbackConfigurator} to
* improve startup time. See also the {@code base.xml}, {@code console-appender.xml} and
* {@code file-appender.xml} files provided for classic {@code logback.xml} use.
*
* @author Phillip Webb
* @since 1.1.2
*/
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([%15.15t{14}]){faint} %clr(%-40.40logger{39}){cyan} "
+ "%clr(:){faint} %m%n%wex";
private static final String FILE_LOG_PATTERN = "%d{yyyy-MM-dd HH:mm:ss.SSS} %5p "
+ "${PID:- } [%t] --- %-40.40logger{39} : %m%n%wex";
private static final Charset UTF8 = Charset.forName("UTF-8");
private final String logFile;
public DefaultLogbackConfiguration(String logFile) {
this.logFile = logFile;
}
@SuppressWarnings("unchecked")
public void apply(LogbackConfigurator config) {
synchronized (config.getConfigurationLock()) {
base(config);
Appender<ILoggingEvent> consoleAppender = consoleAppender(config);
if (StringUtils.hasLength(this.logFile)) {
Appender<ILoggingEvent> fileAppender = fileAppender(config, this.logFile);
config.root(Level.INFO, consoleAppender, fileAppender);
}
else {
config.root(Level.INFO, consoleAppender);
}
}
}
private void base(LogbackConfigurator config) {
config.conversionRule("clr", ColorConverter.class);
config.conversionRule("wex", WhitespaceThrowableProxyConverter.class);
LevelRemappingAppender debugRemapAppender = new LevelRemappingAppender(
"org.springframework.boot");
config.start(debugRemapAppender);
config.appender("DEBUG_LEVEL_REMAPPER", debugRemapAppender);
config.logger("", Level.ERROR);
config.logger("org.apache.catalina.startup.DigesterFactory", Level.ERROR);
config.logger("org.apache.catalina.util.LifecycleBase", Level.ERROR);
config.logger("org.apache.coyote.http11.Http11NioProtocol", Level.WARN);
config.logger("org.apache.sshd.common.util.SecurityUtils", Level.WARN);
config.logger("org.apache.tomcat.util.net.NioSelectorPool", Level.WARN);
config.logger("org.crsh.plugin", Level.WARN);
config.logger("org.crsh.ssh", Level.WARN);
config.logger("org.eclipse.jetty.util.component.AbstractLifeCycle", Level.ERROR);
config.logger("org.hibernate.validator.internal.util.Version", Level.WARN);
config.logger("org.springframework.boot.actuate.autoconfigure."
+ "CrshAutoConfiguration", Level.WARN);
config.logger("org.springframework.boot.actuate.endpoint.jmx", null, false,
debugRemapAppender);
config.logger("org.thymeleaf", null, false, debugRemapAppender);
}
private Appender<ILoggingEvent> consoleAppender(LogbackConfigurator config) {
ConsoleAppender<ILoggingEvent> appender = new ConsoleAppender<ILoggingEvent>();
PatternLayoutEncoder encoder = new PatternLayoutEncoder();
encoder.setPattern(OptionHelper.substVars(CONSOLE_LOG_PATTERN,
config.getContext()));
encoder.setCharset(UTF8);
config.start(encoder);
appender.setEncoder(encoder);
config.appender("CONSOLE", appender);
return appender;
}
private Appender<ILoggingEvent> fileAppender(LogbackConfigurator config,
String logFile) {
RollingFileAppender<ILoggingEvent> appender = new RollingFileAppender<ILoggingEvent>();
PatternLayoutEncoder encoder = new PatternLayoutEncoder();
encoder.setPattern(FILE_LOG_PATTERN);
appender.setEncoder(encoder);
config.start(encoder);
appender.setFile(logFile);
FixedWindowRollingPolicy rollingPolicy = new FixedWindowRollingPolicy();
rollingPolicy.setFileNamePattern(logFile + ".%i");
appender.setRollingPolicy(rollingPolicy);
rollingPolicy.setParent(appender);
config.start(rollingPolicy);
SizeBasedTriggeringPolicy<ILoggingEvent> triggeringPolicy = new SizeBasedTriggeringPolicy<ILoggingEvent>();
triggeringPolicy.setMaxFileSize("10MB");
appender.setTriggeringPolicy(triggeringPolicy);
config.start(triggeringPolicy);
config.appender("FILE", appender);
return appender;
}
}
...@@ -50,6 +50,20 @@ public class LevelRemappingAppender extends AppenderBase<ILoggingEvent> { ...@@ -50,6 +50,20 @@ public class LevelRemappingAppender extends AppenderBase<ILoggingEvent> {
private Map<Level, Level> remapLevels = DEFAULT_REMAPS; private Map<Level, Level> remapLevels = DEFAULT_REMAPS;
/**
* Create a new {@link LevelRemappingAppender}.
*/
public LevelRemappingAppender() {
}
/**
* Create a new {@link LevelRemappingAppender} with a specific destination logger.
* @param destinationLogger the destination logger
*/
public LevelRemappingAppender(String destinationLogger) {
this.destinationLogger = destinationLogger;
}
@Override @Override
protected void append(ILoggingEvent event) { protected void append(ILoggingEvent event) {
AppendableLogger logger = getLogger(this.destinationLogger); AppendableLogger logger = getLogger(this.destinationLogger);
......
/*
* Copyright 2012-2014 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.
* 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 org.springframework.boot.logging.logback;
import java.util.HashMap;
import java.util.Map;
import org.springframework.util.Assert;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.Appender;
import ch.qos.logback.core.CoreConstants;
import ch.qos.logback.core.pattern.Converter;
import ch.qos.logback.core.spi.ContextAware;
import ch.qos.logback.core.spi.LifeCycle;
import ch.qos.logback.core.spi.PropertyContainer;
/**
* Allows programmatic configuration of logback which is usually faster than parsing XML.
*
* @author Phillip Webb
* @since 1.2.0
*/
class LogbackConfigurator {
private LoggerContext context;
public LogbackConfigurator(LoggerContext context) {
Assert.notNull(context, "Context must not be null");
this.context = context;
}
public PropertyContainer getContext() {
return this.context;
}
public Object getConfigurationLock() {
return this.context.getConfigurationLock();
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public void conversionRule(String conversionWord,
Class<? extends Converter> converterClass) {
Assert.hasLength(conversionWord, "Conversion word must not be empty");
Assert.notNull(converterClass, "Converter class must not be null");
Map<String, String> registry = (Map<String, String>) this.context
.getObject(CoreConstants.PATTERN_RULE_REGISTRY);
if (registry == null) {
registry = new HashMap<String, String>();
this.context.putObject(CoreConstants.PATTERN_RULE_REGISTRY, registry);
}
registry.put(conversionWord, converterClass.getName());
}
public void appender(String name, Appender<?> appender) {
appender.setName(name);
start(appender);
}
public void logger(String name, Level level) {
logger(name, level, true);
}
public void logger(String name, Level level, boolean additive) {
logger(name, level, additive, null);
}
public void logger(String name, Level level, boolean additive,
Appender<ILoggingEvent> appender) {
Logger logger = this.context.getLogger(name);
if (level != null) {
logger.setLevel(level);
}
logger.setAdditive(additive);
if (appender != null) {
logger.addAppender(appender);
}
}
public void root(Level level, Appender<ILoggingEvent>... appenders) {
Logger logger = this.context.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
if (level != null) {
logger.setLevel(level);
}
for (Appender<ILoggingEvent> appender : appenders) {
logger.addAppender(appender);
}
}
public void start(LifeCycle lifeCycle) {
if (lifeCycle instanceof ContextAware) {
((ContextAware) lifeCycle).setContext(this.context);
}
lifeCycle.start();
}
}
...@@ -94,12 +94,11 @@ public class LogbackLoggingSystem extends Slf4JLoggingSystem { ...@@ -94,12 +94,11 @@ public class LogbackLoggingSystem extends Slf4JLoggingSystem {
@Override @Override
protected void loadDefaults(String logFile) { protected void loadDefaults(String logFile) {
if (StringUtils.hasLength(logFile)) { LoggerContext context = getLoggerContext();
loadConfiguration(getPackagedConfigFile("logback-file.xml"), logFile); context.stop();
} context.reset();
else { LogbackConfigurator configurator = new LogbackConfigurator(context);
loadConfiguration(getPackagedConfigFile("logback.xml"), logFile); new DefaultLogbackConfiguration(logFile).apply(configurator);
}
} }
@Override @Override
...@@ -108,18 +107,7 @@ public class LogbackLoggingSystem extends Slf4JLoggingSystem { ...@@ -108,18 +107,7 @@ public class LogbackLoggingSystem extends Slf4JLoggingSystem {
if (StringUtils.hasLength(logFile)) { if (StringUtils.hasLength(logFile)) {
System.setProperty("LOG_FILE", logFile); System.setProperty("LOG_FILE", logFile);
} }
ILoggerFactory factory = StaticLoggerBinder.getSingleton().getLoggerFactory(); LoggerContext context = getLoggerContext();
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).",
factory.getClass(), factory.getClass().getProtectionDomain()
.getCodeSource().getLocation()));
LoggerContext context = (LoggerContext) factory;
context.stop(); context.stop();
context.reset(); context.reset();
try { try {
...@@ -137,6 +125,21 @@ public class LogbackLoggingSystem extends Slf4JLoggingSystem { ...@@ -137,6 +125,21 @@ public class LogbackLoggingSystem extends Slf4JLoggingSystem {
getLogger(loggerName).setLevel(LEVELS.get(level)); getLogger(loggerName).setLevel(LEVELS.get(level));
} }
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).",
factory.getClass(), factory.getClass().getProtectionDomain()
.getCodeSource().getLocation()));
return (LoggerContext) factory;
}
private ch.qos.logback.classic.Logger getLogger(String name) { private ch.qos.logback.classic.Logger getLogger(String name) {
ILoggerFactory factory = StaticLoggerBinder.getSingleton().getLoggerFactory(); ILoggerFactory factory = StaticLoggerBinder.getSingleton().getLoggerFactory();
return (ch.qos.logback.classic.Logger) factory.getLogger(StringUtils return (ch.qos.logback.classic.Logger) factory.getLogger(StringUtils
......
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!--
Base logback configuration provided for import, equivalent to the programmatic
initialization performed by Boot
-->
<included> <included>
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" /> <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" /> <conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
......
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!--
Console appender logback configuration provided for import, equivalent to the programmatic
initialization performed by Boot
-->
<included> <included>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder> <encoder>
......
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!--
File appender logback configuration provided for import, equivalent to the programmatic
initialization performed by Boot
-->
<included> <included>
<appender name="FILE" <appender name="FILE"
class="ch.qos.logback.core.rolling.RollingFileAppender"> class="ch.qos.logback.core.rolling.RollingFileAppender">
......
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
<include resource="org/springframework/boot/logging/logback/file-appender.xml"/>
<root level="INFO">
<appender-ref ref="CONSOLE" />
<appender-ref ref="FILE" />
</root>
</configuration>
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml"/>
<include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
<root level="INFO">
<appender-ref ref="CONSOLE" />
</root>
</configuration>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment