/** * Copyright 2007-2008 University Of Southern California * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package edu.isi.pegasus.common.logging.logger; import edu.isi.pegasus.common.logging.LogManager; import edu.isi.pegasus.common.logging.LogFormatter; import edu.isi.pegasus.common.util.Currently; import org.apache.log4j.Level; import java.io.OutputStream; import java.io.PrintWriter; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; import java.io.Writer; import java.util.Properties; /** * The logging class that to log messages at different levels. * Currently the following levels are supported.<p> * * Eventually, each of the level can have a different writer stream underneath. * * <p> * The messages can be logged at various levels. The various levels of logging * with increasing levels of verbosity are displayed in the following table. * * <p> * <table border="1"> * <tr align="left"><th>Logging Level</th><th>Description</th></tr> * <tr align="left"><th>FATAL</th> * <td>all fatal error messages are logged in this level.</td> * </tr> * <tr align="left"><th>ERROR</th> * <td>all non fatal error messages are logged in this level.</td> * </tr> * <tr align="left"><th>WARNING</th> * <td>all warning messages are logged in this level.</td> * </tr> * <tr align="left"><th>INFO</th> * <td>all information logging messages are logged in this level.</td> * </tr> * <tr align="left"><th>CONFIG</th> * <td>all configuration messages are logged in this level.</td> * </tr> * <tr align="left"><th>DEBUG</th> * <td>all debug messages are logged in this level.</td> * </tr> * </table> * * @author Karan Vahi * @author Gaurang Mehta * @version $Revision$ */ public class Default extends LogManager{ //level constants that loosely match Log4J and are used //to generate the appropriate mask values. /** * The type value to indicate a FATAL error message. */ private static final int FATAL_MESSAGE_TYPE = 0x1; /** * The type value to indicate an ERROR message. */ private static final int ERROR_MESSAGE_TYPE = 0x2; /** * The type value to indicate a CONSOLE message. */ private static final int CONSOLE_MESSAGE_TYPE = 0x4; /** * The type value to indicate a WARNING message. */ private static final int WARNING_MESSAGE_TYPE = 0x8; /** * The type value to indicate an INFORMATIVE message. */ private static final int INFO_MESSAGE_TYPE = 0x10; /** * The type value to indicate a CONFIG message. */ private static final int CONFIG_MESSAGE_TYPE = 0x20; /** * The type value to indicate a DEBUG message. */ private static final int DEBUG_MESSAGE_TYPE = 0x40; /** * The type value to indicate a DEBUG message. */ private static final int TRACE_MESSAGE_TYPE = 0x80; /** * Ensures only one object is created always. Implements the Singleton. */ private static Default logger; /** * The stream to which one writes. It is System.out by default for the * current release. One can set it using setOutputWriter. * * @see #setOutputWriter */ private PrintStream mOutStream; /** * The stream to which all the error messages are logged.By default it is * System.err */ private PrintStream mErrStream; /** * The mask that needs to be deployed to determine what messages are to be * logged. */ private int mMask; /** * This is used to format the time stamp. */ private static Currently mFormatter ; /** * The constructor. */ public Default(){ mDebugLevel = 0; mOutStream = new PrintStream(System.out,true); mErrStream = new PrintStream(System.err,true); Default.mFormatter = new Currently( "yyyy.MM.dd HH:mm:ss.SSS zzz: " ); //by default we are logging only CONSOLE //and all message less than WARN mMask = generateMask( WARNING_MESSAGE_LEVEL, false ); } /** * Sets the log formatter to use for formatting the messages. * * @param formatter the formatter to use. * @param properties properties that the underlying implementations understand */ public void initialize( LogFormatter formatter, Properties properties ){ mLogFormatter = formatter; } /** * Sets the debug level. All those messages are logged which have a * level less than equal to the debug level. * * @param level the level to which the debug level needs to be set to. */ public void setLevel(Level level){ int value = level.toInt(); switch(value){ case Level.DEBUG_INT: value = Default.DEBUG_MESSAGE_LEVEL; break; case Level.INFO_INT: value = Default.INFO_MESSAGE_LEVEL; break; case Level.WARN_INT: value = Default.WARNING_MESSAGE_LEVEL; break; case Level.ERROR_INT: value = Default.ERROR_MESSAGE_LEVEL; break; default: value = Default.FATAL_MESSAGE_LEVEL; break; } setLevel(value,false); } /** * Sets the debug level. All those messages are logged which have a * level less than equal to the debug level. * * @param level the level to which the debug level needs to be set to. */ public void setLevel(int level){ setLevel(level, false ); } /** * Sets the debug level. All those messages are logged which have a * level less than equal to the debug level. In case the boolean info * is set, all the info messages are also logged. * * @param level the level to which the debug level needs to be set to. * @param info boolean denoting whether the INFO messages need to be * logged or not. */ protected void setLevel(int level, boolean info){ mDebugLevel = level; mMask = generateMask(level,info); } /** * Returns the debug level. * * @return the level to which the debug level has been set to. */ public int getLevel(){ return mDebugLevel; } /** * Sets both the output writer and the error writer to the same * underlying writer. * * @param out is the name of a file to append to. Special names are * <code>stdout</code> and <code>stderr</code>, which map to the * system's respective streams. * * @see #setWriters(OutputStream) */ public void setWriters(String out){ try{ // mOutStream = (PrintStream)getPrintStream(out); // mErrStream = mOutStream; PrintStream ps = (PrintStream)getPrintStream(out); System.setOut( ps ); System.setErr( ps ); mOutStream = System.out; mErrStream = System.err; } catch(IOException e){ //log on the existing streams !!! log("Unable to set streams for logging ",e, this.WARNING_MESSAGE_LEVEL); } } /** * Sets both the output writer and the error writer to the same * underlying writer. * * Note: The previous stream is not closed automatically. * * @param err the stream to which error messages are to be logged. */ /* public void setWriters(OutputStream err){ mOutStream = new PrintWriter( err, true ); mErrStream = mOutStream; } */ /** * Sets the writer associated with the class to the one specified for all * type of messages other than error messages. * * @param out is the name of a file to append to. Special names are * <code>stdout</code> and <code>stderr</code>, which map to the * system's respective streams. * * @see #setOutputWriter(OutputStream) */ public void setOutputWriter(String out){ try{ mOutStream = (PrintStream)getPrintStream(out); } catch(IOException e){ //log on the existing streams !!! log("Unable to set streams for logging ",e, this.WARNING_MESSAGE_LEVEL); } } /** * Sets the writer associated with the class to the one specified for all * type of messages other than error messages. * By default it is System.out. * * @param out the stream to which the messages are logged. * * @see #setErrorWriter(OutputStream) */ public void setOutputWriter(OutputStream out){ mOutStream = new PrintStream( out, true ); } /** * Certains levels like FATAL, ERROR and WARN can be set to log to a * different stream, than the default stream used for writing other messages. * By default, these messages are logged to stderr. * Note: The previous stream is not closed automatically. * * @param out is the name of a file to append to. Special names are * <code>stdout</code> and <code>stderr</code>, which map to the * system's respective streams. * * @see #setErrorWriter(OutputStream) */ public void setErrorWriter(String out){ try{ mErrStream = (PrintStream)getPrintStream(out); } catch(IOException e){ //log on the existing streams !!! log("Unable to set streams for logging ",e, this.WARNING_MESSAGE_LEVEL); } } /** * Certains levels like FATAL, ERROR and WARN can be set to log to a * different stream, than the default stream used for writing other messages. * By default, these messages are logged to stderr. * Note: The previous stream is not closed automatically. * * @param err the stream to which error messages are to be logged. */ public void setErrorWriter(OutputStream err){ mErrStream = new PrintStream( err, true ); } /** * Logs the exception on the appropriate queue if the level of the message * is less than or equal to the level set for the Logger. For INFO level * message, the boolean indicating that a completion message is to follow * is set to true always. * * @param message the message to be logged. * @param e the exception to be logged * @param level the level on which the message has to be logged. * * @see #setLevel(int) * @see #log(String,int) */ public void log(String message, Exception e,int level){ StringBuffer msg = new StringBuffer(); msg.append(message).append(" ").append( e.getClass() ).append( ": ").append(e.getMessage()); log(msg.toString(),level); } /** * Logs the message on the appropriate queue if the level of the message * is less than or equal to the level set for the Logger. For INFO level * message, the boolean indicating that a completion message is to follow * is set to true always. * * @param message the message to be logged. * @param level the level on which the message has to be logged. * * @see #setLevel(int) * @see #log(String,int,boolean) */ public void logAlreadyFormattedMessage(String message, int level){ log(message,level,(level == this.INFO_MESSAGE_LEVEL) ? true : false); } /** * Logs the message on the appropriate queue if the level of the message * is less than or equal to the level set for the Logger. * * @param message the message to be logged. * @param level the level on which the message has to be logged. * @param comp boolean indicating whether a completion message * follows or not. * * @see #setLevel(int) */ private void log(String message, int level, boolean comp){ int type = (int)Math.pow(2, level); if( (type & mMask) != 0x0 ){ //we need to log the message //get hold of the writer to be used to logging the message. PrintStream writer = getPrintStream(level); writer.print(Default.mFormatter.now()); String prefix = getPrefix(type); message = prefix + " " + message; /* *uncomment if we want commpetion message for INFO *on same line if(comp){ if((mMask & INFO_MESSAGE_TYPE) == INFO_MESSAGE_TYPE){ //we need to just print the message writer.print(message); } else{ //write out on a new line and //push the message to the stack writer.println(message); // mMsgStack.push(message); } } else{ writer.println(message); } */ writer.println(message); writer.flush(); } } /** * Gets the timestamp nicely formatted. It generates the date-timestamp * in extended ISO 8601 format. It generates the timestamp using * the local timezone not the UTC. An example of the date-timestamp * generated would be 2003-06-06T14:31:27-07:00 where -07:00 denotes the timezone * offset of the local timezone from UTC. * * @return the formattted timestamp; */ public String getTimeStamp(){ String st = Default.mFormatter.now(); st = Currently.iso8601(false); return st; } /** * Logs the completion message on the basis of the debug level. * * @param level the debug level of the start message for whose completion * you want. */ public void logEventCompletion( int level ){ String message = mLogFormatter.getEndEventMessage(); mLogFormatter.popEvent(); int type = (int)Math.pow(2, level); if( (type & mMask) != 0x0 ){ PrintStream writer = getPrintStream(level); /*uncomment if we want commpetion message for INFO on same line if ( (mMask & INFO_MESSAGE_TYPE) == INFO_MESSAGE_TYPE) { writer.println(" (completed)"); } else { writer.print(LogManager.mFormatter.now()); writer.println(message + " (completed)"); } */ String prefix = getPrefix(type); message = prefix + " " + message; writer.print(Default.mFormatter.now()); writer.println( message ); //writer.println(message + " (completed)"); } } /** * Generates the appropriate mask value, corresponding to the level * passed. * * @param level the level to which the debug level needs to be set to. * @param info boolean denoting whether the CONSOLE messages need to be * logged or not. * * @return mask corresponding to the debug level passed. */ private int generateMask(int level,boolean info){ //construct the appropriate mask int mask = 0x0; for(int i = 0; i <= level; i++){ mask |= (int)Math.pow(2,i); } if(info){ mask |= CONSOLE_MESSAGE_TYPE; } return mask; } /** * Returns the prefix that needs to be logged corresponding to a particular * message type, when a message is being logged. * Should be returning an enumerated data type. * * @param type the type for which prefix is required. * * @return the message type */ private String getPrefix(int type){ String result = null; switch(type){ case FATAL_MESSAGE_TYPE: result = "[FATAL ERROR]"; break; case ERROR_MESSAGE_TYPE: result = "[ERROR]"; break; case CONSOLE_MESSAGE_TYPE: result = ""; break; case WARNING_MESSAGE_TYPE: result = "[WARNING]"; break; case INFO_MESSAGE_TYPE: result = "[INFO]"; break; case CONFIG_MESSAGE_TYPE: result = "[CONFIG]"; break; case DEBUG_MESSAGE_TYPE: result = "[DEBUG]"; break; case TRACE_MESSAGE_TYPE: result = "[TRACE]"; break; default: result = "[UNKNOWN]"; } return result; } /** * Sets an internal writer to point to a particular stream. * * @param out is the name of a file to append to. Special names are * <code>stdout</code> and <code>stderr</code>, which map to the * system's respective streams. * * @return the corresponding PrintStream. * * @throws IOException in case of being unable to open a stream. */ private PrintStream getPrintStream( String out ) throws IOException{ //check if value refers to any of the predefined streams OutputStream stream; if( out.equalsIgnoreCase("stdout")){ stream = System.out; } else if( out.equalsIgnoreCase("stderr")){ stream = System.err; } else{ //try to create an output stream to file specified File f = new File( out ); //do some sanity checks on file sanityCheckOnFile( f ); stream = new FileOutputStream( f); } return new PrintStream(stream); } /** * Returns a PrintWriter stream on which to log the message. Later on * this, function would return the appropriate LOG4J queue on which * the message needs to be logged. * * @param level the level * * @return PrintWriter for logging the message. */ private PrintStream getPrintStream(int level){ return ( (level >= FATAL_MESSAGE_LEVEL && level < CONSOLE_MESSAGE_LEVEL) || level == WARNING_MESSAGE_LEVEL )? mErrStream: mOutStream; } }