我有来自multicast的OnExceptionAspect Postsharp,它适用于装配级别。这自然意味着所有方法在抛出异常时都会调用Aspect。
在Aspect中我记录了异常细节,包括发生异常时传递的参数值,这是正常的。
但是,因为这适用于程序集中的所有方法,所以会为堆栈中的每个方法创建一个日志条目,因为异常会向每个方法冒泡。
我没有关于如何防止这种情况的想法,最初我要比较异常(看它是否是同一个),但这看起来很混乱。以前有人一定有这个问题,有什么想法吗?
这个问题有两种解决方案。
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);
}
}
}
仅供参考 - 盖尔是一名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推荐的解决方案之一。)
我可以看到这样做的一种方法是定义一个自定义异常,然后在你的方面抛出一个。然后在你的方面检查loggin之前的异常,如果它不是你的自定义异常日志,否则不记录它(重新抛出?)。
这就是示例代码的样子:
[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");
}
}
当然,您仍然必须定义该异常和所有内容。 :)