F# Exception Formatter


200px-exception-printerDetailed pretty printed exception is a key to understand the real cause of the problem. We have a piece of code in C# that was reused for many projects, which formats exceptions sequence into human readable form with all exception details.

This source code was translated from C# and hopefully will be helpful to someone else:

open System
open System.Reflection
open System.Text
open Microsoft.FSharp.Core.Printf

let formatDisplayMessage (e:Exception) =
    let sb = StringBuilder()
    let delimeter = String.replicate 50 "*"
    let nl = Environment.NewLine
    let rec printException (e:Exception) count =
        if (e 😕 TargetException && e.InnerException <> null)
        then printException (e.InnerException) count
        else
            if (count = 1) then bprintf sb "%s%s%s" e.Message nl delimeter
            else bprintf sb "%s%s%d)%s%s%s" nl nl count e.Message nl delimeter
            bprintf sb "%sType: %s" nl (e.GetType().FullName)
            // Loop through the public properties of the exception object
            // and record their values.
            e.GetType().GetProperties()
            |> Array.iter (fun p ->
                // Do not log information for the InnerException or StackTrace.
                // This information is captured later in the process.
                if (p.Name <> "InnerException" && p.Name <> "StackTrace" &&
                    p.Name <> "Message" && p.Name <> "Data") then
                    try
                        let value = p.GetValue(e, null)
                        if (value <> null)
                        then bprintf sb "%s%s: %s" nl p.Name (value.ToString())
                    with
                    | e2 -> bprintf sb "%s%s: %s" nl p.Name e2.Message
            )
            if (e.StackTrace <> null) then
                bprintf sb "%s%sStackTrace%s%s%s" nl nl nl delimeter nl
                bprintf sb "%s%s" nl e.StackTrace
            if (e.InnerException <> null)
            then printException e.InnerException (count+1)
    printException e 1
    sb.ToString()

Now, you can print exceptions into a more readable form. For example, if you execute this expression:

let x =
    try
        try
            Some(10 / 0)
        with
        | e -> InvalidOperationException("Incorrect operation",e) |> raise
    with
    | e ->
         printfn "%s" (e|>formatDisplayMessage)
         None

you will see the following output

Incorrect operation
**************************************************
Type: System.InvalidOperationException
TargetSite: Void main@()
Source: FSI-ASSEMBLY
HResult: -2146233079

StackTrace
**************************************************

at <StartupCode$FSI_0019>.$FSI_0019.main@() in D:\Projects\Exception.fs:line 48

2)Attempted to divide by zero.
**************************************************
Type: System.DivideByZeroException
TargetSite: Void main@()
Source: FSI-ASSEMBLY
HResult: -2147352558

StackTrace
**************************************************

at <StartupCode$FSI_0019>.$FSI_0019.main@() in D:\Projects\Exception.fs:line 46

P.S. If you wish, you can add this function into FSI printers list.

fsi.AddPrinter(formatDisplayMessage)

3 thoughts on “F# Exception Formatter

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s