Below article consists of a Factory Class that facilitates easy pick of a Log Provider helper.
Please note: This article use Microsoft Enterprise Library as logging component for reference.
The Article has its steps marked in BOLD.
To start with Create an interface, as shown below. List down all the methods that you think any Logging component would have.
#region Namespaces
using System;
#endregion Namespaces
namespace Infrastructure.Logging
{
/// <summary>
/// ILoggerProvider defines a contract for the Logger Providers.
/// </summary>
public interface ILoggerProvider
{
/// <summary>
/// Log a message against a single category.
/// </summary>
/// <param name="message">Message to be logged</param>
/// <param name="category">Category the message belongs to</param>
//void Write(string message, LogCategory category);
void Write(string message, LogCategory category);
/// <summary>
/// Log a message against multiple categories.
/// </summary>
/// <param name="message">Message to be logged</param>
/// <param name="categories">Categories the message belongs to</param>
void Write(string message, LogCategory[] categories);
/// <summary>
/// Log a message and category.
/// </summary>
/// <typeparam name="T">Type of message</typeparam>
/// <param name="message">Message body</param>
/// <param name="category">Category</param>
void Write<T>(T message, LogCategory category);
/// <summary>
/// Log a message and its categories.
/// </summary>
/// <typeparam name="T">Type of message</typeparam>
/// <param name="message">Message body</param>
/// <param name="categories">Array of categories</param>
void Write<T>(T message, LogCategory[] categories);
/// <summary>
/// Logs the specified ex.
/// </summary>
/// <param name="ex">The ex.</param>
void Log(Exception ex);
/// <summary>
/// Searialize the message body passed to it based on type.
/// </summary>
/// <typeparam name="T">Type of message</typeparam>
/// <param name="message">Message body</param>
/// <returns>Serialized message string</returns>
string GetSerializedMessage<T>(T message);
}
}
Define an Master Log Category Enum.
namespace Infrastructure.Logging
{
/// <summary>
/// Categories available for logging.
/// </summary>
public enum LogCategory
{
/// <summary>
/// Critical
/// </summary>
Critical,
/// <summary>
/// Error
/// </summary>
Error,
/// <summary>
/// Warning
/// </summary>
Warning,
/// <summary>
/// Security
/// </summary>
Security,
/// <summary>
/// Audit
/// </summary>
Audit,
/// <summary>
/// Service
/// </summary>
Service,
/// <summary>
/// UI
/// </summary>
UI,
/// <summary>
/// Information
/// </summary>
Information,
/// <summary>
/// Debug
/// </summary>
Debug,
/// <summary>
/// Trace
/// </summary>
Trace
}
}
Create a Factory Class to return an instance of the Log Provider.
namespace Infrastructure.Logging
{
/// <summary>
/// Creates logging Provider.
/// </summary>
/// <typeparam name="TLogProvider">The type of the log provider.</typeparam>
public class LogProviderFactory<TLogProvider> where TLogProvider : ILoggerProvider, new()
{
/// <summary>
/// Creates an instance for TClass
/// </summary>
/// <returns>instance of TClass</returns>
public static TLogProvider CreateProvider()
{
TLogProvider instance = new TLogProvider();
return instance;
}
}
}
Create a Helper Class for Log Provider. Please note the use of Log Provider type. This is what corresponds to multiple log Providers. Currently Microsoft Enterprise Library is used as an example. We can add as many as the need be.
Also create a Property in the setting file – LogProviderType. This Property can be used to switch between the Log Providers. Currently it holds the default value of “ENTLIB”.
#region Namespaces
using System;
#endregion Namespaces
namespace Infrastructure.Logging
{
/// <summary>
/// Enumurator for LogProviderType
/// </summary>
public enum LogProviderType
{
/// <summary>
/// Microsoft Enterprise Library
/// </summary>
ENTLIB
}
/// <summary>
/// Helper class for Log Provider
/// </summary>
public class LogProviderHelper
{
private static LogProviderType _logProviderType = LogProviderType.ENTLIB;
private static ILoggerProvider _logProvider = null;
/// <summary>
/// Initializes the <see cref="LogProviderHelper"/> class.
/// </summary>
static LogProviderHelper()
{
try
{
_logProviderType = (LogProviderType)Enum.Parse(typeof(LogProviderType), Properties.Settings.Default.LogProviderType.Trim().ToUpper());
}
catch (Exception exception)
{
//Set the default to EntLib
_logProviderType = LogProviderType.ENTLIB;
//TODO:Log exception Message
}
}
/// <summary>
/// Gets the log provider.
/// </summary>
/// <returns></returns>
public static ILoggerProvider GetLogProvider()
{
switch (_logProviderType)
{
case LogProviderType.ENTLIB:
return _logProvider = LogProviderFactory<EntLibLoggingHelper>.CreateProvider();
default:
return _logProvider = LogProviderFactory<EntLibLoggingHelper>.CreateProvider();
}
}
}
}
And Finally, the implementation of the Entlib Logging Helper class.
Note the use of the local Log Category Enum. Yes, we need to map it to the Master Log Category defined above. Note the use of the extra category “Unknown”. This is an example of handling categories that are not supported by the log provider
#region Namespaces
using System;
using System.Xml;
using System.Text;
using System.Xml.Serialization;
using Microsoft.Practices.EnterpriseLibrary.Logging;
#endregion Namespaces
namespace Infrastructure.Logging
{
/// <summary>
/// EntLibLogCategory
/// </summary>
public enum EntLibLogCategory
{
/// <summary>
/// Critical
/// </summary>
Critical,
/// <summary>
/// Error
/// </summary>
Error,
/// <summary>
/// Warning
/// </summary>
Warning,
/// <summary>
/// Security
/// </summary>
Security,
/// <summary>
/// Audit
/// </summary>
Audit,
/// <summary>
/// Service
/// </summary>
Service,
/// <summary>
/// UI
/// </summary>
UI,
/// <summary>
/// Information
/// </summary>
Information,
/// <summary>
/// Debug
/// </summary>
Debug,
/// <summary>
/// Trace
/// </summary>
Trace,
/// <summary>
/// Unknown
/// </summary>
Unknown
}
/// <summary>
/// LoggingHelper class. Uses Microsoft Enterprise Library
/// </summary>
public sealed class EntLibLoggingHelper : ILoggerProvider
{
/// <summary>
/// Converts the log category to ent lib log category.
/// </summary>
/// <param name="logCategory">The log category.</param>
/// <returns></returns>
EntLibLogCategory ConvertLogCategoryToEntLibLogCategory(LogCategory logCategory)
{
switch (logCategory)
{
case LogCategory.Audit:
return EntLibLogCategory.Audit;
break;
case LogCategory.Critical:
return EntLibLogCategory.Critical;
break;
case LogCategory.Debug:
return EntLibLogCategory.Debug;
break;
case LogCategory.Error:
return EntLibLogCategory.Error;
break;
case LogCategory.Information:
return EntLibLogCategory.Information;
break;
case LogCategory.Security:
return EntLibLogCategory.Security;
break;
case LogCategory.Service:
return EntLibLogCategory.Service;
break;
case LogCategory.Trace:
return EntLibLogCategory.Trace;
break;
case LogCategory.UI:
return EntLibLogCategory.UI;
break;
case LogCategory.Warning:
return EntLibLogCategory.Warning;
break;
default:
return EntLibLogCategory.Unknown;
break;
}
}
/// <summary>
/// Converts the log category array to ent lib log category array.
/// </summary>
/// <param name="logCategory">The log category.</param>
/// <returns></returns>
EntLibLogCategory[] ConvertLogCategoryArrayToEntLibLogCategoryArray(LogCategory[] logCategory)
{
EntLibLogCategory[] entLibCategoryArray = new EntLibLogCategory[logCategory.Length];// { };
for (int i = 0; i < logCategory.Length; i++)
{
entLibCategoryArray[i] = ConvertLogCategoryToEntLibLogCategory(logCategory[i]);
}
return entLibCategoryArray;
}
/// <summary>
/// Log a message against a single category.
/// </summary>
/// <param name="message">Message to be logged</param>
/// <param name="category">Category the message belongs to</param>
public void Write(string message, LogCategory category)
{
Write(message, new LogCategory[] { category });
}
/// <summary>
/// Log a message against multiple categories.
/// </summary>
/// <param name="message">Message to be logged</param>
/// <param name="categories">Categories the message belongs to</param>
public void Write(string message, LogCategory[] categories)
{
LogEntry logEntry = new LogEntry();
logEntry.Message = message;
EntLibLogCategory[] entLibLogCategories = ConvertLogCategoryArrayToEntLibLogCategoryArray(categories);
for (int count = 0; count < entLibLogCategories.Length; count++)
logEntry.Categories.Add(entLibLogCategories[count].ToString());
Log(logEntry);
}
/// <summary>
/// Log a message and category.
/// </summary>
/// <typeparam name="T">Type of message</typeparam>
/// <param name="message">Message body</param>
/// <param name="category">Category</param>
public void Write<T>(T message, LogCategory category)
{
Write<T>(message, new LogCategory[] { category });
}
/// <summary>
/// Log a message and its categories.
/// </summary>
/// <typeparam name="T">Type of message</typeparam>
/// <param name="message">Message body</param>
/// <param name="categories">Array of categories</param>
public void Write<T>(T message, LogCategory[] categories)
{
if (message == null)
return;
LogEntry logEntry = new LogEntry();
EntLibLogCategory[] entLibLogCategories = ConvertLogCategoryArrayToEntLibLogCategoryArray(categories);
for (int count = 0; count < entLibLogCategories.Length; count++)
logEntry.Categories.Add(entLibLogCategories[count].ToString());
//Message is serialized only if logging is turned on for the
//defined categories. This will avoid any performance overhead in logging.
if (Microsoft.Practices.EnterpriseLibrary.Logging.Logger.ShouldLog(logEntry))
{
logEntry.Message = GetSerializedMessage<T>(message);
Log(logEntry);
}
}
/// <summary>
/// Logs the specified ex.
/// </summary>
/// <param name="ex">The ex.</param>
public void Log(Exception ex)
{
Write(string.Format("{0} {1}", ex.Message, ex.StackTrace), LogCategory.Error);
if (ex.InnerException != null)
Log(ex.InnerException);
}
/// <summary>
/// Log a message with all associated details in LogEntry to
/// EntLib logger.
/// </summary>
/// <param name="logEntry">The log entry.</param>
private static void Log(LogEntry logEntry)
{
Microsoft.Practices.EnterpriseLibrary.Logging.Logger.Write(logEntry);
}
/// <summary>
/// Searialize the message body passed to it based on type.
/// </summary>
/// <typeparam name="T">Type of message</typeparam>
/// <param name="message">Message body</param>
/// <returns>Serialized message string</returns>
public string GetSerializedMessage<T>(T message)
{
string messageString = null;
if (typeof(T).IsPrimitive || message is string)
{
messageString = message.ToString();
}
else
{
XmlSerializer xmlSerializer = new XmlSerializer(typeof(T), "");
StringBuilder stringBuilder = new StringBuilder();
XmlWriterSettings xmlWriterSettings = new XmlWriterSettings();
xmlWriterSettings.Encoding = Encoding.UTF8;
XmlWriter xmlWriter = XmlWriter.Create(stringBuilder, xmlWriterSettings);
xmlSerializer.Serialize(xmlWriter, message);
messageString = stringBuilder.ToString();
}
return messageString;
}
}
}
The only other thing left is call the Logger Provider.
/// <summary>
/// loggerProvider
/// </summary>
ILoggerProvider loggerProvider;
//Create the Log Provider
loggerProvider = LogProviderHelper.GetLogProvider();
loggerProvider.Write(string.Format("Test Message by {0}.", App.UserName), LogCategory.Audit);
Don’t forget to add the Logging configuration in the Application config file
<!-- ENTLIB LOGGING CONFIGURATION START --><loggingConfiguration name="Logging Application Block" tracingEnabled="true"
defaultCategory="General" logWarningsWhenNoCategoriesMatch="true" >
<listeners>
<add name="Rolling Flat File"
fileName="C:\LogFiles\Infrastructure\trace.log"
rollSizeKB="2000" timeStampPattern="yyyy-MM-dd" rollFileExistsBehavior="Increment"
rollInterval="Day"
listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.RollingFlatFileTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging"
traceOutputOptions="None" type="Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners.RollingFlatFileTraceListener, Microsoft.Practices.EnterpriseLibrary.Logging"
/>
</listeners>
<formatters>
<add name="Text Formatter"
type="Microsoft.Practices.EnterpriseLibrary.Logging.Formatters.TextFormatter, Microsoft.Practices.EnterpriseLibrary.Logging"
template="Timestamp: {timestamp}
Message: {message}
Category: {category}
Priority: {priority}
EventId: {eventid}
Severity: {severity}
Activity: {activity}
Title: {title}
Machine: {machine}
Application Domain: {appDomain}
Process Id: {processId}
Process Name: {processName}
Win32 Thread Id: {win32ThreadId}
Thread Name: {threadName}
Extended Properties: {dictionary({key} - {value}
)}"
/>
</formatters>
<specialSources>
<allEvents switchValue="All" name="All Events" >
<listeners>
<add name="Rolling Flat File" />
</listeners>
</allEvents>
<notProcessed switchValue="All" name="Unprocessed Category" >
<listeners>
<add name="Rolling Flat File" />
</listeners>
</notProcessed>
<errors switchValue="All" name="Logging Errors & Warnings">
<listeners>
<add name="Rolling Flat File" />
</listeners>
</errors>
</specialSources>
<logFilters>
<!-- Comment the categories that you do not want logged below.-->
<add name="Category"
type="Microsoft.Practices.EnterpriseLibrary.Logging.Filters.CategoryFilter, Microsoft.Practices.EnterpriseLibrary.Logging"
categoryFilterMode="DenyAllExceptAllowed">
<categoryFilters>
<add name="Critical"></add>
<add name="Error"></add>
<add name="Security"></add>
<add name="Warning"></add>
<add name="Audit"></add>
<add name="UI"></add>
<add name="Service"></add>
<add name="Information"></add>
<add name="Debug"></add>
<add name="Trace"></add>
<add name="Unknown"></add>
</categoryFilters>
</add>
<add name="LogEnabled Filter"
type="Microsoft.Practices.EnterpriseLibrary.Logging.Filters.LogEnabledFilter, Microsoft.Practices.EnterpriseLibrary.Logging"
enabled="true"
/>
</logFilters>
</loggingConfiguration>
<!-- ENTLIB LOGGING CONFIGURATION END --> |