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 }