Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to write attribute that catches exceptions and removes stacktrace?

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?

like image 388
cm007 Avatar asked Feb 02 '12 14:02

cm007


People also ask

When should you Rethrow an exception?

If a catch block cannot handle the particular exception it has caught, you can rethrow the exception. The rethrow expression ( throw without assignment_expression) causes the originally thrown object to be rethrown.

Why do we Rethrow an exception C#?

Re-throwing Exceptions The exception from the previous section propagates up the call stack and since we have no code to catch and handle the exception, it crashes. When an exception is caught, we can perform some operations, like logging the error, and then re-throw the exception.

Can we throw exception from catch block C#?

A throw statement can be used in a catch block to re-throw the exception that is caught by the catch statement. The following example extracts source information from an IOException exception, and then throws the exception to the parent method. You can catch one exception and throw a different exception.


2 Answers

[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

like image 140
Dustin Davis Avatar answered Oct 29 '22 22:10

Dustin Davis


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.

like image 33
Igal Tabachnik Avatar answered Oct 29 '22 22:10

Igal Tabachnik