6

我们的错误处理程序通过电子邮件发送 SysAdmin 异常。现在它们作为纯文本看起来很糟糕。

有没有办法获取异常并将其格式化为漂亮的 html,以便系统管理员可以更轻松地阅读它?

4

3 回答 3

12
<pre>
... htmlencoded output from Exception.ToString() goes here ...
</pre>
于 2012-12-06T19:43:00.550 回答
2

我会将异常序列化为 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;
        })())
    { }
}
于 2012-12-06T22:04:43.287 回答
0

我正在考虑更多关于制作带有 %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;
}
于 2012-12-06T19:44:42.283 回答