Mega Code Archive

 
Categories / Java / Threads
 

Thread-based logging utility

/*  * Copyright Aduna (http://www.aduna-software.com/) (c) 1997-2006.  *  * Licensed under the Aduna BSD-style license.  */ import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; /**  * Thread-based logging utility. The ThreadLog requires threads to register  * themselves with the ThreadLog. In this registration procedure, a mapping is  * made between the thread calling the registration method and an instance of  * ThreadLog. This mapping is later used in all calls to logging-methods to  * look-up whether these messages should be logged, and where they should be  * logged.  * <p>  * Log messages are assigned a "level of importance". From high to low, these  * levels are ERROR, WARNING, STATUS and TRACE. Messages can be suppressed based  * on these levels. If a Thread registers itself with log level WARNING, only  * errors and warning will be logged; status messages and traces will be  * suppressed.  */ public class ThreadLog {   /*-----------*    * Constants *    *-----------*/   public static final int NONE = 0;   public static final int ERROR = 1;   public static final int WARNING = 2;   public static final int STATUS = 3;   public static final int TRACE = 4;   public static final int ALL = 5;   private static final String LINE_SEPARATOR = System.getProperty("line.separator");   /*------------------*    * Static variables *    *------------------*/   static SimpleDateFormat _formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");   static String[] _levelNames = { "NONE   ", "ERROR  ", "WARNING", "STATUS ", "TRACE  ", "ALL    " };   /** Keeps track of which Threads maps to which ThreadLogs. */   static InheritableThreadLocal<ThreadLog> _threadContext = new InheritableThreadLocal<ThreadLog>();   static ThreadLog _defaultThreadLog;   /** Maps (absolute) file paths to PrintWriters. */   static HashMap<String, Writer> _writerTable = new HashMap<String, Writer>();   /**    * set the ThreadLog to log warning and error messages to System.err by    * default    */   static {     setDefaultLog(null, WARNING);   }   /*----------------*    * Static methods *    *----------------*/   /**    * Sets a default log file for all threads that have not registered    * themselves. Logging calls from any of these threads to ThreadLog will be    * logged to the specified log file if their priority is equal to or higher    * then the specified log level. If 'logFile' is equal to 'null', messages    * will be printed to System.err.    *     * @param logFile    *        The file to log to, or <tt>null</tt> to log messages to    *        System.err.    * @param logLevel    *        One of the constants ERROR, WARNING, STATUS, TRACE, or ALL.    * @see #ERROR    * @see #WARNING    * @see #STATUS    * @see #TRACE    * @see #ALL    */   public static void setDefaultLog(String logFile, int logLevel) {     if (_defaultThreadLog == null) {       _defaultThreadLog = new ThreadLog();     }     Writer logWriter = null;     try {       logWriter = _getLogWriter(logFile);     }     catch (IOException e) {       System.err.println(e.getMessage());       e.printStackTrace();       try {         logWriter = _getLogWriter(null);       }       catch (IOException ignore) {         // ignore       }     }     _defaultThreadLog.setLogWriter(logWriter);     _defaultThreadLog.setLogLev(logLevel);   }   /**    * Unsets the default log file for all threads that have not registered    * themselves.    */   public static void unsetDefaultLog() {     _defaultThreadLog = null;   }   public static int getDefaultLogLevel() {     if (_defaultThreadLog == null) {       _defaultThreadLog = new ThreadLog();     }     return _defaultThreadLog.getLogLev();   }   /**    * Registers the calling thread with the ThreadLog. Logging calls to    * ThreadLog will be logged to the specified log file if their priority is    * equal to or higher then the specified log level. If 'logFile' is equal to    * 'null', messages will be printed to System.err.    *     * @param logFile    *        The file to log to, or <tt>null</tt> to log messages to    *        System.err.    * @param logLevel    *        One of the constants ERROR, WARNING, STATUS, TRACE, or ALL.    * @see #ERROR    * @see #WARNING    * @see #STATUS    * @see #TRACE    * @see #ALL    */   public static void registerThread(String logFile, int logLevel) {     ThreadLog threadLog = _createThreadLog();     Writer logWriter = null;     try {       logWriter = _getLogWriter(logFile);     }     catch (IOException e) {       System.err.println(e.getMessage());       e.printStackTrace();       try {         logWriter = _getLogWriter(null);       }       catch (IOException ignore) {         // ignore       }     }     threadLog.setLogWriter(logWriter);     threadLog.setLogLev(logLevel);     Thread currentThread = Thread.currentThread();     threadLog.setThreadName(currentThread.getName());   }   /**    * Changes the log level for the calling thread.    *     * @param logLevel    *        One of the constants ERROR, WARNING, STATUS, TRACE, or ALL.    * @see #ERROR    * @see #WARNING    * @see #STATUS    * @see #TRACE    */   public static void setLogLevel(int logLevel) {     ThreadLog threadLog = null;     synchronized (_threadContext) {       threadLog = _threadContext.get();     }     if (threadLog != null) {       threadLog.setLogLev(logLevel);     }   }      public static int getLogLevel() {     ThreadLog threadLog = _getThreadLog();          if (threadLog != null) {       return threadLog._logLevel;     }     else {       return NONE;     }   }   public static boolean logLevelEnabled(int logLevel) {     return getLogLevel() >= logLevel;   }      /**    * Creates a ThreadLog for the thread calling this method. If the thread has    * already created a ThreadLog before, this existing ThreadLog will be    * returned.    */   static ThreadLog _createThreadLog() {     ThreadLog threadLog = null;     synchronized (_threadContext) {       threadLog = _threadContext.get();       if (threadLog == null) {         threadLog = new ThreadLog();         _threadContext.set(threadLog);       }     }     return threadLog;   }   /**    * Gets a (possibly shared) Writer to the specified logFile.    */   static Writer _getLogWriter(String logFile)     throws IOException   {     Writer logWriter = null;     String absPath = null;     File file = null;     if (logFile != null) {       file = new File(logFile);       absPath = file.getAbsolutePath();     }     synchronized (_writerTable) {       logWriter = _writerTable.get(absPath);       if (logWriter == null) {         // Create a new log writer         if (absPath != null) {           // Check if parent directory exists yet           if (!file.getParentFile().exists()) {             file.getParentFile().mkdirs();           }           logWriter = new FileWriter(absPath, true);         }         else {           logWriter = new OutputStreamWriter(System.err);         }         _writerTable.put(absPath, logWriter);       }     }     return logWriter;   }   /**    * Deregisters the calling thread with the ThreadLog. Logging calls to    * ThreadLog on the calling thread will no longer be logged.    */   public static void deregisterThread() {     synchronized (_threadContext) {       _threadContext.set(null);     }   }   /**    * Gets the ThreadLog for the thread calling this method. If no ThreadLog is    * available, null will be returned.    */   static ThreadLog _getThreadLog() {     ThreadLog threadLog = null;     synchronized (_threadContext) {       threadLog = _threadContext.get();     }     if (threadLog == null) {       threadLog = _defaultThreadLog;     }     return threadLog;   }   /*-----------*    * Variables *    *-----------*/   /** Writer for the log file. */   Writer _logWriter;   /** Log level */   int _logLevel;   /**    * Name of the thread.    */   String _threadName;   /*--------------*    * Constructors *    *--------------*/   ThreadLog() {     this(null, ALL);   }   ThreadLog(Writer logWriter) {     this(logWriter, ALL);   }   ThreadLog(Writer logWriter, int logLevel) {     setLogWriter(logWriter);     setLogLev(logLevel);     _threadName = null;   }   /*---------*    * Methods *    *---------*/   void setLogWriter(Writer logWriter) {     _logWriter = logWriter;   }   void setLogLev(int logLevel) {     _logLevel = logLevel;   }   int getLogLev() {     return _logLevel;   }   void setThreadName(String threadName) {     _threadName = threadName;   }   String getThreadName() {     return _threadName;   }   void doLog(String msg, Object arg, int level) {     // First check log level     if (_logLevel < level) {       return;     }     // Create log message     String logMsg = _createLogMessage(msg, arg, level, _threadName);     // Write log message     // Synchronize on _logWriter to prevent mixed lines     try {       synchronized (_logWriter) {         _logWriter.write(logMsg);         _logWriter.flush();       }     }     catch (Exception e) {       e.printStackTrace();     }   }   static String _createLogMessage(String msg, Object arg, int level, String threadName) {     StringBuilder logMsg = new StringBuilder(128);     logMsg.append(_formatter.format(new Date()));     if (threadName != null) {       logMsg.append(" [");       logMsg.append(threadName);       logMsg.append("]");     }     logMsg.append(" [");     logMsg.append(_levelNames[level]);     logMsg.append("] ");     logMsg.append(msg);     if (arg != null) {       logMsg.append(": ");       if (arg instanceof Throwable) {         Throwable throwable = (Throwable)arg;         logMsg.append(throwable.getMessage());         logMsg.append(LINE_SEPARATOR);         java.io.StringWriter stackTrace = new java.io.StringWriter();         java.io.PrintWriter pw = new java.io.PrintWriter(stackTrace);         throwable.printStackTrace(pw);         logMsg.append(stackTrace.toString());       }       else {         String argString = arg.toString();         if (argString.contains("\n") || argString.contains("\r")) {           // argument formatted with newlines, do not append on same line           logMsg.append(LINE_SEPARATOR);         }         logMsg.append(argString);       }     }     logMsg.append(LINE_SEPARATOR);     return logMsg.toString();   }   /*------------------------*    * Static utility methods *    *------------------------*/   /**    * Logs an error.    *     * @param msg    *        A indicative message for the error.    */   public static void error(String msg) {     _log(msg, null, ERROR);   }   /**    * Logs an error.    *     * @param msg    *        A indicative message for the error.    * @param arg    *        An argument related to the error. In case <tt>arg</tt> is an    *        instance of <tt>java.lang.Throwable</tt>, the message and stack    *        trace of the argument will be logged.    */   public static void error(String msg, Object arg) {     _log(msg, arg, ERROR);   }   /**    * Logs a warning.    *     * @param msg    *        A indicative message for the warning.    */   public static void warning(String msg) {     _log(msg, null, WARNING);   }   /**    * Logs a warning.    *     * @param msg    *        A indicative message for the warning.    * @param arg    *        An argument related to the warning. In case <tt>arg</tt> is an    *        instance of <tt>java.lang.Throwable</tt>, the message and stack    *        trace of the argument will be logged.    */   public static void warning(String msg, Object arg) {     _log(msg, arg, WARNING);   }   /**    * Logs a message.    *     * @param msg    *        A indicative message for the message.    */   public static void log(String msg) {     _log(msg, null, STATUS);   }   /**    * Logs a message.    *     * @param msg    *        A indicative message for the message.    * @param arg    *        An argument related to the message. In case <tt>arg</tt> is an    *        instance of <tt>java.lang.Throwable</tt>, the message and stack    *        trace of the argument will be logged.    */   public static void log(String msg, Object arg) {     _log(msg, arg, STATUS);   }   /**    * Logs a trace message.    *     * @param msg    *        A indicative message for the trace message.    */   public static void trace(String msg) {     _log(msg, null, TRACE);   }   /**    * Logs a trace message.    *     * @param msg    *        A indicative message for the trace message.    * @param arg    *        An argument related to the trace message. In case <tt>arg</tt> is    *        an instance of <tt>java.lang.Throwable</tt>, the message and    *        stack trace of the argument will be logged.    */   public static void trace(String msg, Object arg) {     _log(msg, arg, TRACE);   }   /**    * Logs a message on the specified level.    *     * @param msg    *        A indicative message for the trace message.    * @param arg    *        An argument related to the trace message. In case <tt>arg</tt> is    *        an instance of <tt>java.lang.Throwable</tt>, the message and    *        stack trace of the argument will be logged.    * @param level    *        One of the constants ERROR, WARNING, STATUS, TRACE, or ALL.    * @see #ERROR    * @see #WARNING    * @see #STATUS    * @see #TRACE    * @see #ALL    */   protected static void _log(String msg, Object arg, int level) {     ThreadLog threadLog = _getThreadLog();     if (threadLog != null) {       threadLog.doLog(msg, arg, level);     }   } }