Implementing a logger in CSharp
This tutorial explains how to implement a simple logger in C#.
Logging Need
Have you ever ran into a situation where you want to log messages in order to debug a program. There are many logging packages/framework available but there always a learning curve involved with setup and usage. So this simplified yet featurefull logger is all you might need.
Logging Explained
Logging could be described as an simple process of writing(appending) a message (a string) to a file.
As the message it written to the file, other valueable piece of information could captured such as
- timestamp of recording,
- the thread/request that wrote it,
- the class/component that wrote it etc.
Similarly the logging message can be categorized into serverity levels.
Using levels help us to filter messages as needed.
The following log levels have been implemented.
If needed following the example, other loggging levels like trace, warn, fatal etc. can be implemented.
public const string LOG_LEVEL_DEBUG = "debug";
public const string LOG_LEVEL_INFO = "info";
public const string LOG_LEVEL_ERROR = "error";
public const string LOG_LEVEL_NONE = "none";
Each level has a logging priority order.
The priorty of logging can is in the following order
public enum LogLevel
{
NONE = 0,
DEBUG = 1,
INFO = 2,
ERROR = 3
}
From an API perpective the logger API provides the following methods.
// to get the handle for the logger instance for a given class
public static Logger getInstance(string className)
public void debug(string msg)
public void info(string msg)
public void error(string msg)
Components
The logger implementation has 2 components.
1- Logger - provides the API interface
2- LogWriter - Pushes those messages to the file

The logger is implemented as singleton for each class/component name.
Internally it maintins a Hastable to store this logger instance for each class.
To create a logger and refer it in the target class the following method is used.
public static Logger getInstance(string className)
The log writer is implemented as a singleton. It basically provides 3 functions
- openLog (opens the log file in append mode onces)
private void openLog()
{
try
{
_writer = File.AppendText(_sLogFile);
_writer.AutoFlush = true;
_bIsOpen = true;
}
catch
{
_bIsOpen = false;
throw new Exception("Failed to initialize the log File...");
}
}
- close (closes the log file )
private void closeLog()
{
if (_bIsOpen)
{
try
{
_writer.Close();
}
catch
{
Console.WriteLine("Failed to close the Log...");
}
}
}
- writeLog (writes the formatted message to log file)
private void writeLog(string msg)
{
if (_bIsOpen)
{
try
{
_writer.WriteLine(msg);
}
catch
{
Console.WriteLine("Error : Failed to write the log messages...");
}
}
else
{
Console.WriteLine("Error : Log is not open...");
}
}
- log (filter logging based on the configured level)
The main logic that does the formatting of the message, determination of whether to log or not log the message etc. is done in the log writer as:
public void log(LogLevel logLevel, string msg, string className)
{
// log is disabled as log level is none
if (_nLogLevel == LogLevel.NONE)
return;
// if the current log level is less than defined log level
if (logLevel < _nLogLevel)
return;
string sLog = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ");
switch (logLevel)
{
case LogLevel.DEBUG:
sLog += "[DEBUG]";
break;
case LogLevel.INFO:
sLog += "[INFO]";
break;
case LogLevel.ERROR:
sLog += "[ERROR]";
break;
}
sLog += "[" + className + "]:";
sLog += msg;
if (isConsoleLogEnabled())
Console.WriteLine(sLog);
// write to the log
writeLog(sLog);
}
Logger Configuration
Basically the logger needs 2 pieces of information to configure.
1. Log file name
2. Level of log
The singelton instance of log writer can be accessed using
public static LogWriter configure(string logfile, LogLevel logLevel)
The configure method needs to be called once per application startup before any other logging takes place.
Console Logging
The logger also supports console logging. Using the preprocessor directive console logging is enabled in DEBUG mode and is off in Release mode. This can be turned on/off programatically.
#if (DEBUG)
private LogLevel _nLogLevel = LogLevel.DEBUG;
private bool _bIsConsoleLogEnabled = true;
#else
private LogLevel _nLogLevel = LogLevel.NONE;
private bool _bIsConsoleLogEnabled = false;
#endif
Sample logs
The following is the sample of the logs generated by the logger.
2008-10-13 00:09:59 [DEBUG][FileSizeRange]:in : getRangeBySize(int size)
2008-10-13 00:09:59 [DEBUG][GraphGenerator]:in : createPieChart(....)
2008-10-13 00:09:59 [DEBUG][DirectoryAnalyzerRenderer]:in : renderFileSizeRangeGraph()
2008-10-13 00:09:59 [DEBUG][DirectoryAnalyzerRenderer]:Values: System.Double[]
2008-10-13 00:09:59 [DEBUG][DirectoryAnalyzerRenderer]:Values: 12
2008-10-13 00:09:59 [DEBUG][FileSizeRange]:in : getRangeNames()
2008-10-13 00:09:59 [DEBUG][GraphGenerator]:in : createHorizontalBarChart(....)
2008-10-13 00:33:13 [DEBUG][FormDeskee]:Disposing Component...
2008-10-13 00:33:13 [DEBUG][FormDeskee]:Calling Application Exit..
2008-10-13 00:33:14 [DEBUG][FormDeskee]:Calling Application Exit..
2008-10-13 00:34:21 [DEBUG][Config]:display.popup=true
Extension
This logger can be extended to include some other feautres like file rollover,different types of appenders etc. The logger class can be distributed as it or can be packaged into a library like dll and distributed for reuse.
Download
The logger classes can be download as the part of this tutorial. These classes are part of the gems project.
| Attachment | Size |
|---|---|
| gems.zip | 47.21 KB |













Comments
Post new comment