Detailed 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: -2146233079StackTrace
**************************************************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: -2147352558StackTrace
**************************************************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)









– “What can C# do that F# cannot?”