Microsoft Enterprise Library 4.1 – Logging Application Block: Set Log File Name Without Using App.Config

by joshgrenon on June 4, 2009

Welcome fellow programmers! First of all, I want to say this is the easiest and most noninvasive way, that I have found, to set a log file name and location without modifying an App.config file. Any other way would require modifying the source on the Enterprise Library itself, but that would corrupt its integrity. I tried to modify the source myself but it was too complicated for me.

Here is a brief description of the Enterprise Library: “Enterprise Library consists of reusable software components that are designed to assist developers with common enterprise development challenges. It includes a collection of application blocks and a set of core features, such as object generation, configuration, and instrumentation mechanisms.” Go here for more detail.

Alright, enough talk and more code! Let’s dive right in!

The problem: I needed to be able to set the name and path of a log file dynamically as a logentry is written for each category(eg. Debug, Trace, and Error). I did setup a MSMQ listener for each category but I needed to create a log file for each category, username and process id (eg. DEBUG_JKOG_1234.log) One feature of the Logging Application Block is that you specify one log file and location to where you want to write your log statements. Writing to one log file is okay for a small applications but not work in my case. I tried to modify the MsmqDistributor service’s App.config by to saving a new file location into it for each log call but there was a problem. There is a small delay when App.config is being saved and this delay caused log info to be written into the incorrect log file. This is a big problem if you rely on your logs for debugging purposes. You can see the discussions where I was doing my initial investigation for this problem on codeplex here and here.

The solution: The final solution was to set an extended property with the filename and location for each LogEntry object created. Now each LogEntry object knows where it is supposed to log itself,  you can modify the MsmqDistributor service to handle the writing to the appropriate log file.

Let’s just take a look at the clientside code:

private void btnLogIt_Click(object  sender, EventArgs e)
{
   //Create container for extra properties
   IDictionary extraProperties = new Dictionary;()
   extraProperties.Add(“filelocation”, @”C:\Logs\Test1.txt);
 
   LogEntry logEntry = new LogEntry();
 
   //Set the category of the LogEntry
   logEntry.Categories.Add(“DEBUG”);
 
   //Set the message of the LogEntry
   logEntry.Message = txtMessage.Text;
 
   //Add any extended propties, such as filename and path
   logEntry.ExtendedProperties = extraProperties;
   Logger.Write(logEntry);
}

Lines 25 and 26: Here a container is created for the extra properties which hold the file location. You could also add as many properties as you like by us doing this:

extra.Properties.Add(”pathOnly”, @”C:\Logs”);
extra.Properties.Add(”newLocation”, @”E:\Logs”);

This code is adding two extra properties to the dictionary object. The reason to use properties instead of putting the filelocation in the LogEntry.Message is that its easier to extract it this way. You wouldn’t want to have parse through each Logentry.Message to get the filelocation, now would you?

Line 31: This property is where the category is set. The category tells us that the DEBUG MSMQListener, which sends the Logentry to the myQueue private queue, is going to be used for this Logentry .

Line 38: This line calls Logger.Write method which is passed an LogEntry object.

Those are all the steps needed for the client side of your program. Now onto the MsmqDistributor service project!  This Windows service, provided by Microsoft, removes messages from a specified Message Queue and logs them where ever you want!

The code modifications that you will need to do are:

1. Open the MsmqLogDistributor.cs file, remove the code from line 217 and replace it with the following code:

Line to remove:

logWriter.Write(logEntry);

Line to add:

WriteOutToLog(logEntry);

2. Add the following code after the ReceivedQueueMessages() method:

private static  object  syncRoot = new object();
 
private void WriteOutToLog(LogEntry logentry)
{
   try
   {
      //Get the filelocation from the extended properties
      string fullPath = Path.GetFullPath(logentry.ExtendedProperties["filelocation"].ToString());
 
      //Create the directory where the log file is written to if it does not exist.
      DirectoryInfo directoryInfo = new DirectoryInfo(Path .GetDirectoryName(fullPath));
 
      if(directoryInfo.Exists == false)
         directoryInfo.Create();
 
      //The lock is here to prevent another process from using this file
      //as data is being written to it.
 
      lock(syncRoot)
      {
          using(FileStream fs = new FileStream(fullPath, FileMode.Append, FileAccess.Write, FileShare.Write, 4096, true))
          {
             using(StreamWriter sw = new StreamWriter(fs, Encoding.UTF8))
             {
                 Log(logentry, sw);
                 sw.Close();
             }
             fs.Close();
          }
      }
   }
   catch(Exception ex)
   {
      throw new LoggingException(ex.Message, ex);
   }
}
 
public static void Log(LogEntry logMessage,TextWriter w)
{
   w.WriteLine("{0}", logMessage.Message);
}

And that is how you modify the MsmqDistributor Windows service to write to any location you want. Thanks for reading this everyone! Please leave a comment if this was helpful to you or if you have any questions. I will try to get back with you soon!

No related posts.

Related posts brought to you by Yet Another Related Posts Plugin.

  • Ryan

    Very helpful. Thank you for posting it. My only comment is that it would be helpful if the code text contained line numbers.

  • Ryan

    Very helpful. Thank you for posting it. My only comment is that it would be helpful if the code text contained line numbers.

  • Ryan

    Very helpful. Thank you for posting it. My only comment is that it would be helpful if the code text contained line numbers.

  • http://inspirageek.com/ Josh Grenon

    Thanks Ryan! I had removed the line numbers awhile back and forgot to put them back. I'll update the post later on today.

  • http://inspirageek.com Josh Grenon

    Thanks Ryan! I had removed the line numbers awhile back and forgot to put them back. I'll update the post later on today.

  • http://inspirageek.com Josh Grenon

    Thanks Ryan! I had removed the line numbers awhile back and forgot to put them back. I'll update the post later on today.

  • Mark stone

    Hi Josh, clever solution you found to a problem I'm also having. I'm not familiar with MsmqDistributor, where can I find more information about it, does it require anything special, for instance will it run on WinXP.ThanksMark.

  • Mark stone

    Hi Josh, clever solution you found to a problem I'm also having. I'm not familiar with MsmqDistributor, where can I find more information about it, does it require anything special, for instance will it run on WinXP.

    Thanks

    Mark.

  • Mark stone

    Hi Josh, clever solution you found to a problem I'm also having. I'm not familiar with MsmqDistributor, where can I find more information about it, does it require anything special, for instance will it run on WinXP.

    Thanks

    Mark.

  • http://inspirageek.com/ Josh Grenon

    Mark,MsmqDistributor is just a Windows Service so it should run fine on WinXP. You can more info about the service here: http://msdn.microsoft.com/en-us/library/cc51184...Just in case you can't fine the MsmqDistributor service, it is installed under the path: C:EntLib4SrcBlocksLoggingSrcMsmsDistributor when you install Microsoft Enterprise Library 4.1 – October 2008.Hope that helps and let me know if you have anymore questions!Thanks!

  • http://inspirageek.com Josh Grenon

    Mark,
    MsmqDistributor is just a Windows Service so it should run fine on WinXP. You can more info about the service here: http://msdn.microsoft.com/en-us/library/cc51184...

    Just in case you can't fine the MsmqDistributor service, it is installed under the path: C:EntLib4SrcBlocksLoggingSrcMsmsDistributor when you install Microsoft Enterprise Library 4.1 – October 2008.

    Hope that helps and let me know if you have anymore questions!

    Thanks!

  • http://inspirageek.com Josh Grenon

    Mark,
    MsmqDistributor is just a Windows Service so it should run fine on WinXP. You can more info about the service here: http://msdn.microsoft.com/en-us/library/cc51184...

    Just in case you can't fine the MsmqDistributor service, it is installed under the path: C:EntLib4SrcBlocksLoggingSrcMsmsDistributor when you install Microsoft Enterprise Library 4.1 – October 2008.

    Hope that helps and let me know if you have anymore questions!

    Thanks!

  • Mark stone

    Josh, your article was certainly very helpful and introduced me to a new concept (MSMQ), but I ultimately went with a CustomTraceListener, which using your code sample was very quick to implement. My sample writes the log information to a named file directly without using MSMQ (Still find it hard to believe this is not part of EL's default behavior to begin with. ) The solution requires a class like the example below be included in the project. The Trace listener in app.config is then declared as acustom listener pointing to the compiled class. The LogEntries are created in the exact same fashion as yours, using extended properties. Thanks for the guidance.

    Imports Microsoft.Practices.EnterpriseLibrary.Common.Configuration
    Imports Microsoft.Practices.EnterpriseLibrary.Logging.Configuration
    Imports Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners
    Imports Microsoft.Practices.EnterpriseLibrary.Logging
    Imports System.IO
    Imports System.Text

    <ConfigurationElementType(GetType(CustomTraceListenerData))> _
    Public Class Log2FileTraceListener
    Inherits CustomTraceListener

    Private Shared syncRoot As New Object()

    Public Overrides Sub TraceData(ByVal eventCache As TraceEventCache, ByVal source As String, _
    ByVal eventType As TraceEventType, ByVal id As Integer, ByVal data As Object)

    If (TypeOf data Is LogEntry) And Me.Formatter IsNot Nothing Then
    WriteOutToLog(Me.Formatter.Format(DirectCast(data, LogEntry)), DirectCast(data, LogEntry))
    Else
    WriteOutToLog(data.ToString(), DirectCast(data, LogEntry))
    End If
    End Sub

    Public Overrides Sub Write(ByVal message As String)
    Debug.Print(message.ToString)
    End Sub

    Public Overrides Sub WriteLine(ByVal message As String)
    Debug.Print(message.ToString)
    End Sub

    Private Sub WriteOutToLog(ByVal BodyText As String, ByVal logentry As LogEntry)
    Try
    'Get the filelocation from the extended properties
    Dim fullPath As String = Path.GetFullPath(logentry.ExtendedProperties(“filelocation”).ToString())

    'Create the directory where the log file is written to if it does not exist.
    Dim directoryInfo As New DirectoryInfo(Path.GetDirectoryName(fullPath))

    If directoryInfo.Exists = False Then
    directoryInfo.Create()
    End If

    'The lock is here to prevent another process from using this file
    'as data is being written to it.

    SyncLock syncRoot
    Using fs As New FileStream(fullPath, FileMode.Append, FileAccess.Write, FileShare.Write, 4096, True)
    Using sw As New StreamWriter(fs, Encoding.UTF8)
    Log(BodyText, sw)
    sw.Close()
    End Using
    fs.Close()
    End Using
    End SyncLock
    Catch ex As Exception
    Throw New LoggingException(ex.Message, ex)
    End Try
    End Sub

    ''' <summary>
    ''' Write message to named file
    ''' </summary>
    Public Shared Sub Log(ByVal logMessage As String, ByVal w As TextWriter)
    w.WriteLine(“{0}”, logMessage)
    End Sub
    End Class

  • http://inspirageek.com Josh Grenon

    I'm glad that my blog post helped you! The reason I used MSMQ was to persist the log data if the logging machine shut off unexpectedly or crashed. When the machine is rebooted, the MSMQ Distributor Service starts running again and pulls the saved log data from the Message Queue to write it to flat files.

  • cpatel

    Thanks Josh! This is very helpful article

  • http://chaz-agro.ru plerledly

    thank!

blog comments powered by Disqus

Previous post:

Next post: