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)
Discover more from Sergey Tihon's Blog
Subscribe to get the latest posts sent to your email.
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