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
19package org.eclipse.jetty.util.log;
20
21import java.io.IOException;
22import java.io.InputStream;
23import java.lang.reflect.Method;
24import java.net.URL;
25import java.security.AccessController;
26import java.security.PrivilegedAction;
27import java.util.Collection;
28import java.util.Collections;
29import java.util.Enumeration;
30import java.util.HashMap;
31import java.util.Map;
32import java.util.Properties;
33import java.util.concurrent.ConcurrentHashMap;
34import java.util.concurrent.ConcurrentMap;
35
36import org.eclipse.jetty.util.IO;
37import org.eclipse.jetty.util.Loader;
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 */
55public class Log
56{
57    public final static String EXCEPTION= "EXCEPTION ";
58    public final static String IGNORED= "IGNORED ";
59
60    /**
61     * Logging Configuration Properties
62     */
63    protected static Properties __props;
64    /**
65     * The {@link Logger} implementation class name
66     */
67    public static String __logClass;
68    /**
69     * Legacy flag indicating if {@link Log#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<String, Logger>();
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                URL testProps = Loader.getResource(Log.class,"jetty-logging.properties",true);
95                if (testProps != null)
96                {
97                    InputStream in = null;
98                    try
99                    {
100                        in = testProps.openStream();
101                        __props.load(in);
102                    }
103                    catch (IOException e)
104                    {
105                        System.err.println("Unable to load " + testProps);
106                        e.printStackTrace(System.err);
107                    }
108                    finally
109                    {
110                        IO.close(in);
111                    }
112                }
113
114                /* Now load the System.properties as-is into the __props, these values will override
115                 * any key conflicts in __props.
116                 */
117                @SuppressWarnings("unchecked")
118                Enumeration<String> systemKeyEnum = (Enumeration<String>)System.getProperties().propertyNames();
119                while (systemKeyEnum.hasMoreElements())
120                {
121                    String key = systemKeyEnum.nextElement();
122                    String val = System.getProperty(key);
123                    //protect against application code insertion of non-String values (returned as null)
124                    if (val != null)
125                        __props.setProperty(key,val);
126                }
127
128                /* Now use the configuration properties to configure the Log statics
129                 */
130                __logClass = __props.getProperty("org.eclipse.jetty.util.log.class","org.eclipse.jetty.util.log.Slf4jLog");
131                __ignored = Boolean.parseBoolean(__props.getProperty("org.eclipse.jetty.util.log.IGNORED","false"));
132                return null;
133            }
134        });
135    }
136
137    private static Logger LOG;
138    private static boolean __initialized;
139
140    public static boolean initialized()
141    {
142        if (LOG != null)
143        {
144            return true;
145        }
146
147        synchronized (Log.class)
148        {
149            if (__initialized)
150            {
151                return LOG != null;
152            }
153            __initialized = true;
154        }
155
156        try
157        {
158            Class<?> log_class = Loader.loadClass(Log.class, __logClass);
159            if (LOG == null || !LOG.getClass().equals(log_class))
160            {
161                LOG = (Logger)log_class.newInstance();
162                LOG.debug("Logging to {} via {}", LOG, log_class.getName());
163            }
164        }
165        catch(Throwable e)
166        {
167            // Unable to load specified Logger implementation, default to standard logging.
168            initStandardLogging(e);
169        }
170
171        return LOG != null;
172    }
173
174    private static void initStandardLogging(Throwable e)
175    {
176        Class<?> log_class;
177        if(e != null && __ignored)
178        {
179            e.printStackTrace();
180        }
181
182        if (LOG == null)
183        {
184            log_class = StdErrLog.class;
185            LOG = new StdErrLog();
186            LOG.debug("Logging to {} via {}", LOG, log_class.getName());
187        }
188    }
189
190    public static void setLog(Logger log)
191    {
192        Log.LOG = log;
193    }
194
195    /**
196     * @deprecated anonymous logging is deprecated, use a named {@link Logger} obtained from {@link #getLogger(String)}
197     */
198    @Deprecated
199    public static Logger getLog()
200    {
201        initialized();
202        return LOG;
203    }
204
205    /**
206     * Get the root logger.
207     * @return the root logger
208     */
209    public static Logger getRootLogger() {
210        initialized();
211        return LOG;
212    }
213
214    static boolean isIgnored()
215    {
216        return __ignored;
217    }
218
219    /**
220     * Set Log to parent Logger.
221     * <p>
222     * If there is a different Log class available from a parent classloader,
223     * call {@link #getLogger(String)} on it and construct a {@link LoggerLog} instance
224     * as this Log's Logger, so that logging is delegated to the parent Log.
225     * <p>
226     * This should be used if a webapp is using Log, but wishes the logging to be
227     * directed to the containers log.
228     * <p>
229     * If there is not parent Log, then this call is equivalent to<pre>
230     *   Log.setLog(Log.getLogger(name));
231     * </pre>
232     * @param name Logger name
233     */
234    public static void setLogToParent(String name)
235    {
236        ClassLoader loader = Log.class.getClassLoader();
237        if (loader!=null && loader.getParent()!=null)
238        {
239            try
240            {
241                Class<?> uberlog = loader.getParent().loadClass("org.eclipse.jetty.util.log.Log");
242                Method getLogger = uberlog.getMethod("getLogger", new Class[]{String.class});
243                Object logger = getLogger.invoke(null,name);
244                setLog(new LoggerLog(logger));
245            }
246            catch (Exception e)
247            {
248                e.printStackTrace();
249            }
250        }
251        else
252        {
253            setLog(getLogger(name));
254        }
255    }
256
257    /**
258     * @deprecated anonymous logging is deprecated, use a named {@link Logger} obtained from {@link #getLogger(String)}
259     */
260    @Deprecated
261    public static void debug(Throwable th)
262    {
263        if (!isDebugEnabled())
264            return;
265        LOG.debug(EXCEPTION, th);
266    }
267
268    /**
269     * @deprecated anonymous logging is deprecated, use a named {@link Logger} obtained from {@link #getLogger(String)}
270     */
271    @Deprecated
272    public static void debug(String msg)
273    {
274        if (!initialized())
275            return;
276        LOG.debug(msg);
277    }
278
279    /**
280     * @deprecated anonymous logging is deprecated, use a named {@link Logger} obtained from {@link #getLogger(String)}
281     */
282    @Deprecated
283    public static void debug(String msg, Object arg)
284    {
285        if (!initialized())
286            return;
287        LOG.debug(msg, arg);
288    }
289
290    /**
291     * @deprecated anonymous logging is deprecated, use a named {@link Logger} obtained from {@link #getLogger(String)}
292     */
293    @Deprecated
294    public static void debug(String msg, Object arg0, Object arg1)
295    {
296        if (!initialized())
297            return;
298        LOG.debug(msg, arg0, arg1);
299    }
300
301    /**
302     * Ignore an exception unless trace is enabled.
303     * This works around the problem that log4j does not support the trace level.
304     * @param thrown the Throwable to ignore
305     */
306    /**
307     * @deprecated anonymous logging is deprecated, use a named {@link Logger} obtained from {@link #getLogger(String)}
308     */
309    @Deprecated
310    public static void ignore(Throwable thrown)
311    {
312        if (!initialized())
313            return;
314        LOG.ignore(thrown);
315    }
316
317    /**
318     * @deprecated anonymous logging is deprecated, use a named {@link Logger} obtained from {@link #getLogger(String)}
319     */
320    @Deprecated
321    public static void info(String msg)
322    {
323        if (!initialized())
324            return;
325        LOG.info(msg);
326    }
327
328    /**
329     * @deprecated anonymous logging is deprecated, use a named {@link Logger} obtained from {@link #getLogger(String)}
330     */
331    @Deprecated
332    public static void info(String msg, Object arg)
333    {
334        if (!initialized())
335            return;
336        LOG.info(msg, arg);
337    }
338
339    /**
340     * @deprecated anonymous logging is deprecated, use a named {@link Logger} obtained from {@link #getLogger(String)}
341     */
342    @Deprecated
343    public static void info(String msg, Object arg0, Object arg1)
344    {
345        if (!initialized())
346            return;
347        LOG.info(msg, arg0, arg1);
348    }
349
350    /**
351     * @deprecated anonymous logging is deprecated, use a named {@link Logger} obtained from {@link #getLogger(String)}
352     */
353    @Deprecated
354    public static boolean isDebugEnabled()
355    {
356        if (!initialized())
357            return false;
358        return LOG.isDebugEnabled();
359    }
360
361    /**
362     * @deprecated anonymous logging is deprecated, use a named {@link Logger} obtained from {@link #getLogger(String)}
363     */
364    @Deprecated
365    public static void warn(String msg)
366    {
367        if (!initialized())
368            return;
369        LOG.warn(msg);
370    }
371
372    /**
373     * @deprecated anonymous logging is deprecated, use a named {@link Logger} obtained from {@link #getLogger(String)}
374     */
375    @Deprecated
376    public static void warn(String msg, Object arg)
377    {
378        if (!initialized())
379            return;
380        LOG.warn(msg, arg);
381    }
382
383    /**
384     * @deprecated anonymous logging is deprecated, use a named {@link Logger} obtained from {@link #getLogger(String)}
385     */
386    @Deprecated
387    public static void warn(String msg, Object arg0, Object arg1)
388    {
389        if (!initialized())
390            return;
391        LOG.warn(msg, arg0, arg1);
392    }
393
394    /**
395     * @deprecated anonymous logging is deprecated, use a named {@link Logger} obtained from {@link #getLogger(String)}
396     */
397    @Deprecated
398    public static void warn(String msg, Throwable th)
399    {
400        if (!initialized())
401            return;
402        LOG.warn(msg, th);
403    }
404
405    /**
406     * @deprecated anonymous logging is deprecated, use a named {@link Logger} obtained from {@link #getLogger(String)}
407     */
408    @Deprecated
409    public static void warn(Throwable th)
410    {
411        if (!initialized())
412            return;
413        LOG.warn(EXCEPTION, th);
414    }
415
416    /**
417     * Obtain a named Logger based on the fully qualified class name.
418     *
419     * @param clazz
420     *            the class to base the Logger name off of
421     * @return the Logger with the given name
422     */
423    public static Logger getLogger(Class<?> clazz)
424    {
425        return getLogger(clazz.getName());
426    }
427
428    /**
429     * Obtain a named Logger or the default Logger if null is passed.
430     * @param name the Logger name
431     * @return the Logger with the given name
432     */
433    public static Logger getLogger(String name)
434    {
435        if (!initialized())
436            return null;
437
438        if(name==null)
439            return LOG;
440
441        Logger logger = __loggers.get(name);
442        if(logger==null)
443            logger = LOG.getLogger(name);
444
445        return logger;
446    }
447
448    static ConcurrentMap<String, Logger> getMutableLoggers()
449    {
450        return __loggers;
451    }
452
453    /**
454     * Get a map of all configured {@link Logger} instances.
455     *
456     * @return a map of all configured {@link Logger} instances
457     */
458    public static Map<String, Logger> getLoggers()
459    {
460        return Collections.unmodifiableMap(__loggers);
461    }
462}
463