Introduction
In this article, I will explain how I solved a common problem I used to have regarding logging within my business layer. Normally, I would have a function call from my presentation layer to my business layer; however, once in the business layer, I had no access to the presentation layer anymore. There are many ways to solve this problem. In the MFC days, a Doc / View approach based on the Observer pattern was used. In .NET, we can use custom events to notify the presentation layer that something happened. But, in this article, I will show the Composite pattern in action, and some of its nice advantages.
The Requirements
Suppose we had a function in our business layer that performs a lot of updates. It would be nice that during all these updates, we log the exact action taking place. We want to easily log a string describing the activity to one of, all of, or some of the following loggers: TextBox
, ListBox
, text file and/or the EventLog.
Creating the ILogger Interface
To keep the example simple, I will implement a simple ILogger
interface that only requires a logger class to implement its own way of logging an activity.
public interface ILogger
{
void LogMessage(string strMessage);
}
Creating Logger Classes
Create the following logger classes:
- Text box logger - logs a message into a text box
- List box logger - adds a message to the list
- File logger - logs a message into a file
- EventLog logger - logs a message into the system event log
ListBox Logger
Notice that the ListBox
and TextBox
loggers are thread safe, by using the InvokeRequired
method.
class ListBoxLogger : ILogger
{
ListBox m_listBox;
public ListBoxLogger(ListBox listBox)
{
m_listBox = listBox;
}
public void LogMessage(string strMessage)
{
MethodInvoker logDelegate = delegate
{
m_listBox.Items.Add(strMessage);
};
if (m_listBox.InvokeRequired)
m_listBox.Invoke(logDelegate);
else
logDelegate();
}
}
TextBox Logger
class TextBoxLogger : ILogger
{
private TextBox m_textBox;
public TextBoxLogger(TextBox txtBox)
{
m_textBox = txtBox;
}
public void LogMessage(string strLogMessage)
{
MethodInvoker logDelegate = delegate { m_textBox.Text = strLogMessage; };
if (m_textBox.InvokeRequired)
m_textBox.Invoke(logDelegate);
else
logDelegate();
}
}
File Logger
class FileLogger : ILogger
{
private string m_strFileName;
private object m_sync = new object();
public FileLogger(string strFileName)
{
m_strFileName = strFileName;
}
public void LogMessage(string strMessage)
{
lock (m_sync)
{
using (StreamWriter writer = new StreamWriter(m_strFileName))
{
writer.WriteLine(strMessage);
}
}
}
}
Notice that the FileLogger
is thread-safe, and that it opens and closes the file before writing to it. This guarantees that the message is flushed after each call to LogMessage
.
EventLog Logger
class EventLogger : ILogger
{
public EventLogger()
{
}
public void LogMessage(string strMessage)
{
EventLog.WriteEntry("Logger", strMessage);
}
}
Using the ILogger Object
So far, there is nothing special here; we can have a function that takes a ILogger
object and allow us to log messages. For example:
private void DoSomthing(ILogger logger)
{
for(int i=0; i < 10; i++)
{
logger.LogMessage("Logging a message " + i.ToString());
}
}
The client code looks like this:
// pass the File Logger
DoSomthing(new FileLogger("C://LogMessage.txt"));
// txtBox is a text box control on the form
DoSomthing(new TextBoxLogger(textBox));
Introducing the CompositeLogger
But what if I want to log to all my loggers at the same time, or what if I want to log to only some of my loggers? To solve this requirement, I create a new logger called a Composite Logger.
// Logger of composite of loggers
class CompositeLogger : ILogger
{
private ILogger[] m_loggerArray;
// pass a ILoggers that are part of this composite logger
public CompositeLogger(params ILogger[] loggers)
{
m_loggerArray = loggers;
}
public void LogMessage(string strMessage)
{
// loop around all the loggers, and log the message.
foreach (ILogger logger in m_loggerArray)
logger.LogMessage(strMessage);
}
}
Let's Use Our Composite Logger to "Configure" Which Loggers to Use
Using all the loggers:
CompositeLogger compositeLogger =
new CompositeLogger(new TextBoxLogger(textBox),
new ListBoxLogger(listBox),
new FileLogger("C:\\LogPattern.txt"),
new EventLogger() );
Creating a composite logger with only TextBoxLogger
and ListBoxLogger
:
CompositeLogger compositeLogger =
new CompositeLogger(new TextBoxLogger(textBox),
new ListBoxLogger(listBox));
Because our composite logger implements ILogger
like all the other loggers, we can pass it to a function that expects an ILogger
object type.
DoSomthing(compositeLogger);
Testing the Composite Logger in a Thread
Let’s test our composite logger in a separate thread, just to make sure we can update the UI from a thread other than the UI-thread.
// create a composite logger with all the loggers
CompositeLogger compositeLogger =
new CompositeLogger(
new TextBoxLogger(textBox),
new ListBoxLogger(listBox),
new FileLogger("C:\\LogPattern.txt"),
new EventLogger());
// create a anonymous function to call DoSomthing
ParameterizedThreadStart threadDelegate = delegate(object obj)
{
ILogger logger = (ILogger)obj;
DoSomthing(logger);
};
// Do Somthing in a thread
Thread t = new Thread(threadDelegate);
t.Start(compositeLogger);
Conclusion
Notice that I am now able to pass an ILogger
object to a method within the business layer or to the data layer, and provide an easy way to log a message. This is done by using the ILogger
interface, and therefore, your business layer or data layer requires no extra references to File.Io
, or Windows.Forms
, it only needs to have a reference to ILogger
. It is also nice that you can easily add and remove loggers by using your composite Logger. Thank you for reading, have a good day!
2 comments:
Good information,
but your Download link seems to be broken on CodePorject...
Thanks
Jim
Who knows where to download XRumer 5.0 Palladium?
Help, please. All recommend this program to effectively advertise on the Internet, this is the best program!
Post a Comment