Building a Logging Object in .NET
Designing the Logging Object
Sample Abstract Log Class
The following code outlines a base class for the log objects with which our logger will interact.
using System;
namespace CodeGuru.ErrorLog.Logs
{
/// <remarks>
/// Abstract class to dictate the format for the logs that our
/// logger will use.
/// </remarks>
public abstract class Log
{
/// <value>Available message severities</value>
public enum MessageType
{
/// <value>Informational message</value>
Informational = 1,
/// <value>Failure audit message</value>
Failure = 2,
/// <value>Warning message</value>
Warning = 3,
/// <value>Error message</value>
Error = 4
}
public abstract void RecordMessage(Exception Message,
MessageType Severity);
public abstract void RecordMessage(string Message,
MessageType Severity);
}
}
Creating the Logging Object to Write to a File
Sample File Logging Class
using System;
using System.IO;
using System.Text;
namespace CodeGuru.ErrorLog.Logs
{
/// <remarks>
/// Log messages to a file location.
/// </remarks>
public class FileLog : Log
{
// Internal log file name value
private string _FileName = "";
/// <value>Get or set the log file name</value>
public string FileName
{
get { return this._FileName; }
set { this._FileName = value; }
}
// Internal log file location value
private string _FileLocation = "";
/// <value>Get or set the log file directory location</value>
public string FileLocation
{
get { return this._FileLocation; }
set
{
this._FileLocation = value;
// Verify a '\' exists on the end of the location
if( this._FileLocation.LastIndexOf("\\") !=
(this._FileLocation.Length - 1) )
{
this._FileLocation += "\\";
}
}
}
/// <summary>
/// Constructor
/// </summary>
public FileLog()
{
this.FileLocation = "C:\\";
this.FileName = "mylog.txt";
}
/// <summary>
/// Log an exception.
/// </summary>
/// <param name="Message">Exception to log. </param>
/// <param name="Severity">Error severity level. </param>
public override void RecordMessage(Exception Message,
Log.MessageType Severity)
{
this.RecordMessage(Message.Message, Severity);
}
/// <summary>
/// Log a message.
/// </summary>
/// <param name="Message">Message to log. </param>
/// <param name="Severity">Error severity level. </param>
public override void RecordMessage(string Message,
Log.MessageType Severity)
{
FileStream fileStream = null;
StreamWriter writer = null;
StringBuilder message = new StringBuilder();
try
{
fileStream = new FileStream(this._FileLocation +
this._FileName, FileMode.OpenOrCreate,
FileAccess.Write);
writer = new StreamWriter(fileStream);
// Set the file pointer to the end of the file
writer.BaseStream.Seek(0, SeekOrigin.End);
// Create the message
message.Append(System.DateTime.Now.ToString())
.Append(",").Append(Message);
// Force the write to the underlying file
writer.WriteLine(message.ToString());
writer.Flush();
}
finally
{
if( writer != null ) writer.Close();
}
}
}
}
Creating the Logging Object to Write to the Event Log
Sample Event Logging Class
using System;
using System.Diagnostics;
using System.Text;
namespace CodeGuru.ErrorLog.Logs
{
/// <remarks>
/// Log messages to the Windows Event Log.
/// </remarks>
public class EventLog : Log
{
// Internal EventLogName destination value
private string _EventLogName = "";
/// <value>Get or set the name of the destination log</value>
public string EventLogName
{
get { return this._EventLogName; }
set { this._EventLogName = value; }
}
// Internal EventLogSource value
private string _EventLogSource;
/// <value>Get or set the name of the source of entry</value>
public string EventLogSource
{
get { return this._EventLogSource; }
set { this._EventLogSource = value; }
}
// Internal MachineName value
private string _MachineName = "";
/// <value>Get or set the name of the computer</value>
public string MachineName
{
get { return this._MachineName; }
set { this._MachineName = value; }
}
/// <summary>
/// Constructor
/// </summary>
public EventLog()
{
this.MachineName = ".";
this.EventLogName = "MyEventLog";
this.EventLogSource = "MyApplication";
}
/// <summary>
/// Log an exception.
/// </summary>
/// <param name="Message">Exception to log.</param>
/// <param name="Severity">Error severity level.</param>
public override void RecordMessage(Exception Message,
Log.MessageType Severity)
{
this.RecordMessage(Message.Message, Severity);
}
/// <summary>
/// Log a message.
/// </summary>
/// <param name="Message">Message to log.</param>
/// <param name="Severity">Error severity level.</param>
public override void RecordMessage(string Message,
Log.MessageType Severity)
{
StringBuilder message = new StringBuilder();
System.Diagnostics.EventLog eventLog =
new System.Diagnostics.EventLog();
// Create the source if it does not already exist
if( !System.Diagnostics.EventLog.SourceExists(
this._EventLogSource) )
{
System.Diagnostics.EventLog.CreateEventSource(
this._EventLogSource, this._EventLogName);
}
eventLog.Source = this._EventLogSource;
eventLog.MachineName = this._MachineName;
// Determine what the EventLogEventType should be
// based on the LogSeverity passed in
EventLogEntryType type = EventLogEntryType.Information;
switch(Severity.ToString().ToUpper())
{
case "INFORMATIONAL":
type = EventLogEntryType.Information;
break;
case "FAILURE":
type = EventLogEntryType.FailureAudit;
break;
case "WARNING":
type = EventLogEntryType.Warning;
break;
case "ERROR":
type = EventLogEntryType.Error;
break;
}
message.Append(Severity.ToString()).Append(",").Append(
System.DateTime.Now).Append(",").Append(Message);
eventLog.WriteEntry(message.ToString(), type);
}
}
}
Building the Logger
Sample Logging Class
using System;
namespace CodeGuru.ErrorLog
{
/// <remarks>
/// Managing class to provide the interface for and control
/// application logging. It utilizes the logging objects in
/// ErrorLog.Logs to perform the actual logging as configured.
/// </remarks>
public class Logger
{
/// <value>Available log types.</value>
public enum LogTypes
{
/// <value>Log to the event log.</value>
Event = 1,
/// <value>Log to a file location.</value>
File = 2
}
// Internal logging object
private Logs.Log _Logger;
// Internal log type
private LogTypes _LogType;
/// <value></value>
public LogTypes LogType
{
get { return this._LogType; }
set
{
// Set the Logger to the appropriate log when
// the type changes.
switch( value )
{
case LogTypes.Event:
this._Logger = new Logs.EventLog();
break;
default:
this._Logger = new Logs.FileLog();
break;
}
}
}
/// <summary>
/// Constructor
/// </summary>
public Logger()
{
this.LogType = LogTypes.File;
}
/// <summary>
/// Log an exception.
/// </summary>
/// <param name="Message">Exception to log.</param>
/// <param name="Severity">Error severity level.</param>
public void RecordMessage(Exception Message,
Logs.Log.MessageType Severity)
{
this._Logger.RecordMessage(Message, Severity);
}
/// Log a message.
/// </summary>
/// <param name="Message">Message to log.</param>
/// <param name="Severity">Error severity level.</param>
public void RecordMessage(string Message,
Logs.Log.MessageType Severity)
{
this._Logger.RecordMessage(Message, Severity);
}
}
}
Using the Logger
Sample Logger Usage
Logger logger = new Logger();
// Log to a file (default settings)
logger.RecordMessage("Testing", Logs.Log.MessageType.Error);
logger.RecordMessage(new Exception("My test exception"),
Logs.Log.MessageType.Error);
// Log to the event log
logger.LogType = Logger.LogTypes.Event;
logger.RecordMessage("Testing", Logs.Log.MessageType.Error);
logger.RecordMessage(new Exception("My test exception"),
Logs.Log.MessageType.Error);
Possible Enhancements
- Create additional log objects to log to locations such as a database or send an email to a desired address.
- Setup the logger to have a backup logging location in the event that the primary location fails.
- Setup the logger to log to multiple locations simultaneously depending upon the severity of the error. This would allow you the capability to log informational types of messages to a place other than the event log.
- Setup the logger to read the logging configuration from a configuration file of some sort. Have the logger configure the appropriate logging location based on the contents of the configuration instead of the current hard coding.
No comments:
Post a Comment