| Logger.java |
1 /**
2 * Distribution License:
3 * JSword is free software; you can redistribute it and/or modify it under
4 * the terms of the GNU Lesser General Public License, version 2.1 as published by
5 * the Free Software Foundation. This program is distributed in the hope
6 * that it will be useful, but WITHOUT ANY WARRANTY; without even the
7 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
8 * See the GNU Lesser General Public License for more details.
9 *
10 * The License is available on the internet at:
11 * http://www.gnu.org/copyleft/lgpl.html
12 * or by writing to:
13 * Free Software Foundation, Inc.
14 * 59 Temple Place - Suite 330
15 * Boston, MA 02111-1307, USA
16 *
17 * Copyright: 2005
18 * The copyright to this program is held by it's authors.
19 *
20 * ID: $Id: Logger.java 2050 2010-12-09 15:31:45Z dmsmith $
21 */
22 package org.crosswire.common.util;
23
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.util.MissingResourceException;
27 import java.util.logging.Handler;
28 import java.util.logging.Level;
29 import java.util.logging.LogManager;
30 import java.util.logging.LogRecord;
31
32 /**
33 * This class is very similar to Commons-Logging except it should be even
34 * smaller and have an API closer to the Log4J API (and even J2SE 1.4 logging).
35 *
36 * This implementation is lazy. The actual internal logger is not initialized
37 * until first use. Turns out that this class indirectly depends upon JSword's
38 * Project class to help find the logging configuration file. If it is not lazy,
39 * it looks in the wrong places for the configuration file.
40 *
41 * @see gnu.lgpl.License for license details.<br>
42 * The copyright to this program is held by it's authors.
43 * @author Joe Walker [joe at eireneh dot com]
44 * @author DM Smith [dmsmith555 at yahoo dot com]
45 */
46 public final class Logger {
47 /**
48 * Get a new logger for the class that shows the class, method and line
49 * number of the caller.
50 * @param clazz the class that holds the logger.
51 */
52 public static <T> Logger getLogger(Class<T> clazz) {
53 return getLogger(clazz, true);
54 }
55
56 /**
57 * Get a new logger for the class that shows the class of the caller.
58 * @param clazz the class that holds the logger.
59 * @param showLocation when true it will get the method and line where logging occurred.
60 */
61 public static <T> Logger getLogger(Class<T> clazz, boolean showLocation) {
62 return new Logger(clazz, showLocation);
63 }
64
65 /**
66 * Set the level at which output occurs for this Logger.
67 *
68 * @param newLevel
69 * the level to apply
70 */
71 public void setLevel(Level newLevel) {
72 logger.setLevel(newLevel);
73 }
74
75 /**
76 * Stop all logging output
77 */
78 public static synchronized void outputNothing() {
79 level = Level.OFF;
80 }
81
82 /**
83 * Output a minimum of stuff
84 */
85 public static synchronized void outputInfoMinimum() {
86 level = Level.WARNING;
87 }
88
89 /**
90 * Output everything
91 */
92 public static synchronized void outputEverything() {
93 level = Level.ALL;
94 }
95
96 /**
97 * Log a message object with the SEVERE level.
98 *
99 * @param msg
100 * the message to log.
101 */
102 public void fatal(String msg) {
103 doLogging(Level.SEVERE, msg, null);
104 }
105
106 /**
107 * Log a message object with the SEVERE level.
108 *
109 * @param msg
110 * the message object to log.
111 */
112 public void fatal(String msg, Throwable th) {
113 doLogging(Level.SEVERE, msg, th);
114 }
115
116 /**
117 * Log a message object with the WARNING level.
118 *
119 * @param msg
120 * the message to log.
121 */
122 public void error(String msg) {
123 doLogging(Level.WARNING, msg, null);
124 }
125
126 /**
127 * Log a message object with the WARNING level.
128 *
129 * @param msg
130 * the message to log.
131 * @param th
132 * the exception to note when not null
133 */
134 public void error(String msg, Throwable th) {
135 doLogging(Level.WARNING, msg, th);
136 }
137
138 /**
139 * Log a message object with the INFO level.
140 *
141 * @param msg
142 * the message object to log.
143 */
144 public void info(String msg) {
145 doLogging(Level.INFO, msg, null);
146 }
147
148 /**
149 * Log a message object with the INFO level.
150 *
151 * @param msg
152 * the message object to log.
153 * @param th
154 * the exception to note when not null
155 */
156 public void info(String msg, Throwable th) {
157 doLogging(Level.INFO, msg, th);
158 }
159
160 /**
161 * Log a message object with the FINE level.
162 *
163 * @param msg
164 * the message object to log.
165 */
166 public void warn(String msg) {
167 doLogging(Level.FINE, msg, null);
168 }
169
170 /**
171 * Log a message object with the FINE level.
172 *
173 * @param msg
174 * the message object to log.
175 * @param th
176 * the exception to note when not null
177 */
178 public void warn(String msg, Throwable th) {
179 doLogging(Level.FINE, msg, th);
180 }
181
182 /**
183 * Log a message object with the FINEST level.
184 *
185 * @param msg
186 * the message object to log.
187 */
188 public void debug(String msg) {
189 doLogging(Level.FINEST, msg, null);
190 }
191
192 /**
193 * Log a message with the supplied level.
194 *
195 * @param lev
196 * the level at which to log.
197 * @param msg
198 * the message to log.
199 */
200 public void log(Level lev, String msg) {
201 doLogging(lev, msg, null);
202 }
203
204 /**
205 * Log a message with the supplied level, recording the exception when not
206 * null.
207 *
208 * @param msg
209 * the message object to log.
210 */
211 public void log(Level lev, String msg, Throwable th) {
212 doLogging(lev, msg, th);
213 }
214
215 /**
216 * Create a logger for the class. Wrapped by {@link #java.util.logging.Logger.getLogger(String)}.
217 */
218 private <T> Logger(Class<T> id, boolean showLocation) {
219 this.logger = java.util.logging.Logger.getLogger(id.getName());
220 this.showLocation = showLocation;
221 }
222
223 // Private method to infer the caller's class and method names
224 private void doLogging(Level theLevel, String message, Throwable th) {
225 initialize();
226
227 LogRecord logRecord = new LogRecord(theLevel, message);
228 logRecord.setLoggerName(logger.getName());
229 logRecord.setSourceClassName(CallContext.getCallingClass(1).getName());
230 logRecord.setThrown(th);
231
232 if (showLocation) {
233 String methodName = null;
234 int lineNumber = -1;
235
236 // Get the stack trace.
237 StackTraceElement[] stack = (new Throwable()).getStackTrace();
238
239 // First, search back to a method in the Logger class.
240 int ix = 0;
241 while (ix < stack.length) {
242 StackTraceElement frame = stack[ix];
243 String cname = frame.getClassName();
244 if (cname.equals(CLASS_NAME)) {
245 break;
246 }
247 ix++;
248 }
249
250 // Now search for the first frame with the name of the caller.
251 while (ix < stack.length) {
252 StackTraceElement frame = stack[ix];
253 if (!frame.getClassName().equals(CLASS_NAME)) {
254 // We've found the relevant frame.
255 methodName = frame.getMethodName();
256 lineNumber = frame.getLineNumber();
257 break;
258 }
259 ix++;
260 }
261
262 logRecord.setSourceMethodName(methodName);
263 // This is a non-standard use of sequence number.
264 // We could just subclass LogRecord and add line number.
265 logRecord.setSequenceNumber(lineNumber);
266 }
267
268 logger.log(logRecord);
269 }
270
271 private synchronized void initialize() {
272 Logger.establishLogging();
273 Logger.setLevel();
274 }
275
276 private static void establishLogging() {
277 if (established) {
278 return;
279 }
280 established = true;
281
282 Exception ex = null;
283 try {
284 InputStream cwConfigStream = ResourceUtil.getResourceAsStream("CWLogging.properties");
285 LogManager.getLogManager().readConfiguration(cwConfigStream);
286 } catch (SecurityException e) {
287 ex = e;
288 } catch (MissingResourceException e) {
289 ex = e;
290 } catch (IOException e) {
291 ex = e;
292 }
293 if (ex != null) {
294 cwLogger.info("Can't load CWLogging.properties", ex);
295 }
296 }
297
298 private static void setLevel() {
299 // If there was a request to change the minimum level of logging
300 // handle it now.
301 if (Logger.level != null) {
302 // There are two parts of making a log message get out.
303 // It has to be more important than the level:
304 // a) of the logger
305 // b) of the handler
306 // So we need to set both.
307 // In the case of the handlers, we set all of them.
308 java.util.logging.Logger rootLogger = java.util.logging.Logger.getLogger(ROOT_LOGGER);
309 Handler[] handlers = rootLogger.getHandlers();
310 for (int index = 0; index < handlers.length; index++) {
311 handlers[index].setLevel(Level.FINE);
312 }
313 rootLogger.setLevel(Logger.level);
314 // Don't do this again unless asked.
315 Logger.level = null;
316 }
317 }
318
319 private static final String ROOT_LOGGER = "";
320 private static final String CLASS_NAME = Logger.class.getName();
321 private static volatile boolean established;
322 private static volatile Level level;
323
324 /**
325 * The actual logger.
326 */
327 private java.util.logging.Logger logger;
328 private static Logger cwLogger = getLogger(Logger.class);
329
330 /**
331 * Whether we dig into the call stack to get the method and line number of
332 * the caller.
333 */
334 private boolean showLocation;
335 }
336