我们的错误处理程序通过电子邮件发送 SysAdmin 异常。现在它们作为纯文本看起来很糟糕。
有没有办法获取异常并将其格式化为漂亮的 html,以便系统管理员可以更轻松地阅读它?
<pre>
... htmlencoded output from Exception.ToString() goes here ...
</pre>
我会将异常序列化为 XML 元素,然后使用自定义 XSLT 对其进行格式化。
有一种关于如何序列化的有趣方法Exception
,您可以在此处阅读:Serializing Exceptions to XML。总而言之,如果您尝试System.Exception
使用该[Serializable]
属性装饰继承自的自定义类,然后在其上使用XmlSerializer类,您将得到一个运行时异常,因为Exception.Data属性正在实现System.Collections.IDictionary
。因此,您可以轻松使用新的System.Xml.Linq API(从 .NET 3.5 开始的新 API)。
这是一个生成异常并将其格式化为 HTML 的简单程序。
using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Xsl;
using ConsoleApplication2.Properties;
class Program
{
public static void Main(string[] args)
{
try
{
//throw an DivideByZeroException
var a=0;
var b=1/a;
}
catch (Exception ex)
{
//using the ExceptionXElement class
var xmlException = new ExceptionXElement(ex);
XslCompiledTransform myXslTrans = new XslCompiledTransform();
//Resources.formatter is the xsl file added as a Resource to the project (ConsoleApplication2.Properties.Resources.formatter)
//So, here we load the xsl
myXslTrans.Load(XmlReader.Create(new StringReader(Resources.formatter)));
//initialize a TextWriter, in this case a StringWriter and set it to write to a StringBuilder
StringBuilder stringBuilder = new StringBuilder();
XmlTextWriter myWriter = new XmlTextWriter(new StringWriter(stringBuilder));
//apply the XSL transformations to the xmlException and output them to the XmlWriter
myXslTrans.Transform(xmlException.CreateReader(), null, myWriter);
//outputting to the console the HTML exception (you can send it as the message body of an email)
Console.WriteLine(stringBuilder);
}
}
}
这是Formatter.xsl
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="utf-8" indent="no"/>
<xsl:template match="/">
<html>
<body>
<h1>
<xsl:value-of select="name(/*)"/>
</h1>
<h2>
<xsl:value-of select="//Message"/>
</h2>
<table border="1">
<tr bgcolor="#9acd32">
<th>StackTrace</th>
</tr>
<xsl:for-each select="//Frame">
<tr>
<td>
<xsl:value-of select="."/>
</td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
这是ExceptionXElement 类定义:
using System;
using System.Collections;
using System.Linq;
using System.Xml.Linq;
/// <summary>Represent an Exception as XML data.</summary>
public class ExceptionXElement : XElement
{
/// <summary>Create an instance of ExceptionXElement.</summary>
/// <param name="exception">The Exception to serialize.</param>
public ExceptionXElement(Exception exception)
: this(exception, false)
{ }
/// <summary>Create an instance of ExceptionXElement.</summary>
/// <param name="exception">The Exception to serialize.</param>
/// <param name="omitStackTrace">
/// Whether or not to serialize the Exception.StackTrace member
/// if it's not null.
/// </param>
public ExceptionXElement(Exception exception, bool omitStackTrace)
: base(new Func<XElement>(() =>
{
// Validate arguments
if (exception == null)
{
throw new ArgumentNullException("exception");
}
// The root element is the Exception's type
XElement root = new XElement
(exception.GetType().ToString());
if (exception.Message != null)
{
root.Add(new XElement("Message", exception.Message));
}
// StackTrace can be null, e.g.:
// new ExceptionAsXml(new Exception())
if (!omitStackTrace && exception.StackTrace != null)
{
root.Add
(
new XElement("StackTrace",
from frame in exception.StackTrace.Split('\n')
let prettierFrame = frame.Substring(6).Trim()
select new XElement("Frame", prettierFrame))
);
}
// Data is never null; it's empty if there is no data
if (exception.Data.Count > 0)
{
root.Add
(
new XElement("Data",
from entry in
exception.Data.Cast<DictionaryEntry>()
let key = entry.Key.ToString()
let value = (entry.Value == null) ?
"null" : entry.Value.ToString()
select new XElement(key, value))
);
}
// Add the InnerException if it exists
if (exception.InnerException != null)
{
root.Add
(
new ExceptionXElement
(exception.InnerException, omitStackTrace)
);
}
return root;
})())
{ }
}
我正在考虑更多关于制作带有 %message% 标记的 html 文件的内容。将 html 文件放入您的资源中,以便您可以像字符串一样调用它。然后只需使用 string.Replace("%message%", error.Message); 然后通过电子邮件发送。我在某处读到了一个很好的解决方案。我会找到这篇文章并编辑我的答案
我修改了一些类似的代码。看看这是否有帮助
public System.Net.Mail.AlternateView GenerateHTMLErrorEmail(Exception ex)
{
string body = ProductionEmailer.Properties.Resources.ShippedEmail;
//Build replacement collection to replace fields in email.html file
body = body.Replace("%MESSAGE%", ex.Message);
AlternateView html = AlternateView.CreateAlternateViewFromString(body, null, MediaTypeNames.Text.Html);
//add a image to the html email, optional
Bitmap b = new Bitmap(Properties.Resources.html_email_header_01);
ImageConverter ic = new ImageConverter();
Byte[] ba = (Byte[])ic.ConvertTo(b, typeof(Byte[]));
MemoryStream logo = new MemoryStream(ba);
LinkedResource header1 = new LinkedResource(logo, "image/gif");
header1.ContentId = "header1";
html.LinkedResources.Add(header1);
return html;
}