/*
* Util.Logger.cs
* Authors: Adam Nabinger
* Copyright (c) 2007-2008 Cornell University
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
using System;
using System.Diagnostics;
namespace Util
{
///
/// Util.Logger class for quickly and easily writing anything to timestamped log files.
/// Any number of loggers created with the default constructor or the copy constructor are guaranteed to be thread safe and will write all log calls to the file in the order the calls were made.
/// All IO is done in the background, so any number of log calls will not degrade game performance.
///
public sealed class Logger : IDisposable
{
#region Constants
///
/// String constant for used for separating files in a single string
///
internal const string LogSeparator = "\t";
#endregion
#region Fields
// The writer used for writing out the logged events
private readonly LogWriter MyLogWriter;
// Whether or not the logger has been initialized
private bool initialized;
// Whether or not the logger has been disposed
private bool disposed;
#if DEBUG
///
/// In Debug Mode, all log output is duplicated to the console.
/// As with writing anything to the console, this could degrade performance in debug mode.
///
public static bool DEBUG_DuplicateToConsole = true;
///
/// Should the loggers append to an existing file, rather than overwritting it.
///
public static bool Append;
///
/// Should Debug Events (those with negative event codes) be written to the log file.
///
private bool writeDebugEvents = true;
#else
///
/// Should the loggers append to an existing file, rather than overwritting it.
///
public static bool Append = true;
#endif
///
/// Should the high-precision cpu tick time stamps be written to the log files.
///
public static bool WriteHighPrecisionTicks = true;
// Whether or not writing to the parallel port has been enabled
private readonly bool parallelPortEnabled;
// The writer for writing out the logged events to the parallel port
internal ParallelPortWriter parallelPortWriter;
#endregion
#region Properties
///
/// Gets whether or not writing to the parallel port has been enabled
///
public bool ParallelPortEnabled { get { return parallelPortEnabled; } }
///
/// Gets whether or not the logger has been disposed
///
[Obsolete("If you've disposed of the logger, you shouldn't be writing to it. This shouldn't be used, fix your code instead of checking the logger.")]
public bool Disposed { get { return disposed; } }
#endregion
#region Creation
///
/// Create a new logger writing to the given logFile.
///
/// The file to write to.
public Logger(string logFile)
{
MyLogWriter = new LogWriter(logFile);
parallelPortEnabled = Settings.ParallelPortEnabled;
}
///
/// Create a new logger writing to the given logFile, specifying parallelPortEnabled.
///
/// The file to write to.
/// Whether or not writing to the parallel port is enabled.
public Logger(string logFile, bool parallelPortEnabled)
{
MyLogWriter = new LogWriter(logFile);
this.parallelPortEnabled = parallelPortEnabled;
}
///
/// Create a new logger for writing to the same file as the given logger.
///
/// The logger to copy the file to write to from.
public Logger(Logger logger)
{
#if DEBUG
if (!logger.initialized || logger.disposed)
{
throw new Exception("Logger can only be cloned from an active Logger.");
}
#endif
MyLogWriter = logger.MyLogWriter;
parallelPortEnabled = logger.parallelPortEnabled;
}
///
/// Create a new logger for writing to the same file as the given logger,
/// specifying parallelPortEnabled.
///
/// The logger to copy the file to write to from.
/// Whether or not writing to the parallel port is enabled.
public Logger(Logger logger, bool parallelPortEnabled)
{
#if DEBUG
if (!logger.initialized || logger.disposed)
{
throw new Exception("Logger can only be cloned from an active Logger.");
}
#endif
MyLogWriter = logger.MyLogWriter;
this.parallelPortEnabled = parallelPortEnabled;
}
#endregion
#region Destruction
///
/// A Logger should be disposed when it's finished with.
/// This finalizer will attempt cleanup, and complain if appropriate.
///
~Logger()
{
#if DEBUG
if (disposed || !initialized)
{
return;
}
#endif
Dispose();
#if DEBUG
Console.WriteLine("Logger was not disposed!");
MainGame.LogError("Logger was not disposed!");
throw new InvalidOperationException("Logger was not disposed!");
#endif
}
///
/// Dispose this Logger and any resources it's using, Automatically flushing
/// any pending writes.
///
public void Dispose()
{
if (!initialized || disposed)
{
return;
}
disposed = true;
MyLogWriter.Dispose();
if (parallelPortEnabled)
{
parallelPortWriter.Dispose();
}
GC.SuppressFinalize(this);
}
#endregion
#region Initialization
///
/// A Logger must be initialized before use.
///
public void Initialize()
{
if (initialized || disposed)
{
return;
}
initialized = true;
MyLogWriter.Initialize();
if (!parallelPortEnabled)
{
return;
}
parallelPortWriter = new ParallelPortWriter();
parallelPortWriter.Initialize();
}
#endregion
#region Write
///
/// Write an event code to the log, with no info text.
///
/// Numeric representation of the event code
public void Write(int eventCode)
{
#if DEBUG
if (!initialized || disposed)
{
throw new InvalidOperationException("Logger was not initialized, or was disposed.");
}
#endif
if (parallelPortEnabled && eventCode > 0 && (eventCode & 4294967040) == 0)
{
parallelPortWriter.Write((byte)eventCode);
}
MyLogWriter.Enqueue(new LogEvent(string.Empty, Stopwatch.GetTimestamp(), eventCode));
}
///
/// Write an event code to the log, with the given info text.
///
/// Numeric representation of the event code
/// Text to log.
public void Write(int eventCode, string text)
{
#if DEBUG
if (!initialized || disposed)
{
throw new InvalidOperationException("Logger was not initialized, or was disposed.");
}
#endif
if (parallelPortEnabled && eventCode > 0 && (eventCode & 4294967040) == 0)
{
parallelPortWriter.Write((byte)eventCode);
}
MyLogWriter.Enqueue(new LogEvent(text, Stopwatch.GetTimestamp(), eventCode));
}
///
/// Write a line of text to the log.
/// This function will automatically timestamp and flag the message.
/// This function will return immediately without waiting for the IO to finish.
///
/// Text to log.
public void Write(string text)
{
MyLogWriter.Enqueue(new LogEvent(text, Stopwatch.GetTimestamp()));
}
public void Write(int code, Type eType)
{
MyLogWriter.Enqueue(new LogEvent(Enum.GetName(eType, code), Stopwatch.GetTimestamp(), code));
}
public void Write(int code, Type eType, string text)
{
MyLogWriter.Enqueue(new LogEvent(Enum.GetName(eType, code) + " " + text, Stopwatch.GetTimestamp(), code));
}
internal void Write(InputEvent e)
{
MyLogWriter.Enqueue(new LogEvent(string.Empty, e.Timestamp, e.EventCode));
}
///
/// Write a line of text to the log.
/// This function will return immediately without waiting for the IO to finish.
///
/// The input event with the time stamp and event code information
/// The associated information for the logged event
internal void Write(InputEvent e, string info)
{
MyLogWriter.Enqueue(new LogEvent(info, e.Timestamp, e.EventCode));
}
#endregion
}
}