/*
** j###t ########## #### ####
** j###t ########## #### ####
** j###T "###L J###"
** ######P' ########## #########
** ######k, ########## T######T
** ####~###L ####
** #### q###L ########## .#####
** #### \###L ########## #####"
**
** $Id$
**
** Class History
**
** Date Name Description
** ---------|------------|-----------------------------------------------
** 24Nov98 subtle created
**
*/
package key;
import key.primitive.*;
import key.collections.*;
import java.net.*;
import java.io.*;
import java.util.*;
import java.lang.*;
import key.config.BaseConfiguration;
/**
*/
public class Log
{
public static boolean DEBUG_MODE = true;
public static Log instance;
// directories
protected static transient File logsBasePath;
protected static transient File clanLogsBasePath;
public Log()
{
instance = this;
//logsBasePath = Key.confirmedDirectory( "logs" );
//clanLogsBasePath = Key.confirmedDirectory( "logs/clans" );
}
/**
* A generalised debugging log function
*
* @param message The message to store
*/
public static final void debug( Object from, String message )
{
if( from instanceof Class )
debug( ((Class)from).getName(), message );
else
debug( Type.typeOf( from ).getName(), message );
}
private static final String ERROR_PRE =
"--- ERROR --------------------------------------";
private static final String ERROR_POST =
"-------------------------------------- ERROR ---";
/**
* A handled-error has occurred. The message (and a stack
* trace) should be logged for later examination (and output
* to System.err), but no other action need be taken. This
* is a more serious condition than a 'debug' message (since
* debug messages may occur often and no stack trace is associated
* with them).
*/
public static final void error( String message, Throwable e )
{
//log( ERROR, ERROR_PRE );
//log( ERROR, message );
if( e instanceof ThreadDeath )
throw ((ThreadDeath) e);
System.out.println( ERROR_PRE );
System.out.println( message );
System.out.println();
writeExceptionToLog( ERROR, ERROR_PRE + "\n" + message, ERROR_POST, e );
e.printStackTrace();
System.out.println( ERROR_POST );
}
public static final void error( Throwable e )
{
//log( ERROR, ERROR_PRE );
if( e instanceof ThreadDeath )
throw ((ThreadDeath) e);
System.out.println( ERROR_PRE );
writeExceptionToLog( ERROR, ERROR_PRE, ERROR_POST, e );
e.printStackTrace();
//log( ERROR, ERROR_POST );
System.out.println( ERROR_POST );
}
public static final void error( String message )
{
try
{
throw new Exception();
}
catch( Exception e )
{
error( message, e );
}
}
/**
* Use for static routines only, please
*/
public static final void debug( String name, String message )
{
log( DEBUG, name + " - " + message );
}
public static void fatal( String from, String message )
{
bootLog( Type.typeOf( from ).getName() + " -FATAL- " + message );
if( !Key.isRunning() )
{
System.exit( 1 );
}
else
{
// This is a more serious error - something has triggered
// a fatal failure while the program was running
try
{
Key.instance().sync();
}
catch( Throwable t )
{
debug( t.getClass().getName(), t.getMessage() );
}
finally
{
System.exit( 1 );
}
}
}
/**
* A generalised fatal error message
*/
public static void fatal( Object from, String message )
{
fatal( Type.typeOf( from ).getName(), message );
}
public static final File getLogsBasePath()
{
return( logsBasePath );
}
/**
* This is a global variable that can be used
* by any command to indicate that _everything_
* on the program should start doing verbose
* logging. At any given point in time, there
* should be only _one_ section of code that
* can turn this flag on.
*/
public static boolean verbose = false;
/** A log used for global debug messages */
private static final String DEBUG = "debug";
private static final String ERROR = "error";
/** A log used for bootup messages */
private static final String BOOT = "boot";
/**
* A special type of log call which sends the output to the
* boot log (if available), but will also always send the
* message to System.err
*/
public static final void bootLog( String message )
{
Key k = Key.instance();
if( !DEBUG_MODE )
System.out.println( "[" + BOOT + "]: " + message );
if( k != null && logsBasePath != null )
writeLog( BOOT, message );
}
/**
* A log routine which will send the message to the
* supplied log file, if available, or System.out
* otherwise.
*/
public static void log( String logKey, String message )
{
Key k = Key.instance();
if( k != null && logsBasePath != null )
{
writeLog( logKey, message );
}
else
System.out.println( "[" + logKey + "]: " + message );
}
private static String cachedKey = null;
private static RandomAccessFile cachedRAF = null;
/**
* Actually write to the log file
*/
private static synchronized final void writeLog( String logKey, String message )
{
try
{
if( logKey != cachedKey )
{
if( cachedRAF != null )
cachedRAF.close();
cachedRAF = new RandomAccessFile( new File( logsBasePath, logKey ), "rw" );
cachedRAF.seek( cachedRAF.length() );
cachedKey = logKey;
}
cachedRAF.writeBytes( new DateTime().toString() + ": " + message + "\n" );
}
catch( IOException e )
{
cachedRAF = null;
cachedKey = null;
System.out.println( "(write to log error: " + e.getMessage() + ") - [" + logKey + "]: " + message );
return;
}
if( DEBUG_MODE )
System.out.println( "[" + logKey + "]: " + message );
}
private static synchronized final void writeExceptionToLog( String logKey, String pre, String post, Throwable exce )
{
try
{
if( logKey != cachedKey )
{
if( cachedRAF != null )
cachedRAF.close();
cachedRAF = new RandomAccessFile( new File( logsBasePath, logKey ), "rw" );
cachedRAF.seek( cachedRAF.length() );
cachedKey = logKey;
}
cachedRAF.writeBytes( pre + "\n" );
PrintWriter pw = new PrintWriter( new FileOutputStream( cachedRAF.getFD() ) );
exce.printStackTrace( pw );
pw.flush();
cachedRAF.seek( cachedRAF.length() );
cachedRAF.writeBytes( post + "\n" );
pw.close();
cachedRAF.close();
cachedRAF = null;
cachedKey = null;
}
catch( IOException e )
{
cachedRAF = null;
cachedKey = null;
System.out.println( "(exception write to log error: " + e.getMessage() + ") - [" + logKey + "]: " );
e.printStackTrace();
exce.printStackTrace();
return;
}
}
public static final FileReader viewLog( String logName ) throws IOException
{
if( logName == null || logName.length() == 0 )
return( null );
// check permissions: META: Add a permission check in here for
// who may read log files at will.
File logFile = new File( logsBasePath, logName );
String lfc = logFile.getCanonicalPath();
// check to see if this is a valid path
if( lfc.startsWith( logsBasePath.getCanonicalPath() ) )
{
// this is okay
return( new FileReader( logFile ) );
}
else
{
Player p = Player.getCurrent();
if( p != null )
Log.log( "security", "ViewLog: Attempt to read file '" + lfc + "' while running as player " + p.getName() + "." );
else
Log.log( "security", "ViewLog: Attempt to read file '" + lfc + "' while running as a system process" );
throw new AccessViolationException( Log.class, "trying to read a logfile outside the logs directory" );
}
}
static void shutdown()
{
// shutdown logging
if( cachedRAF != null )
{
try
{
cachedRAF.close();
}
catch( IOException e )
{
}
cachedRAF = null;
cachedKey = null;
}
}
}