Chances are you have come across a need within one of your applications to log events. This may stem from the need to track long running jobs or to log debugging type information. Whatever the case maybe, this is a fairly common need within an application.
Well, I too have had this need so I built a small event management/logging component that could easily be tailored to fulfill various needs related to event management and logging.
In this article I explain the event management component I created. My event management component can either do or exposes the hooks to do:
- Log events asynchronously to various types of event logs.
- Tracking long running processes or debugging type information.
- Log events to either a file, database or some in memory object implemented within the application.
- Log events to different event logs. For example, security events can get logged to a file, while other informational type events can get logged directly into something like the Windows application event log.
- Turn event logging on and off. Moreover, fine tune the way events are logged. For example, during development, only debug type events are logged but during deployment they are not.
- “Listen in” on events being raised by an application.
- Customize the type of events logged within each event log. For example one event log may only care about security events while another event log may care about both debugging and security events.
- Filter event logs by the types of events logged. For example, filter on all error events.
If you need any of this functionality, you may be able to use my component or at the very least use some of the concepts I present here.
So then, how does this whole event management component work? Prior to getting into the details let me give you a brief overview.
Within an application I have the concept of a global event manager. The application is responsible for informing the global event manager any time a notable event occurs. In short, the application kind of “throws” events and lets the event manager handle the rest. This is accomplished through a simple static method called
HandleEvent(). A parameter in the method
HandleEvent is an interface called
IEvent. Within an application you can implement your own version(s) of
IEvent. This interface defines information such as where an event occurred, the name of the event, the time of the event, any associated message etc. The thinking was an application can have numerous custom implementations of
IEvent, each of which can be raised whenever and wherever applicable.
The event manager is initially loaded with some default subscribers defined within the app.config file. The event manager will notify any subscribers when an event has been raised by the application. Each subscriber then in turn determines how and where the events are logged. Each subscriber can be configured to handle all events or some specific set of events. Likewise, a subscriber can be configured to be either on or off in the sense it is doing some logging. Again, the app.config is the place where these settings are specified.
Last, the event manager exposes methods
RemoveSubscriber. These methods allow for adding and removing additional subscribers at runtime. This works great in cases like long running jobs. For example, during a long running job, methods can raise events at specific check points allowing for any registered subscriber to receive these notifications and relay pertinent status information to an end user.
Noteworthy at this point is the fact that the event management component is based on the publisher-subscriber pattern (design patterns is another large topic way outside the scope of this article).
Prior to getting into the finer details of each class within the component, let me mention a few key concepts used within the context of the component.
Reflection is used at application startup time in order to load the correct instance of the event manager along with the default subscribers defined within the app.config. The app.config defines the type of the classes for the
IEvent(s) (see below).
Threads are used to invoke long running jobs asynchronously in the sample application. More importantly, threads are used to notify subscribers when an event has been raised through the event manager.
A delegate is a special type of managed class that allows you to work with type-safe function pointers. Each delegate type is based on a single method signature. In the event management component, each event “logger” or subscriber class implements a specific function called
OnReceiveEvent. This method is called by the event manager for each registered subscriber every time an event is “thrown” to the event manager.
The Factory pattern is used at startup time along with reflection in order to create the correct instance of the event manager being implemented within the application. The
EventManagerSettingsHandler is responsible for loading the event manager with all the correct cached objects.
Finally, let me also mention that a fully functional sample is available for download in which you can explore all the gory details at your “leisure”.
OK, now that we have defined the event management component and the key concepts used, let’s describe the major classes/interfaces and the role they play. After which time, we will get into what we all know and love, the .NET code.
Classes and Interfaces
This is the interface that defines the global event manager that will receive all event notifications from the application. The implementation of this interface is the class responsible for receiving events raised by the application. Likewise it is responsible for notifying any registered subscribers.
This is the interface that defines a subscriber to the event manager. I use the word subscriber and logger interchangeably because they are really one and the same. From one perspective, an implementation of
IEventLogger subscribes to events raised to the event manager, while from another, they handle the physical logging of the events. Each implementation of
IEventLogger will define how it should handle the events received from the
IEventManager. For example, one logger may write to a file while another may write to the application event log.
This is the interface that defines the events raised within an application. A user can choose to implement their application specific versions of
This is the class responsible for reading the application configuration file and instantiating the corresponding class type of
IEventLogger(s) respectively. In the sample code, the actual implementation of
IEventManager is the class
ApplicationEventManager. Note, the scope of this article does not cover reading custom sections in the app.config so suffice to say
EventManagementSettingsHandler contains all the plumbing that makes it possible to read the section
<EventManagment> in the app.config. There are some other helper classes involved but I don’t cover them in this article.
This is the class that contains a static method called
getEventManager. This method provides access to the event manager created by the
EvenManagementSettingsHandler. Note, once the event manager is created it is cached within the factory. As a result, the application always calls
getEventManger to get a handle on the application event manager.
With all this background covered, let’s now roll up our sleeves and look at the code. The first item we need to examine is a node from the app.config.
<daEventLogger name="ApplicationLogger" mode="on"
<daEvent name="ApplicationEvents" mode="on"
As I stated earlier, the
EventManagementSettingsHandler is responsible for reading this section of the app.config in order to create the global instance of the event manager. In the above node, the attribute type of the
<daEventManager> tag defines
.Common.Framework.EventManagement.ApplicationEventManager. This is the class type of the event manager that will be created for the application. Once this is created, the next step is to create all the subscribers. For each
<daEventLogger> under the
<daEventManager> tag, the corresponding subscriber will be created and registered with the
ApplicationEventManager. Note, the first
<daEventLogger> defines the type
CSI.Common.Framework.EventManagement.MessageQueueLogger. As a result, our event manager will have at least one subscriber of type
MessageQueueLogger. The last notable item about the node is each subscriber will handle all the events defined within the
<daEvent> tags. If no
<daEvents> tags are defined, then that corresponding subscriber/logger will handle all the events.
The code to make all this happen is in the class
EventManagerFactory.GetEventManger. In particular, note the section below:
If _eventManager Is Nothing Then
myManagerSettings = _
CType(ConfigurationSettings.GetConfig("EventManagement" & _
myEventManager = _
If myManagerSettings.daEventLogger Is Nothing = False Then
For Each myLoggerSettings InmyManagerSettings.daEventLogger
If myLoggerSettings.mode = "on" Then
myLogger = _
If myLoggerSettings.daEvent Is Nothing = False Then
For Each myEventSettings In myLoggerSettings.daEvent
If myEventSettings.mode = "on" Then
myEvent = _
_eventManager = myEventManager
Now that we have our event manager loaded, let’s examine how we raise events and what happens when an event is raised. In order to raise an event, we would invoke something like the following in our application:
CSI.Common.Framework.EventManagement.ApplicationEvent("Start LongEvent", _
Me, "Start Long Event", _
This code will create a new application event and it will be sent to our event manager. What the event manager does next is broadcast this event to any subscribers listening.
Let’s examine the code for two important methods within the
ApplicationEvent and then describe what they do.
Public Sub HandleEvent(ByRef anEvent As IEvent, _
ByRef source As Object) Implements _
Public Sub OnReceiveEvent(ByRef sender As Object, ByRef evt AsEventArgs)
Dim targets() As System.Delegate
targets = Me.EventReceivedEvent.GetInvocationList()
Catch ex As Exception
If targets.Length > 0 Then
Dim listener As _
Dim ar As IAsyncResult
For Each listener Intargets
ar =listener.BeginInvoke(sender, _
CType(evt,System.EventArgs), Nothing, Nothing)
As we can see,
OnReceiveEvent does is iterate through all the delegate references to the event
EventReceived declared in
IEventManager and calls the corresponding function
OnReceiveEvent within each one of the registered subscribers. Remember, a delegate is a special type of managed class that allows you to work with type-safe function pointers. We registered all these pointers to
OnReceiveEvent in our subscriber objects through the method
Public Sub AddSubscriber(ByRef aSubscriber As IEventLogger)_
If Me._subscribers.Contains(aSubscriber) Then
AddHandler Me.EventReceived, _
Catch ex As Exception
One thing worth mentioning is the
SyncLock in the
OnReceiveEvent method. What this does is lock the event manager so that we are not caught in the middle of getting all the registered subscribers while some other portion of the application is adding another subscriber. This way we will be sure all subscribers at the time an event is raised gets notified.
The last idea to discuss is what happens when our event manager calls the
OnReceiveEvent function in a subscriber object. The cool thing is this is really up to each individual implementation. If we look at class
MessageQueueLogger, one default implementation in my sample, we see:
Public Overrides Sub OnReceiveEvent(ByRef sender _
As Object, ByRef evt As System.EventArgs)
If Me._handledEvents.Count = 0 Then
If Me._handledEvents.ContainsValue(evt.GetType) Then
Catch ex As Exception
Within this specific implementation, we first check if the subscriber is set up to handle the event raised. If it is, then we put the event in our message queue. Other implementations may take this event and put it right in the Windows event log.
At this point if you feel this event management component could be used / tailored for your application then I think the best thing to do is download and step through the sample source code. The sample code is stripped down for example purposes. I recommend following
Sub Button1_Click within form
GenericForm. This form was set up to be a subscriber to the event manager. It will also show how to add a subscriber, handle events, and remove subscribers from the event manager.
That’s it. Note the sample code is scaled down for the purposes of this article but it is fully functional. Good luck and if you have any further questions feel free to contact me at email@example.com.