Skip to content

Commit b690d8a

Browse files
committed
Simple json writes exception (+ max 20 inner exceptions) as flattened array, mimicing serilog behavior
1 parent 46e82d2 commit b690d8a

File tree

2 files changed

+189
-73
lines changed

2 files changed

+189
-73
lines changed

src/Elasticsearch.Net/Serialization/ElasticsearchNetJsonStrategy.cs

Lines changed: 56 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Globalization;
4+
using System.Linq;
45
using System.Reflection;
56
using System.Runtime.Serialization;
67

@@ -16,40 +17,67 @@ public override bool TrySerializeNonPrimitiveObject(object input, out object out
1617
if (input is Exception)
1718
{
1819
var e = input as Exception;
19-
var o = new JsonObject();
20-
21-
var si = new SerializationInfo(e.GetType(), new FormatterConverter());
22-
var sc = new StreamingContext();
23-
e.GetObjectData(si, sc);
24-
25-
var helpUrl = si.GetString("HelpURL");
26-
var stackTrace = si.GetString("StackTraceString");
27-
var remoteStackTrace = si.GetString("RemoteStackTraceString");
28-
var remoteStackIndex = si.GetInt32("RemoteStackIndex");
29-
var exceptionMethod = si.GetString("ExceptionMethod");
30-
var hresult = si.GetInt32("HResult");
31-
var source = si.GetString("Source");
32-
var className = si.GetString("ClassName");
33-
34-
//TODO Loop over ISerializable data
35-
36-
o.Add("ClassName", className);
37-
o.Add("Message", e.Message);
38-
o.Add("Source", source);
39-
o.Add("StackTraceString", stackTrace);
40-
o.Add("RemoteStackTraceString", remoteStackTrace);
41-
o.Add("RemoteStackIndex", remoteStackIndex);
42-
o.Add("HResult", hresult);
43-
o.Add("HelpURL", helpUrl);
44-
this.WriteStructuredExceptionMethod(o, exceptionMethod);
45-
46-
output = o;
20+
var exceptionsJson = this.FlattenExceptions(e).ToList();
21+
var array = new JsonArray(exceptionsJson.Count);
22+
array.AddRange(exceptionsJson);
23+
output = array;
4724
return true;
4825

4926
}
5027
return base.TrySerializeNonPrimitiveObject(input, out output);
5128
}
5229

30+
31+
private IEnumerable<JsonObject> FlattenExceptions(Exception e)
32+
{
33+
int depth = 0;
34+
int maxExceptions = 20;
35+
do
36+
{
37+
JsonObject o = ToExceptionJsonObject(e, depth);
38+
depth++;
39+
yield return o;
40+
e = e.InnerException;
41+
42+
}
43+
while (depth < maxExceptions && e != null);
44+
}
45+
46+
47+
48+
49+
private JsonObject ToExceptionJsonObject(Exception e, int depth)
50+
{
51+
var o = new JsonObject();
52+
53+
var si = new SerializationInfo(e.GetType(), new FormatterConverter());
54+
var sc = new StreamingContext();
55+
e.GetObjectData(si, sc);
56+
57+
var helpUrl = si.GetString("HelpURL");
58+
var stackTrace = si.GetString("StackTraceString");
59+
var remoteStackTrace = si.GetString("RemoteStackTraceString");
60+
var remoteStackIndex = si.GetInt32("RemoteStackIndex");
61+
var exceptionMethod = si.GetString("ExceptionMethod");
62+
var hresult = si.GetInt32("HResult");
63+
var source = si.GetString("Source");
64+
var className = si.GetString("ClassName");
65+
66+
//TODO Loop over ISerializable data
67+
68+
o.Add("Depth", depth);
69+
o.Add("ClassName", className);
70+
o.Add("Message", e.Message);
71+
o.Add("Source", source);
72+
o.Add("StackTraceString", stackTrace);
73+
o.Add("RemoteStackTraceString", remoteStackTrace);
74+
o.Add("RemoteStackIndex", remoteStackIndex);
75+
o.Add("HResult", hresult);
76+
o.Add("HelpURL", helpUrl);
77+
this.WriteStructuredExceptionMethod(o, exceptionMethod);
78+
return o;
79+
}
80+
5381
private void WriteStructuredExceptionMethod(JsonObject o, string exceptionMethodString)
5482
{
5583
if (string.IsNullOrWhiteSpace(exceptionMethodString)) return;
@@ -74,7 +102,6 @@ private void WriteStructuredExceptionMethod(JsonObject o, string exceptionMethod
74102
exceptionMethod.Add("Signature", signature);
75103
exceptionMethod.Add("MemberType", memberType);
76104
o.Add("ExceptionMethod", exceptionMethod);
77-
78105
}
79106

80107

Lines changed: 133 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Globalization;
34
using System.IO;
5+
using System.Linq;
46
using System.Reflection;
57
using System.Runtime.Serialization;
68
using System.Text;
@@ -12,75 +14,149 @@ namespace Tests.Reproduce
1214
{
1315
public class GithubIssue2052
1416
{
17+
private const string _objectMessage = "My message";
18+
private static object _bulkHeader =
19+
new { index = new { _index = "myIndex", _type = "myDocumentType" } };
20+
private readonly ElasticLowLevelClient _client;
21+
private AssemblyName _assemblyName = new AssemblyName(typeof(GithubIssue2052).Assembly.FullName);
1522

16-
[U]
17-
public void BadSimpleJsonDeserialization()
23+
public GithubIssue2052()
1824
{
1925

20-
var ex = this.GimmeACaughtException();
21-
22-
var document = new Dictionary<string, object>{
23-
{ "message", "My message"},
24-
{ "exception", ex }
25-
};
26-
2726
var pool = new StaticConnectionPool(new List<Uri> { new Uri("http://localhost:9200") });
2827
var memoryConnection = new InMemoryConnection();
2928
var connectionSettings = new ConnectionConfiguration(pool, memoryConnection)
3029
.DisableDirectStreaming();
31-
var client = new ElasticLowLevelClient(connectionSettings);
30+
this._client = new ElasticLowLevelClient(connectionSettings);
31+
}
3232

3333

34-
var header = new { index = new { _index = "myIndex", _type = "myDocumentType" } };
35-
var payload = new List<object>{
36-
header,
37-
document
38-
};
39-
var response = client.Bulk<byte[]>(payload);
34+
[U] public void SingleThrownExceptionCanBeSerializedUsingSimpleJson()
35+
{
36+
37+
var ex = this.GimmeACaughtException();
4038

39+
var request = this.CreateRequest(ex);
40+
var postData = this.CreatePostData(ex);
4141

42-
var request = Encoding.UTF8.GetString(response.RequestBodyInBytes);
43-
var a = new AssemblyName(this.GetType().Assembly.FullName);
42+
this.AssertRequestEquals(request, postData);
43+
}
44+
45+
[U] public void MultipleThrownExceptionCanBeSerializedUsingSimpleJson()
46+
{
47+
48+
var ex = this.GimmeAnExceptionWithInnerException();
49+
50+
var request = this.CreateRequest(ex);
51+
var postData = this.CreatePostData(ex);
52+
53+
this.AssertRequestEquals(request, postData);
54+
}
4455

45-
var si = new SerializationInfo(ex.GetType(), new FormatterConverter());
46-
var sc = new StreamingContext();
47-
ex.GetObjectData(si, sc);
56+
private PostData<object> CreatePostData(Exception e)
57+
{
4858
PostData<object> postData = new List<object>
4959
{
50-
header,
60+
_bulkHeader,
5161
new
5262
{
5363
message = "My message",
54-
exception = new
55-
{
56-
ClassName = "System.Exception",
57-
Message = "Some exception",
58-
Source = "Tests",
59-
StackTraceString = ex.StackTrace,
60-
RemoteStackTraceString = si.GetString("RemoteStackTraceString"),
61-
RemoteStackIndex = 0,
62-
HResult = si.GetInt32("HResult"),
63-
HelpURL = si.GetString("HelpURL"),
64-
ExceptionMethod = new
65-
{
66-
Name = nameof(GimmeACaughtException),
67-
AssemblyName = a.Name,
68-
AssemblyVersion = a.Version.ToString(),
69-
AssemblyCulture = a.CultureName,
70-
ClassName = this.GetType().FullName,
71-
Signature = $"System.Exception {nameof(GimmeACaughtException)}()",
72-
MemberType = 8
73-
}
74-
},
64+
exception = this.ExceptionJson(e).ToArray(),
7565
}
7666
};
67+
return postData;
68+
}
69+
70+
private IEnumerable<object> ExceptionJson(Exception e)
71+
{
72+
int depth = 0;
73+
int maxExceptions = 20;
74+
do
75+
{
76+
77+
var si = new SerializationInfo(e.GetType(), new FormatterConverter());
78+
var sc = new StreamingContext();
79+
e.GetObjectData(si, sc);
80+
81+
var helpUrl = si.GetString("HelpURL");
82+
var stackTrace = si.GetString("StackTraceString");
83+
var remoteStackTrace = si.GetString("RemoteStackTraceString");
84+
var remoteStackIndex = si.GetInt32("RemoteStackIndex");
85+
var exceptionMethod = si.GetString("ExceptionMethod");
86+
var hresult = si.GetInt32("HResult");
87+
var source = si.GetString("Source");
88+
var className = si.GetString("ClassName");
7789

90+
yield return new
91+
{
92+
Depth = depth,
93+
ClassName = className,
94+
Message = e.Message,
95+
Source = source,
96+
StackTraceString = stackTrace,
97+
RemoteStackTraceString = remoteStackTrace,
98+
RemoteStackIndex = remoteStackIndex,
99+
HResult = hresult,
100+
HelpURL = helpUrl,
101+
ExceptionMethod = this.WriteStructuredExceptionMethod(exceptionMethod)
102+
};
103+
depth++;
104+
e = e.InnerException;
78105

106+
}
107+
while (depth < maxExceptions && e != null);
108+
}
79109

110+
private object WriteStructuredExceptionMethod(string exceptionMethodString)
111+
{
112+
if (string.IsNullOrWhiteSpace(exceptionMethodString)) return null;
80113

114+
var args = exceptionMethodString.Split('\0', '\n');
115+
116+
if (args.Length != 5) return null;
117+
118+
var memberType = Int32.Parse(args[0], CultureInfo.InvariantCulture);
119+
var name = args[1];
120+
var assemblyName = args[2];
121+
var className = args[3];
122+
var signature = args[4];
123+
var an = new AssemblyName(assemblyName);
124+
return new
125+
{
126+
Name = name,
127+
AssemblyName = an.Name,
128+
AssemblyVersion = an.Version.ToString(),
129+
AssemblyCulture = an.CultureName,
130+
ClassName = className,
131+
Signature = signature,
132+
MemberType = memberType,
133+
};
134+
}
135+
136+
private string CreateRequest(Exception ex)
137+
{
138+
var document = new Dictionary<string, object>{
139+
{ "message", _objectMessage},
140+
{ "exception", ex }
141+
};
142+
143+
144+
var payload = new List<object>{
145+
_bulkHeader,
146+
document
147+
};
148+
var response = this._client.Bulk<byte[]>(payload);
149+
150+
151+
var request = Encoding.UTF8.GetString(response.RequestBodyInBytes);
152+
return request;
153+
}
154+
155+
private void AssertRequestEquals(string request, PostData<object> postData)
156+
{
81157
using (var ms = new MemoryStream())
82158
{
83-
postData.Write(ms, client.Settings);
159+
postData.Write(ms, this._client.Settings);
84160
var expectedString = Encoding.UTF8.GetString(ms.ToArray());
85161
request.Should().Be(expectedString);
86162
}
@@ -98,5 +174,18 @@ private Exception GimmeACaughtException()
98174
}
99175
}
100176

177+
178+
private Exception GimmeAnExceptionWithInnerException()
179+
{
180+
try
181+
{
182+
var e = this.GimmeACaughtException();
183+
throw new Exception("Some exception", e);
184+
}
185+
catch (Exception e)
186+
{
187+
return e;
188+
}
189+
}
101190
}
102191
}

0 commit comments

Comments
 (0)