io.github.tfahub.dropwizard.logging.XmlBasedLoggingFactory.java Source code

Java tutorial

Introduction

Here is the source code for io.github.tfahub.dropwizard.logging.XmlBasedLoggingFactory.java

Source

/*
 * Copyright 2016-2017 TFA Hub Team
 * 
 * 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.
 */

/*
 * NOTICE: This class is derivative works of the DefaultLoggingFactory on Dropwizard licensed under the Apache License,
 * Version 2.0. The Dropwizard contains the following notice below:
 * 
 * Dropwizard Copyright 2010-2013 Coda Hale and Yammer, Inc., 2014-2016 Dropwizard Team
 * 
 * This product includes software developed by Coda Hale and Yammer, Inc.
 */

package io.github.tfahub.dropwizard.logging;

import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.jmx.JMXConfigurator;
import ch.qos.logback.classic.joran.JoranConfigurator;
import ch.qos.logback.core.joran.spi.JoranException;
import ch.qos.logback.core.util.StatusPrinter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.logback.InstrumentedAppender;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonTypeName;
import io.dropwizard.logging.LoggingFactory;
import io.dropwizard.logging.LoggingUtil;
import org.hibernate.validator.constraints.NotEmpty;
import org.xml.sax.InputSource;

import java.io.PrintStream;
import java.io.StringReader;
import java.lang.management.ManagementFactory;
import java.util.concurrent.locks.ReentrantLock;

import javax.management.JMException;
import javax.management.MBeanServer;
import javax.management.ObjectName;

/**
 * XML configuration based implementation of the {@code LoggingFactory} interface for configuring Logback. This
 * implementation provides useful and flexible features derived from Logback, missing in {@code DefaultLoggingFactory}.
 * 
 * <p>
 * Example of a Dropwizard's YAML configuration file below:
 * </p>
 * 
 * <pre>
 * logging:
 *   type: xml
 *   configuration: |-
 *     &lt;configuration&gt;
 *       &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"&gt;
 *         &lt;encoder&gt;
 *           &lt;pattern&gt;%-5p %d %m%n&lt;/pattern&gt;
 *         &lt;/encoder&gt;
 *       &lt;/appender&gt;
 *       &lt;root level="DEBUG"&gt;
 *         &lt;appender-ref ref="STDOUT"/&gt;
 *       &lt;/root&gt;
 *       &lt;contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator"&gt;
 *         &lt;resetJUL&gt;true&lt;/resetJUL&gt;
 *       &lt;/contextListener&gt;
 *     &lt;/configuration&gt;
 * </pre>
 * 
 * @see io.dropwizard.logging.LoggingFactory
 * @see io.dropwizard.logging.DefaultLoggingFactory
 */
@JsonTypeName("xml")
public class XmlBasedLoggingFactory implements LoggingFactory {

    private static final ReentrantLock MBEAN_REGISTRATION_LOCK = new ReentrantLock();

    private static final ReentrantLock CHANGE_LOGGER_CONTEXT_LOCK = new ReentrantLock();

    @JsonIgnore
    private final LoggerContext loggerContext;

    @JsonIgnore
    private final PrintStream configurationErrorsStream;

    @NotEmpty
    private String configuration;

    /**
     * Create a new instance.
     */
    public XmlBasedLoggingFactory() {
        this(LoggingUtil.getLoggerContext(), System.err);
    }

    /**
     * Create a new instance with the logger context and target of print.
     * 
     * @param loggerContext the logger context of Logback.
     * @param configurationErrorsStream the target of print the contents of the context status.
     */
    public XmlBasedLoggingFactory(LoggerContext loggerContext, PrintStream configurationErrorsStream) {
        this.loggerContext = loggerContext;
        this.configurationErrorsStream = configurationErrorsStream;
    }

    /**
     * Configuring the Logback using the JoranConfigurator.
     */
    @Override
    public void configure(MetricRegistry metricRegistry, String name) {
        LoggingUtil.hijackJDKLogging();

        JoranConfigurator configurator = new JoranConfigurator();
        configurator.setContext(loggerContext);

        CHANGE_LOGGER_CONTEXT_LOCK.lock();
        try {
            loggerContext.reset();
            configurator.doConfigure(new InputSource(new StringReader(configuration)));
        } catch (JoranException ignore) {
            // fallthrough: StatusPrinter will handle this
        } finally {
            CHANGE_LOGGER_CONTEXT_LOCK.unlock();
        }

        StatusPrinter.setPrintStream(configurationErrorsStream);
        try {
            StatusPrinter.printIfErrorsOccured(loggerContext);
        } finally {
            StatusPrinter.setPrintStream(System.out);
        }

        MBEAN_REGISTRATION_LOCK.lock();
        try {
            registerJmxConfiguratorMBean();
        } catch (JMException ex) {
            throw new RuntimeException(ex);
        } finally {
            MBEAN_REGISTRATION_LOCK.unlock();
        }

        final Logger root = loggerContext.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
        configureInstrumentation(root, metricRegistry);
    }

    /**
     * Stopping the Logback context will close all appenders attached to loggers defined by the context and stop any
     * active threads.
     */
    @Override
    public void stop() {
        // Should acquire the lock to avoid concurrent listener changes
        CHANGE_LOGGER_CONTEXT_LOCK.lock();
        try {
            loggerContext.stop();
        } finally {
            CHANGE_LOGGER_CONTEXT_LOCK.unlock();
        }
    }

    /**
     * Sets the Logback configuration.
     */
    @JsonProperty
    public void setConfiguration(String configuration) {
        this.configuration = configuration;
    }

    void registerJmxConfiguratorMBean() throws JMException {
        final MBeanServer server = ManagementFactory.getPlatformMBeanServer();
        final ObjectName objectName = new ObjectName("io.dropwizard:type=Logging");
        if (!server.isRegistered(objectName)) {
            server.registerMBean(new JMXConfigurator(loggerContext, server, objectName), objectName);
        }
    }

    private void configureInstrumentation(Logger root, MetricRegistry metricRegistry) {
        final InstrumentedAppender appender = new InstrumentedAppender(metricRegistry);
        appender.setContext(loggerContext);
        appender.start();
        root.addAppender(appender);
    }

}