search
Japanese Chinese Nederlands Espanol Italiano Deutsch Francais Twitter Rss Feeds
MicrosoftArticlesForumsFAQs
C# .NET
VB.NET
Visual Studio .NET
ADO.NET
Xml / Xslt
VB 6.0
.NET CF
GDI+
LINQ
Deployment
Security
FoxPro
Silverlight / WPF
Entity Framework
RIA Services

Web ProgrammingArticlesForumsFAQs
JavaScript
ASP
ASP.NET
Web Services

Non-MicrosoftArticlesForumsFAQs
NHibernate
Perl
PHP
Ruby
Java
Linux / Unix
Apple
Open Source

DatabasesArticlesForumsFAQs
SQL Server
Access
Oracle
MySQL
Other Databases

OfficeArticlesForumsFAQs
Excel
Word
Powerpoint
Outlook
Publisher
Money

Operating SystemsArticlesForumsFAQs
Windows 7
Windows Server
Windows Vista
Windows XP
Windows Update
MAC
Linux / UNIX

Server PlatformsArticlesForumsFAQs
BizTalk
Site Server
Exhange Server
IIS

Graphic DesignArticlesForumsFAQs
Macromedia Flash
Adobe PhotoShop
Expression Blend
Expression Design
Expression Web

OtherArticlesForumsFAQs
Subversion / CVS
Ask Dr. Dotnetsky
Active Directory
Networking
Uninstall Virus
Job Openings
Product Reviews
Search Engines
Resumes

 

Developing a Log Provider and a Factory Class to facilitate easy switching between Logging Components


By [)ia6l0 iii
Printer Friendly Version
View My Articles
7 Views
    

Lot of us need logging as a necessary integral part of our Projects. We use logging components from Microsoft, or any third party as and when we discuss them. But when we find that we have to switch between them we find it so tough, because they are so wildly integrated into our Module code which makes use of them.


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}&#xD;&#xA;Message: {message}&#xD;&#xA;Category: {category}&#xD;&#xA;Priority: {priority}&#xD;&#xA;EventId: {eventid}&#xD;&#xA;Severity: {severity}&#xD;&#xA;Activity: {activity}&#xD;&#xA;Title: {title}&#xD;&#xA;Machine: {machine}&#xD;&#xA;Application Domain: {appDomain}&#xD;&#xA;Process Id: {processId}&#xD;&#xA;Process Name: {processName}&#xD;&#xA;Win32 Thread Id: {win32ThreadId}&#xD;&#xA;Thread Name: {threadName}&#xD;&#xA;Extended Properties: {dictionary({key} - {value}&#xD;&#xA;)}" /> </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 &amp; 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 -->


button
Article Discussion: Developing a Log Provider and a Factory Class to facilitate easy switching between Logging Components
[)ia6l0 iii posted at Saturday, November 22, 2008 1:01 PM
Original Article