/** * Distribution License: * JSword is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License, version 2.1 as published by * the Free Software Foundation. This program is distributed in the hope * that it will be useful, but WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * The License is available on the internet at: * http://www.gnu.org/copyleft/lgpl.html * or by writing to: * Free Software Foundation, Inc. * 59 Temple Place - Suite 330 * Boston, MA 02111-1307, USA * * Copyright: 2005 * The copyright to this program is held by it's authors. * * ID: $Id: Logger.java 2050 2010-12-09 15:31:45Z dmsmith $ */ package org.crosswire.common.util; import java.io.IOException; import java.io.InputStream; import java.util.MissingResourceException; import java.util.logging.Handler; import java.util.logging.Level; import java.util.logging.LogManager; import java.util.logging.LogRecord; /** * This class is very similar to Commons-Logging except it should be even * smaller and have an API closer to the Log4J API (and even J2SE 1.4 logging). * * This implementation is lazy. The actual internal logger is not initialized * until first use. Turns out that this class indirectly depends upon JSword's * Project class to help find the logging configuration file. If it is not lazy, * it looks in the wrong places for the configuration file. * * @see gnu.lgpl.License for license details.<br> * The copyright to this program is held by it's authors. * @author Joe Walker [joe at eireneh dot com] * @author DM Smith [dmsmith555 at yahoo dot com] */ public final class Logger { /** * Get a new logger for the class that shows the class, method and line * number of the caller. * @param clazz the class that holds the logger. */ public static <T> Logger getLogger(Class<T> clazz) { return getLogger(clazz, true); } /** * Get a new logger for the class that shows the class of the caller. * @param clazz the class that holds the logger. * @param showLocation when true it will get the method and line where logging occurred. */ public static <T> Logger getLogger(Class<T> clazz, boolean showLocation) { return new Logger(clazz, showLocation); } /** * Set the level at which output occurs for this Logger. * * @param newLevel * the level to apply */ public void setLevel(Level newLevel) { logger.setLevel(newLevel); } /** * Stop all logging output */ public static synchronized void outputNothing() { level = Level.OFF; } /** * Output a minimum of stuff */ public static synchronized void outputInfoMinimum() { level = Level.WARNING; } /** * Output everything */ public static synchronized void outputEverything() { level = Level.ALL; } /** * Log a message object with the SEVERE level. * * @param msg * the message to log. */ public void fatal(String msg) { doLogging(Level.SEVERE, msg, null); } /** * Log a message object with the SEVERE level. * * @param msg * the message object to log. */ public void fatal(String msg, Throwable th) { doLogging(Level.SEVERE, msg, th); } /** * Log a message object with the WARNING level. * * @param msg * the message to log. */ public void error(String msg) { doLogging(Level.WARNING, msg, null); } /** * Log a message object with the WARNING level. * * @param msg * the message to log. * @param th * the exception to note when not null */ public void error(String msg, Throwable th) { doLogging(Level.WARNING, msg, th); } /** * Log a message object with the INFO level. * * @param msg * the message object to log. */ public void info(String msg) { doLogging(Level.INFO, msg, null); } /** * Log a message object with the INFO level. * * @param msg * the message object to log. * @param th * the exception to note when not null */ public void info(String msg, Throwable th) { doLogging(Level.INFO, msg, th); } /** * Log a message object with the FINE level. * * @param msg * the message object to log. */ public void warn(String msg) { doLogging(Level.FINE, msg, null); } /** * Log a message object with the FINE level. * * @param msg * the message object to log. * @param th * the exception to note when not null */ public void warn(String msg, Throwable th) { doLogging(Level.FINE, msg, th); } /** * Log a message object with the FINEST level. * * @param msg * the message object to log. */ public void debug(String msg) { doLogging(Level.FINEST, msg, null); } /** * Log a message with the supplied level. * * @param lev * the level at which to log. * @param msg * the message to log. */ public void log(Level lev, String msg) { doLogging(lev, msg, null); } /** * Log a message with the supplied level, recording the exception when not * null. * * @param msg * the message object to log. */ public void log(Level lev, String msg, Throwable th) { doLogging(lev, msg, th); } /** * Create a logger for the class. Wrapped by {@link #java.util.logging.Logger.getLogger(String)}. */ private <T> Logger(Class<T> id, boolean showLocation) { this.logger = java.util.logging.Logger.getLogger(id.getName()); this.showLocation = showLocation; } // Private method to infer the caller's class and method names private void doLogging(Level theLevel, String message, Throwable th) { initialize(); LogRecord logRecord = new LogRecord(theLevel, message); logRecord.setLoggerName(logger.getName()); logRecord.setSourceClassName(CallContext.getCallingClass(1).getName()); logRecord.setThrown(th); if (showLocation) { String methodName = null; int lineNumber = -1; // Get the stack trace. StackTraceElement[] stack = (new Throwable()).getStackTrace(); // First, search back to a method in the Logger class. int ix = 0; while (ix < stack.length) { StackTraceElement frame = stack[ix]; String cname = frame.getClassName(); if (cname.equals(CLASS_NAME)) { break; } ix++; } // Now search for the first frame with the name of the caller. while (ix < stack.length) { StackTraceElement frame = stack[ix]; if (!frame.getClassName().equals(CLASS_NAME)) { // We've found the relevant frame. methodName = frame.getMethodName(); lineNumber = frame.getLineNumber(); break; } ix++; } logRecord.setSourceMethodName(methodName); // This is a non-standard use of sequence number. // We could just subclass LogRecord and add line number. logRecord.setSequenceNumber(lineNumber); } //MJD this line added because I could not get this logger to work on Android System.out.println("JSword:"+message); logger.log(logRecord); } private synchronized void initialize() { Logger.establishLogging(); Logger.setLevel(); } private static void establishLogging() { if (established) { return; } established = true; Exception ex = null; try { InputStream cwConfigStream = ResourceUtil.getResourceAsStream("CWLogging.properties"); LogManager.getLogManager().readConfiguration(cwConfigStream); } catch (SecurityException e) { ex = e; } catch (MissingResourceException e) { ex = e; } catch (IOException e) { ex = e; } if (ex != null) { cwLogger.info("Can't load CWLogging.properties", ex); } } private static void setLevel() { // If there was a request to change the minimum level of logging // handle it now. if (Logger.level != null) { // There are two parts of making a log message get out. // It has to be more important than the level: // a) of the logger // b) of the handler // So we need to set both. // In the case of the handlers, we set all of them. java.util.logging.Logger rootLogger = java.util.logging.Logger.getLogger(ROOT_LOGGER); Handler[] handlers = rootLogger.getHandlers(); for (int index = 0; index < handlers.length; index++) { handlers[index].setLevel(Level.FINE); } rootLogger.setLevel(Logger.level); // Don't do this again unless asked. Logger.level = null; } } private static final String ROOT_LOGGER = ""; private static final String CLASS_NAME = Logger.class.getName(); private static volatile boolean established; private static volatile Level level; /** * The actual logger. */ private java.util.logging.Logger logger; private static Logger cwLogger = getLogger(Logger.class); /** * Whether we dig into the call stack to get the method and line number of * the caller. */ private boolean showLocation; }