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 以降の新機能) を簡単に使用できます。

以下は、例外を生成し、それを 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 に答える