在 .NET / C# 程序中出现异常是很常见的事情,程序出现异常后记录日志或者收集到统一的地方可以便于分析程序中各种各样此前未知的问题。但是,有些异常表示的是同一个异常,只是因为参数不同、状态不同、用户的语言环境不同就分开成多个异常的话,分析起来会有些麻烦。
本文将提供一个方法,将异常的关键信息提取出来,这样可以比较多次抛出的不同的异常实例是否表示的是同一个异常。
Exception.ToString()
以下是捕获到的一个异常实例,调用 ToString()
方法后拿到的结果:
System.NotSupportedException: BitmapMetadata 在 BitmapImage 上可用。
在 System.Windows.Media.Imaging.BitmapImage.get_Metadata()
在 System.Windows.Media.Imaging.BitmapFrame.Create(BitmapSource source)
在 Walterlv.Demo.Exceptions.Foo.Take(string fileName)
在英文的系统上,拿到的结果可能是这样的:
System.NotSupportedException: BitmapMetadata is not available on BitmapImage.
at System.Windows.Media.Imaging.BitmapImage.get_Metadata()
at System.Windows.Media.Imaging.BitmapFrame.Create(BitmapSource source)
at Walterlv.Demo.Exceptions.Foo.Take(string fileName)
这样,我们就不能使用 ToString()
来判断两个异常是否表示同一个异常了。
另外,在 ToString()
方法中,如果包含 PDB,那么异常堆栈中还会包含源代码文件的路径以及行号信息。
关于 ToString()
中输出的信息,可以阅读 StackTrace.ToString()
方法的源码来了解:
哪些信息是异常的关键信息
从默认的 ToString()
中我们可以得知,它包含三个部分:
- 异常类型的全名
Type.FullName
- 异常信息
Exception.Message
- 异常堆栈
Exception.StackTrace
考虑到 Message
部分受多语言影响非常严重,很难作为关键异常特征,所以我们在提取关键异常特征的时候,需要将这一部分去掉,只能作为此次异常的附加信息,而不能作为关键特征。
所以我们的关键特征就是:
- 异常类型的全名
Type.FullName
- 异常堆栈中所有帧的方法签名(这能保证语言无关)
比如本文一开始列举出来的异常堆栈,我们应该提取成:
System.NotSupportedException
System.Windows.Media.Imaging.BitmapImage.get_Metadata()
System.Windows.Media.Imaging.BitmapFrame.Create(BitmapSource source)
Walterlv.Demo.Exceptions.Foo.Take(string fileName)
提取特征的 C# 代码
为了提取出以上的关键特征,我需要写一段 C# 代码来做这样的事情:
public (string<