org.eclipse.gyrex.boot.internal.logback.LogbackConfigurator.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.gyrex.boot.internal.logback.LogbackConfigurator.java

Source

/*******************************************************************************
 * Copyright (c) 2010, 2013 AGETO and others.
 * All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html.
 *
 * Contributors:
 *     Gunnar Wagenknecht - initial API and implementation
 *******************************************************************************/
package org.eclipse.gyrex.boot.internal.logback;

import java.io.File;
import java.nio.charset.Charset;
import java.util.Map.Entry;
import java.util.logging.LogManager;

import org.eclipse.gyrex.boot.internal.BootActivator;
import org.eclipse.gyrex.server.Platform;

import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.osgi.service.datalocation.Location;

import org.apache.commons.lang.StringUtils;

import org.slf4j.LoggerFactory;
import org.slf4j.bridge.SLF4JBridgeHandler;

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.filter.ThresholdFilter;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.classic.jul.LevelChangePropagator;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.BasicStatusManager;
import ch.qos.logback.core.ConsoleAppender;
import ch.qos.logback.core.joran.spi.Interpreter;
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.status.InfoStatus;
import ch.qos.logback.core.status.OnConsoleStatusListener;
import ch.qos.logback.core.status.StatusManager;
import ch.qos.logback.core.util.StatusPrinter;

public class LogbackConfigurator {

    // default pattern (note, this is also emulated by GyrexSlf4jForwarder)
    public static final String DEFAULT_PATTERN = "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n";

    private static final LogbackLevelDebugOptionsBridge LOGBACK_DEBUG_OPTIONS_BRIDGE = new LogbackLevelDebugOptionsBridge();

    private static File logConfigurationFile;

    public static void configureDefaultContext() throws Exception {
        // reset JUL (this should disable the default JUL console output)
        LogManager.getLogManager().reset();

        // install SLF4J Bridge
        if (!SLF4JBridgeHandler.isInstalled()) {
            SLF4JBridgeHandler.install();
        }

        // don't perform any further configuration if a config file is specified
        if (StringUtils.isNotBlank(System.getProperty("logback.configurationFile")))
            return;

        // reset LoggerContext
        final LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
        lc.reset();

        // turn of packaging data calculation in production (http://jira.qos.ch/browse/LOGBACK-730)
        lc.setPackagingDataEnabled(Platform.inDevelopmentMode());

        // install SLF4J Bridge
        if (!SLF4JBridgeHandler.isInstalled()) {
            SLF4JBridgeHandler.install();
        }

        // configure status manager
        if (lc.getStatusManager() == null) {
            lc.setStatusManager(new BasicStatusManager());
        }
        final StatusManager sm = lc.getStatusManager();

        // always good to have a console status listener
        if (Platform.inDebugMode() || Platform.inDevelopmentMode()) {
            final OnConsoleStatusListener onConsoleStatusListener = new OnConsoleStatusListener();
            onConsoleStatusListener.setContext(lc);
            sm.add(onConsoleStatusListener);
            onConsoleStatusListener.start();
        }

        // signal Gyrex configuration
        sm.add(new InfoStatus("Setting up Gyrex log configuration.", lc));

        // ensure log directory exists
        final IPath instanceLogfileDirectory = getLogfileDir();
        if (null != instanceLogfileDirectory) {
            instanceLogfileDirectory.toFile().mkdirs();
        }

        // prefer configuration file from workspace
        final File configurationFile = getLogConfigurationFile();
        if ((null != configurationFile) && configurationFile.isFile() && configurationFile.canRead()) {

            sm.add(new InfoStatus(
                    String.format("Loading configuration from '%s'.", configurationFile.getAbsolutePath()), lc));

            // create our customized configurator
            final JoranConfigurator configurator = new JoranConfigurator() {
                @Override
                protected void addImplicitRules(final Interpreter interpreter) {
                    super.addImplicitRules(interpreter);
                    // set some properties for log file substitution
                    if (null != instanceLogfileDirectory) {
                        interpreter.getInterpretationContext().addSubstitutionProperty("gyrex.instance.area.logs",
                                instanceLogfileDirectory.addTrailingSeparator().toOSString());
                    }
                }
            };
            configurator.setContext(lc);

            // configuration
            configurator.doConfigure(configurationFile);

            // print logback's internal status
            StatusPrinter.printIfErrorsOccured(lc);

            // done'
            return;
        }

        // get root logger
        final Logger rootLogger = lc.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);

        // propagate level changes to java.util.logging
        final LevelChangePropagator levelChangePropagator = new LevelChangePropagator();
        levelChangePropagator.setResetJUL(true);
        levelChangePropagator.setContext(lc);
        lc.addListener(levelChangePropagator);
        levelChangePropagator.start();

        // add console logger
        rootLogger.addAppender(createConsoleAppender(lc));

        // add error logger
        if (null != instanceLogfileDirectory) {
            rootLogger.addAppender(createErrorLogAppender(lc, instanceLogfileDirectory));
        }

        // set log level
        if (Platform.inDebugMode() || Platform.inDevelopmentMode()) {
            rootLogger.setLevel(Level.DEBUG);
        } else {
            rootLogger.setLevel(Level.INFO);
        }

        // some of our components are very communicative
        // we apply some "smart" defaults for those known 3rdParty libs
        lc.getLogger("org.apache.commons").setLevel(Level.WARN);
        lc.getLogger("httpclient.wire").setLevel(Level.WARN);
        lc.getLogger("org.apache.http").setLevel(Level.WARN);
        lc.getLogger("org.apache.zookeeper").setLevel(Level.WARN);
        lc.getLogger("org.apache.solr").setLevel(Level.WARN);
        lc.getLogger("org.apache.sshd").setLevel(Level.WARN);
        lc.getLogger("org.apache.mina").setLevel(Level.WARN);
        lc.getLogger("org.mortbay.log").setLevel(Level.WARN);
        lc.getLogger("org.eclipse.jetty").setLevel(Level.INFO);
        lc.getLogger("org.quartz").setLevel(Level.INFO);
        lc.getLogger("sun").setLevel(Level.INFO);

        // apply overrides
        if (!LOGBACK_DEBUG_OPTIONS_BRIDGE.overriddenLogLevels.isEmpty()) {
            for (final Entry<String, String[]> e : LOGBACK_DEBUG_OPTIONS_BRIDGE.overriddenLogLevels.entrySet()) {
                lc.getLogger(e.getKey()).setLevel(Level.toLevel(e.getValue()[0], null));
            }
        }

        // print logback's internal status
        StatusPrinter.printIfErrorsOccured(lc);
    }

    private static ConsoleAppender<ILoggingEvent> createConsoleAppender(final LoggerContext lc) {
        final ConsoleAppender<ILoggingEvent> ca = new ConsoleAppender<ILoggingEvent>();
        ca.setContext(lc);
        ca.setName("console");
        final PatternLayoutEncoder pl = new PatternLayoutEncoder();
        pl.setContext(lc);
        pl.setPattern(DEFAULT_PATTERN);
        pl.start();
        ca.setEncoder(pl);
        ca.start();
        return ca;
    }

    private static RollingFileAppender<ILoggingEvent> createErrorLogAppender(final LoggerContext lc,
            final IPath instanceLogfileDirectory) {
        final RollingFileAppender<ILoggingEvent> rfa = new RollingFileAppender<ILoggingEvent>();
        rfa.setContext(lc);
        rfa.setName("error-log");
        rfa.setFile(instanceLogfileDirectory.append("error.log").toOSString());

        final FixedWindowRollingPolicy rollingPolicy = new FixedWindowRollingPolicy();
        rollingPolicy.setMinIndex(1);
        rollingPolicy.setMaxIndex(3);
        rollingPolicy.setFileNamePattern("error.%i.log.zip");
        rollingPolicy.setParent(rfa);
        rfa.setRollingPolicy(rollingPolicy);

        rfa.setTriggeringPolicy(new SizeBasedTriggeringPolicy<ILoggingEvent>("5MB"));

        final PatternLayoutEncoder pl = new PatternLayoutEncoder();
        pl.setContext(lc);
        pl.setPattern(DEFAULT_PATTERN);
        pl.setCharset(Charset.forName("UTF-8"));
        pl.start();
        rfa.setEncoder(pl);

        final ThresholdFilter tf = new ThresholdFilter();
        tf.setContext(lc);
        tf.setLevel(Level.ERROR.toString());
        tf.start();
        rfa.addFilter(tf);

        rfa.start();
        return rfa;
    }

    private static File getLogConfigurationFile() {
        // use configured file
        final File file = logConfigurationFile;
        if (null != file)
            return file;

        // use default file
        final Location instanceLocation = BootActivator.getInstance().getInstanceLocation();
        return new Path(instanceLocation.getURL().getPath()).append("etc/logback.xml").toFile();
    }

    private static IPath getLogfileDir() {
        try {
            final Location instanceLocation = BootActivator.getInstance().getInstanceLocation();
            return new Path(instanceLocation.getURL().getPath()).append("logs");
        } catch (final IllegalStateException e) {
            // not available, fallback null (which means no log files will be written)
            return null;
        }
    }

    /**
     * Initialize log level overrides from debug options.
     * <p>
     * This may only be called during bootstrapping before any custom overrides
     * are set. Your milage may vary if called while the application is running.
     * </p>
     * 
     * @throws Exception
     */
    public static void initializeLogLevelOverrides() throws Exception {
        LOGBACK_DEBUG_OPTIONS_BRIDGE.initializeLogLevelOverrides();
    }

    public static void reset() throws Exception {
        // reset LoggerContext
        final LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
        lc.reset();

        // print logback's internal status
        StatusPrinter.printIfErrorsOccured(lc);
    }

    /**
     * Allows to change the log configuration file at runtime.
     * 
     * @param file
     *            the file to set
     * @return the previously used file
     */
    public static File setLogConfigurationFile(final File file) {
        final File oldFile = logConfigurationFile;
        logConfigurationFile = file;
        return oldFile;
    }

    /**
     * Sets or unsets a log level override.
     * 
     * @param loggerName
     * @param level
     * @throws Exception
     */
    public static void setLogLevelOverride(final String loggerName, final String level) throws Exception {
        LOGBACK_DEBUG_OPTIONS_BRIDGE.setLogLevelOverride(loggerName, level);
    }
}