Geeks With Blogs
Subodh Pushpak Technical musings on Win8, mobile (iOS / android / WP7) and WCF

One of my colleagues came-up with a unique situation where it was required to create log files based on the input file which is uploaded. For example if A.xml is uploaded, the corresponding log file should be A_log.txt.

I am a strong believer that Logging / EH / caching are cross-cutting architecture aspects and should be least invasive to the business-logic written in enterprise application.

I have been using Enterprise Library for logging / EH (i use to work with Avanade, so i have affection towards the library!! :D ). I have been also using excellent library called PostSharp for cross cutting aspect. Here i present a solution with and without PostSharp all in a unit test. Please see full source code at end of the this blog post.

But first, we need to tweak the enterprise library so that the log files are created at runtime based on input given. Below is Custom trace listner which writes log into a given file extracted out of Logentry extendedProperties property.

using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Logging.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners;
using Microsoft.Practices.EnterpriseLibrary.Logging;
using System.IO;
using System.Text;
using System;
using System.Diagnostics;
 
namespace Subodh.Framework.Logging
{
    [ConfigurationElementType(typeof(CustomTraceListenerData))]
    public class LogToFileTraceListener : CustomTraceListener
    {
 
        private static object syncRoot = new object();
 
        public override void TraceData(TraceEventCache eventCache, string source, TraceEventType eventType, int id, object data)
        {
 
            if ((data is LogEntry) & this.Formatter != null)
            {
                WriteOutToLog(this.Formatter.Format((LogEntry)data), (LogEntry)data);
            }
            else
            {
                WriteOutToLog(data.ToString(), (LogEntry)data);
            }
        }
 
        public override void Write(string message)
        {
            Debug.Print(message.ToString());
        }
 
        public override void WriteLine(string message)
        {
            Debug.Print(message.ToString());
        }
 
        private void WriteOutToLog(string BodyText, LogEntry logentry)
        {
            try
            {
                //Get the filelocation from the extended properties
                if (logentry.ExtendedProperties.ContainsKey("filelocation"))
                {
                    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();
                    }
 
                    //Lock the file 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(BodyText, sw);
                                sw.Close();
                            }
                            fs.Close();
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                throw new LoggingException(ex.Message, ex);
            }
        }
 
        /// <summary>
        /// Write message to named file
        /// </summary>
        public static void Log(string logMessage, TextWriter w)
        {
            w.WriteLine("{0}", logMessage);
        }
    }
}

 

The above can be “plugged into” the code using below configuration

<loggingConfiguration name="Logging Application Block" tracingEnabled="true"
    defaultCategory="Trace" logWarningsWhenNoCategoriesMatch="true">
    <listeners>
       <add listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.CustomTraceListenerData, 
Microsoft.Practices.EnterpriseLibrary.Logging, Version=4.1.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"

traceOutputOptions="None" filter="All" type="Subodh.Framework.Logging.LogToFileTraceListener,

Subodh.Framework.Logging, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"

        name="Subodh Custom Trace Listener" initializeData="" formatter="Text Formatter" />
   </listeners>

Similarly we can use PostSharp to expose the above as cross cutting aspects as below

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using PostSharp.Laos;
using System.Diagnostics;
using GC.FrameworkServices.ExceptionHandler;
using Subodh.Framework.Logging;
 
namespace Subodh.Framework.ExceptionHandling
{
    [Serializable]
    public sealed class LogExceptionAttribute : OnExceptionAspect
    {
         private string prefix;
        private MethodFormatStrings formatStrings;
 
        // This field is not serialized. It is used only at compile time.
        [NonSerialized] private readonly Type exceptionType;
        private string fileName;
 
        /// <summary>
        /// Declares a <see cref="XTraceExceptionAttribute"/> custom attribute
        /// that logs every exception flowing out of the methods to which
        /// the custom attribute is applied.
        /// </summary>
        public LogExceptionAttribute()
        {
        }
 
        /// <summary>
        /// Declares a <see cref="XTraceExceptionAttribute"/> custom attribute
        /// that logs every exception derived from a given <see cref="Type"/>
        /// flowing out of the methods to which
        /// the custom attribute is applied.
        /// </summary>
        /// <param name="exceptionType"></param>
        public LogExceptionAttribute( Type exceptionType )
        {
            this.exceptionType = exceptionType;
        }
 
        public LogExceptionAttribute(Type exceptionType, string fileName)
        {
            this.exceptionType = exceptionType;
            this.fileName = fileName;
        }
 
        /// <summary>
        /// Gets or sets the prefix string, printed before every trace message.
        /// </summary>
        /// <value>
        /// For instance <c>[Exception]</c>.
        /// </value>
        public string Prefix { get { return this.prefix; } set { this.prefix = value; } }
 
        /// <summary>
        /// Initializes the current object. Called at compile time by PostSharp.
        /// </summary>
        /// <param name="method">Method to which the current instance is
        /// associated.</param>
        public override void CompileTimeInitialize( MethodBase method )
        {
            // We just initialize our fields. They will be serialized at compile-time
            // and deserialized at runtime.
            this.formatStrings = Formatter.GetMethodFormatStrings( method );
            this.prefix = Formatter.NormalizePrefix( this.prefix );
        }
 
        public override Type GetExceptionType( MethodBase method )
        {
            return this.exceptionType;
        }
 
        /// <summary>
        /// Method executed when an exception occurs in the methods to which the current
        /// custom attribute has been applied. We just write a record to the tracing
        /// subsystem.
        /// </summary>
        /// <param name="context">Event arguments specifying which method
        /// is being called and with which parameters.</param>
        public override void OnException( MethodExecutionEventArgs context )
        {
           
            string message = String.Format("{0}Exception {1} {{{2}}} in {{{3}}}. \r\n\r\nStack Trace {4}",
                              this.prefix,
                              context.Exception.GetType().Name,
                              context.Exception.Message,
                              this.formatStrings.Format(context.Instance, context.Method, context.GetReadOnlyArgumentArray()),
                              context.Exception.StackTrace);
            if(!string.IsNullOrEmpty(fileName))
            {
                ApplicationLogger.LogException(message, fileName);
            }
            else
            {
                ApplicationLogger.LogException(message, Source.UtilityService);
            }
        }
    }
}

To use the above below is the unit test

 [TestMethod]
        [ExpectedException(typeof(NotImplementedException))]
        public void TestMethod1()
        {
            MethodThrowingExceptionForLog();
            try
            {
                MethodThrowingExceptionForLogWithPostSharp();
            }
            catch (NotImplementedException ex)
            {
                throw ex;
            }
        }
 
        private void MethodThrowingExceptionForLog()
        {
            try
            {
                throw new NotImplementedException();
            }
            catch (NotImplementedException ex)
            {
                // create file and then write log
                ApplicationLogger.TraceMessage("this is a trace message which will be logged in Test1MyFile", @"D:\EL\Test1Myfile.txt");
                ApplicationLogger.TraceMessage("this is a trace message which will be logged in YetAnotherTest1Myfile", @"D:\EL\YetAnotherTest1Myfile.txt");
            }
        }
 
        // Automatically log details using attributes
        // Log exception using attributes .... A La WCF [FaultContract(typeof(FaultMessage))] style]
        [Log(@"D:\EL\Test1MyfileLogPostsharp.txt")]
        [LogException(typeof(NotImplementedException), @"D:\EL\Test1MyfileExceptionPostsharp.txt")]
        private void MethodThrowingExceptionForLogWithPostSharp()
        {
            throw new NotImplementedException();
        }

The good thing about the approach is that all the logging and EH is done at centralized location controlled by PostSharp. Of Course, if some other library has to be used instead of EL, it can easily be plugged in. Also, the coder ARE ONLY involved in writing business code in methods, which makes code cleaner.

Here is the full source code. The third party assemblies provided are from EL and PostSharp and i presume you will find these useful.

Do let me know your thoughts / ideas on the same.

Posted on Tuesday, May 11, 2010 2:10 PM | Back to top


Comments on this post: Enterprise Library Logging / Exception handling and Postsharp

# re: Enterprise Library Logging / Exception handling and Postsharp
Requesting Gravatar...
Great article, I'm a big fan of Postsharp. I was trying to download the example though and the link did not work for me.

Thanks,

Darren
Left by darren on May 17, 2010 8:27 AM

# re: Enterprise Library Logging / Exception handling and Postsharp
Requesting Gravatar...
Hi,
Please follow the below link for code. Sorry for inconvenience.

http://cid-db22cddc0bcaf16e.skydrive.live.com/self.aspx/Other%20Resources/LoggingEHusingELandPostSharp.zip
Left by Subodh on May 18, 2010 7:29 PM

# re: Enterprise Library Logging / Exception handling and Postsharp
Requesting Gravatar...
Why did you make it so the aspect explicitly requires a type and a log file destination? This isn't very flexible and really takes away one of the big advantages of EntLib. I would suggest using an aspect that uses the EH block tied to the Logging Block and create, at worst, a custom listener to handle your file naming scheme.

I'm going to be posting similar things in the next week or so along with some advanced aspects on dotnetnate.com.
Left by Nathan Smith on Jun 23, 2010 9:03 PM

# re: Enterprise Library Logging / Exception handling and Postsharp
Requesting Gravatar...
http://public.blu.livefilestore.com/y1pwGxFQXhhcNpXmbkGbGYNsDkjnxp0s44cqbXfMI0p_dXcUZFgzwJFW_HDfa0LXuCW2y5kGGgHeKNmk9cn8t4yqw/LoggingEHusingELandPostSharp.zip?download fails for me, 404 error

please update post with url right, correct

Thanks mister,you're great
Left by espinete on Mar 30, 2011 6:58 PM

# re: Enterprise Library Logging / Exception handling and Postsharp
Requesting Gravatar...
Nice post man Best Hybrid Bikes​ njoyed it. :)
Left by Pro ranker on Sep 05, 2017 12:48 AM

# re: Enterprise Library Logging / Exception handling and Postsharp
Requesting Gravatar...
The oost is ducbiu bub uib n oiBest z170 Motherboard ui iuui ib uib iu bui bui b.
Left by Pro ranker on Sep 05, 2017 12:49 AM

# re: Enterprise Library Logging / Exception handling and Postsharp
Requesting Gravatar...
Hello man Corcoran Boots you can sleep well now as am ranking am banking hard
Left by Pro ranker on Sep 05, 2017 12:50 AM

Your comment:
 (will show your gravatar)


Copyright © subodhnpushpak | Powered by: GeeksWithBlogs.net