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 ";
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 = Loader.loadClass(Log.class, __logClass);
173 if (LOG == 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 public static void setLog(Logger log)
213 {
214 Log.LOG = log;
215 }
216
217 /**
218 * Get the root logger.
219 * @return the root logger
220 */
221 public static Logger getRootLogger() {
222 initialized();
223 return LOG;
224 }
225
226 static boolean isIgnored()
227 {
228 return __ignored;
229 }
230
231 /**
232 * Set Log to parent Logger.
233 * <p>
234 * If there is a different Log class available from a parent classloader,
235 * call {@link #getLogger(String)} on it and construct a {@link LoggerLog} instance
236 * as this Log's Logger, so that logging is delegated to the parent Log.
237 * <p>
238 * This should be used if a webapp is using Log, but wishes the logging to be
239 * directed to the containers log.
240 * <p>
241 * If there is not parent Log, then this call is equivalent to<pre>
242 * Log.setLog(Log.getLogger(name));
243 * </pre>
244 * @param name Logger name
245 */
246 public static void setLogToParent(String name)
247 {
248 ClassLoader loader = Log.class.getClassLoader();
249 if (loader!=null && loader.getParent()!=null)
250 {
251 try
252 {
253 Class<?> uberlog = loader.getParent().loadClass("org.eclipse.jetty.util.log.Log");
254 Method getLogger = uberlog.getMethod("getLogger", new Class[]{String.class});
255 Object logger = getLogger.invoke(null,name);
256 setLog(new LoggerLog(logger));
257 }
258 catch (Exception e)
259 {
260 e.printStackTrace();
261 }
262 }
263 else
264 {
265 setLog(getLogger(name));
266 }
267 }
268
269 /**
270 * Obtain a named Logger based on the fully qualified class name.
271 *
272 * @param clazz
273 * the class to base the Logger name off of
274 * @return the Logger with the given name
275 */
276 public static Logger getLogger(Class<?> clazz)
277 {
278 return getLogger(clazz.getName());
279 }
280
281 /**
282 * Obtain a named Logger or the default Logger if null is passed.
283 * @param name the Logger name
284 * @return the Logger with the given name
285 */
286 public static Logger getLogger(String name)
287 {
288 initialized();
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 }