Logging
There’s really nothing much to say about logging. I just thought, I’d share some code that might spare you some time writing it yourself. Basically a Log class. Create an instance for each separate Log type (e.g. Game, Core, Editor, …) and assign n ILogOutput instances to each of them. There are ILogOutput classes for System.Console or a TextWriter in general (any Stream).
The Log class comes with some static properties holding different Logs I set up. They all write only to the Console and use a different background color and log prefix. PushIndent and PopIndent is intended to be used for logging across the a function call hierarchy. Just call PushIndent before calling a function and PopIndent afterwards if you want its log entries to be indented. There are also some utility methods to write a specific log only once or only once in a specified time period. You will need to adjust out the WriteTimed methods as I’m using my engines timer for them. Replace it with some global clock and you’re fine – or just comment them out for a quick test.
Anyway, here is the code:
From Log.cs:
public class Log
{
private static Log logGame = new Log(new ConsoleLogOutput("[Game] ", ConsoleColor.DarkGray));
private static Log logCore = new Log(new ConsoleLogOutput("[Core] ", ConsoleColor.DarkBlue));
private static Log logEditor = new Log(new ConsoleLogOutput("[Editor] ", ConsoleColor.DarkMagenta));
public static Log Game
{
get { return logGame; }
}
public static Log Core
{
get { return logCore; }
}
public static Log Editor
{
get { return logEditor; }
}
private HashSet<string> onceWritten = new HashSet<string>();
private Dictionary<string,float> timedLast = new Dictionary<string,float>();
private List<ILogOutput> strOut = null;
private int indent = 0;
public Log(params ILogOutput[] output)
{
this.strOut = new List<ILogOutput>(output);
}
public void RegisterOutput(ILogOutput writer)
{
this.strOut.Add(writer);
}
public void UnregisterOutput(ILogOutput writer)
{
this.strOut.Remove(writer);
}
public void Reset()
{
this.onceWritten.Clear();
this.timedLast.Clear();
}
public void PushIndent()
{
this.indent++;
}
public void PopIndent()
{
this.indent--;
}
private void Write(LogMessageType type, string msg)
{
foreach (ILogOutput log in this.strOut)
{
log.Write(type, msg, this.indent);
}
}
public void Write(string format, params object[] obj)
{
this.Write(LogMessageType.Message, String.Format(format, obj));
}
public void WriteWarning(string format, params object[] obj)
{
this.Write(LogMessageType.Warning, String.Format(format, obj));
}
public void WriteError(string format, params object[] obj)
{
this.Write(LogMessageType.Error, String.Format(format, obj));
}
public void WriteOnce(string format, params object[] obj)
{
string t = String.Format(format, obj);
if (!this.onceWritten.Contains(t))
{
this.onceWritten.Add(t);
this.Write(LogMessageType.Message, t);
}
}
public void WriteWarningOnce(string format, params object[] obj)
{
string t = String.Format(format, obj);
if (!this.onceWritten.Contains(t))
{
this.onceWritten.Add(t);
this.Write(LogMessageType.Warning, t);
}
}
public void WriteErrorOnce(string format, params object[] obj)
{
string t = String.Format(format, obj);
if (!this.onceWritten.Contains(t))
{
this.onceWritten.Add(t);
this.Write(LogMessageType.Error, t);
}
}
public void WriteTimed(int delayMs, string timerId, string format, params object[] obj)
{
float last;
if (!this.timedLast.TryGetValue(timerId, out last) || Time.MainTimer - last > delayMs)
{
this.timedLast[timerId] = Time.MainTimer;
this.Write(LogMessageType.Message, String.Format(format, obj));
}
}
public void WriteWarningTimed(int delayMs, string timerId, string format, params object[] obj)
{
float last;
if (!this.timedLast.TryGetValue(timerId, out last) || Time.MainTimer - last > delayMs)
{
this.timedLast[timerId] = Time.MainTimer;
this.Write(LogMessageType.Warning, String.Format(format, obj));
}
}
public void WriteErrorTimed(int delayMs, string timerId, string format, params object[] obj)
{
float last;
if (!this.timedLast.TryGetValue(timerId, out last) || Time.MainTimer - last > delayMs)
{
this.timedLast[timerId] = Time.MainTimer;
this.Write(LogMessageType.Error, String.Format(format, obj));
}
}
}
public enum LogMessageType
{
Message,
Warning,
Error
}
public interface ILogOutput
{
void Write(LogMessageType type, string msg, int indent);
}
From TextWriterLogOutput.cs:
public class TextWriterLogOutput : ILogOutput
{
private string prefix = null;
private TextWriter writer = null;
public TextWriterLogOutput(TextWriter writer, string prefix = null)
{
this.writer = writer;
this.prefix = prefix;
}
public virtual void Write(LogMessageType type, string msg, int indent)
{
string[] lines = msg.Split(new char[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < lines.Length; i++)
{
if (i == 0)
{
switch (type)
{
case LogMessageType.Message:
lines[i] = this.prefix + "Info: " + new string(' ', indent * 4) + lines[i];
break;
case LogMessageType.Warning:
lines[i] = this.prefix + "Warning: " + new string(' ', indent * 4) + lines[i];
break;
case LogMessageType.Error:
lines[i] = this.prefix + "ERROR: " + new string(' ', indent * 4) + lines[i];
break;
}
}
else
{
lines[i] = this.prefix + " " + new string(' ', indent * 4) + lines[i];
}
writer.WriteLine(lines[i]);
}
}
}
From ConsoleLogOutput.cs:
public class ConsoleLogOutput : TextWriterLogOutput, ILogOutput
{
private ConsoleColor bgColor;
public ConsoleLogOutput(string prefix = null, ConsoleColor bgColor = ConsoleColor.Black) : base(Console.Out, prefix)
{
this.bgColor = bgColor;
}
public override void Write(LogMessageType type, string msg, int indent)
{
ConsoleColor clrBg = Console.BackgroundColor;
ConsoleColor clrFg = Console.ForegroundColor;
Console.BackgroundColor = this.bgColor;
if (type == LogMessageType.Warning) Console.ForegroundColor = ConsoleColor.Yellow;
else if (type == LogMessageType.Error) Console.ForegroundColor = ConsoleColor.Red;
else Console.ForegroundColor = ConsoleColor.Gray;
base.Write(type, msg, indent);
Console.ForegroundColor = clrFg;
Console.BackgroundColor = clrBg;
}
}