View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.util.log;
20  
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.lang.reflect.Method;
24  import java.net.URL;
25  import java.security.AccessController;
26  import java.security.PrivilegedAction;
27  import java.util.Collections;
28  import java.util.Enumeration;
29  import java.util.Map;
30  import java.util.Properties;
31  import java.util.concurrent.ConcurrentHashMap;
32  import java.util.concurrent.ConcurrentMap;
33  
34  import org.eclipse.jetty.util.Loader;
35  import org.eclipse.jetty.util.annotation.ManagedAttribute;
36  
37  /**
38   * Logging.
39   * This class provides a static logging interface.  If an instance of the
40   * org.slf4j.Logger class is found on the classpath, the static log methods
41   * are directed to a slf4j logger for "org.eclipse.log".   Otherwise the logs
42   * are directed to stderr.
43   * <p>
44   * The "org.eclipse.jetty.util.log.class" system property can be used
45   * to select a specific logging implementation.
46   * <p>
47   * If the system property org.eclipse.jetty.util.log.IGNORED is set,
48   * then ignored exceptions are logged in detail.
49   *
50   * @see StdErrLog
51   * @see Slf4jLog
52   */
53  public class Log
54  {
55      public final static String EXCEPTION= "EXCEPTION ";
56      public final static String IGNORED= "IGNORED ";
57  
58      /**
59       * Logging Configuration Properties
60       */
61      protected static final Properties __props;
62      /**
63       * The {@link Logger} implementation class name
64       */
65      public static String __logClass;
66      /**
67       * Legacy flag indicating if {@link Logger#ignore(Throwable)} methods produce any output in the {@link Logger}s
68       */
69      public static boolean __ignored;
70  
71      /**
72       * Hold loggers only.
73       */
74      private final static ConcurrentMap<String, Logger> __loggers = new ConcurrentHashMap<>();
75  
76  
77      static
78      {
79          /* Instantiate a default configuration properties (empty)
80           */
81          __props = new Properties();
82  
83          AccessController.doPrivileged(new PrivilegedAction<Object>()
84          {
85              public Object run()
86              {
87                  /* First see if the jetty-logging.properties object exists in the classpath.
88                   * This is an optional feature used by embedded mode use, and test cases to allow for early
89                   * configuration of the Log class in situations where access to the System.properties are
90                   * either too late or just impossible.
91                   */
92                  URL testProps = Loader.getResource(Log.class,"jetty-logging.properties",true);
93                  if (testProps != null)
94                  {
95                      InputStream in = null;
96                      try
97                      {
98                          in = testProps.openStream();
99                          __props.load(in);
100                     }
101                     catch (IOException e)
102                     {
103                         System.err.println("Unable to load " + testProps);
104                         e.printStackTrace(System.err);
105                     }
106                     finally
107                     {
108                         safeCloseInputStream(in);
109                     }
110                 }
111 
112                 /* Now load the System.properties as-is into the __props, these values will override
113                  * any key conflicts in __props.
114                  */
115                 @SuppressWarnings("unchecked")
116                 Enumeration<String> systemKeyEnum = (Enumeration<String>)System.getProperties().propertyNames();
117                 while (systemKeyEnum.hasMoreElements())
118                 {
119                     String key = systemKeyEnum.nextElement();
120                     String val = System.getProperty(key);
121                     //protect against application code insertion of non-String values (returned as null)
122                     if (val != null)
123                         __props.setProperty(key,val);
124                 }
125 
126                 /* Now use the configuration properties to configure the Log statics
127                  */
128                 __logClass = __props.getProperty("org.eclipse.jetty.util.log.class","org.eclipse.jetty.util.log.Slf4jLog");
129                 __ignored = Boolean.parseBoolean(__props.getProperty("org.eclipse.jetty.util.log.IGNORED","false"));
130                 return null;
131             }
132         });
133     }
134 
135     private static void safeCloseInputStream(InputStream in)
136     {
137         try
138         {
139             if (in != null)
140                 in.close();
141         }
142         catch (IOException e)
143         {
144             LOG.ignore(e);
145         }
146     }
147 
148     private static Logger LOG;
149     private static boolean __initialized;
150 
151     public static boolean initialized()
152     {
153         if (LOG != null)
154         {
155             return true;
156         }
157 
158         synchronized (Log.class)
159         {
160             if (__initialized)
161             {
162                 return LOG != null;
163             }
164             __initialized = true;
165         }
166 
167         try
168         {
169             Class<?> log_class = Loader.loadClass(Log.class, __logClass);
170             if (LOG == null || !LOG.getClass().equals(log_class))
171             {
172                 LOG = (Logger)log_class.newInstance();
173                 LOG.debug("Logging to {} via {}", LOG, log_class.getName());
174             }
175         }
176         catch(Throwable e)
177         {
178             // Unable to load specified Logger implementation, default to standard logging.
179             initStandardLogging(e);
180         }
181 
182         return LOG != null;
183     }
184 
185     private static void initStandardLogging(Throwable e)
186     {
187         Class<?> log_class;
188         if(e != null && __ignored)
189         {
190             e.printStackTrace();
191         }
192 
193         if (LOG == null)
194         {
195             log_class = StdErrLog.class;
196             LOG = new StdErrLog();
197             LOG.debug("Logging to {} via {}", LOG, log_class.getName());
198         }
199     }
200     
201     public static Logger getLog()
202     {
203         initialized();
204         return LOG;
205     }
206 
207     public static void setLog(Logger log)
208     {
209         Log.LOG = log;
210     }
211 
212     /**
213      * Get the root logger.
214      * @return the root logger
215      */
216     public static Logger getRootLogger() {
217         initialized();
218         return LOG;
219     }
220 
221     static boolean isIgnored()
222     {
223         return __ignored;
224     }
225 
226     /**
227      * Set Log to parent Logger.
228      * <p>
229      * If there is a different Log class available from a parent classloader,
230      * call {@link #getLogger(String)} on it and construct a {@link LoggerLog} instance
231      * as this Log's Logger, so that logging is delegated to the parent Log.
232      * <p>
233      * This should be used if a webapp is using Log, but wishes the logging to be
234      * directed to the containers log.
235      * <p>
236      * If there is not parent Log, then this call is equivalent to<pre>
237      *   Log.setLog(Log.getLogger(name));
238      * </pre>
239      * @param name Logger name
240      */
241     public static void setLogToParent(String name)
242     {
243         ClassLoader loader = Log.class.getClassLoader();
244         if (loader!=null && loader.getParent()!=null)
245         {
246             try
247             {
248                 Class<?> uberlog = loader.getParent().loadClass("org.eclipse.jetty.util.log.Log");
249                 Method getLogger = uberlog.getMethod("getLogger", new Class[]{String.class});
250                 Object logger = getLogger.invoke(null,name);
251                 setLog(new LoggerLog(logger));
252             }
253             catch (Exception e)
254             {
255                 e.printStackTrace();
256             }
257         }
258         else
259         {
260             setLog(getLogger(name));
261         }
262     }
263 
264     /**
265      * Obtain a named Logger based on the fully qualified class name.
266      *
267      * @param clazz
268      *            the class to base the Logger name off of
269      * @return the Logger with the given name
270      */
271     public static Logger getLogger(Class<?> clazz)
272     {
273         return getLogger(clazz.getName());
274     }
275 
276     /**
277      * Obtain a named Logger or the default Logger if null is passed.
278      * @param name the Logger name
279      * @return the Logger with the given name
280      */
281     public static Logger getLogger(String name)
282     {
283         if (!initialized())
284         {
285             IllegalStateException e = new IllegalStateException();
286             e.printStackTrace();
287             throw e;
288         }
289 
290         if(name==null)
291             return LOG;
292 
293         Logger logger = __loggers.get(name);
294         if(logger==null)
295             logger = LOG.getLogger(name);
296 
297         return logger;
298     }
299 
300     static ConcurrentMap<String, Logger> getMutableLoggers()
301     {
302         return __loggers;
303     }
304     
305     /**
306      * Get a map of all configured {@link Logger} instances.
307      *
308      * @return a map of all configured {@link Logger} instances
309      */
310     @ManagedAttribute("list of all instantiated loggers")
311     public static Map<String, Logger> getLoggers()
312     {
313         return Collections.unmodifiableMap(__loggers);
314     }
315 }