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 -->