Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CosmosException: Adds diagnostics to message for 408, 500, 503, 404/1002 scenarios #2719

Merged
merged 15 commits into from
Sep 10, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ namespace Microsoft.Azure.Cosmos
public class CosmosException : Exception
{
private readonly string stackTrace;
private readonly Lazy<string> lazyMessage;

internal CosmosException(
HttpStatusCode statusCode,
Expand All @@ -26,17 +27,21 @@ internal CosmosException(
ITrace trace,
Error error,
Exception innerException)
: base(CosmosException.GetMessageHelper(
statusCode,
headers,
message), innerException)
// The message is overridden. Base exception does not have CTOR for just innerException and the property is not virtual
: base(string.Empty, innerException)
{
this.ResponseBody = message;
this.stackTrace = stackTrace;
this.StatusCode = statusCode;
this.Headers = headers ?? new Headers();
this.Error = error;
this.Trace = trace;
this.Diagnostics = new CosmosTraceDiagnostics(this.Trace ?? NoOpTrace.Singleton);
this.lazyMessage = new Lazy<string>(() => CosmosException.GetMessageHelper(
statusCode,
this.Headers,
this.ResponseBody,
this.Diagnostics));
}

/// <summary>
Expand All @@ -59,6 +64,7 @@ public CosmosException(
this.StatusCode = statusCode;
this.ResponseBody = message;
this.Trace = NoOpTrace.Singleton;
this.lazyMessage = new Lazy<string>(() => message);
this.Headers = new Headers()
{
SubStatusCode = (SubStatusCodes)subStatusCode,
Expand All @@ -71,6 +77,9 @@ public CosmosException(
}
}

/// <inheritdoc/>
public override string Message => this.lazyMessage.Value;

/// <summary>
/// The body of the cosmos response message as a string
/// </summary>
Expand Down Expand Up @@ -117,7 +126,7 @@ public CosmosException(
/// <summary>
/// Gets the diagnostics for the request
/// </summary>
public virtual CosmosDiagnostics Diagnostics => new CosmosTraceDiagnostics(this.Trace ?? NoOpTrace.Singleton);
public virtual CosmosDiagnostics Diagnostics { get; }

/// <inheritdoc/>
public override string StackTrace
Expand Down Expand Up @@ -189,32 +198,61 @@ internal ResponseMessage ToCosmosResponseMessage(RequestMessage request)
private static string GetMessageHelper(
HttpStatusCode statusCode,
Headers headers,
string responseBody)
string responseBody,
CosmosDiagnostics diagnostics)
{
StringBuilder stringBuilder = new StringBuilder();

CosmosException.AppendMessageWithoutDiagnostics(
stringBuilder,
statusCode,
headers,
responseBody);

// Include the diagnostics for exceptions where it is critical
// to root cause failures.
if (statusCode == HttpStatusCode.RequestTimeout
|| statusCode == HttpStatusCode.InternalServerError
|| statusCode == HttpStatusCode.ServiceUnavailable
|| (statusCode == HttpStatusCode.NotFound && headers.SubStatusCode == SubStatusCodes.ReadSessionNotAvailable))
{
stringBuilder.Append("; Diagnostics:");
stringBuilder.Append(diagnostics.ToString());
}

return stringBuilder.ToString();
}

private static void AppendMessageWithoutDiagnostics(
StringBuilder stringBuilder,
HttpStatusCode statusCode,
Headers headers,
string responseBody)
{
stringBuilder.Append($"Response status code does not indicate success: ");
stringBuilder.Append($"{statusCode} ({(int)statusCode})");
stringBuilder.Append("; Substatus: ");
stringBuilder.Append(headers?.SubStatusCodeLiteral ?? "0" );
stringBuilder.Append(headers?.SubStatusCodeLiteral ?? "0");
stringBuilder.Append("; ActivityId: ");
stringBuilder.Append(headers?.ActivityId ?? string.Empty);
stringBuilder.Append("; Reason: (");
stringBuilder.Append(responseBody ?? string.Empty);
stringBuilder.Append(");");

return stringBuilder.ToString();
}

private string ToStringHelper(
StringBuilder stringBuilder)
StringBuilder stringBuilder)
{
if (stringBuilder == null)
{
throw new ArgumentNullException(nameof(stringBuilder));
}

stringBuilder.Append(this.Message);
CosmosException.AppendMessageWithoutDiagnostics(
stringBuilder,
this.StatusCode,
this.Headers,
this.ResponseBody);
stringBuilder.AppendLine();

if (this.InnerException != null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ namespace Microsoft.Azure.Cosmos
public class CosmosOperationCanceledException : OperationCanceledException
{
private readonly OperationCanceledException originalException;
private readonly Lazy<string> lazyMessage;

/// <summary>
/// Create an instance of CosmosOperationCanceledException
Expand All @@ -30,6 +31,7 @@ public CosmosOperationCanceledException(
{
this.originalException = originalException ?? throw new ArgumentNullException(nameof(originalException));
this.Diagnostics = diagnostics ?? throw new ArgumentNullException(nameof(diagnostics));
this.lazyMessage = this.CreateLazyMessage();
}

internal CosmosOperationCanceledException(
Expand All @@ -45,6 +47,7 @@ internal CosmosOperationCanceledException(

trace.AddDatum("Operation Cancelled Exception", originalException);
this.Diagnostics = new CosmosTraceDiagnostics(trace);
this.lazyMessage = this.CreateLazyMessage();
}

/// <inheritdoc/>
Expand All @@ -55,7 +58,7 @@ public override string Source
}

/// <inheritdoc/>
public override string Message => this.originalException.Message;
public override string Message => this.lazyMessage.Value;

/// <inheritdoc/>
public override string StackTrace => this.originalException.StackTrace;
Expand Down Expand Up @@ -86,5 +89,10 @@ public override string ToString()
{
return $"{this.originalException} {Environment.NewLine}CosmosDiagnostics: {this.Diagnostics}";
}

private Lazy<string> CreateLazyMessage()
{
return new Lazy<string>(() => $"{this.originalException.Message} {Environment.NewLine}CosmosDiagnostics: {this.Diagnostics}");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ namespace Microsoft.Azure.Cosmos.SDK.EmulatorTests
using PartitionKey = Documents.PartitionKey;
using static Microsoft.Azure.Cosmos.SDK.EmulatorTests.TransportClientHelper;
using System.Reflection;
using System.Text.RegularExpressions;

[TestClass]
public class CosmosItemTests : BaseCosmosClientHelper
Expand Down Expand Up @@ -88,6 +89,7 @@ public async Task CreateDropItemWithInvalidIdCharactersTest()
{
string message = ce.ToString();
Assert.IsNotNull(message);
CosmosItemTests.ValidateCosmosException(ce);
}

// Get a container reference that use RID values
Expand Down Expand Up @@ -216,6 +218,7 @@ public async Task NegativeCreateItemTest()
Assert.IsTrue(exception.StartsWith("Microsoft.Azure.Cosmos.CosmosException : Response status code does not indicate success: Forbidden (403); Substatus: 999999; "));
string diagnostics = ce.Diagnostics.ToString();
Assert.IsTrue(diagnostics.Contains("999999"));
CosmosItemTests.ValidateCosmosException(ce);
}
}

Expand Down Expand Up @@ -351,6 +354,7 @@ public async Task CreateDropItemPartitionKeyNotInTypeTest()
catch (CosmosException ex)
{
Assert.AreEqual(HttpStatusCode.NotFound, ex.StatusCode);
CosmosItemTests.ValidateCosmosException(ex);
}
}

Expand Down Expand Up @@ -391,6 +395,7 @@ public async Task CreateDropItemMultiPartPartitionKeyTest()
catch (CosmosException ex)
{
Assert.AreEqual(HttpStatusCode.NotFound, ex.StatusCode);
CosmosItemTests.ValidateCosmosException(ex);
}
}

Expand Down Expand Up @@ -1710,7 +1715,7 @@ await containerInternal.PatchItemAsync<ToDoActivity>(
Assert.AreEqual(HttpStatusCode.NotFound, ex.StatusCode);
Assert.IsTrue(ex.Message.Contains("Resource Not Found"));
Assert.IsTrue(ex.Message.Contains("https://aka.ms/cosmosdb-tsg-not-found"));

CosmosItemTests.ValidateCosmosException(ex);
}

// adding a child when parent / ancestor does not exist - 400 BadRequest response
Expand All @@ -1727,6 +1732,7 @@ await containerInternal.PatchItemAsync<ToDoActivity>(
{
Assert.AreEqual(HttpStatusCode.BadRequest, ex.StatusCode);
Assert.IsTrue(ex.Message.Contains("Add Operation only support adding a leaf node of an existing node(array or object), no path found beyond: 'nonExistentParent'"), ex.Message);
CosmosItemTests.ValidateCosmosException(ex);
}

// precondition failure - 412 response
Expand All @@ -1749,6 +1755,7 @@ await containerInternal.PatchItemAsync<ToDoActivity>(
{
Assert.AreEqual(HttpStatusCode.PreconditionFailed, ex.StatusCode);
Assert.IsTrue(ex.Message.Contains("One of the specified pre-condition is not met"));
CosmosItemTests.ValidateCosmosException(ex);
}
}

Expand Down Expand Up @@ -2925,6 +2932,23 @@ private static async Task VerifyQueryToManyExceptionAsync(
}
}

private static void ValidateCosmosException(CosmosException exception)
{
if(exception.StatusCode == HttpStatusCode.RequestTimeout ||
exception.StatusCode == HttpStatusCode.InternalServerError ||
exception.StatusCode == HttpStatusCode.ServiceUnavailable)
{
Assert.IsTrue(exception.Message.Contains("Diagnostics"));
}
else
{
Assert.IsFalse(exception.Message.Contains("Diagnostics"));
}

string toString = exception.ToString();
Assert.AreEqual(1, Regex.Matches(toString, "Client Configuration").Count, $"The Cosmos Diagnostics does not exists or multiple instance are in the ToString(). {toString}");
}

private static async Task ExecuteQueryAsync(Container container, HttpStatusCode expected)
{
FeedIterator iterator = container.GetItemQueryStreamIterator("select * from r");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,9 @@ await container.CreateItemAsync<ToDoActivity>(
string diagnostics = ce.Diagnostics.ToString();
string toString = ce.ToString();
Assert.IsTrue(toString.Contains(diagnostics));
Assert.IsTrue(toString.Contains(message));
Assert.IsTrue(message.Contains(diagnostics));
string messageWithoutDiagnostics = message.Substring(0, message.IndexOf(Environment.NewLine)).Trim();
Assert.IsTrue(toString.Contains(messageWithoutDiagnostics));
}

try
Expand All @@ -115,13 +117,13 @@ await container.CreateItemAsync<ToDoActivity>(
}
catch (CosmosOperationCanceledException ce)
{
Assert.IsNotNull(ce);
string message = ce.Message;
string diagnostics = ce.Diagnostics.ToString();
Assert.IsTrue(diagnostics.Contains("The operation was canceled."));
string toString = ce.ToString();
Assert.IsTrue(toString.Contains(diagnostics));
Assert.IsTrue(toString.Contains(message));
Assert.IsTrue(message.Contains(diagnostics));
string messageWithoutDiagnostics = message.Substring(0, message.IndexOf(Environment.NewLine)).Trim();
Assert.IsTrue(toString.Contains(messageWithoutDiagnostics));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
"MockedItemBenchmark.DeleteItemExists;[Type=OfTWithClientTelemetryEnabled]": 39840.0,
"MockedItemBenchmark.DeleteItemExists;[Type=OfTWithDiagnosticsToString]": 70924.0,
"MockedItemBenchmark.DeleteItemExists;[Type=Stream]": 29872.0,
"MockedItemBenchmark.DeleteItemNotExists;[Type=OfT]": 48270.0,
"MockedItemBenchmark.DeleteItemNotExists;[Type=OfTCustom]": 48264.0,
"MockedItemBenchmark.DeleteItemNotExists;[Type=OfTWithClientTelemetryEnabled]": 51146.0,
"MockedItemBenchmark.DeleteItemNotExists;[Type=OfTWithDiagnosticsToString]": 82144.0,
"MockedItemBenchmark.DeleteItemNotExists;[Type=Stream]": 43180.0,
"MockedItemBenchmark.DeleteItemNotExists;[Type=OfT]": 42786.0,
"MockedItemBenchmark.DeleteItemNotExists;[Type=OfTCustom]": "43144.0",
"MockedItemBenchmark.DeleteItemNotExists;[Type=OfTWithClientTelemetryEnabled]": 44694.0,
"MockedItemBenchmark.DeleteItemNotExists;[Type=OfTWithDiagnosticsToString]": 77632.0,
"MockedItemBenchmark.DeleteItemNotExists;[Type=Stream]": 37610.0,
"MockedItemBenchmark.ReadFeed;[Type=OfT]": 560692.0,
"MockedItemBenchmark.ReadFeed;[Type=OfTCustom]": 571334.0,
"MockedItemBenchmark.ReadFeed;[Type=OfTWithClientTelemetryEnabled]": 565196.0,
Expand All @@ -26,11 +26,11 @@
"MockedItemBenchmark.ReadItemExists;[Type=OfTWithClientTelemetryEnabled]": 44440.0,
"MockedItemBenchmark.ReadItemExists;[Type=OfTWithDiagnosticsToString]": 76298.0,
"MockedItemBenchmark.ReadItemExists;[Type=Stream]": 34464.0,
"MockedItemBenchmark.ReadItemNotExists;[Type=OfT]": 52826.0,
"MockedItemBenchmark.ReadItemNotExists;[Type=OfTCustom]": 52864.0,
"MockedItemBenchmark.ReadItemNotExists;[Type=OfTWithClientTelemetryEnabled]": 55754.0,
"MockedItemBenchmark.ReadItemNotExists;[Type=OfTWithDiagnosticsToString]": 87640.0,
"MockedItemBenchmark.ReadItemNotExists;[Type=Stream]": 47994.0,
"MockedItemBenchmark.ReadItemNotExists;[Type=OfT]": 47408.0,
"MockedItemBenchmark.ReadItemNotExists;[Type=OfTCustom]": 47406.0,
"MockedItemBenchmark.ReadItemNotExists;[Type=OfTWithClientTelemetryEnabled]": 49298.0,
"MockedItemBenchmark.ReadItemNotExists;[Type=OfTWithDiagnosticsToString]": 81450.0,
"MockedItemBenchmark.ReadItemNotExists;[Type=Stream]": 42246.0,
"MockedItemBenchmark.UpdateItem;[Type=OfT]": 45542.0,
"MockedItemBenchmark.UpdateItem;[Type=OfTCustom]": 45532.0,
"MockedItemBenchmark.UpdateItem;[Type=OfTWithClientTelemetryEnabled]": 48372.0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2446,9 +2446,11 @@
"Attributes": [],
"MethodInfo": "Microsoft.Azure.Cosmos.CosmosDiagnostics Diagnostics;CanRead:True;CanWrite:False;Microsoft.Azure.Cosmos.CosmosDiagnostics get_Diagnostics();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
},
"Microsoft.Azure.Cosmos.CosmosDiagnostics get_Diagnostics()": {
"Microsoft.Azure.Cosmos.CosmosDiagnostics get_Diagnostics()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": {
"Type": "Method",
"Attributes": [],
"Attributes": [
"CompilerGeneratedAttribute"
],
"MethodInfo": "Microsoft.Azure.Cosmos.CosmosDiagnostics get_Diagnostics();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
},
"Microsoft.Azure.Cosmos.Headers get_Headers()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": {
Expand Down Expand Up @@ -2495,6 +2497,11 @@
"Attributes": [],
"MethodInfo": "System.String get_ActivityId();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
},
"System.String get_Message()": {
"Type": "Method",
"Attributes": [],
"MethodInfo": "System.String get_Message();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
},
"System.String get_ResponseBody()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": {
"Type": "Method",
"Attributes": [
Expand All @@ -2507,6 +2514,11 @@
"Attributes": [],
"MethodInfo": "System.String get_StackTrace();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
},
"System.String Message": {
"Type": "Property",
"Attributes": [],
"MethodInfo": "System.String Message;CanRead:True;CanWrite:False;System.String get_Message();IsAbstract:False;IsStatic:False;IsVirtual:True;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
},
"System.String ResponseBody": {
"Type": "Property",
"Attributes": [],
Expand Down
Loading