Java Logback configuration replacing classic logback.xml use
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.javaetmoi.logback; | |
import ch.qos.logback.classic.Level; | |
import ch.qos.logback.classic.Logger; | |
import ch.qos.logback.classic.LoggerContext; | |
import ch.qos.logback.classic.encoder.PatternLayoutEncoder; | |
import ch.qos.logback.classic.joran.action.JMXConfiguratorAction; | |
import ch.qos.logback.classic.jul.LevelChangePropagator; | |
import ch.qos.logback.classic.spi.ILoggingEvent; | |
import ch.qos.logback.core.Appender; | |
import ch.qos.logback.core.ConsoleAppender; | |
import ch.qos.logback.core.Context; | |
import ch.qos.logback.core.joran.spi.ActionException; | |
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.spi.ContextAware; | |
import ch.qos.logback.core.spi.ContextAwareBase; | |
import ch.qos.logback.core.spi.LifeCycle; | |
import ch.qos.logback.core.status.OnConsoleStatusListener; | |
import ch.qos.logback.core.util.FileSize; | |
import ch.qos.logback.core.util.StatusListenerConfigHelper; | |
import org.slf4j.bridge.SLF4JBridgeHandler; | |
import org.springframework.util.ClassUtils; | |
import org.xml.sax.helpers.AttributesImpl; | |
import javax.servlet.ServletContext; | |
import javax.servlet.ServletContextEvent; | |
import javax.servlet.ServletContextListener; | |
import javax.servlet.annotation.WebListener; | |
import java.nio.charset.Charset; | |
import java.util.ArrayList; | |
import java.util.List; | |
import java.util.logging.ConsoleHandler; | |
import java.util.logging.Handler; | |
import java.util.logging.LogManager; | |
/** | |
* Java Logback configuration replacing classic logback.xml use. | |
*/ | |
@WebListener | |
public class LogbackListener extends ContextAwareBase implements ServletContextListener { | |
private static final Charset APPENDER_CHARSET = Charset.forName("UTF-8"); | |
private static final String CONSOLE_LOG_PATTERN = | |
"%d{HH:mm:ss.SSS} | %highlight(%-5level) | %cyan(%-50.50logger{49}) | %green(%-16.16X{user}) | %-200m %C.%M\\(%F:%L\\)%n"; | |
private static final String FILE_LOG_PATTERN = | |
"%d{yyyy/MM/dd HH:mm:ss.SSS} | %-5level | %-16.16X{user} | %-50.50logger{49} | %-200m | %X{httpSessionID} | %-5thread |%C.%M\\(%F:%L\\) %n"; | |
@Override | |
public void contextInitialized(ServletContextEvent servletContextEvent) { | |
setContext(getLogbackContext()); | |
configureJdkLoggingBridgeHandler(); | |
synchronized (getContext().getConfigurationLock()) { | |
stopAndReset(); | |
enableDebugMode(); | |
declareAppenders(); | |
base(); | |
enableJMX(servletContextEvent.getServletContext()); | |
} | |
} | |
private LoggerContext getLogbackContext() { | |
return (ch.qos.logback.classic.LoggerContext) org.slf4j.LoggerFactory.getILoggerFactory(); | |
} | |
@Override | |
public void contextDestroyed(ServletContextEvent servletContextEvent) { | |
} | |
private void configureJdkLoggingBridgeHandler() { | |
try { | |
if (isBridgeHandlerAvailable()) { | |
java.util.logging.Logger rootLogger = LogManager.getLogManager().getLogger(""); | |
Handler[] handlers = rootLogger.getHandlers(); | |
for (Handler handler : handlers) { | |
if (handler instanceof ConsoleHandler) { | |
addInfo("Removing the JUL handler: " + handler.getClass().getName()); | |
rootLogger.removeHandler(handler); | |
} | |
} | |
SLF4JBridgeHandler.install(); | |
} | |
} catch (RuntimeException ex) { | |
addError("SLF4J bridge for JUL could not be installed", ex); | |
} | |
} | |
private boolean isBridgeHandlerAvailable() { | |
return ClassUtils.isPresent("org.slf4j.bridge.SLF4JBridgeHandler", ClassUtils.getDefaultClassLoader()); | |
} | |
private void stopAndReset() { | |
LoggerContext loggerContext = (LoggerContext) getContext(); | |
loggerContext.stop(); | |
loggerContext.reset(); | |
if (isBridgeHandlerAvailable()) { | |
LevelChangePropagator levelChangePropagator = new LevelChangePropagator(); | |
levelChangePropagator.setResetJUL(true); | |
levelChangePropagator.setContext(loggerContext); | |
loggerContext.addListener(levelChangePropagator); | |
} | |
} | |
private void enableDebugMode() { | |
LoggerContext loggerContext = getLogbackContext(); | |
StatusListenerConfigHelper.addOnConsoleListenerInstance(loggerContext, new OnConsoleStatusListener()); | |
} | |
private void enableJMX(ServletContext servletContext) { | |
Context loggerContext = getContext(); | |
String servletContextName = servletContext.getServletContextName(); | |
if (servletContextName != null) { | |
loggerContext.setName(servletContextName); | |
} | |
JMXConfiguratorAction jmxConfiguratorAction = new JMXConfiguratorAction(); | |
jmxConfiguratorAction.setContext(loggerContext); | |
try { | |
jmxConfiguratorAction.begin(null, null, new AttributesImpl()); | |
} catch (ActionException e) { | |
addError("The Logback JMX configuration failed", e); | |
} | |
} | |
private void declareAppenders() { | |
List<Appender<ILoggingEvent>> appenders = new ArrayList<>(); | |
if ("dev".equals(System.getProperty("env", "prod"))) { | |
appenders.add(consoleAppender()); | |
} | |
appenders.add(troubleshootingAppender()); | |
root(Level.INFO, appenders); | |
} | |
private ConsoleAppender<ILoggingEvent> consoleAppender() { | |
ConsoleAppender<ILoggingEvent> appender = new ConsoleAppender<>(); | |
PatternLayoutEncoder encoder = new PatternLayoutEncoder(); | |
encoder.setPattern(CONSOLE_LOG_PATTERN); | |
start(encoder); | |
appender.setEncoder(encoder); | |
appender("console", appender); | |
return appender; | |
} | |
private RollingFileAppender<ILoggingEvent> troubleshootingAppender() { | |
RollingFileAppender<ILoggingEvent> appender = new RollingFileAppender<>(); | |
PatternLayoutEncoder encoder = new PatternLayoutEncoder(); | |
// Do not rely on the OS default charset | |
encoder.setCharset(APPENDER_CHARSET); | |
encoder.setPattern(FILE_LOG_PATTERN); | |
appender.setEncoder(encoder); | |
start(encoder); | |
appender.setFile("/tmp/MyApp-troubleshooting.log"); | |
SizeBasedTriggeringPolicy<ILoggingEvent> triggeringPolicy = new SizeBasedTriggeringPolicy<>(); | |
triggeringPolicy.setMaxFileSize(FileSize.valueOf("10MB")); | |
triggeringPolicy.start(); | |
appender.setTriggeringPolicy(triggeringPolicy); | |
FixedWindowRollingPolicy rollingPolicy = new FixedWindowRollingPolicy(); | |
rollingPolicy.setMinIndex(1); | |
rollingPolicy.setMaxIndex(10); | |
String rollingName = "/tmp/backup/MyApp-troubleshooting_%i.log.zip"; | |
rollingPolicy.setFileNamePattern(rollingName); | |
appender.setRollingPolicy(rollingPolicy); | |
rollingPolicy.setParent(appender); | |
start(rollingPolicy); | |
appender("troubleshooting-file", appender); | |
return appender; | |
} | |
private void base() { | |
logger("com.atomikos", Level.WARN); | |
logger("org.apache.cxf", Level.INFO); | |
logger("org.hibernate", Level.INFO); | |
logger("org.springframework", Level.INFO); | |
} | |
private void logger(String name, Level level) { | |
Logger logger = this.getLogbackContext().getLogger(name); | |
logger.setLevel(level); | |
} | |
private void root(Level level, List<Appender<ILoggingEvent>> rootAppenders) { | |
ch.qos.logback.classic.Logger logger = getLogbackContext().getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME); | |
if (level != null) { | |
logger.setLevel(level); | |
} | |
rootAppenders.forEach(logger::addAppender); | |
} | |
private void start(LifeCycle lifeCycle) { | |
if (lifeCycle instanceof ContextAware) { | |
((ContextAware) lifeCycle).setContext(this.context); | |
} | |
lifeCycle.start(); | |
} | |
private void appender(String name, Appender<?> appender) { | |
appender.setName(name); | |
start(appender); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment