Showing posts with label Enterprise Library. Show all posts
Showing posts with label Enterprise Library. Show all posts

Thursday, 23 October 2008

Unit Testing the Enterprise Library #1 - Unit Testing the Logging Application Block

So I've decided that using the enterprise library in my application is a great idea. We're also following TDD and I'm using a design pattern called Object Method to abstract the implementation of my framework classes from my application. But I've come across a problem. When I'm unit testing my implementation I find that it's very difficult to unit test the implementation of the Enterprise Library. Then I came across this article by Mark Seemann showing how to unit test the Logging Application block. It's very simple really, you create a custom logging trace listener in your test project and have the custom listener store all log messages in a list you can unit test later. What the article was not specific about was the implementation of the enterprise library in your test project. If you add an app.config file to your test project then your test library will load the config settings from the test project's app.config file. Add your custom block to the test project and add the trace listener to the "All Events" section and it should work just fine. So for example, this is Mark Seemann's custom trace listener:
   1: ///
   2: /// This is a custom trace listener that loads the trace information into a
   3: /// list of entries in the log file.
   4: /// Found here: http://blogs.msdn.com/ploeh/archive/2006/04/06/UnitTestYourEnterpriseLibraryLoggingLogic.aspx
   5: /// Author: Mark Seemann
   6: ///
   7: [ConfigurationElementType(typeof(CustomTraceListenerData))]
   8: public class StubTraceListener : CustomTraceListener
   9: {
  10:     private readonly static List logEntries_ = new List();
  11:     private readonly static List logMessages_ = new List();
  12:     
  13:     public override void Write(string message)
  14:     {
  15:         StubTraceListener.logMessages_.Add(message);
  16:     }
  17:     
  18:     public override void WriteLine(string message)
  19:     {
  20:         StubTraceListener.logMessages_.Add(message);
  21:     }
  22:     
  23:     public override void TraceData(TraceEventCache eventCache, string source, TraceEventType eventType, int id, object data)
  24:     {
  25:          LogEntry le = data as LogEntry;
  26:         
  27:          if (le != null)
  28:          {
  29:              StubTraceListener.logEntries_.Add(le);
  30:         
  31:              if (this.Formatter != null)
  32:              {
  33:                  this.Write(this.Formatter.Format(le));
  34:                  return;
  35:              }
  36:          }
  37:         
  38:          base.TraceData(eventCache, source, eventType, id, data);
  39:     }
  40:     
  41:     internal static IList GetLogMessages()
  42:     {
  43:          return new ReadOnlyCollection(StubTraceListener.logMessages_);
  44:     }
  45:     
  46:     internal static IList GetLogEntries()
  47:     {
  48:          return new ReadOnlyCollection(StubTraceListener.logEntries_);
  49:     }
  50:     
  51:     internal static void Reset()
  52:     {
  53:          StubTraceListener.logEntries_.Clear();
  54:          StubTraceListener.logMessages_.Clear();
  55:     }
  56: }
And here is my test case:
   1: [TestMethod()]
   2: public void WarningTest()
   3: {
   4:     EntLibLogger target = new EntLibLogger();
   5:     string message = "warning message";
   6:     LogPriority priority = LogPriority.Low;
   7:     CallingMethod callingMethod = new CallingMethod();
   8:     
   9:     target.Warning(message, priority, callingMethod);
  10:     IList entries = StubTraceListener.GetLogEntries();
  11:     
  12:     Assert.IsNotNull(entries);
  13:     Assert.AreEqual(1, entries.Count);
  14:     Assert.AreEqual((int)priority, entries[0].Priority);
  15:     Assert.AreEqual(message, entries[0].Message);
  16: }

I'm not testing much, but you can extend your test cases to test as much as you need. Just make sure you have an app.config file in your test project liks so:

And that you've used the LAB to setup your custom trace listener:

Tuesday, 14 October 2008

Microsoft Enterprise Library #5 – Cache Management

So I had a look into the caching applicaiton block and was impressed with some of the things that it could do. After this I went on a practical use adventure in which I hit a couple of road blocks, so I thought I'd blog about them. In this case I came across a problem where for each object I'm referencing by key I need to setup a new Cache Manager in the application block. If I have to do this for each object I wish to cache I'm going to have a very large cache config file that I don't really want to have to manage. So here is how I got around it. I created a Key class that held the unique information about my object.

class CacheKey
{
 public object Id {get;set;}
 public Type {get;set;}

 public override string ToString()
 {
  return type.ToString() + "." + id.ToString();
 }
}
In my cache interface I have methods to create a generic cached object like so.

interface ICache
{
 void UpdateCache(object key, object objectToCache);

 void UpdateCache(object key, object objectToCache, CacheManager manager);

 object GetObjectFromCache(object key, Type objectType);

 object GetObjectFromCache(object key, Type objectType, CacheManager manager);
}
So in my implementation I would do the following.

public void UpdateCache(object key, object objectToCache)
{
 CacheKey keyObj = new CacheKey();
 keyObj.Id = key.ToString();
 keyObj.Type = objectToCache.GetType();
 
 ICacheManager defaultCache = CacheFactory.GetCacheManager("DefaultManager");
 defaultCache.Add(keyObj.ToString(), objectToCache);
}

object GetObjectFromCache(object key, Type objectType)
{
 CacheKey keyObj = new CacheKey();
 keyObj.Id = key;
 keyObj.Type = objectType;

 object returnObject = userCache.GetData(keyObj.ToString());
}
If I want to handle multiple managers then I just pass through the manager to the methods from the interface when I implement them. This allows me to use the same rule for every object in the default cache without setting up a specific rule for each object I want to cache. There are some disadvantages to this method. I wouldn't recommend that you use this for large volumes of data as it could slow down your cache. Keep large volumes in their own rules, I.E. for postal codes (or zip codes for americans), you don't want searching throuh the possible zip codes to find your logged in user details to slow you down.

Wednesday, 8 October 2008

Microsoft Enterprise Library #4 – Cache Application Block

At first glance there wasn't anything useful in the Cache Application Block. As we delved further into the requirements of our project I've found that there are many things that we would like to keep at hand and not have go delve off into the database to retrieve. But is the Cache application block worth my time to use? If it saves on retrieval time for data objects that I would like to keep handy then it is definitely worth while. Even it it removes the need to perform a transaction against the database that will be heavily utilised it will be worth while.

My Requirements

For my Requirements I have a very simple task.

  1. Cache the user that I've loaded from the database using the Entity Framework
  2. Reload that user at any time
  3. Expire the user after 60 seconds
  4. Perform a particular action when the user's cache expires

I could use membership to do a similar task, but if I'm not using membership (like if I use Windows authentication), then this may be useful. There are lots of other ways to do this I'm aware, but this will show off the cache application block using a simple scenario.

Step 1 - Setup the block

I added the DLL Files that I needed. Again Vikas Goyal has a good tutorial on this one here and I used this to help setup the config files that I need and get an idea on how the block works. I used an ASP.NET MVC project as that is the tool we will be going with, but you don't need to use one if you prefer something else. Next I opened the web.config (or app.config in winforms) with the enterprise library config tool and added the config application block to my project. I want to cache the logged in user's details so I added a new cache manager and called it UserManager. I set the poll frequency to 10 seconds because I want my timeout to be 60 seconds or there about but left the rest as default. I should be done now.

Step 2 - Create My Cache Object

I've got my simple class:

namespace EntLibCABDemo.Models
{
 public class UserDetails
 {
    public String UserName { get; set; }
    public String State { get; set; }
    public String EmailAddress { get; set; }
 }
}

Step 3 - Implement the Cache

So now I'm going to create a new action for viewing the data in the cache. If the action doesn't find the data in the cache it will display a message telling me that it has re-created it, otherwise it will display the data. If I refresh the page after 60 seconds it should let me know it has re-created it. I created a CreateUser action to create the user with the code below. The SlidingTime specifies that every time I access the object that it will restart the timer. Once created, we'll redirect to the view method.

public ActionResult CreateUser()
{
 Models.UserDetails user = new Models.UserDetails();
 user.UserName = "Steve";
 user.EmailAddress = "Steve@company.com";
 user.State = "NSW";
 
 ICacheManager userCache = CacheFactory.GetCacheManager("UserManager");
 userCache.Add(user.UserName, user, CacheItemPriority.Normal,
     null, new SlidingTime(TimeSpan.FromSeconds(60)));
 
 return Redirect("ViewUser");
}

And then a ViewUser to view the user. Here we get the user from the cache and setup the view data with a string to display.

public ActionResult ViewUser()
{
 ICacheManager userCache = CacheFactory.GetCacheManager("UserManager");
 Models.UserDetails user = (Models.UserDetails)userCache.GetData("Steve");
 
 if (user == null)
 {
     // Not found, tell the user.
     ViewData["Message"] = "No current user found for 'Steve'";
 }
 else
 {
     // Found, set the string.
     ViewData["Message"] = "Found Steve: " + user.State + " " + user.EmailAddress;
 }
 
 return View("User");
}

Then the view to display the user, just getting the view data and spitting it out to the screen.

Details: <%=Html.Encode(ViewData["Message"])%>

And I'm ready to test, Lo and behold it works. Hitting the create method will return the view and show the user's details "Found Steve: NSW Steve@company.com". If I leave it 60 seconds and refresh the view I get "No current user found for 'Steve'". If I refresh before the 60 seconds is up the cache restarts the timeout and I get another 60 seconds of time.

Step 4 - Handling Events on Expiration

Now I want to handle an event when the content expires. All I need to do is create a class that implements the ICacheItemRefreshAction interface and set it when I add the item. To test it, when the user expires I'll change the email address and reset it in the cache.

public class RefreshCache : ICacheItemRefreshAction
{
  public void Refresh(string removedKey, object expiredValue,
      CacheItemRemovedReason removalReason)
  {
      UserDetails user = (UserDetails)expiredValue;
      user.EmailAddress = "new@company.com";
      CacheFactory.GetCacheManager("UserManager").Add(removedKey, user);
  }
}

That's it. When testing, after 60 seconds the cache expired and fired the event. The user was set back into the cache and when I refreshed the page the email address changed from Steve@company.com to new@company.com.

Final Thoughts

The Cache Application Block is very simple and works very well from all my first tests. I can't think of a reason not to use it. We will be using it to cache items from the database and file system information that we don't want to continually reload.

Wednesday, 24 September 2008

Microsoft Enterprise Library #3 – Validation Block

Apparently the Validation block is new, as I've never used the library before it's no newer to me than any of the others. I did find that there were fewer examples of the more difficult stuff on the web for me to find, hopefully I can provide one or two here. I had my reservations about the validation block before I started this entry, I'm curious to see if they were valid. Data validation is very important to our new project here, the validation block appears at a first glance to fill the need, but we don't want to create a maintenance nightmare between the database changes and the code changes. Adjusting the length of a field could cause us no end of trouble when validating, unless any framework we use can check the database for the field length.

My Requirements

This time my requirements are a little more difficult to fill. I'm getting a good handle on how to do things with the enterprise library so I'm confident that I'll be able to get over any problems I encounter. 1. Validate data formats (like email addresses) 2. Validate fields against the database (strings against length for example) 3. Validate my POCs (Plain Old C# Objects) to keep a strong level of abstraction

Step 1 - Data Format Validation

Seems like the easiest to work with to start. I can also fill requirement 3 while I'm at it. I've created a class called Employees and added it to my project. The class is as so:

public class Employee
{
 public string Name { get; set; }
 public int Age { get; set; }
 public string EmailAddress { get; set; }
}

Pretty straight forward. I want to validate the EmailAddress field. Now this is a POC object so if I can do this then I'm filling requirement 3 also. Make sure your class is public or the configuration tool won't be able to see it. First thing to do is add a Validation Application Block to the App.config file with the enterprise library configuration tool. Once added, right click the application block and add a new type. You'll have to load the assembly for your project to see your public class, in my case I selected the exe file and there was my Employee class. Some people have noted a problem that the class didn't show until restarting, I didn't get this problem. Just make sure you've built your application first. Once the type is there add a new Rule Set.

Make sure you set this ruleset as the default ruleset, or you'll feel like a fool when you see that answer after going to Google to find out why it doesn't work. Trust me, I know. Next, right click the ruleset and under new is the option to choose members. I want to validate the email address so I chose that member from the list. For an email address I want to use a regular expression to validate that it is correct, so I right click the field I just added and add a new regular expression validator.

Lucky for me there is a pattern for email addresses so I don't have to devise my own. I set the message template to something useful and my settings ended up looking something like this:

 

Ok, now to test it. I added the assembly for validation as I did for logging and exceptions (this time Microsoft.Practices.EnterpriseLibrary.Validation and Microsoft.Practices.EnterpriseLibrary.Validation.Configuration). The following code shows how to test it.

Employee myEmployee = new Employee();
myEmployee.EmailAddress = "memine.net";
ValidationResults results = Validation.Validate(myEmployee);
if (!results.IsValid)
{
foreach (ValidationResult result in results)
{
Console.WriteLine(result.Message);
}
}
 

When I run this, the address memine.net will fail and write the error out to the console "Email Address Invalid". If I change the EmailAddress to me@mine.com it comes back valid. That was remarkably simple, I now have email address validation on my Employee object and I'm using POCs to do it. Requirement 1 and 3 satisfied.

Step 2 - Validating Against the Database

Ok so at first this was my main reservation with the validation block. There is no default validator to validate a POC field against it's corresponding database field. I knew I'd have to write my own. I figured it was going to be hard, and I wasn't dissappointed. There are a few gotcha's, and hopefully this post might help someone figure out theirs faster than it took me. I decided that the employee name was a good field to create a custom validator for. So I added the field to the employee type in the app.config using the Enterprise Library Config tool and added a custom validator to the field. When you add a custom validator you need to choose an object to validate with, upon loading my assembly it specified that no objects that inherit from Validator were found, so I had a starting point. A little code inspection led me to this:

namespace Microsoft.Practices.EnterpriseLibrary.Validation
{
public abstract class Validator
{
 protected Validator(string messageTemplate, string tag);
 
 protected abstract string DefaultMessageTemplate { get; }
 public string MessageTemplate { get; set; }
 public string Tag { get; set; }
 
 protected internal abstract void DoValidate(object objectToValidate, object currentTarget, string key, ValidationResults validationResults);
 protected virtual string GetMessage(object objectToValidate, string key);
 protected void LogValidationResult(ValidationResults validationResults, string message, object target, string key);
 protected void LogValidationResult(ValidationResults validationResults, string message, object target, string key, IEnumerable nestedValidationResults);
 public void Validate(object target, ValidationResults validationResults);
}
}

So I was going to have to override the DoValidate method and the DefaultMessageTemplate property. Didn't seem so hard. I created my class:

public class DatabaseValidator : Validator
{
protected DatabaseValidator(string messageTemplate, string tag)
: base(null, null) { }
 
protected override string DefaultMessageTemplate
{
get
{
return "";
}
}
 
protected override void DoValidate(object objectToValidate, object
currentTarget, string key, ValidationResults validationResults)
{
}
}
 
And compiled. No Errors, fantastic. But when I tried to load the assembly to add the validator to the Name field as a type it didn't show. After some searching I found I needed to set a configuration element type on the class as so:

[ConfigurationElementType(typeof(CustomValidatorData))]
public class DatabaseValidator : Validator
{
...
{

After this it would show up just fine. Great I thought, I'm almsot there. I added the new validator type to the Name field (using custom validator and selecting the assembly exe file) and compiled. No errors, but when running the application, BANG:

Additional information: Constructor on type 'ApplicaitonBlocksTechDemo.Validators.DatabaseValidator' not found told me a little, my constructor has the wrong parameters. It took me a while to find the solution, but eventually I did. Your validator class will need a constructor that takes a NameValueCollection as a parameter. I added this in and now my class looks like so (I also added in a default constructor to be safe).

[ConfigurationElementType(typeof(CustomValidatorData))]
public class DatabaseValidator : Validator
{
public DatabaseValidator(NameValueCollection collection)
  : base(null, null) { }
public DatabaseValidator()
  : base(null, null) { }
protected DatabaseValidator(string messageTemplate, string tag)
  : base(null, null) { }
 
protected override string DefaultMessageTemplate
{
  get
  {
    return "";
  }
}
 
protected override void DoValidate(object objectToValidate,
  object currentTarget, string key, ValidationResults
  validationResults)
{
}
}
 

And recompiled. Success! The validator returned, and without any code the validation was considered successful. To implement the validator all you need to do is fill out the DoValidate method with whatever validation you require, in this case I'll tell it to go to the database, from the key (the field name) and the target (class name) I'll be able to discern the field and thus the limitations in the database to return correct validation.

Final Notes

With the success of the custom validator I'm actually quite excited to use the validation block in our new application, I can see the amount of code I can avoid having to write myself by using this tool, especially if we use ASP.NET MVC (the way it binds objects will lend itself to this very nicely). I hope this post helps someone.

Tuesday, 23 September 2008

Microsoft Enterprise Library #2 – Exception Application Block

This is the second in a series of blogs looking at the Enterprise Library for development. In the last session I looked at the logging block, this session I'm going to look at the Exception block. There is no coincidence about the order that I'm doing these in, the Exception block has an optional dependency to the logging block that I'm going to exploit for the purposes of filling my requirements of the Exception block. For this blog post I'll be re-using some of the setup from the previous post for this reason.

My Requirements

My requirements for exception handling are far more simple than my logging requirements. I want to:

  1. Log all exceptions to the event log
  2. Email security exceptions to an email address
  3. Rethrow database exceptions to the application to handle at a higher level (so the user will see them)

Step 1 - Logging to the Event Log

I'm going to re-use my logging block for this, previously I've setup that all error type logs go into the event log. The easy way to use this is to just associate the base exception with the error logging mechanism. Firstly I'll add a new Exception Handling Application block to my config file. I'll add a new Exception Policy to that, and as I'm only going to have a single policy for this project I'll leave the default name. This is the base that I'll work from for this example. I want to log all exceptions, in .NET every exception inherits from Exception, so I'll add a new exception type and choose System.Exception. After this is added I'll right click the new Exception type and add a new Logging Handler. In my logging handler I'll choose the LogCategory of Error, which I previously setup in the Logging Application Block and a FormatterType of TextFormatter as the event log is in text. Your App.config should look something like this:

 

Now we need to add some code to show how to use the exception. I'll add a new reference to

  • Microsoft.Practices.EnterpriseLibrary.ExceptionHandling
  • Microsoft.Parctices.EnterpriseLibrary.ExceptionHandling.Logging
(you'll need to browse to the DLL file to do so) and add a using to my code. Then I'll write some code to throw an exception, catch it and send it to the exception handler.
public static void TestExceptionHandling()
{
 Console.WriteLine("Exception handling starting");

 try
 {
  throw new System.Exception("Testing exception!");
 }
 catch (System.Exception Ex)
 {
  try
  {
   if (ExceptionPolicy.HandleException(Ex,
    "Exception Policy"))
   {
    throw;
   }
  }
  catch (Exception ExNew)
  {
   Console.WriteLine("Exception caught: " +
   ExNew.Message);
  }
 }

 Console.WriteLine("Exception handling finished");
 Console.ReadLine();
}

Now when I run this code my exception handler is going to use the error logging facility that I've already setup and log to the event log. It will also log to the log file because I've told the system to log everything to the log file as well. How easy was that?

Step 2 - Emailing Security Exceptions

I want to email security exceptions to someone becuase we need to be sure that we investigate these exceptions straight away. I've already setup warnings to be emailed to a user, this security exception sounds like it fits in this category so I'll re-use the warning log type the same as before. I don't want to use any of the custom exceptions so I'm going to create my own, I'll call it CustomSecurityException. Make sure you make it public because otherwise the enterprise library won't be able to see it. Here is the code for the custom exception.

public class CustomSecurityException : System.Exception
{
}

And as you can see it does nothing, it's just for example. Now we'll add a new handler for this type. Right click the policy, add a new type. You'll need to load an assembly, I selected my .exe file where the public exception class resides and it added my namespace and exception class to the selectable types. I selected my custom exception and then added a logging handler as before, this time selecting the Warning log category. Something to note, I've been told that the library is only loaded once into enterprise library configuraiton tool, if your objects are not there try restarting the environment and trying again. I didn't have this problem however. After running the application I get an email in my inbox specifying the exception details and the log format wrapper around it.

Step 3 - Rethrowing Database Exceptions

The last thing I want to do with my exception handling environment is have the ability to rethrow new exceptions after processing. This time I'm just going to catch a System.Data.DataException. It pays to know the object heirarchy for exceptions when handling exceptions. As before I add the System.Data.DataException type and this time I set it to "ThrowNewException" in the PostHandlingAction property. From here I added a Replace Handler to the DataException type and set the ReplaceExceptionType to System.Exception (select it from the list). I don't want the user seeing the exception so I put my own validation message ino there. It should look something like this:

Now when I write some code to throw the a Data Exception all I need to do is pass through an output exception to rethrow like so;

try
{
System.Exception newException;

if (ExceptionPolicy.HandleException(Ex,
"Exception Policy", out newException))
{
throw newException;
}
}
catch (System.Exception ExNew)
{
Console.WriteLine("Exception caught: " + ExNew.Message);
}

The rethrown exception shows the message in the screenshot above written out to the console.

Final Thoughts

Adding exception handling policies to your application is remarkably simple, even if it's something as simple as logging them to the event log you can be sure that every handled exception is picked up and handled. I can think of many real work examples in the past where I would have found this tool remarkably useful and I hope to find places for it in the future. One thing I did not investigate in this blog, and may have a look at next time, is creating a bunch of custom handlers for exceptions. This allows you to use your code structure to handle your exceptions when they happen and the exception block to catch and direct them.

Monday, 22 September 2008

Microsoft Enterprise Library #1 – Logging Application Block

This all started as a means to evaluate different logging techniques. A colleague of mine found the enterprise library in his searches and referred it to me so I decided it was worth looking into rather than going with my gut instinct and using Log4Net and Spring.Net. After my initial investigation into the logging application block I found a few things that I took for granted that Log4Net does out of the box (I’ll cover these below) but other than that it was a powerful logging engine that was far more easy to setup and manage changes through a graphical user interface. More importantly the enterprise library seemed to offer integration of logging with other important areas of the application that we are also currently evaluating solutions for, like exception handling, validation and caching. This blog entry is the first in several designed to evaluate the use of the application blocks included in the Microsoft Enterprise Library 4.0.

Step 1 - Download and Install the Enterprise Library 4.0

You can find the Library Installer here. I used the default settings when installing and had no problems with that. You will need to note the installation directory.

Step 2 - Setting up a Logging Block

First you need a project. I’ve created a console application and called it ApplicationBlocksTechDemo. Once you’ve got your project created you’ll need to add all the references and do a quick test to see if the block is working. I don’t want to re-invent the wheel with this post so I suggest if you don’t know how to setup the block then you should use this tutorial from Vikas Goyal. It’s very simple and straight forward and will give you the starting point for this guide. It is worth mentioning that in Enterprise Library 4.0 the Microsoft.Practices.ObjectBuilder seems to be called Microsoft.Practices.ObjectBuilder2. You will also need to browse for the DLL files directly; they are in the bin directory of your Enterprise Library installation directory. Add the references to your project for:

  • Microsoft.Practices.EnterpriseLibrary.Common
  • Microsoft.Practices.EnterpriseLibrary.Logging
  • Microsoft.Practices.ObjectBuilder2

Once these are added you’ll be able log the most basic of information, by default it will log to the event log. If you’re building an enterprise application you’ll almost certainly want to capture logs to many different locations so this tutorial will show you how to do that. At this point you should also add an App.config file to your project. Your project solution should look something like this now.

 

 

Step 2 - Adding the Logging Application Block In the bin directory of the Enterprise Library install directory you will find the EngLibConfig.exe file. Run the file and open the Config file for your solution that you just added. Right click the project and add a new logging application block, the default settings will be added. Save your changes and go back to your project. Your App.config file will ask to be re-loaded, when you choose yes you’ll be able to see the configuration that has been added. In my opinion this is part of the power of the Logging Application Block, you don’t need to edit the XML directly, so I close my logging Config region in my App.config.

Step 3 Adding My Requirements Before adding my requirements into the block, I need to understand the application block sections. These are:

  1. Filters – You can filter out log messages before they reach the distributor.
  2. Category Sources – These are the types of log messages you are capturing, with each one specifying the trace listener it will log to.
  3. Special Sources – Another way to capture the messages and send them to trace listeners.
  4. Trace Listeners – Output forms for the logs.
  5. Formatters – The format of the output.
My requirements are simple, I want to:
  1. Send errors to the event log
  2. Send warnings to an email address
  3. Log all messages to a log file using a simple one line format.

Step 4 - The Event Log

First remove the event log listener from the General category. Next Right click the category sources and add a new category, I called mine Error. Right click the category once it’s created and create a new trace listener reference. Choose the Formatted EventLog TraceListener (added by default) and save the Config file. Your Logging Application Block should look something like this. In your application add some code to create an error, it’s very simple:

 
 
using Microsoft.Practices.EnterpriseLibrary.Common;
using Microsoft.Practices.EnterpriseLibrary.Logging;
using Microsoft.Practices.ObjectBuilder2;
 
namespace ApplicationBlocksTechDemo
{
class Program
{
 static void Main(string[] args)
 {
  Console.WriteLine("Logging tool starting");
  Logger.Write("Error Message", "Error", 1, 1,
   System.Diagnostics.TraceEventType.Error);
  Console.ReadLine();
 }
}
}
 

When you check your application event log you’ll see an error after running your application now.

Step 5 - Send Warnings to an Email Address

Your requirements will probably be different to mine as mine are setup to show off some of the features that I like with the Enterprise Library, but if you want to email warnings to someone this is how it’s done. We don’t have a warning category yet, so add one in. I called mine Warning. We now need to create a Trace Listener to allow us to send warnings to an email address. Right click the Trace Listeners and add a new Email Trace Listener. Set all the parameters, they’re pretty straight forward. Right click your Warning category and add a new trace listener reference to your email trace listener.

 

Add in a line of code to log a Warning

Logger.Write("Warning Message", "Warning", 1, 1,
System.Diagnostics.TraceEventType.Warning);

and run your program. Warnings are now sent by email to the user specified.

Step 6 - Logging to a File

Lastly we want to log everything to a file, even our errors and warnings we’ve handled already. Also, we only want each log entry to take up one line so that we can review the information a little more easily. We’ll comma separate the fields so we can open up the log in Excel for reviewing if required. We also don’t want our log file growing out of proportion so that it gets to a size that is impossible to read. The first thing we need to do is add a new formatter. Right click the formatters and choose to add a new one, I called mine LogFileCSV Formatter. You can edit the template once it’s added, mine looks like this:

{category},{timestamp},{priority},{machine},{message}

Now we need a new Trace Listener, add a new one of type “Rolling Flat File Trace Listener”. This will create a new file based on the rules you choose at the intervals you set. The options are pretty straight forward, I chose mine to create a new file every 1000Kbs and to move the old one to a timestamp pattern of yyyy-MM-dd. Choose your new formatter as the formatter option and make sure you remove the header and footer if you want it to show only one line. Finally under Special Sources add the new trace listener to the All Events option, this will log every event into your log file.

Finished

Looking back over my project, I’ve added in the framework to complete all my requirements without writing a single line of code. My requirements were incredibly basic, but there is no reason why it should take more than this to create your logging framework.

Log4Net Comparison

I’ve always used Log4Net in the past, and I will do so again for small projects that don’t require an enterprise level solution. But why would I use the Logging Application Block over Log4Net for an enterprise level solution. Here are some reasons. 1. Speed I setup a test where two different applications would log 10 000 messages to a file and 10 000 messages to the event log in exactly the same format. I tried to make the code as efficient as possible in both cases. The Logging Application Block completed this task in a little over one second, the Log4Net test took a little over 8 seconds. Most of this was accessing the event log in both cases, logging to file was far quicker as you would expect. 2. No XML editing Using the Logging Application Block you can utilize the graphical interface and are not required to edit the XML Config at all. For small projects dealing with XML is easy as your logging isn’t required to do much, but the more complex your requirements become the harder it is to sift through the XML. The Logging Application block excels here. Why would you use Log4Net over the Logging Application Block? 1. Smaller Footprint Log4Net is certainly smaller. If this is an issue for you then perhaps Log4Net is the better option. 2. Tracing back to source If you wish to trace back to the source (log the class or function that threw the exception) then Log4Net handles this natively. You would have to implement this yourself in the Logging Application Block (though admittedly it is not very difficult and the time you would have to spend writing XML for Log4Net would easily cover it).

Final Notes

No matter which logging framework you choose I highly recommend that you abstract it from the rest of your code and hide the implementation so that you can switch without any trouble should the need arise. The Logging Application Block is remarkably simple to implement and even if it is a sledge hammer I wouldn’t want to break up a block of concrete with a chisel. Interoperability with the other Application Blocks in the Enterprise Library will be something that I will investigate very shortly, this promises to be the main reason I wish to use the Logging Application Block.