Skip to content

Commit f6fd5e9

Browse files
committed
Merge branch 'feature/debug-information'
Conflicts: src/Tests/tests.yaml
2 parents f571a1a + ed514ec commit f6fd5e9

File tree

16 files changed

+164
-44
lines changed

16 files changed

+164
-44
lines changed

src/Elasticsearch.Net/Exceptions/ElasticsearchClientException.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Text;
34

45
namespace Elasticsearch.Net
56
{
@@ -30,7 +31,32 @@ public ElasticsearchClientException(PipelineFailure failure, string message, IAp
3031
Response = apiCall;
3132
FailureReason = failure;
3233
AuditTrail = apiCall?.AuditTrail;
34+
}
3335

36+
public string DebugInformation
37+
{
38+
get
39+
{
40+
var sb = new StringBuilder();
41+
sb.AppendLine($"# FailureReason: {FailureReason.GetStringValue()} when trying to {Request.Method.GetStringValue()} {Request.Uri}");
42+
if (this.Response != null)
43+
ResponseStatics.DebugInformationBuilder(this.Response, sb);
44+
else
45+
{
46+
ResponseStatics.DebugAuditTrail(this.AuditTrail, sb);
47+
ResponseStatics.DebugAuditTrailExceptions(this.AuditTrail, sb);
48+
}
49+
if (InnerException != null)
50+
{
51+
sb.AppendLine($"# Inner Exception: {InnerException.Message}");
52+
sb.AppendLine(InnerException.ToString());
53+
}
54+
sb.AppendLine($"# Exception:");
55+
sb.AppendLine(this.ToString());
56+
57+
return sb.ToString();
58+
}
3459
}
60+
3561
}
3662
}

src/Elasticsearch.Net/Responses/ElasticsearchResponse.cs

Lines changed: 48 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,49 @@
33
using System.ComponentModel;
44
using System.Globalization;
55
using System.Linq;
6+
using System.Text;
67

78
namespace Elasticsearch.Net
89
{
9-
internal static class ResponseStatics
10+
public static class ResponseStatics
1011
{
11-
public static readonly string PrintFormat = "StatusCode: {1}, {0}\tMethod: {2}, {0}\tUrl: {3}, {0}\tRequest: {4}, {0}\tResponse: {5}";
12-
public static readonly string ErrorFormat = "{0}\tExceptionMessage: {1}{0}\t StackTrace: {2}";
13-
public static readonly string AlreadyCaptured = "<Response stream not captured or already read to completion by serializer. Set DisableDirectStreaming() on ConnectionSettings to force it to be set on the response.>";
12+
private static readonly string ResponseAlreadyCaptured = "<Response stream not captured or already read to completion by serializer. Set DisableDirectStreaming() on ConnectionSettings to force it to be set on the response.>";
13+
private static readonly string RequestAlreadyCaptured = "<Request stream not captured or already read to completion by serializer. Set DisableDirectStreaming() on ConnectionSettings to force it to be set on the response.>";
14+
public static string DebugInformationBuilder(IApiCallDetails r, StringBuilder sb)
15+
{
16+
sb.AppendLine($"# Audit trail of this API call:");
17+
var auditTrail = (r.AuditTrail ?? Enumerable.Empty<Audit>()).ToList();
18+
DebugAuditTrail(auditTrail, sb);
19+
if (r.ServerError != null) sb.AppendLine($"# ServerError: {r.ServerError}");
20+
if (r.OriginalException != null) sb.AppendLine($"# OriginalException: {r.OriginalException}");
21+
DebugAuditTrailExceptions(auditTrail, sb);
22+
23+
var response = r.ResponseBodyInBytes?.Utf8String() ?? ResponseStatics.ResponseAlreadyCaptured;
24+
var request = r.RequestBodyInBytes?.Utf8String() ?? ResponseStatics.RequestAlreadyCaptured;
25+
sb.AppendLine($"# Request:\r\n{request}");
26+
sb.AppendLine($"# Response:\r\n{response}");
27+
28+
return sb.ToString();
29+
}
30+
31+
public static void DebugAuditTrailExceptions(List<Audit> auditTrail, StringBuilder sb)
32+
{
33+
var auditExceptions = auditTrail.Select((audit, i) => new {audit, i}).Where(a => a.audit.Exception != null);
34+
foreach (var a in auditExceptions)
35+
sb.AppendLine($"# Audit exception in step {a.i} {a.audit.Event.GetStringValue()}:\r\n{a.audit.Exception}");
36+
}
37+
38+
public static void DebugAuditTrail(List<Audit> auditTrail, StringBuilder sb)
39+
{
40+
if (auditTrail == null) return;
41+
foreach (var audit in auditTrail)
42+
{
43+
sb.Append($" - {audit.Event.GetStringValue()}:");
44+
if (audit.Node?.Uri != null) sb.Append($" Node: {audit.Node.Uri}");
45+
if (audit.Exception != null) sb.Append($" Exception: {audit.Exception.GetType().Name}");
46+
sb.AppendLine($" Took: {(audit.Ended - audit.Started)}");
47+
}
48+
}
1449
}
1550

1651
public class ElasticsearchResponse<T> : IApiCallDetails
@@ -59,25 +94,16 @@ public ElasticsearchResponse(int statusCode, IEnumerable<int> allowedStatusCodes
5994
this.HttpStatusCode = statusCode;
6095
}
6196

62-
public override string ToString()
97+
public string DebugInformation
6398
{
64-
var r = this;
65-
var e = r.OriginalException;
66-
var response = this.ResponseBodyInBytes?.Utf8String() ?? ResponseStatics.AlreadyCaptured;
67-
68-
var requestJson = r.RequestBodyInBytes?.Utf8String();
69-
70-
var print = string.Format(ResponseStatics.PrintFormat,
71-
Environment.NewLine,
72-
r.HttpStatusCode.HasValue ? r.HttpStatusCode.Value.ToString(CultureInfo.InvariantCulture) : "-1",
73-
r.HttpMethod,
74-
r.Uri,
75-
requestJson,
76-
response
77-
);
78-
if (!this.Success && e != null)
79-
print += string.Format(ResponseStatics.ErrorFormat,Environment.NewLine, e.Message, e.StackTrace);
80-
return print;
99+
get
100+
{
101+
var sb = new StringBuilder();
102+
sb.AppendLine(this.ToString());
103+
return ResponseStatics.DebugInformationBuilder(this, sb);
104+
}
81105
}
106+
107+
public override string ToString() => $"{(Success ? "S" : "Uns")}uccesful low level call on {HttpMethod.GetStringValue()}: {Uri.PathAndQuery}";
82108
}
83109
}

src/Elasticsearch.Net/Responses/IApiCallDetails.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,5 +47,7 @@ public interface IApiCallDetails
4747
byte[] RequestBodyInBytes { get; }
4848

4949
List<Audit> AuditTrail { get; }
50+
51+
string DebugInformation { get; }
5052
}
5153
}

src/Elasticsearch.Net/Responses/ServerError.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.IO;
44
using System.Linq;
5+
using System.Text;
56
using System.Threading;
67
using System.Threading.Tasks;
78

@@ -53,6 +54,15 @@ internal static ServerError Create(IDictionary<string, object> dict, IJsonSerial
5354
Error = (Error)strategy.DeserializeObject(error, typeof(Error))
5455
};
5556
}
57+
58+
public override string ToString()
59+
{
60+
var sb = new StringBuilder();
61+
sb.Append($"ServerError: {Status}");
62+
if (Error != null)
63+
sb.Append(Error);
64+
return sb.ToString();
65+
}
5666
}
5767

5868
public interface IRootCause
@@ -85,6 +95,8 @@ internal static Error Create(IDictionary<string, object> dict, IJsonSerializerSt
8595
error.RootCause = os.Select(o => (RootCause)strategy.DeserializeObject(o, typeof(RootCause))).ToList();
8696
return error;
8797
}
98+
99+
public override string ToString() => $"Type: {this.Type} Reason: \"{this.Reason}\"";
88100
}
89101

90102
public class RootCause : IRootCause
@@ -100,7 +112,9 @@ internal static RootCause Create(IDictionary<string, object> dict, IJsonSerializ
100112
var rootCause = new RootCause();
101113
rootCause.FillValues(dict);
102114
return rootCause;
103-
}
115+
}
116+
117+
public override string ToString() => $"Type: {this.Type} Reason: \"{this.Reason}\"";
104118
}
105119

106120
internal static class RootCauseExtensions

src/Nest/Cluster/NodesStats/NodesStatsRequest.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
public partial interface INodesStatsRequest { }
44

55
public partial class NodesStatsRequest { }
6+
67
[DescriptorFor("NodesStats")]
78
public partial class NodesStatsDescriptor { }
89
}

src/Nest/CommonAbstractions/Response/ApiCallDetailsOverride.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ internal class ApiCallDetailsOverride : IApiCallDetails
2323
public byte[] ResponseBodyInBytes => this._original.ResponseBodyInBytes;
2424
public byte[] RequestBodyInBytes => this._original.RequestBodyInBytes;
2525
public List<Audit> AuditTrail => this._original.AuditTrail;
26+
public string DebugInformation => this._original.DebugInformation;
2627

2728
public ApiCallDetailsOverride(IApiCallDetails original, bool isValid)
2829
{

src/Nest/CommonAbstractions/Response/ResponseBase.cs

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
using System;
2+
using System.Diagnostics;
3+
using System.Linq;
4+
using System.Text;
25
using Elasticsearch.Net;
36
using Newtonsoft.Json;
47

@@ -18,6 +21,9 @@ public interface IResponse : IBodyWithApiCallDetails
1821
[JsonIgnore]
1922
Exception OriginalException { get; }
2023

24+
[JsonIgnore]
25+
string DebugInformation { get; }
26+
2127
}
2228

2329
public abstract class ResponseBase : IResponse
@@ -27,9 +33,26 @@ public abstract class ResponseBase : IResponse
2733
IApiCallDetails IBodyWithApiCallDetails.CallDetails { get; set; }
2834

2935
public virtual IApiCallDetails ApiCall => ((IBodyWithApiCallDetails)this).CallDetails;
30-
31-
public virtual ServerError ServerError => this.ApiCall?.ServerError;
3236

33-
public Exception OriginalException => this.ApiCall?.OriginalException;
37+
public virtual ServerError ServerError => this.ApiCall?.ServerError;
38+
39+
public Exception OriginalException => this.ApiCall?.OriginalException;
40+
41+
public string DebugInformation
42+
{
43+
get
44+
{
45+
var sb = new StringBuilder();
46+
sb.Append($"{(!IsValid ? "Inv" : "V")}alid NEST response built from a ");
47+
sb.AppendLine(ApiCall?.ToString().ToCamelCase() ?? "null ApiCall which is highly exceptional, please open a bug if you see this");
48+
if (!this.IsValid) this.DebugIsValid(sb);
49+
ResponseStatics.DebugInformationBuilder(ApiCall, sb);
50+
return sb.ToString();
51+
}
52+
}
53+
protected virtual void DebugIsValid(StringBuilder sb) { }
54+
55+
public override string ToString() => $"{(!IsValid ? "Inv" : "V")}alid NEST response built from a {this.ApiCall?.ToString().ToCamelCase()}";
56+
3457
}
3558
}

src/Nest/CommonOptions/Failures/BulkError.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,6 @@ public class BulkError
1717
[JsonProperty("reason")]
1818
public string Reason { get; internal set; }
1919

20+
public override string ToString() => $"Type: {Type} Reason: \"{Reason}\"";
2021
}
2122
}

src/Nest/Document/Multiple/Bulk/BulkResponse.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using System.Collections.Generic;
22
using System.Linq;
3+
using System.Text;
34
using Newtonsoft.Json;
45

56
namespace Nest
@@ -16,6 +17,12 @@ public interface IBulkResponse : IResponse
1617
public class BulkResponse : ResponseBase, IBulkResponse
1718
{
1819
public override bool IsValid => base.IsValid && !this.Errors && !this.ItemsWithErrors.HasAny();
20+
protected override void DebugIsValid(StringBuilder sb)
21+
{
22+
sb.AppendLine($"# Invalid Bulk items:");
23+
foreach(var i in Items.Select((item, i) => new { item, i}).Where(i=>!i.item.IsValid))
24+
sb.AppendLine($" operation[{i.i}]: {i.item}");
25+
}
1926

2027
[JsonProperty("took")]
2128
public int Took { get; internal set; }

src/Nest/Document/Multiple/Bulk/BulkResponseItem/BulkResponseItemBase.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,7 @@ public bool IsValid
4343
}
4444
}
4545
}
46+
47+
public override string ToString() => $"{Operation} returned {Status} _index: {Index} _type: {Type} _id: {Id} _version: {Version} error: {Error}";
4648
}
4749
}

0 commit comments

Comments
 (0)