package com.github.triceo.splitlog.logging; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.lang.reflect.Proxy; import java.util.concurrent.atomic.AtomicLong; /** * When Splitlog is running inside an app server such as JBoss, its internal * logging may be caught up in the server log. If Splitlog is also set up to * watch that server log, a vicious cycle is created, with Splitlog reading its * own internal messages. The purpose of this class is to prevent that. * * Splitlog internals will only log if a system property, whose name is * specified in {@link #LOGGING_PROPERTY_NAME}, is set to the value specified in * {@link #ON_STATE}. Alternatively, you can force Splitlog to log by calling * {@link #enableLogging()}. * * In order to avoid the aforementioned problem, it is imperative that every * SLF4J {@link Logger} is requested through {@link #getLogger(Class)} or * {@link #getLogger(String)}. Otherwise, we are powerless and the app server * needs to be specifically configured to disregard Splitlog logging. * */ public final class SplitlogLoggerFactory { public static final String LOGGING_PROPERTY_NAME = "splitlog.logging"; private static final AtomicLong messageCounter = new AtomicLong(0); public static final String OFF_STATE = "off"; public static final String ON_STATE = "on"; private static SplitlogLoggingState state = SplitlogLoggingState.DEFAULT; static { /* * intentionally using the original logger so that this message can not * be silenced */ final Logger l = LoggerFactory.getLogger(SplitlogLoggerFactory.class); if (SplitlogLoggerFactory.isLoggingEnabled()) { l.info("Splitlog's internal logging system can be disabled by setting '{}' system property to '{}'.", SplitlogLoggerFactory.LOGGING_PROPERTY_NAME, SplitlogLoggerFactory.OFF_STATE); } else { l.warn("This will be the last message from Splitlog, unless you enable Splitlog's internal logging system by setting '{}' system property to '{}'.", SplitlogLoggerFactory.LOGGING_PROPERTY_NAME, SplitlogLoggerFactory.ON_STATE); } } /** * Force Splitlog's internal logging to be enabled. */ public synchronized static void enableLogging() { if (SplitlogLoggerFactory.state == SplitlogLoggingState.ON) { return; } SplitlogLoggerFactory.messageCounter.set(0); SplitlogLoggerFactory.state = SplitlogLoggingState.ON; /* * intentionally using the original logger so that this message can not * be silenced */ LoggerFactory.getLogger(SplitlogLoggerFactory.class).info("Forcibly enabled Splitlog's internal logging."); } public static Logger getLogger(final Class<?> cls) { return (Logger) Proxy.newProxyInstance(Logger.class.getClassLoader(), new Class[] { Logger.class }, new SplitlogLoggerInvocationHandler(LoggerFactory.getLogger(cls))); } public static Logger getLogger(final String cls) { return (Logger) Proxy.newProxyInstance(Logger.class.getClassLoader(), new Class[] { Logger.class }, new SplitlogLoggerInvocationHandler(LoggerFactory.getLogger(cls))); } /** * Purely for testing purposes. * * @return How many times {@link #increaseMessageCounter()} has been called * since {@link #state} has last changed. */ static long getMessagesSinceLastStateChange() { return SplitlogLoggerFactory.messageCounter.get(); } /** * Purely for testing purposes. * {@link SplitlogLoggerInvocationHandler#invoke(Object, java.lang.reflect.Method, Object[])} * will call this every time when {@link #isLoggingEnabled()} is true. */ static void increaseMessageCounter() { SplitlogLoggerFactory.messageCounter.incrementAndGet(); } /** * Whether or not Splitlog internals will log if logging is requested at * this time. * * @return True if logging, false if not. */ public synchronized static boolean isLoggingEnabled() { switch (SplitlogLoggerFactory.state) { case ON: return true; case OFF: return false; default: final String propertyValue = System.getProperty(SplitlogLoggerFactory.LOGGING_PROPERTY_NAME, SplitlogLoggerFactory.OFF_STATE); return propertyValue.equals(SplitlogLoggerFactory.ON_STATE); } } /** * Whether or not Splitlog's internal messages are logged will depend on the * value of {@link #LOGGING_PROPERTY_NAME} system property at the time when * logging of the message is requested. */ public synchronized static void resetLoggingToDefaultState() { final SplitlogLoggingState previousState = SplitlogLoggerFactory.state; if (previousState == SplitlogLoggingState.DEFAULT) { // nothing to change return; } /* * intentionally using the original logger so that this message can not * be silenced */ LoggerFactory.getLogger(SplitlogLoggerFactory.class).info( "Splitlog's internal logging reset back to property-driven."); SplitlogLoggerFactory.state = SplitlogLoggingState.DEFAULT; SplitlogLoggerFactory.messageCounter.set(0); } /** * Force Splitlog's internal logging to be disabled. */ public synchronized static void silenceLogging() { if (SplitlogLoggerFactory.state == SplitlogLoggingState.OFF) { return; } SplitlogLoggerFactory.state = SplitlogLoggingState.OFF; SplitlogLoggerFactory.messageCounter.set(0); /* * intentionally using the original logger so that this message can not * be silenced */ LoggerFactory.getLogger(SplitlogLoggerFactory.class).info("Forcibly disabled Splitlog's internal logging."); } }