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!