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)
Interesting. Just a minor suggestion:
let delimeter = String.replicate 50 “*”
It’s a bit clearer.
Oh, Thanks! Source code been updated.
did you mean “if (e 😕 TargetException && e.InnerException <> null)” in the code above? Looks like some autocorrect kicked in.
… it was WordPress. Substitute “:nospace?” in place of the injected HTML tag