Click here to Skip to main content
Click here to Skip to main content

Logging Using the Composite Pattern

, 15 Jul 2006 CPOL
Rate this:
Please Sign up or sign in to vote.
Allow flexiable logging using the Composite design pattern.

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!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

mikeperetz
Web Developer
Canada Canada
I am currently working as a team leader with a group of amazing .NET programmers. I love coding with .NET, and I love to apply design patterns into my work. Lately I had some free time, so I decided to write some articles, hoping I will spare someone frustration and anxiety.

Comments and Discussions

 
QuestionThank you Pinmembersankargeet4-Sep-12 1:57 
NewsMore Pinmembermikeperetz16-Nov-07 1:43 
GeneralRe: More PinmemberRickyJiao7-Nov-11 15:57 
GeneralAnother method ... Pinmemberdavid_k1328-Aug-07 10:32 
GeneralNice easy-going article Pinmemberdavid_k1330-May-07 7:51 
GeneralHi Pinmemberzuboss_826-Aug-06 3:48 
GeneralNice example for composite pattern PinmemberColonelSender25-Jul-06 10:46 
GeneralI like it... PinmemberBoneSoft25-Jul-06 4:12 
GeneralCompare to built in logging Pinmemberbri189b18-Jul-06 15:11 
GeneralWell done Pinmemberjonavi18-Jul-06 9:25 
GeneralRe: Well done Pinmembermikeperetz18-Jul-06 10:20 
QuestionCombine with Provider? PinmemberMike Ellison18-Jul-06 6:46 
AnswerRe: Combine with Provider? Pinmembermikeperetz18-Jul-06 10:19 
I am very happy that you started to think this way. You should know that my goal writing the article is not to teach people how to log messages, but rather how to apply well known design pattern to solve every day programming problems. I believe you can use Composite to solve many problems. It is a powerful and yet simple pattern. Good luck implementing it.Big Grin | :-D
AnswerRe: Combine with Provider? Pinmembermikeperetz18-Jul-06 10:34 
QuestionWhy not log4net? PinmemberMikeP_AIS18-Jul-06 5:51 
AnswerRe: Why not log4net? Pinmembermikeperetz18-Jul-06 10:41 
GeneralRe: Why not log4net? Pinmemberbri189b18-Jul-06 15:10 
GeneralRe: Why not log4net? PinmemberMikeP_AIS19-Jul-06 11:02 
GeneralRe: Why not log4net? Pinmemberbri189b19-Jul-06 12:53 
GeneralRe: Why not log4net? PinmemberMikeP_AIS20-Jul-06 4:48 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.141030.1 | Last Updated 15 Jul 2006
Article Copyright 2006 by mikeperetz
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid