唯一标识.NET异常

时间:2011-03-29 22:58:29

标签: .net exception logging exception-handling

我希望通过对异常进行分组来改进我们的.NET应用程序日志记录。理想情况下,我们会按代码中出现的位置进行分组,以及它们是什么类型的异常。所以基本上我们最终会得到每个异常的出现列表(具有不同的日期,服务器变量等)。

例如,如果我的寄存器使用页面上有一个方法已经引发了5次空引用异常,那么我将列出该异常一次,并且计数为5次。另一个页面上抛出的另一个空引用异常将单独列出。

是否可以使用堆栈跟踪来可靠地识别异常,还是有其他我错过的东西?

4 个答案:

答案 0 :(得分:4)

如果捕获异常然后重新抛出,则其堆栈跟踪将显示为在重新抛出点而不是原始位置发生异常。希望您的代码中没有太多内容。如果没有,那么单独的堆栈跟踪应该足以区分异常位置。

另一个问题是堆栈跟踪是否相同,因此当它们真的相同时,它们似乎不是两个不同的异常。这也应该不是问题,因为根据我的经验,堆栈跟踪是相同的。 (通常,异常对象上的ToString()结果不会相同。)

<强>已更新

有一些关于Exception.StackTrace将被破坏的确切情况的讨论。基本规则是任何时候

throw *expression*; 
执行

,然后Exception标识的*expression*对象将设置其StackTrace属性。如果省略*expression*

throw;

然后StackTrace将不受影响。

我找不到这两种形式的任何术语,所以我将分别称它们为“显式”和“隐式”。请注意,*expression*解析为的异常对象是new还是已存在的对象无关紧要。

这是一个程序来说明这种行为:

using System;
namespace FunWithExceptions
{
    class Program
    {
        static void Main(string[] args)
        {
            try { Replace(); }
            catch (InvalidOperationException ex) { DisplayResult("Replace resulted in", ex); }

            try { RethrowExplicit(); }
            catch (InvalidOperationException ex) { DisplayResult("RethrowExplicit resulted in", ex); }

            try { RethrowImplicit(); }
            catch (InvalidOperationException ex) { DisplayResult("RethrowImplicit resulted in", ex); }

            InvalidOperationException myException = new InvalidOperationException();
            DisplayResult("myException starts with", myException);
            try { throw myException; }
            catch (InvalidOperationException) { }
            DisplayResult("myException changes to", myException);
            Console.ReadLine();
        }

        static void ThrowAnException()
        { throw new InvalidOperationException("You messed up!"); }

        static void Replace()
        {
            try { ThrowAnException(); }
            catch (InvalidOperationException ex)
            {
                DisplayResult("Replace caught", ex);
                throw new InvalidOperationException("Another mistake.");
            }
        }

        static void RethrowExplicit()
        {
            try { ThrowAnException(); }
            catch (InvalidOperationException ex)
            {
                DisplayResult("RethrowExplicit caught", ex);
                throw ex;
            }
        }

        static void RethrowImplicit()
        {
            try { ThrowAnException(); }
            catch (InvalidOperationException ex)
            {
                DisplayResult("RethrowImplicit caught", ex);
                throw;
            }
        }

        static void DisplayResult(string context, Exception ex)
        {
            Console.WriteLine("{0} exception thrown at {1}", context, FirstMethodName(ex.StackTrace));
        }

        private const string methodNamePrefix = "   at FunWithExceptions.Program.";

        private static object FirstMethodName(string stackTrace)
        {
            stackTrace = stackTrace ?? string.Empty;
            if (stackTrace.StartsWith(methodNamePrefix))
                stackTrace = stackTrace.Substring(methodNamePrefix.Length);
            int methodNameEndIndex = stackTrace.IndexOf(')');
            if (methodNameEndIndex != -1)
                stackTrace = stackTrace.Substring(0, methodNameEndIndex + 1);
            if (stackTrace.Length > 0)
                return stackTrace;
            else
                return "--empty--";
        }
    }
}

此程序产生以下输出:

  

替换ThrowAnException()

时抛出的捕获异常      

替换导致在Replace()

中抛出异常      

RethrowExplicit捕获了ThrowAnException()

抛出的异常      

RethrowExplicit导致RethrowExplicit()

抛出异常      

在ThrowAnException()

中抛出了RethrowImplicit异常      

RethrowImplicit导致ThrowAnException()

抛出异常      

myException从--empty -

抛出的异常开始      

myException更改为Main(String [] args)

抛出的异常

第六行是有趣的一行。

在那里,我现在已经完全击败了这一点。 : - )

答案 1 :(得分:1)

只要您不吞咽异常然后抛出新的异常(如下面的错误示例),您应该没问题。

try
{
    ... your code
}
catch (ExceptionA exA)
{
    ... some error handling
    throw new ExceptionZ();
}
catch (ExceptionB exB)
{
    ... some error handling
    throw new ExceptionZ();
}

此代码中的代码很糟糕,因为抛出异常会替换原始异常中的堆栈跟踪。一般来说,这不是一般的好习惯,但在这种情况下,它会阻止您唯一地查找和记录您的异常。

答案 2 :(得分:1)

好的,所以你基本上想要可靠地识别异常的异常PLUS位置。 堆栈跟踪对我来说似乎很好。 但您也可以使用方法名称

来确定您的位置标识
System.Reflection.MethodBase.GetCurrentMethod()

您可以从将在构造函数中调用GetCurrentMethod()的基本异常派生所有异常类型,并通过readonly属性公开方法名称,这样您就知道在要捕获的任何位置创建异常的位置在哪里例外情况。

示例:

public class BaseException : ApplicationException {
    public BaseException() {
        _originalMethod = System.Reflection.MethodBase.GetCurrentMethod().Name;
    }
    private string _originalMethod;
    public string OriginalMethod { get { return _originalMethod; } }
}

//now, create tons of custom exceptions:
public class MyException1 : BaseException {
    public MyException1()
        : base() {
    }
}
//create more custom exceptions...
public class MyExceptionNNN : BaseException {
    public MyExceptionNNN()
        : base() {
    }
}

答案 3 :(得分:0)

在您的示例中(在大多数情况下,实际上),我会抛出两个不同的自定义异常类型,具体取决于null值。捕获这些异常的代码可以记录必要的数据。