1 //
2 // ========================================================================
3 // Copyright (c) 1995-2015 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 ";
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 private static void loadProperties(String resourceName, Properties props)
134 {
135 URL testProps = Loader.getResource(Log.class,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 try
171 {
172 Class<?> log_class = __logClass==null?null:Loader.loadClass(Log.class, __logClass);
173 if (LOG == null || (log_class!=null && !LOG.getClass().equals(log_class)))
174 {
175 LOG = (Logger)log_class.newInstance();
176 LOG.debug("Logging to {} via {}", LOG, log_class.getName());
177 }
178 }
179 catch(Throwable e)
180 {
181 // Unable to load specified Logger implementation, default to standard logging.
182 initStandardLogging(e);
183 }
184
185 if (LOG!=null)
186 LOG.info(String.format("Logging initialized @%dms",Uptime.getUptime()));
187 }
188 }
189
190 private static void initStandardLogging(Throwable e)
191 {
192 Class<?> log_class;
193 if(e != null && __ignored)
194 {
195 e.printStackTrace(System.err);
196 }
197
198 if (LOG == null)
199 {
200 log_class = StdErrLog.class;
201 LOG = new StdErrLog();
202 LOG.debug("Logging to {} via {}", LOG, log_class.getName());
203 }
204 }
205
206 public static Logger getLog()
207 {
208 initialized();
209 return LOG;
210 }
211
212 /**
213 * Set the root logger.
214 * <p>
215 * Note that if any classes have statically obtained their logger instance prior to this call, their Logger will not
216 * be affected by this call.
217 *
218 * @param log
219 * the root logger implementation to set
220 */
221 public static void setLog(Logger log)
222 {
223 Log.LOG = log;
224 __logClass=null;
225 }
226
227 /**
228 * Get the root logger.
229 * @return the root logger
230 */
231 public static Logger getRootLogger() {
232 initialized();
233 return LOG;
234 }
235
236 static boolean isIgnored()
237 {
238 return __ignored;
239 }
240
241 /**
242 * Set Log to parent Logger.
243 * <p>
244 * If there is a different Log class available from a parent classloader,
245 * call {@link #getLogger(String)} on it and construct a {@link LoggerLog} instance
246 * as this Log's Logger, so that logging is delegated to the parent Log.
247 * <p>
248 * This should be used if a webapp is using Log, but wishes the logging to be
249 * directed to the containers log.
250 * <p>
251 * If there is not parent Log, then this call is equivalent to<pre>
252 * Log.setLog(Log.getLogger(name));
253 * </pre>
254 * @param name Logger name
255 */
256 public static void setLogToParent(String name)
257 {
258 ClassLoader loader = Log.class.getClassLoader();
259 if (loader!=null && loader.getParent()!=null)
260 {
261 try
262 {
263 Class<?> uberlog = loader.getParent().loadClass("org.eclipse.jetty.util.log.Log");
264 Method getLogger = uberlog.getMethod("getLogger", new Class[]{String.class});
265 Object logger = getLogger.invoke(null,name);
266 setLog(new LoggerLog(logger));
267 }
268 catch (Exception e)
269 {
270 e.printStackTrace();
271 }
272 }
273 else
274 {
275 setLog(getLogger(name));
276 }
277 }
278
279 /**
280 * Obtain a named Logger based on the fully qualified class name.
281 *
282 * @param clazz
283 * the class to base the Logger name off of
284 * @return the Logger with the given name
285 */
286 public static Logger getLogger(Class<?> clazz)
287 {
288 return getLogger(clazz.getName());
289 }
290
291 /**
292 * Obtain a named Logger or the default Logger if null is passed.
293 * @param name the Logger name
294 * @return the Logger with the given name
295 */
296 public static Logger getLogger(String name)
297 {
298 initialized();
299
300 if(name==null)
301 return LOG;
302
303 Logger logger = __loggers.get(name);
304 if(logger==null)
305 logger = LOG.getLogger(name);
306
307 return logger;
308 }
309
310 static ConcurrentMap<String, Logger> getMutableLoggers()
311 {
312 return __loggers;
313 }
314
315 /**
316 * Get a map of all configured {@link Logger} instances.
317 *
318 * @return a map of all configured {@link Logger} instances
319 */
320 @ManagedAttribute("list of all instantiated loggers")
321 public static Map<String, Logger> getLoggers()
322 {
323 return Collections.unmodifiableMap(__loggers);
324 }
325 }