logging-log4net-user mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Duncan Woods" <duncan.wo...@garradhassan.com>
Subject RE: When to log, when to throw - Help!
Date Mon, 03 Jul 2006 21:50:30 GMT
Ron,

>> Isn't the automatic clean-up more a feature of the 
>> using statement rather than the ThreadContext?

Sorry if I wasn't clear. I consider them intertwined because I wouldn't use explicit (push/pop)
ThreadContext without automatic clear-up. Too error prone.

I have tried to work out a scheme for anonymous delegates to provide context safe exceptions
but sadly there is too much dang strong typing so either you have to declare a delegate type
for anything you wish to execute contextually or do what I attach below which is ugly in so
many different ways! Basically it requires that each level of context is a separate method,
which is perfectly acceptable (and often the case anyway), and then call it as a delegate
but sadly passing arguments requires filthy casting and its broken for ref, out, value-types
and return values.

I post it to see if it sparks the creativity of anyone else to find a more effective scheme
that is simpler and type-safe.

On a related note - the NDC Method 'CloneStack' doesn't work as I expected - it doesn't return
a copy but a reference that is manipulated without consent plus the contained items are classes
internal to log4net that don't have a sensible ToString so you can't do diddly with them.
This is why I had to pop and re-push the ndc to copy it in NdcException. Doesn't FxCop warn
about this?

anyway... on to the ugliness, believe it or not, it is functionally the same example Ron gave
before:


using System;
using System.Collections.Generic;
using System.Text;
using log4net;
using log4net.Appender;
using log4net.Config;
using log4net.Layout;

namespace ConsoleApplication
{
    public delegate void ExecuteInContext(params object[] p);
    
    // execute code passed as an anonymous delegate with context safe exceptions
    public abstract class Context
    {
        public static void Execute(string context, ExecuteInContext call, params object[]
p)
        {            
            using (NDC.Push(context))
            {                                
                try
                {
                    call(p);
                }
                catch (Exception e)
                {
                    if (!(e is NdcException)) throw new NdcException(e);
                    else throw;
                }                
            }
        }
    }

    // Wrap a normal exception and append the ndc stack - requires more effort than it should
    public class NdcException : Exception
    {
        public NdcException(Exception e)
            : base(e.Message, e)
        {
            Stack<string> stack = new Stack<string>();            
            StringBuilder sb = new StringBuilder("Exception : ");
            while (NDC.Depth > 0)
            {
                stack.Push(NDC.Pop());
            }
            while (stack.Count > 0)
            {
                string s = stack.Pop();
                sb.AppendFormat("{0} ", s);
                NDC.Push(s);
            }
            Ndc = sb.ToString();
        }
        public string Ndc;
    }

    class Program
    {
        static ConsoleAppender consoleAppender;
        static ILog log;

        static void Main(string[] args)
        {
            consoleAppender = new ConsoleAppender();
            consoleAppender.Layout = new PatternLayout("MESSAGE [%message] NDC [%ndc]%newline");
            BasicConfigurator.Configure(consoleAppender);
            log = LogManager.GetLogger(typeof(Program));  
            
            int[] oddNumbers = new int[5] { 1, 3, 5, 7, 9 };
            int[] evenNumbers = new int[5] { 2, 4, 6, 8, 10 };
            
            try
            {
                foreach (int oddNumber in oddNumbers)
                {
                    Context.Execute(
                        "oddNumber: " + oddNumber,
                        delegate(object[] p) {EachEven((int) p[0], (int[]) p[1]);},
                        oddNumber, evenNumbers);                    
                }
            }
            // Provide a contextual exception message
            catch (NdcException ex)
            {
                using (NDC.Push(ex.Ndc))
                {
                    log.Fatal(ex.Message);
                }
            }
            catch (Exception ex)
            {
                log.Fatal("Should never happen: " + ex.Message, ex);
            }
        }

        public static void EachEven(int oddNumber, int[] evenNumbers)
        {
            foreach (int evenNumber in evenNumbers)
            {
                Context.Execute(
                    "evenNumber: " + evenNumber,
                    delegate(object[] p) {TestOdd((int) p[0], (int) p[1]);},
                    oddNumber, evenNumber);                
            }
        }

        public static void TestOdd(int oddNumber, int evenNumber)
        {            
            if (oddNumber == 7)
            {
                throw new ApplicationException("Oh no!");
            }
            log.DebugFormat("Sum: {0}", oddNumber + evenNumber);
        }        
    }    
}


Thanks,
Duncan

Mime
View raw message