Our error handler emails the SysAdmin exceptions. Right now they look bad as plain text.
Is there a way to take an Exception and format it into nice looking html so the SysAdmin can read it more easily?
<pre>
... htmlencoded output from Exception.ToString() goes here ...
</pre>
I would serialize the exception into an XML element and then I would format it with a custom XSLT.
There is an interesting approach about how to serialize an Exception
which you can read about here: Serializing Exceptions to XML. To summarize it, if you try to decorate a custom class inheriting from System.Exception
with the [Serializable]
attribute and then use the XmlSerializer class on it, you will get a runtime exception because of the Exception.Data property, which is implementing System.Collections.IDictionary
. So, you may easily use the new System.Xml.Linq API (new as of .NET 3.5).
Here is a simple program which generates an exception and formats it as 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);
}
}
}
Here is the 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>
And here is the ExceptionXElement class definition:
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;
})())
{ }
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With