View Javadoc

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