2

我有一个来自Postsharp的多播 OnExceptionAspect ,它应用于程序集级别。这自然意味着所有方法在抛出异常时都会调用 Aspect。

在方面中,我正在记录异常详细信息,包括发生异常时传递的参数值,这可以正常工作。

但是,由于这适用于程序集中的所有方法,因此会为堆栈中的每个方法创建一个日志条目,因为每个方法都会出现异常。

我对如何防止这种情况一无所知,最初我打算比较异常(看看它是否相同),但这似乎很混乱。以前一定有人遇到过这个问题,有什么想法吗?

4

3 回答 3

4

这个问题有两种解决方案。

A. 使用线程静态字段来存储已记录的任何异常。

[Serializable] 
public class MyAspect : OnExceptionAspect 
{ 
    [ThreadStatic]
    private static Exception lastException;

    public override void OnException(MethodExecutionArgs args) 
    { 
      if(args.Exception != lastException) 
      { 
        string msg = string.Format("{0} had an error @ {1}: {2}\n{3}",  
            args.Method.Name, DateTime.Now,  
            args.Exception.Message, args.Exception.StackTrace); 

        Trace.WriteLine(msg); 
        lastException = args.Exception;
      } 

    }  
}

B. 将标签添加到 Exception 对象。

[Serializable] 
public class MyAspect : OnExceptionAspect 
{ 
    private static object marker = new object();

    public override void OnException(MethodExecutionArgs args) 
    { 
      if(!args.Exception.Data.Contains(marker)) 
      { 
        string msg = string.Format("{0} had an error @ {1}: {2}\n{3}",  
            args.Method.Name, DateTime.Now,  
            args.Exception.Message, args.Exception.StackTrace); 

        Trace.WriteLine(msg); 
        args.Exception.Data.Add(marker, marker);
      } 

    }  
}
于 2012-04-26T07:57:33.487 回答
3

仅供参考——Gael 是 PostSharp 大师,因为他在那里受雇……所以你知道。

对于它的价值,您始终可以通过检查 StackTrace 来判断异常源自何处。StackTrace 通过 args.Exception.StackTrace 提供。您可以在这里尝试 Dustin Davis(另一位 PostSharp 员工)推荐的方法:PostSharp - OnExceptionAspect - Get line number of exception

解析 StackTrace(通过此处概述的方法:How to split a stacktrace line into namespace, class, method file and line number?)然后将 args.Method.Name 与解析结果进行比较。如果您的 args.Method.Name 与原始方法相同(通过解析 StackTrace 找到),那么您知道应该记录它,否则忽略。

这里有一些代码可以让我的解决方案更加具体(基于前面提到的两个解决方案):

[Serializable]
public class ExceptionWrapper : OnExceptionAspect
{
    public override void OnException(MethodExecutionArgs args)
    {
        var st = new StackTrace(args.Exception, true);
        var frame = st.GetFrame(0);
        var lineNumber = frame.GetFileLineNumber();
        var methodName = frame.GetMethod().Name;
        if(methodName.Equals(args.Method.Name))
        {
            string msg = string.Format("{0} had an error @ {1}: {2}\n{3}", 
                args.Method.Name, DateTime.Now, 
                args.Exception.Message, args.Exception.StackTrace);

            Trace.WriteLine(msg);
        }
    } 
}

(或者,老实说,您可以只使用 Gael 推荐的解决方案之一。)

于 2012-09-12T13:48:16.157 回答
0

我可以看到完成此操作的一种方法是定义一个自定义异常,然后在您的方面抛出该异常。然后在您的方面也在登录之前检查异常,如果它不是您的自定义异常记录它,否则不要记录它并(重新抛出?)。

这就是示例代码的样子:

[Serializable]
public class DatabaseExceptionWrapper : OnExceptionAspect
{
    public override void OnException(MethodExecutionArgs args)
    {
      if(!(args.Exception is CustomException))
      {
        string msg = string.Format("{0} had an error @ {1}: {2}\n{3}", 
            args.Method.Name, DateTime.Now, 
            args.Exception.Message, args.Exception.StackTrace);

        Trace.WriteLine(msg);
      }

      throw new CustomException("There was a problem");
    } 
}

当然,您仍然必须定义该异常和所有内容。:)

于 2012-04-25T21:35:35.397 回答