Java tutorial
/* * 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: |- * <configuration> * <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> * <encoder> * <pattern>%-5p %d %m%n</pattern> * </encoder> * </appender> * <root level="DEBUG"> * <appender-ref ref="STDOUT"/> * </root> * <contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator"> * <resetJUL>true</resetJUL> * </contextListener> * </configuration> * </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); } }