Skip to content

Instantly share code, notes, and snippets.

@arey
Created March 30, 2018 16:34
Show Gist options
  • Save arey/bc09e0d77520dc97f12707c6064c8c4e to your computer and use it in GitHub Desktop.
Save arey/bc09e0d77520dc97f12707c6064c8c4e to your computer and use it in GitHub Desktop.
Java Logback configuration replacing classic logback.xml use
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