How to write attribute that catches exceptions and removes stacktrace?
Asked Answered
F

2

6

I wish to write an attribute for a function (or class) that will catch any exception thrown and set its StackTrace property to string.Empty. How can I do this?

EDIT:

If I cannot accomplish this in plain C#, how can I do this in C# with PostSharp?

Foppish answered 2/2, 2012 at 14:31 Comment(3)
Curious: why do you want to do this?Barbiebarbieri
Please see my answer below, though I would discourage doing this. Exceptions exist for a reason, and that is to aid debugging when they occur. By removing the stack trace you're effectively hiding the source of the error, which you won't find if you're not debugging. If you're worried about privacy for some reason, you can try obfuscate or otherwise encrypt the stacktrace (using the same method described in the answer), so you could decrypt it later, say from a log file.Houseline
This is also what I need, and the reason is because I need to pass an Exception object to a webservice, but for some reason I can't get the service to accept it. So I could just pass the Message and StackTrace, then re-create the exception server-side.Iaria
K
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
        }
    }
}

You actually have to throw a NEW exception. @Ani's example will simply rethrow the exception already thrown with the same stack trace (it's the same because of how you got to the aspect). Throwing a new exception will "change" the stack trace but it won't erase it. If you want to erase it, you will need to throw your own class that overrides the stack trace property. passing in the old exception to the new exception will make the old exception the inner exception (if you want that)

You can accomplish this with and without PostSharp. The key is your custom exception class.

Given the following code

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;
    }
}

the output is

at ConsoleApplication5.MyAspect.OnException(MethodExecutionArgs args) in C:\T est\Program.cs:line 69 at ConsoleApplication5.Program.Test3() in C:\Test\Program.cs:line 59
at ConsoleApplication5.Program.Test2() in C:\Test\Program.cs:line 47

at ConsoleApplication5.MyAspect.OnException(MethodExecutionArgs args) in C:\T est\Program.cs:line 69 at ConsoleApplication5.Program.Test3() in C:\Test\Program.cs:line 59
at ConsoleApplication5.Program.Test2() in C:\Test\Program.cs:line 52
at ConsoleApplication5.Program.Test1() in C:\Test\Program.cs:line 34

at ConsoleApplication5.Program.Test1() in C:\Test\Program.cs:line 39 at ConsoleApplication5.Program.Main(String[] args) in C:\Test\Program.cs:line 19

Kosaka answered 2/2, 2012 at 17:0 Comment(3)
Is there a way to do this without a custom class? For example, if I wanted to remove the stack trace of a System.Exception that is thrown?Foppish
You can alwasy use reflection but really this isn't something you want to do in production. I'm hoping it's academic or for testing only. See @hmemcpy answer.Kosaka
In your Test1() method, why are you doing "throw e;" instead of "throw;"?Iaria
H
3

The original stack trace of the exception is stored in a field in the Exception class. If you want to remove it without creating your own exception type, you can remove it via reflection like this:

[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);
        }
    }
}

Your exception will no longer contain the stack trace.

Edit Of course you can accomplish the same thing without PostSharp too, just do it in the catch block.

Houseline answered 2/2, 2012 at 20:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.