如何编写捕获异常并移除堆栈跟踪的属性?

6

我希望为一个函数(或类)编写一个属性,可以捕获抛出的任何异常,并将其StackTrace属性设置为string.Empty。我该如何做到这一点?

编辑:

如果我在普通的C#中无法实现这一点,那么我该如何在使用PostSharp的C#中实现呢?


1
好奇:你为什么想要这样做? - Matthew Groves
1
请看下面我的回答,虽然我不建议这样做。异常存在的原因是为了在出现异常时帮助调试。如果您删除堆栈跟踪,实际上就隐藏了错误的来源,如果您没有进行调试,就无法找到错误的源头。如果您担心某些原因导致隐私泄露,可以尝试混淆或以其他方式加密堆栈跟踪(使用回答中描述的相同方法),以便稍后从日志文件中解密。 - Igal Tabachnik
这也是我需要的,原因是因为我需要将一个异常对象传递给一个Web服务,但由于某种原因,我无法让服务接受它。因此,我可以只传递消息和堆栈跟踪,然后在服务器端重新创建异常。 - ganders
2个回答

3
[Serializable] 
public class MyAspect: OnExceptionAspect 
{
    public override void OnException(MethodExecutionArgs args)
    {
       throw new MyCustomException(args.Exception);
    }
} 


public class MyCustomException : Exception
{
    public override string StackTrace
    {
        get
        {
            //return new StackTrace(10).ToString(); //Skip frames
            return string.Empty; //Return empty string
        }
    }
}

您实际上需要抛出一个新的异常。@Ani的示例将简单地重新抛出已经具有相同堆栈跟踪的异常(由于您如何进入方面,它是相同的)。抛出新异常将“更改”堆栈跟踪,但不会删除它。如果要删除它,您需要抛出自己的类,该类覆盖了堆栈跟踪属性。将旧异常传递给新异常将使旧异常成为内部异常(如果您想要这样做)。
您可以使用或不使用PostSharp来完成此操作。关键是您的自定义异常类。
给定以下代码。
class Program
{
    static void Main(string[] args)
    {
        try
        {
            Test1();
        }
        catch (Exception e)
        {
            Console.WriteLine(e.StackTrace + Environment.NewLine);
        }

        Console.ReadKey();
    }

    private static void Test1()
    {
        try
        {
            Test2();
        }
        catch (Exception e)
        {
            Console.WriteLine(e.StackTrace + Environment.NewLine);
            throw e;
        }
    }

    private static void Test2()
    {
        try
        {
            Test3();
        }
        catch (Exception e)
        {
            Console.WriteLine(e.StackTrace + Environment.NewLine);
            throw;
        }
    }

    [MyAspect]
    private static void Test3()
    {
        throw new InvalidOperationException();
    }
}

[Serializable]
public class MyAspect : OnExceptionAspect
{
    public override void OnException(MethodExecutionArgs args)
    {
        throw args.Exception;
    }
}

输出结果为:

在 C:\Test\Program.cs 的第 69 行,ConsoleApplication5.MyAspect.OnException(MethodExecutionArgs args)
在 C:\Test\Program.cs 的第 59 行,ConsoleApplication5.Program.Test3()
在 C:\Test\Program.cs 的第 47 行,ConsoleApplication5.Program.Test2()

在 C:\Test\Program.cs 的第 69 行,ConsoleApplication5.MyAspect.OnException(MethodExecutionArgs args)
在 C:\Test\Program.cs 的第 59 行,ConsoleApplication5.Program.Test3()
在 C:\Test\Program.cs 的第 52 行,ConsoleApplication5.Program.Test2()
在 C:\Test\Program.cs 的第 34 行,ConsoleApplication5.Program.Test1()

在 C:\Test\Program.cs 的第 39 行,ConsoleApplication5.Program.Test1()
在 C:\Test\Program.cs 的第 19 行,ConsoleApplication5.Program.Main(String[] args)


1
有没有一种方法可以不使用自定义类来实现这个?例如,如果我想要删除抛出的 System.Exception 的堆栈跟踪? - cm007
你总是可以使用反射,但在生产环境中这并不是你想要做的事情。我希望这只是学术或仅用于测试。请参考@hmemcpy的答案。 - Dustin Davis
在你的Test1()方法中,为什么要写"throw e;"而不是"throw;"? - ganders

3

异常的原始堆栈跟踪信息存储在Exception类的一个字段中。如果您不想创建自己的异常类型,但又想删除这些信息,可以使用反射来移除它,如下所示:

[Serializable] 
public sealed class NoStackTraceException : OnExceptionAspect 
{
    public override void OnException(MethodExecutionArgs args)
    {
       RemoveStackTrace(args.Exception);
    }

    private void RemoveStackTrace(Exception exception)
    {
        FieldInfo stackTraceField = typeof(Exception).GetField("_stackTrace",
             BindingFlags.NonPublic | BindingFlags.Instance);
        if (stackTraceField != null)
        {
            // sets the value of _stackTrace to null
            stackTraceField.SetValue(exception, null);
        }
    }
}

您的异常将不再包含堆栈跟踪。

编辑 当然,您也可以在catch块中实现同样的功能,而不需要使用PostSharp。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接