package net.sf.microlog; import java.util.Enumeration; import java.util.Vector; import net.sf.microlog.util.PropertiesGetter; /** * The Logger class is used for logging. * * @author Johan Karlsson (johan.karlsson@jayway.se) * @author Darius Katz * @author Karsten Ohme */ public final class Logger { public static final String LOG_LEVEL_STRING = "microlog.level"; public static final String APPENDER_STRING = "microlog.appender"; public static final String FORMATTER_STRING = "microlog.formatter"; public static final String PROPERTY_DELIMETER = ";"; private static final Logger LOGGER = new Logger(); final Vector appenderList = new Vector(); Level logLevel = Level.FATAL; /** * Create a Logger object. This is made private to prevent the user from * creating a Logger object through a constructor. */ private Logger() { openLog(); } /** * Get the Logger instance. * * @return the Logger. */ public static Logger getLogger() { return LOGGER; } /** * Reset the Logger, i.e. remove all appenders and set the log level to * Level.ERROR. */ public static synchronized void resetLogger() { LOGGER.appenderList.removeAllElements(); LOGGER.logLevel = Level.ERROR; } /** * Get the log level. * * @return Returns the logLevel. */ public Level getLogLevel() { return logLevel; } /** * Set the log level. * * @param logLevel * The logLevel to set. */ public void setLogLevel(Level logLevel) { this.logLevel = logLevel; } /** * Configure the logger. * * @param properties * Properties to configure with */ public void configure(PropertiesGetter properties) { if (properties != null) { configureLogLevel(properties.getString(LOG_LEVEL_STRING)); configureAppender(properties); configureFormatter(properties); } } /** * Configure the log level. * * @param logLevel * The logLevel to set. */ private void configureLogLevel(String logLevel) { if (logLevel != null) { if (logLevel.compareTo(Level.FATAL_STRING) == 0) { setLogLevel(Level.FATAL); } else if (logLevel.compareTo(Level.ERROR_STRING) == 0) { setLogLevel(Level.ERROR); } else if (logLevel.compareTo(Level.WARN_STRING) == 0) { setLogLevel(Level.WARN); } else if (logLevel.compareTo(Level.INFO_STRING) == 0) { setLogLevel(Level.INFO); } else if (logLevel.compareTo(Level.DEBUG_STRING) == 0) { setLogLevel(Level.DEBUG); } else if (logLevel.compareTo(Level.TRACE_STRING) == 0) { setLogLevel(Level.TRACE); } } } /** * Configure the appender for the specified logger. * * @param appenderString * the <code>String</code> to use for configuring the * <code>Appender</code>. */ private void configureAppender(PropertiesGetter properties) { // remove all appenders and close there log removeAllAppenders(); String appenderString = properties.getString(APPENDER_STRING); if ((appenderString != null) && (appenderString.length() > 0)) { try { int delimiterPos = appenderString.indexOf(PROPERTY_DELIMETER); if (delimiterPos == -1) { // There is only one appender Class appenderClass = Class.forName(appenderString); Appender appender = (Appender) appenderClass.newInstance(); appender.configure(properties); appender.openLog(); addAppender(appender); } else { // Loop through all the Appenders in appenderString int startPos = 0; int endPos; boolean finished = false; do { // find out if and where the next string is delimiterPos = appenderString.indexOf( PROPERTY_DELIMETER, startPos); if (delimiterPos == -1) { // this is the last appender endPos = appenderString.length(); finished = true; } else { // has a delimiter at the end endPos = delimiterPos; } // get the appender string String singleAppenderString = appenderString.substring( startPos, endPos); // Advance the start position startPos = endPos + 1; // create the appender if (singleAppenderString.length() > 0) { Class appenderClass = Class .forName(singleAppenderString); Appender appender = (Appender) appenderClass .newInstance(); appender.configure(properties); appender.openLog(); addAppender(appender); } } while (!finished); } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (MicroLogException e) { e.printStackTrace(); } } } /** * Configure the formatter for the specified logger. * * @param formatterString * the <code>String</code> to use for configuring the * <code>Formatter</code>. */ private void configureFormatter(PropertiesGetter properties) { String formatterString = properties.getString(FORMATTER_STRING); if (formatterString != null) { try { String className = formatterString; if (formatterString.indexOf(PROPERTY_DELIMETER) != -1) { className = formatterString.substring(0, formatterString .indexOf(PROPERTY_DELIMETER)); } Class formatterClass = Class.forName(className); Formatter formatter = (Formatter) formatterClass.newInstance(); int nofAppenders = getNumberOfAppenders(); for (int index = 0; index < nofAppenders; index++) { Appender appender = getAppender(index); if (appender != null) { configureFormatterProperty(formatterString, formatter); appender.setFormatter(formatter); } } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } /** * Clear the log. The call is forwarded to the appender. For some appenders * this is ignored. */ public void clearLog() { int nofAppenders = appenderList.size(); for (int index = 0; index < nofAppenders; index++) { Appender appender = (Appender) appenderList.elementAt(index); appender.clearLog(); } } /** * Close the log. From this point on, no logging is done. */ public void closeLog() { int nofAppenders = appenderList.size(); for (int index = 0; index < nofAppenders; index++) { Appender appender = (Appender) appenderList.elementAt(index); appender.closeLog(); } } /** * Open the log. The logging is now turned on. */ public void openLog() { try { int nofAppenders = appenderList.size(); for (int index = 0; index < nofAppenders; index++) { Appender appender = (Appender) appenderList.elementAt(index); appender.openLog(); } } catch (MicroLogException e) { System.err.println("Failed to open appender."); } } /** * Add the specified appender to the output appenders. * * @param appender * the appender to add. */ public void addAppender(Appender appender) { if (appender == null) { throw new IllegalArgumentException( "Appender not allowed to be null"); } else if (!appenderList.contains(appender)) { appenderList.addElement(appender); try { appender.openLog(); } catch (MicroLogException e) { e.printStackTrace(); } } } /** * Remove the specified appender from the appender list. * * @param appender * the appender to remove. */ public void removeAppender(Appender appender) { appender.closeLog(); appenderList.removeElement(appender); } /** * Remove all the appenders. * */ public void removeAllAppenders() { for (Enumeration e = appenderList.elements(); e.hasMoreElements();) { Appender appender = (Appender) e.nextElement(); appender.closeLog(); } appenderList.removeAllElements(); } /** * Get the number of active appenders. * * @return the number of appenders. */ public int getNumberOfAppenders() { return appenderList.size(); } /** * Get the specified appender, starting at index = 0. * * @param index * the index of the appender. * @return the appender. */ public Appender getAppender(int index) { return (Appender) appenderList.elementAt(index); } /** * MicroLog the message at the specified level. * * @param level * the level to log at. * @param message * the object to log. */ public void log(Level level, Object message) { this.log(level, message, null); } /** * MicroLog the message and the Throwable object at the specified level. * * @param level * the log level * @param message * the message * @param e * the exception. */ public void log(Level level, Object message, Throwable e) { if (checkLogLevel(level)) { int nofAppenders = appenderList.size(); for (int index = 0; index < nofAppenders; index++) { Appender appender = (Appender) appenderList.elementAt(index); appender.doLog(level, message, e); } } } /** * Is this LOGGER enabled for TRACE level? * * @return true if logging is enabled. */ public boolean isTraceEnabled() { return logLevel.toInt() <= Level.TRACE_INT; } /** * MicroLog the message at TRACE level. * * @param message * the message to log. */ public void trace(Object message) { log(Level.TRACE, message.toString()); } /** * MicroLog the message and the Throwable object at TRACE level. * * @param message * the message to log. * @param t * the Throwable object to log. */ public void trace(Object message, Throwable t) { log(Level.TRACE, message, t); } /** * Is this LOGGER enabled for DEBUG level? * * @return true if logging is enabled. */ public boolean isDebugEnabled() { return logLevel.toInt() <= Level.DEBUG_INT; } /** * MicroLog the message at DEBUG level. * * @param message * the message to log. */ public void debug(Object message) { log(Level.DEBUG, message.toString()); } /** * MicroLog the message and the Throwable object at DEBUG level. * * @param message * the message to log. * @param t * the Throwable object to log. */ public void debug(Object message, Throwable t) { log(Level.DEBUG, message, t); } /** * Is this LOGGER enabled for INFO level. * * @return true if the INFO level is enabled. */ public boolean isInfoEnabled() { return logLevel.toInt() <= Level.INFO_INT; } /** * MicroLog the specified message at INFO level. * * @param message * the object to log. */ public void info(Object message) { log(Level.INFO, message); } /** * MicroLog the specified message and the Throwable at INFO level. * * @param message * the object to log. * @param t * the <code>Throwable</code> to log. */ public void info(Object message, Throwable t) { log(Level.INFO, message, t); } /** * Is this LOGGER enabled for WARN level? * * @return true if WARN level is enabled. */ public boolean isWarnEnabled() { return logLevel.toInt() <= Level.WARN_INT; } /** * MicroLog the specified message at WARN level. * * @param message * the object to log. */ public void warn(Object message) { log(Level.WARN, message); } /** * MicroLog the specified message and Throwable object at WARN level. * * @param message * the object to log. * @param t * the <code>Throwable</code> to log. */ public void warn(Object message, Throwable t) { log(Level.WARN, message, t); } /** * Is this LOGGER enabled for ERROR level? * * @return true if the ERROR level is enabled. */ public boolean isErrorEnabled() { return logLevel.toInt() <= Level.ERROR_INT; } /** * MicroLog the specified message at ERROR level. * * @param message * the object to log. */ public void error(Object message) { log(Level.ERROR, message); } /** * MicroLog the specified message and Throwable object at ERROR level. * * @param message * the object to log. * @param t * the <code>Throwable</code> to log. */ public void error(Object message, Throwable t) { log(Level.ERROR, message, t); } /** * Is this LOGGER enabled for FATAL level? * * @return true if the FATAL level is enabled. */ public boolean isFatalEnabled() { return logLevel.toInt() <= Level.FATAL_INT; } /** * MicroLog the specified message at FATAL level. * * @param message * the object to log. */ public void fatal(Object message) { log(Level.FATAL, message); } /** * MicroLog the specified message and Throwable object at FATAL level. * * @param message * the object to log. * @param t * the <code>Throwable</code> to log. */ public void fatal(Object message, Throwable t) { log(Level.FATAL, message, t); } /** * @see java.lang.Object#toString() */ public String toString() { StringBuffer stringBuffer = new StringBuffer(); stringBuffer.append(super.toString()); stringBuffer.append('['); int nofAppenders = appenderList.size(); for (int index = 0; index < nofAppenders; index++) { Appender appender = (Appender) appenderList.elementAt(index); stringBuffer.append(appender); stringBuffer.append(';'); } stringBuffer.append(']'); return stringBuffer.toString(); } /** * Check if the level is enabled for logging. * * @param level * the level to check. * @return true if logging is enabled for this level. */ private boolean checkLogLevel(Level level) { boolean logLevelOk = false; if (level != null) { logLevelOk = logLevel.toInt() <= level.toInt(); } return logLevelOk; } /** * Configure the properties of the formatter. * * @param formatterString * the String that is used for configuration. * @param formatter * the Formatter that shall be configured. */ private void configureFormatterProperty(String formatterString, Formatter formatter) { int stringIndex = formatterString.indexOf(PROPERTY_DELIMETER); int equalsIndex = formatterString.indexOf("=", stringIndex); while (stringIndex != -1 && equalsIndex != -1) { String propertyName = formatterString.substring(stringIndex + 1, equalsIndex); stringIndex = formatterString.indexOf(PROPERTY_DELIMETER, stringIndex + 1); String propertyValue = null; if (stringIndex != -1) { propertyValue = formatterString.substring(equalsIndex + 1, stringIndex); formatter.setProperty(propertyName, propertyValue); } else { propertyValue = formatterString.substring(equalsIndex + 1, formatterString.length()); } if (propertyName != null && propertyValue != null) { formatter.setProperty(propertyName, propertyValue); } equalsIndex = formatterString.indexOf("=", stringIndex); } } }