/**
* Copyright (c) 2007-2011, JAGaToo Project Group all rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the 'Xith3D Project Group' nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) A
* RISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE
*/
/**
* :Id: LogManager.java,v 1.9 2003/02/24 00:13:53 wurp Exp $
*
* :Log: LogManager.java,v $
* Revision 1.9 2003/02/24 00:13:53 wurp
* Formatted all java code for cvs (strictSunConvention.xml)
*
* Revision 1.8 2002/02/12 02:22:27 dilvish
* Bunch of bug fixes
*
* Revision 1.7 2002/01/15 03:39:00 dilvish
* Added the ability to dump exception stacks to log
*
* Revision 1.6 2001/06/20 04:05:42 wurp
* added log4j.
*
* Revision 1.5 2001/04/04 01:06:29 wizofid
* New framerate window, new animation system
*
* Revision 1.4 2001/03/13 01:43:34 wizofid
* Added the portal
*
* Revision 1.3 2001/01/28 07:52:20 wurp
* Removed <dollar> from Id and Log in log comments.
* Added several new commands to AdminApp
* Unfortunately, several other changes that I have lost track of. Try diffing this
* version with the previous one.
*
* Revision 1.2 2000/12/16 22:07:33 wurp
* Added Id and Log to almost all of the files that didn't have it. It's
* possible that the script screwed something up. I did a commit and an update
* right before I ran the script, so if a file is screwed up you should be able
* to fix it by just going to the version before this one.
*/
package org.jagatoo.logging;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* This object manages multiple logs. This provides a single point
* at which the application can write to logs, but allows log control
* to be handled centrally. Multiple LogInterface objects are registered
* with the logger. The LogManager will step through them when a logging
* message comes in and give each LogInterface an opportunity to consume
* the log message.
*
* @author David Yazel
* @author Marvin Froehlich (aka Qudus)
*/
public class LogManager
{
private final List<LogHandler> logs;
private int maxRegisteredLogLevel = -Integer.MAX_VALUE;
private int registeredChannels = 0;
private long startTime;
private boolean timestampsEnabled = false;
private boolean channelsVisible = false;
private boolean lastNewLine = true;
private final Set<String> debugPackageFilter = new HashSet<String>();
private String indentationString = " ";
private int indentation = 0;
private static LogManager instance = null;
/**
* Sets the String to be prefixed to the actualy logging output n times.
*
* @param indentationString
*/
public final void setIndentationString( String indentationString )
{
this.indentationString = indentationString;
}
/**
* @return the String to be prefixed to the actualy logging output n times.
*/
public final String getIndentationString()
{
return ( indentationString );
}
/**
* Sets the indentation level to use for the following log outputs.
*
* @param indentation
*/
public final void setIndentation( int indentation )
{
this.indentation = Math.max( 0, indentation );
}
/**
* @return the indentation level to use for the following log outputs.
*/
public final int getIndentation()
{
return ( indentation );
}
public final void setTimestampingEnabled( boolean enabled )
{
this.timestampsEnabled = enabled;
}
public final boolean isTimestampingEnabled()
{
return ( timestampsEnabled );
}
public final void setChannelsVisible( boolean visible )
{
this.channelsVisible = visible;
}
public final boolean areChannelsVisible()
{
return ( channelsVisible );
}
public final void addDebuggingPackage( String pkg )
{
debugPackageFilter.add( pkg );
}
public final void removeDebuggingPackage( String pkg )
{
debugPackageFilter.remove( pkg );
}
public final Set<String> getDebuggingPackageFiler()
{
return ( debugPackageFilter );
}
private String getTimeString()
{
final long delta = System.currentTimeMillis() - startTime;
return ( LogFormatter.formatTime( delta ) );
}
private static String getMemory()
{
Runtime runtime = Runtime.getRuntime();
final long mem = runtime.totalMemory();
final long free = runtime.freeMemory();
return ( LogFormatter.formatMemory( mem - free ) + "/" + LogFormatter.formatMemory( mem ) );
}
/**
* Must be called, if the logLevel of a registeredLogInterface has been changed.
*/
public final void refreshLogInterfaces()
{
maxRegisteredLogLevel = -Integer.MAX_VALUE;
registeredChannels = 0;
for ( int i = 0; i < logs.size(); i++ )
{
final LogHandler log = logs.get( i );
//if ( log.getLogLevel().compareLevel( maxRegisteredLogLevel ) > 0 )
if ( log.getLogLevel().level > maxRegisteredLogLevel )
maxRegisteredLogLevel = log.getLogLevel().level;
registeredChannels |= log.getChannelFilter();
}
}
/**
* This method allows you to register a class that implements the
* LogInterface. Every log so registered will get a copy of every
* log message, along with its mask.
*
* @param log
*/
public final void registerLog( LogHandler log )
{
logs.add( log );
refreshLogInterfaces();
}
/**
* This method allows you to deregister a class that implements the
* LogInterface. Every log so deregistered won't get a copy of every
* log message anymore.
*
* @param log
*/
public final void deregisterLog( LogHandler log )
{
logs.remove( log );
refreshLogInterfaces();
}
public final boolean isAnyLogInterfaceRegistered( LogChannel channel, int logLevel )
{
if ( logs.size() == 0 )
return ( false );
if ( logLevel > maxRegisteredLogLevel )
return ( false );
if ( !channel.isInFilter( registeredChannels ) )
return ( false );
return ( true );
}
private static final String getCallerPackage()
{
final StackTraceElement[] stes = Thread.currentThread().getStackTrace();
String callerClass = null;
for ( int i = 3; i < stes.length; i++ )
{
//if ( !stes[ i ].getClassName().startsWith( LogManager.class.getPackage().getName() ) )
if ( !stes[ i ].getClassName().toLowerCase().contains( "log" ) )
{
callerClass = stes[ i ].getClassName();
break;
}
}
if ( ( callerClass == null ) || ( callerClass.length() == 0 ) )
return ( "" );
final int lastDot = callerClass.lastIndexOf( '.' );
final String callerPackage;
if ( lastDot >= 0 )
callerPackage = callerClass.substring( 0, lastDot );
else
callerPackage = "";
return ( callerPackage );
}
private final void printThrowable( LogChannel channel, int level, Throwable t, LogHandler log )
{
log.println( channel, level, t.getClass().getName() + ": " + t.getMessage() );
StackTraceElement[] st = t.getStackTrace();
for ( StackTraceElement ste : st )
{
log.println( channel, level, " at " + ste.toString() );
}
}
private synchronized final void internalPrint( LogChannel channel, int logLevel, Object[] message, boolean commaSeparated, boolean appendNL )
{
if ( ( message == null ) || ( message.length == 0 ) )
return;
// redundant!
//if ( !isAnyLogInterfaceRegistered( channel, logLevel ) )
// return;
String callerPackage = null;
if ( !debugPackageFilter.isEmpty() && LogLevel.isError( logLevel ) )
{
callerPackage = getCallerPackage();
if ( !debugPackageFilter.contains( callerPackage ) )
return;
}
String is = getIndentationString();
for ( int i = 0; i < logs.size(); i++ )
{
final LogHandler log = logs.get( i );
if ( log.acceptsChannelAndLevel( channel, logLevel ) )
{
try
{
if ( !( message[0] instanceof Throwable ) )
{
for ( int j = 0; j < indentation; j++ )
{
log.print( channel, logLevel, is );
}
if ( lastNewLine && areChannelsVisible() )
{
log.print( channel, logLevel, channel.getLogString() );
log.print( channel, logLevel, " " );
}
if ( lastNewLine && isTimestampingEnabled() )
{
log.print( channel, logLevel, "[" );
log.print( channel, logLevel, getTimeString() );
log.print( channel, logLevel, ", " );
log.print( channel, logLevel, getMemory() );
log.print( channel, logLevel, "] " );
}
}
for ( int j = 0; j < message.length; j++ )
{
if ( message[j] instanceof Throwable )
{
if ( !debugPackageFilter.isEmpty() )
{
if ( callerPackage == null )
callerPackage = getCallerPackage();
if ( debugPackageFilter.contains( callerPackage ) )
{
int logLevel2 = ( message[j] instanceof Error ) ? LogLevel.ERROR.level : LogLevel.EXCEPTION.level;
if ( logLevel2 <= log.getLogLevelLevel() )
printThrowable( channel, logLevel2, (Throwable)message[j], log );
lastNewLine = true;
}
}
else
{
int logLevel2 = ( message[j] instanceof Error ) ? LogLevel.ERROR.level : LogLevel.EXCEPTION.level;
if ( logLevel2 <= log.getLogLevelLevel() )
printThrowable( channel, logLevel2, (Throwable)message[j], log );
lastNewLine = true;
}
}
else if ( appendNL && ( j == message.length - 1 ) )
{
log.println( channel, logLevel, String.valueOf( message[ j ] ) );
}
else
{
log.print( channel, logLevel, String.valueOf( message[ j ] ) );
}
if ( commaSeparated && ( j < message.length - 1 ) )
{
log.print( channel, logLevel, ", " );
}
}
}
finally
{
log.endMessage();
}
}
}
this.lastNewLine = appendNL;
}
/**
* This method will call all the log objects to store the message,
* if they want to.
*
* @param channel
* @param logLevel the logLevel of this message
* @param message the string message to be printed to the log
*/
public final void print( LogChannel channel, int logLevel, Object[] message )
{
internalPrint( channel, logLevel, message, false, false );
}
/**
* This method will call all the log objects to store the message,
* if they want to.
*
* @param channel
* @param logLevel the logLevel of this message
* @param message the string message to be printed to the log (comma separated)
*/
public final void printCS( LogChannel channel, int logLevel, Object[] message )
{
internalPrint( channel, logLevel, message, true, false );
}
/**
* This method will call all the log objects to store the message,
* if they want to.
*
* @param channel
* @param logLevel the logLevel of this message
* @param message the string message to be printed to the log
*/
public final void println( LogChannel channel, int logLevel, Object[] message )
{
internalPrint( channel, logLevel, message, false, true );
}
/**
* This method will call all the log objects to store the message,
* if they want to.
*
* @param channel
* @param logLevel the logLevel of this message
* @param message the string message to be printed to the log (comma separated)
*/
public final void printlnCS( LogChannel channel, int logLevel, Object[] message )
{
internalPrint( channel, logLevel, message, true, true );
}
/**
* Steps through the logs and flushes all of them. Necessary since they
* could be implemented using files with buffers.
*/
final void flush()
{
for ( int i = 0; i < logs.size(); i++ )
{
logs.get( i ).flush();
}
}
/**
* Steps through the logs and closes all of them. Necessary since they
* could be implemented using files with buffers.
*/
final void close()
{
for ( int i = 0; i < logs.size(); i++ )
{
logs.get( i ).close();
}
}
private LogManager()
{
this.logs = new ArrayList<LogHandler>( 2 );
this.startTime = System.currentTimeMillis();
}
/**
* @return the LogManager's singleton instance.
*/
public static final LogManager getInstance()
{
if ( instance == null )
instance = new LogManager();
return ( instance );
}
}