Search code examples
stack-overflowpostsharp

Code reuse in PostSharp's OnMethodBoundaryAspect leads to StackOverflowException


I have written a custom OnMethodBoundaryAspect called TraceAspect. This aspect checks within the OnEntry, OnExit, and OnException methods whether tracing is enabled or not. I have a central class for reading and writing settings. Both of the two methods Settings.GetLoggingEnabled() and Settings.GetLogLevel() are called from the TraceAspect. They are there, so I reuse them which results in a StackOverflowException.

[assembly: MyCompany.MyProduct.TraceAspect]

[Serializable]
public class TraceAspect : OnMethodBoundaryAspect
{
    public override void OnEntry(MethodExecutionArgs args)
    {
        if (Settings.GetLogginEnabled() && Settings.GetLogLevel() == LogLevel.Trace)
        {
            // Log the message
        }
    }
}

Applying the [TraceAspect(AttributeExclude = true)] attribute to the TraceAspect class leads to the same behaviour.

I could write something like this. But this is code duplication.

[assembly: MyCompany.MyProduct.TraceAspect]

[Serializable]
public class TraceAspect : OnMethodBoundaryAspect
{
    public override void OnEntry(MethodExecutionArgs args)
    {
        if (this.GetLogginEnabled() && this.GetLogLevel() == LogLevel.Trace)
        {
            // Log the message
        }
    }

    private bool GetLoggingEnabled()
    {
        // copy code from Settings.GetLogginEnabled()
    }

    private bool GetLogLevel()
    {
        // copy code from Settings.GetLogLevel()
    }
}

How can I tell that the Settings.GetLoggingEnabled() and Settings.GetLogTrace() methods should not be traced, when they are called by the aspect?


Solution

  • You can break the recursion during logging by introducing a thread static flag to indicate that you're currently inside the logging call.

    [Serializable]
    public class TraceAspect : OnMethodBoundaryAspect
    {
        [ThreadStatic]
        private static bool isLogging;
    
        public override void OnEntry(MethodExecutionArgs args)
        {
            if (isLogging) return;
    
            isLogging = true;
            try
            {
                if (Settings.GetLogginEnabled() && Settings.GetLogLevel() == LogLevel.Trace)
                {
                    // Log the message
                }
            }
            finally
            {
                isLogging = false;
            }
        }
    }