View Javadoc

1   package com.github.triceo.splitlog.logging;
2   
3   import java.lang.reflect.Proxy;
4   import java.util.concurrent.atomic.AtomicLong;
5   
6   import org.slf4j.Logger;
7   import org.slf4j.LoggerFactory;
8   
9   /**
10   * When Splitlog is running inside an app server such as JBoss, its internal
11   * logging may be caught up in the server log. If Splitlog is also set up to
12   * watch that server log, a vicious cycle is created, with Splitlog reading its
13   * own internal messages. The purpose of this class is to prevent that.
14   *
15   * Splitlog internals will only log if a system property, whose name is
16   * specified in {@link #LOGGING_PROPERTY_NAME}, is set to the value specified in
17   * {@link #ON_STATE}. Alternatively, you can force Splitlog to log by calling
18   * {@link #enableLogging()}.
19   *
20   * In order to avoid the aforementioned problem, it is imperative that every
21   * SLF4J {@link Logger} is requested through {@link #getLogger(Class)} or
22   * {@link #getLogger(String)}. Otherwise, we are powerless and the app server
23   * needs to be specifically configured to disregard Splitlog logging.
24   *
25   */
26  public final class SplitlogLoggerFactory {
27  
28      public static final String LOGGING_PROPERTY_NAME = "splitlog.logging";
29  
30      private static final AtomicLong messageCounter = new AtomicLong(0);
31      public static final String OFF_STATE = "off";
32      public static final String ON_STATE = "on";
33      private static SplitlogLoggingState state = SplitlogLoggingState.DEFAULT;
34      static {
35          /*
36           * intentionally using the original logger so that this message can not
37           * be silenced
38           */
39          final Logger l = LoggerFactory.getLogger(SplitlogLoggerFactory.class);
40          if (SplitlogLoggerFactory.isLoggingEnabled()) {
41              l.info("Splitlog's internal logging system can be disabled by setting '{}' system property to '{}'.",
42                      SplitlogLoggerFactory.LOGGING_PROPERTY_NAME, SplitlogLoggerFactory.OFF_STATE);
43          } else {
44              l.warn("This will be the last message from Splitlog, unless you enable Splitlog's internal logging system by setting '{}' system property to '{}'.",
45                      SplitlogLoggerFactory.LOGGING_PROPERTY_NAME, SplitlogLoggerFactory.ON_STATE);
46          }
47      }
48  
49      /**
50       * Force Splitlog's internal logging to be enabled.
51       */
52      public synchronized static void enableLogging() {
53          if (SplitlogLoggerFactory.state == SplitlogLoggingState.ON) {
54              return;
55          }
56          SplitlogLoggerFactory.messageCounter.set(0);
57          SplitlogLoggerFactory.state = SplitlogLoggingState.ON;
58          /*
59           * intentionally using the original logger so that this message can not
60           * be silenced
61           */
62          LoggerFactory.getLogger(SplitlogLoggerFactory.class).info("Forcibly enabled Splitlog's internal logging.");
63      }
64  
65      public static Logger getLogger(final Class<?> cls) {
66          return (Logger) Proxy.newProxyInstance(Logger.class.getClassLoader(), new Class[] { Logger.class },
67                  new SplitlogLoggerInvocationHandler(LoggerFactory.getLogger(cls)));
68      }
69  
70      public static Logger getLogger(final String cls) {
71          return (Logger) Proxy.newProxyInstance(Logger.class.getClassLoader(), new Class[] { Logger.class },
72                  new SplitlogLoggerInvocationHandler(LoggerFactory.getLogger(cls)));
73      }
74  
75      /**
76       * Purely for testing purposes.
77       *
78       * @return How many times {@link #increaseMessageCounter()} has been called
79       *         since {@link #state} has last changed.
80       */
81      static long getMessagesSinceLastStateChange() {
82          return SplitlogLoggerFactory.messageCounter.get();
83      }
84  
85      /**
86       * Purely for testing purposes.
87       * {@link SplitlogLoggerInvocationHandler#invoke(Object, java.lang.reflect.Method, Object[])}
88       * will call this every time when {@link #isLoggingEnabled()} is true.
89       */
90      static void increaseMessageCounter() {
91          SplitlogLoggerFactory.messageCounter.incrementAndGet();
92      }
93  
94      /**
95       * Whether or not Splitlog internals will log if logging is requested at
96       * this time.
97       *
98       * @return True if logging, false if not.
99       */
100     public synchronized static boolean isLoggingEnabled() {
101         switch (SplitlogLoggerFactory.state) {
102             case ON:
103                 return true;
104             case OFF:
105                 return false;
106             default:
107                 final String propertyValue = System.getProperty(SplitlogLoggerFactory.LOGGING_PROPERTY_NAME,
108                         SplitlogLoggerFactory.OFF_STATE);
109                 if (propertyValue.equals(SplitlogLoggerFactory.ON_STATE)) {
110                     return true;
111                 } else {
112                     return false;
113                 }
114         }
115     }
116 
117     /**
118      * Whether or not Splitlog's internal messages are logged will depend on the
119      * value of {@link #LOGGING_PROPERTY_NAME} system property at the time when
120      * logging of the message is requested.
121      */
122     public synchronized static void resetLoggingToDefaultState() {
123         final SplitlogLoggingState previousState = SplitlogLoggerFactory.state;
124         if (previousState == SplitlogLoggingState.DEFAULT) {
125             // nothing to change
126             return;
127         }
128         /*
129          * intentionally using the original logger so that this message can not
130          * be silenced
131          */
132         LoggerFactory.getLogger(SplitlogLoggerFactory.class).info(
133                 "Splitlog's internal logging reset back to property-driven.");
134         SplitlogLoggerFactory.state = SplitlogLoggingState.DEFAULT;
135         SplitlogLoggerFactory.messageCounter.set(0);
136     }
137 
138     /**
139      * Force Splitlog's internal logging to be disabled.
140      */
141     public synchronized static void silenceLogging() {
142         if (SplitlogLoggerFactory.state == SplitlogLoggingState.OFF) {
143             return;
144         }
145         SplitlogLoggerFactory.state = SplitlogLoggingState.OFF;
146         SplitlogLoggerFactory.messageCounter.set(0);
147         /*
148          * intentionally using the original logger so that this message can not
149          * be silenced
150          */
151         LoggerFactory.getLogger(SplitlogLoggerFactory.class).info("Forcibly disabled Splitlog's internal logging.");
152     }
153 
154 }