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