Even though most people don't realize, a bad Log component can be the cause of so much performance "pain" in an application.
Thinking a little bit about it, it is really easy to understand why...
In sum, a Log component is a component that repeatedly performs I/O operations into the file system every single time something is meant to be logged. Also, it is quite common that it'll be called from multiple components, at different layer depths and also from different threads. This makes the log component very much a prime candidate to be the source of quite a few issues.
Here's a take on a possible solution for it.
public static class Log
{
public enum Level
{
Error = 0,
FailureAudit = 1,
Warning = 2,
Information = 3,
SuccessAudit = 4,
Debug = 5
}
private static Level level;
private static Queue events;
private static Timer logWriterTimer;
private const string LogLineTemplate = "{dateTime} - {logLevel} - {message}";
private static string logFilePathValue;
public static bool Initialized
{
get
{
return events != null;
}
}
public static void Initialize(string logFilePath, Level logLevel, int writeDelayInMilliseconds)
{
logFilePathValue = logFilePath;
level = logLevel;
events = new Queue();
WriteToLog(Level.Information, "Starting the Log component.");
logWriterTimer = new Timer(WriteToFile, null, 0, writeDelayInMilliseconds);
}
public static void Dispose()
{
if (events == null)
throw new Exception("You must first Initialize the Log component.");
logWriterTimer.Dispose();
WriteToLog(Level.Information, "Stopped the Log component.");
WriteToFile(null);
}
private static void WriteToFile(object sender)
{
var logPortion = new StringBuilder();
lock (events)
{
while (events.Count > 0)
{
var currentEvent = events.Dequeue();
logPortion.Append(currentEvent.Message + Environment.NewLine);
}
}
try
{
if (logPortion.Length == 0)
return;
using (var writer = new StreamWriter(logFilePathValue.ReplaceDateTimeMarkers(DateTime.Now), true, Encoding.UTF8))
{
writer.WriteLine(logPortion.ToString());
}
}
catch (Exception exception)
{
throw new Exception("An error has occured while trying to write to the log file. See the inner exception for more detail.", exception);
}
}
public static void WriteToLog(Level logLevel, string message)
{
if (logLevel < level)
return;
var eventDateTime = DateTime.Now;
var messageToLog = LogLineTemplate.Replace("{message}", message, true);
messageToLog = messageToLog.Replace("{logLevel}", logLevel.ToString(), true);
messageToLog = messageToLog.Replace("{datetime}", eventDateTime.ToString(), true);
if (events == null)
{
Trace.WriteLine(messageToLog);
return;
}
var logEvent = new LogEvent { EventDateTime = eventDateTime, Message = messageToLog };
lock (events)
{
events.Enqueue(logEvent);
}
}
public static void WriteToLog(Exception exception)
{
if (events == null)
throw new Exception("You must first Initialize the Log component.");
var message = exception.Message;
if (level == Level.Debug)
{
message = message + "\r\nSource:" + exception.Source + "\r\nStackTrace:" + exception.StackTrace;
}
WriteToLog(Level.Error, message);
}
public static void WriteToLog(string message)
{
WriteToLog(Level.Debug, message);
}
public static Level GetLogLevelFromString(string logLevel)
{
switch (logLevel.ToUpper())
{
case "DEBUG":
return Level.Debug;
case "ERROR":
return Level.Error;
case "FAILUREAUDIT":
return Level.FailureAudit;
case "INFORMATION":
return Level.Information;
case "SUCCESSAUDIT":
return Level.SuccessAudit;
case "WARNING":
return Level.Warning;
}
return Level.Error;
}
}
public class LogEvent
{
public DateTime EventDateTime { get; set; }
public string Message { get; set; }
}
internal static class StringExtensions
{
public static string Replace(this string text, string searchText, string replaceText, bool ignoreCase)
{
if (!ignoreCase)
{
return text.Replace(searchText, replaceText);
}
var result = string.Empty;
searchText = searchText.ToUpper();
var temp = text.ToUpper();
var position = temp.IndexOf(searchText);
while (position >= 0)
{
result = result + text.Substring(0, position) + replaceText;
text = text.Substring(position + searchText.Length);
temp = temp.Substring(position + searchText.Length);
position = temp.IndexOf(searchText);
}
return result + text;
}
public static string ReplaceDateTimeMarkers(this string text, DateTime dateTime)
{
var result = text;
result = result.Replace("{year}", dateTime.Year.ToString(), true);
result = result.Replace("{month}", dateTime.Month.ToString(), true);
result = result.Replace("{day}", dateTime.Day.ToString(), true);
result = result.Replace("{hour}", dateTime.Hour.ToString(), true);
result = result.Replace("{minute}", dateTime.Minute.ToString(), true);
result = result.Replace("{second}", dateTime.Second.ToString(), true);
return result;
}
}
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.