From 989bf939b586ae90c347a9b6b3053d6481bde78d Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Wed, 4 Dec 2019 10:38:08 -0700 Subject: [PATCH] Include RPC server stack trace in RemoteInvocationException.ToString() It isn't always possible to do, but when the server provides a `CommonErrorData` object, we certainly can do it. I endeavored to copy the string format that .NET uses for its own exception formatting, including for inner exceptions. Closes #369 --- .../Exceptions/RemoteInvocationException.cs | 52 +++++++++++++++++++ .../netcoreapp2.1/PublicAPI.Unshipped.txt | 1 + .../netstandard2.0/PublicAPI.Unshipped.txt | 1 + 3 files changed, 54 insertions(+) diff --git a/src/StreamJsonRpc/Exceptions/RemoteInvocationException.cs b/src/StreamJsonRpc/Exceptions/RemoteInvocationException.cs index 6b3bf59a..c37292b2 100644 --- a/src/StreamJsonRpc/Exceptions/RemoteInvocationException.cs +++ b/src/StreamJsonRpc/Exceptions/RemoteInvocationException.cs @@ -4,6 +4,8 @@ namespace StreamJsonRpc { using System; + using System.IO; + using System.Text; using Newtonsoft.Json.Linq; using StreamJsonRpc.Protocol; @@ -83,5 +85,55 @@ protected RemoteInvocationException( /// The default implementation of this method produces a object. /// public object? DeserializedErrorData { get; } + + /// + public override string ToString() + { + string result = base.ToString(); + + if (this.DeserializedErrorData is CommonErrorData errorData) + { + var builder = new StringBuilder(result); + builder.AppendLine(); + builder.AppendLine("RPC server exception:"); + builder.AppendLine($"{errorData.TypeName}: {errorData.Message}"); + if (errorData.Inner is object) + { + ContributeInnerExceptionDetails(builder, errorData.Inner); + } + + ContributeStackTrace(builder, errorData); + result = builder.ToString(); + } + + return result; + } + + private static void ContributeInnerExceptionDetails(StringBuilder builder, CommonErrorData errorData) + { + builder.AppendLine($" ---> {errorData.TypeName}: {errorData.Message}"); + if (errorData.Inner is object) + { + ContributeInnerExceptionDetails(builder, errorData.Inner); + } + + ContributeStackTrace(builder, errorData); + + builder.AppendLine(" --- End of inner exception stack trace ---"); + } + + private static void ContributeStackTrace(StringBuilder builder, CommonErrorData errorData) + { + if (errorData.StackTrace != null) + { + using (var sr = new StringReader(errorData.StackTrace)) + { + while (sr.ReadLine() is string line) + { + builder.AppendLine($" {line}"); + } + } + } + } } } diff --git a/src/StreamJsonRpc/netcoreapp2.1/PublicAPI.Unshipped.txt b/src/StreamJsonRpc/netcoreapp2.1/PublicAPI.Unshipped.txt index daac84fb..b64c3f41 100644 --- a/src/StreamJsonRpc/netcoreapp2.1/PublicAPI.Unshipped.txt +++ b/src/StreamJsonRpc/netcoreapp2.1/PublicAPI.Unshipped.txt @@ -72,6 +72,7 @@ const StreamJsonRpc.Reflection.MessageFormatterEnumerableTracker.TokenPropertyNa const StreamJsonRpc.Reflection.MessageFormatterEnumerableTracker.ValuesPropertyName = "values" -> string override StreamJsonRpc.PipeMessageHandler.DisposeReader() -> void override StreamJsonRpc.PipeMessageHandler.DisposeWriter() -> void +override StreamJsonRpc.RemoteInvocationException.ToString() -> string override StreamJsonRpc.RemoteMethodNotFoundException.GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) -> void override StreamJsonRpc.RequestId.Equals(object obj) -> bool override StreamJsonRpc.RequestId.GetHashCode() -> int diff --git a/src/StreamJsonRpc/netstandard2.0/PublicAPI.Unshipped.txt b/src/StreamJsonRpc/netstandard2.0/PublicAPI.Unshipped.txt index daac84fb..b64c3f41 100644 --- a/src/StreamJsonRpc/netstandard2.0/PublicAPI.Unshipped.txt +++ b/src/StreamJsonRpc/netstandard2.0/PublicAPI.Unshipped.txt @@ -72,6 +72,7 @@ const StreamJsonRpc.Reflection.MessageFormatterEnumerableTracker.TokenPropertyNa const StreamJsonRpc.Reflection.MessageFormatterEnumerableTracker.ValuesPropertyName = "values" -> string override StreamJsonRpc.PipeMessageHandler.DisposeReader() -> void override StreamJsonRpc.PipeMessageHandler.DisposeWriter() -> void +override StreamJsonRpc.RemoteInvocationException.ToString() -> string override StreamJsonRpc.RemoteMethodNotFoundException.GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) -> void override StreamJsonRpc.RequestId.Equals(object obj) -> bool override StreamJsonRpc.RequestId.GetHashCode() -> int