package io.dropwizard.logging;

import ch.qos.logback.classic.LoggerContext;
import io.dropwizard.util.Duration;
import org.slf4j.ILoggerFactory;
import org.slf4j.LoggerFactory;
import org.slf4j.bridge.SLF4JBridgeHandler;

import javax.annotation.concurrent.GuardedBy;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LoggingUtil {
    private static final Duration LOGGER_CONTEXT_AWAITING_TIMEOUT = Duration.seconds(10);
    private static final Duration LOGGER_CONTEXT_AWAITING_SLEEP_TIME = Duration.milliseconds(100);

    @GuardedBy("JUL_HIJACKING_LOCK")
    private static boolean julHijacked = false;
    private static final Lock JUL_HIJACKING_LOCK = new ReentrantLock();

    private LoggingUtil() {
    }

    
Acquires the logger context.

It tries to correctly acquire the logger context in the multi-threaded environment. Because of the http://jira.qos.ch/browse/SLF4J-167 a thread, that didn't start initialization has a possibility to get a reference not to a real context, but to a substitute.

To work around this bug we spin-loop the thread with a sensible timeout, while the context is not initialized. We can't just make this method synchronized, because LoggerFactory.getILoggerFactory doesn't safely publish own state. Threads can observe a stale state, even if the logger has been already initialized. That's why this method is not thread-safe, but it makes the best effort to return the correct result in the multi-threaded environment.

/** * Acquires the logger context. * <p/> * <p>It tries to correctly acquire the logger context in the multi-threaded environment. * Because of the <a href="bug">http://jira.qos.ch/browse/SLF4J-167</a> a thread, that didn't * start initialization has a possibility to get a reference not to a real context, but to a * substitute.</p> * <p>To work around this bug we spin-loop the thread with a sensible timeout, while the * context is not initialized. We can't just make this method synchronized, because * {@code LoggerFactory.getILoggerFactory} doesn't safely publish own state. Threads can * observe a stale state, even if the logger has been already initialized. That's why this * method is not thread-safe, but it makes the best effort to return the correct result in * the multi-threaded environment.</p> */
public static LoggerContext getLoggerContext() { final long startTime = System.nanoTime(); while (true) { final ILoggerFactory iLoggerFactory = LoggerFactory.getILoggerFactory(); if (iLoggerFactory instanceof LoggerContext) { return (LoggerContext) iLoggerFactory; } if ((System.nanoTime() - startTime) > LOGGER_CONTEXT_AWAITING_TIMEOUT.toNanoseconds()) { throw new IllegalStateException("Unable to acquire the logger context"); } try { Thread.sleep(LOGGER_CONTEXT_AWAITING_SLEEP_TIME.toMilliseconds()); } catch (InterruptedException e) { throw new IllegalStateException(e); } } }
Gets the root j.u.l.Logger and removes all registered handlers then redirects all active j.u.l. to SLF4J

N.B. This should only happen once, hence the flag and locking
/** * Gets the root j.u.l.Logger and removes all registered handlers * then redirects all active j.u.l. to SLF4J * <p/> * N.B. This should only happen once, hence the flag and locking */
public static void hijackJDKLogging() { JUL_HIJACKING_LOCK.lock(); try { if (!julHijacked) { SLF4JBridgeHandler.removeHandlersForRootLogger(); SLF4JBridgeHandler.install(); julHijacked = true; } } finally { JUL_HIJACKING_LOCK.unlock(); } } }