The code download has been updated for the final release of the Enterprise Library, including design support for the EntLibConfig application.
Introduction
A group of programmers, far smarter than I, have been laboring from some time to create a library of utilities to help developers like myself to create better applications. The fruits of that labor is the Enterprise Library from the Patterns & Practices group at Microsoft. The Enterprise Library encompasses Exception Handling, Logging, Caching, Security, Cryptography, and Database Access. The latest incarnation of the Enterprise Library for the .NET 2.0 Framework also includes an ObjectBuilder
which allows object creation with dependency injection. I'd be lying to you if I told you I understand exactly what the ObjectBuilder
does. Brian Button has released the most complete information I have found so far on the ObjectBuilder
. Documentation is scarce at this point since the Enterprise Library has not been officially released. The code covered in this article is based on the December Community Technology Preview of the new Enterprise Library for the .NET Framework 2.0.
The focus of this article is the Logging bits from the Enterprise Library. For an overview of the Logging framework, check this MSDN documentation. One of the functions of the Logging framework is to send log messages to one or more listeners like a flat file, an email message, a database, or a Microsoft Message Queue. The Logging framework was constructed in such a way to allow developers like myself to create custom listeners for log messages. If you haven't already guessed, I made a custom log listener that sends log messages to an MSN Messenger account.
The good stuff
I used the FormattedDatabaseTraceListener
as a reference for creating my listener. The custom listener is actually composed of three classes:
- The actual listener class, responsible for sending log messages as MSN Messenger messages.
- A data configuration class, responsible for reading configuration data from a CONFIG file (app.config/web.config).
- An assembler class, responsible for creating a listener object based on a configuration class (I think this is the
ObjectBuilder
magic).
Our listener class is the FormattedMsnMessengerTraceListener
which I will cover in depth. FormattedMsnMessengerTraceListenerData
and FormattedMsnMessengerTraceListenerAssembler
handle the configuration and assembly of the custom listener. These two classes are largely self-explanatory but if you have any questions, please post them and I will answer as best as I can.
FormattedMsnMessengerTraceListener
To send MSN Messenger messages, I searched the internet for existing solutions, and found a sweet library created by XIH Solutions called DotMSN. DotMSN provides a managed code library for creating custom MSN Messenger clients. My custom log listener acts as a stripped down client that only sends messages.
ctor
The constructor initializes all of the data used by our listener and starts the process for connecting to the MSN Messenger server. The configuration values for the listener are outlined below:
senderAccount |
the MSN Messenger account to use for sending log messages. |
senderPassword |
the password for the account specified above. |
receiverAccounts |
a semi-colon (;) delimited list of MSN Messenger accounts that will receive log messages. |
connectionRetryInterval |
(optional) the time in minutes between connection attempts. Default value = 5 minutes. |
invitationRetryInterval |
(optional) the time in minutes between invitation attempts. Default value = 5 minutes. |
maxMessageQueue |
(optional) the maximum number of messages to queue for delivery. If this value is exceeded, the oldest message is removed from the queue before adding the latest message. Default value = 5 messages. |
public override void Write( string sMessage )
The Write
method is called by the Logging framework whenever a developer wishes to log information. Our implementation first queues the message:
if (String.Empty != sMessage)
{
if (_iMaxMessageQueue <= _messageQueue.Count)
{
_messageQueue.Dequeue();
}
_messageQueue.Enqueue(sMessage);
}
Messages are queued because it takes time to connect to the MSN Messenger (usually 3-4 seconds depending on the connection speed). Messages are also queued in the event that all of the message receivers are offline. For this reason, you should not rely on the MSN Messenger log listener as your primary log listener. If all recipients are offline, then the log messages will be lost. The sample console application included in the code download uses both a flat file listener and an MSN Messenger listener.
The DotMSN library uses multiple threads and delegates to notify developers of the various Messenger related events. I make no claims of being a multithreaded developer so there may be better ways to synchronize events between the DotMSN library and my custom listener. The next bit of code synchronizes any exceptions thrown by the DotMSN library, propagating them to the Enterprise Library Logging framework:
if (null != _thrownException)
{
lock (_oExceptionLock)
{
if (null != _thrownException)
{
try
{
throw _thrownException;
}
finally
{
_thrownException = null;
}
}
}
}
The last step before logging the message is to ensure we have a valid connection to the MSN Messenger server and a valid MSN conversation with at least one recipient connected. These two conditions are validated by two helper methods covered later. If all is well, any queued messages are sent to the recipients:
if (true == _EnsureConnection() && true == _EnsureConversation())
{
while (0 < _messageQueue.Count)
{
TextMessage message =
new TextMessage(_messageQueue.Dequeue());
_conversation.Switchboard.SendTextMessage(message);
}
}
bool _EnsureConnection()
I developed my custom listener with the understanding that an application that takes advantage of the Enterprise Library Logging framework may be running 24/7 but log message recipients might not be connected to the MSN Messenger network 24/7. With that in mind, the _EnsureConnection()
method checks to see if it is currently connected to the MSN Messenger network. If it is, it returns true
:
if (true == _messenger.Connected &&
true == _messenger.Nameserver.IsSignedIn )
{
return true;
}
If there is no connection to the network, first we check to see if we are in the process of connecting. If we are not connecting, we initiate a connection. The final check is to cover the eventuality of a network outage or dropped connection. If we are in the process of connecting but it has been more than five minutes (by default) since we started connecting, then start the connection process over again:
if (false == _bConnecting)
{
lock (_oConnectingLock)
{
if (false == _bConnecting)
{
_messenger.Disconnect();
_bConnecting = true;
_dtLastConnectionAttempt = DateTime.Now;
_messenger.Connect();
}
}
}
else if( _tsConnectionRetryInterval <=
(DateTime.Now - _dtLastConnectionAttempt) )
{
lock (_oConnectingLock)
{
_bConnecting = false;
}
}
bool _EnsureConversation()
This method performs much the same operation as _EnsureConnection()
, only rather than checking for a connection, we check for an existing conversation with participants. If one does not exist, we initiate the process to create a Conversation
:
if (null != _conversation &&
0 < _conversation.Switchboard.Contacts.Count)
{
return true;
}
if (false == _bInviting)
{
lock (_oInvitingLock)
{
if (false == _bInviting)
{
_bInviting = true;
_dtLastInvitationAttempt = DateTime.Now;
foreach (string receiver in _sReceiverAccounts)
{
_conversation.Invite(receiver);
}
}
}
}
else if ( _tsInvitationRetryInterval <=
(DateTime.Now - _dtLastInvitationAttempt) )
{
lock (_oInvitingLock)
{
_bInviting = false;
}
}
DotMSN events
The last portions of the custom listener are handlers for DotMSN events, including raised exceptions, successfully signing into the MSN Messenger network, and signals for when a recipient joins a conversation.
Testing
Testing the listener is a little tricky because you cannot sign-in to MSN Messenger twice using the same account. I had to have my wife log into MSN Messenger using her account, then configured the listener to use my account as the Sender account. So, long story short, you will need two MSN accounts to test the listener.
EntLibConfig
I added a new project to the code to support the EntLibConfig application that comes with the Enterprise Library. You can now use the graphical configuration editor to add/modify an MSN Messenger Trace Listener to your application. To use EntLibConfig, you need to copy WCPierce.Practices.EnterpriseLibrary.Logging.MsnMessenger.Configuration.Design.dll, WCPierce.Practices.EnterpriseLibrary.Logging.MsnMessenger.dll, and XihSolutions.DotMSN.dll to the same directory as EntLibConfig.exe. You should then be able to add an MSN Messenger Trace Listener. See the graphic below for an example:
Conclusion
The FormattedMsnMessengerTraceListener
was a fun exercise in creating a custom log listener for the Enterprise Library Logging framework. As I stated above, please don't rely on this listener for anything critical. It is most useful when coupled with the Exception Handling framework for instant notification of any critical errors that crop up in your applications (that never happens right?). I encourage you to download the code and checkout the listener, and I would love to hear from anyone who finds it useful enough to use it in their applications.