Skip to content

Commit

Permalink
Added stack trace to CosmosException, ResponseMessage.ErrorMessage in…
Browse files Browse the repository at this point in the history
…cludes full exception info. (#1213)

* Removed Error since it is not being used anywhere.

* Removed error since it's not being used

* Refactored added new exception types

* ResponseMessage.ErrorMessage will now return the full CosmosException string. Returning only the error message makes it not possible to debug.

CosmosException now stores the stack trace. This fixes the issues where the error information is stored and later thrown causing the exception to show the incorrect error location.

Created new CosmosExceptionFactory. This helps produce a CosmosException from the various types.

* Fixed build issue

* Renamed exception to add Cosmos to avoid confusion with Document name space versions.

* Fixed exception handling and updated tests.

* Fixed tests

* Fixed ExceptionWithStackTraceException to use original exception stack trace.

* Fixed merge conflicts with latest

* Added diagnostic context to exceptions.

* Fixed test for retail build

* Updated error message field in linq tests

* Converted the stack trace to a string. Removed creating the stack trace for exception less path.

* Updated changelog

* Removed typed exceptions to avoid exposing internal types.

* Adding transport client exception tests.

* Update Microsoft.Azure.Cosmos/src/Resource/CosmosExceptions/CosmosExceptionFactory.cs

Co-Authored-By: Matias Quaranta <[email protected]>

* Update Microsoft.Azure.Cosmos/src/Resource/CosmosExceptions/CosmosExceptionFactory.cs

Co-Authored-By: Matias Quaranta <[email protected]>

* Removed diagnostics from Response.ErrorMessage

* Fixed unit test

* Adding Error object to CosmosException for back compatability.

* Adding unit test for Error handling

Co-authored-by: Matias Quaranta <[email protected]>
  • Loading branch information
j82w and ealsur authored Feb 28, 2020
1 parent dd820cd commit 1a999ff
Show file tree
Hide file tree
Showing 39 changed files with 1,086 additions and 363 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -212,9 +212,8 @@ internal ResponseMessage ToResponseMessage()
ResponseMessage responseMessage = new ResponseMessage(
statusCode: this.StatusCode,
requestMessage: null,
errorMessage: null,
error: null,
headers: headers,
cosmosException: null,
diagnostics: this.DiagnosticsContext ?? CosmosDiagnosticsContext.Create())
{
Content = this.ResourceStream
Expand Down
3 changes: 2 additions & 1 deletion Microsoft.Azure.Cosmos/src/Encryption/EncryptionProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ namespace Microsoft.Azure.Cosmos
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Azure.Cosmos.Resource.CosmosExceptions;
using Microsoft.Azure.Documents;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
Expand Down Expand Up @@ -134,7 +135,7 @@ public async Task<Stream> DecryptAsync(
EncryptionProperties encryptionProperties = encryptionPropertiesJObj.ToObject<EncryptionProperties>();
if (encryptionProperties.EncryptionFormatVersion != 1)
{
throw new CosmosException(HttpStatusCode.InternalServerError, $"Unknown encryption format version: {encryptionProperties.EncryptionFormatVersion}. Please upgrade your SDK to the latest version.");
throw CosmosExceptionFactory.CreateInternalServerErrorException($"Unknown encryption format version: {encryptionProperties.EncryptionFormatVersion}. Please upgrade your SDK to the latest version.");
}

DataEncryptionKeyCore tempDek = (DataEncryptionKeyInlineCore)database.GetDataEncryptionKey(id: "unknown");
Expand Down
78 changes: 18 additions & 60 deletions Microsoft.Azure.Cosmos/src/Handler/ResponseMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ namespace Microsoft.Azure.Cosmos
using System.Diagnostics;
using System.IO;
using System.Net;
using Microsoft.Azure.Cosmos.Resource.CosmosExceptions;
using Microsoft.Azure.Documents;

/// <summary>
Expand All @@ -22,6 +23,7 @@ public ResponseMessage()
{
this.Headers = new Headers();
this.DiagnosticsContext = CosmosDiagnosticsContext.Create();
this.CosmosException = null;
}

/// <summary>
Expand All @@ -42,32 +44,36 @@ public ResponseMessage(

this.StatusCode = statusCode;
this.RequestMessage = requestMessage;
this.ErrorMessage = errorMessage;
this.Headers = new Headers();
this.DiagnosticsContext = requestMessage?.DiagnosticsContext ?? CosmosDiagnosticsContext.Create();

if (!string.IsNullOrEmpty(errorMessage))
{
this.CosmosException = CosmosExceptionFactory.Create(
statusCode,
requestMessage,
errorMessage);
}
}

/// <summary>
/// Create a <see cref="ResponseMessage"/>
/// </summary>
/// <param name="statusCode">The HttpStatusCode of the response</param>
/// <param name="requestMessage">The <see cref="Cosmos.RequestMessage"/> object</param>
/// <param name="errorMessage">The reason for failures if any.</param>
/// <param name="error">The inner error object</param>
/// <param name="headers">The headers for the response.</param>
/// <param name="cosmosException">The exception if the response is from an error.</param>
/// <param name="diagnostics">The diagnostics for the request</param>
internal ResponseMessage(
HttpStatusCode statusCode,
RequestMessage requestMessage,
string errorMessage,
Error error,
Headers headers,
CosmosException cosmosException,
CosmosDiagnosticsContext diagnostics)
{
this.StatusCode = statusCode;
this.RequestMessage = requestMessage;
this.ErrorMessage = errorMessage;
this.Error = error;
this.CosmosException = cosmosException;
this.Headers = headers ?? new Headers();
this.DiagnosticsContext = diagnostics ?? throw new ArgumentNullException(nameof(diagnostics));
}
Expand All @@ -93,7 +99,7 @@ public virtual Stream Content
/// <summary>
/// Gets the reason for a failure in the current response.
/// </summary>
public virtual string ErrorMessage { get; internal set; }
public virtual string ErrorMessage => this.CosmosException?.ToString(includeDiagnostics: false);

/// <summary>
/// Gets the current <see cref="ResponseMessage"/> HTTP headers.
Expand All @@ -120,10 +126,7 @@ public virtual Stream Content

internal CosmosDiagnosticsContext DiagnosticsContext { get; }

/// <summary>
/// Gets the internal error object.
/// </summary>
internal virtual Error Error { get; set; }
internal CosmosException CosmosException { get; }

private bool disposed;

Expand All @@ -132,24 +135,18 @@ public virtual Stream Content
/// <summary>
/// Asserts if the current <see cref="HttpStatusCode"/> is a success.
/// </summary>
public virtual bool IsSuccessStatusCode => ((int)this.StatusCode >= 200) && ((int)this.StatusCode <= 299);
public virtual bool IsSuccessStatusCode => this.StatusCode.IsSuccess();

/// <summary>
/// Checks if the current <see cref="ResponseMessage"/> has a successful status code, otherwise, throws.
/// </summary>
/// <exception cref="CosmosException">An instance of <see cref="CosmosException"/> representing the error state.</exception>
/// <exception cref="Cosmos.CosmosException">An instance of <see cref="Cosmos.CosmosException"/> representing the error state.</exception>
/// <returns>The current <see cref="ResponseMessage"/>.</returns>
public virtual ResponseMessage EnsureSuccessStatusCode()
{
if (!this.IsSuccessStatusCode)
{
this.EnsureErrorMessage();
string message = $"Response status code does not indicate success: {(int)this.StatusCode} Substatus: {(int)this.Headers.SubStatusCode} Reason: ({this.ErrorMessage}).";

throw new CosmosException(
this,
message,
this.Error);
throw CosmosExceptionFactory.Create(this);
}

return this;
Expand Down Expand Up @@ -210,44 +207,5 @@ private void CheckDisposed()
throw new ObjectDisposedException(this.GetType().ToString());
}
}

private void EnsureErrorMessage()
{
if (this.Error != null
|| !string.IsNullOrEmpty(this.ErrorMessage))
{
return;
}

if (this.content != null
&& this.content.CanRead)
{
try
{
Error error = Documents.Resource.LoadFrom<Error>(this.content);
if (error != null)
{
// Error format is not consistent across modes
if (!string.IsNullOrEmpty(error.Message))
{
this.ErrorMessage = error.Message;
}
else
{
this.ErrorMessage = error.ToString();
}
}
}
catch (Newtonsoft.Json.JsonReaderException)
{
// Content is not Json
this.content.Position = 0;
using (StreamReader streamReader = new StreamReader(this.content))
{
this.ErrorMessage = streamReader.ReadToEnd();
}
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -401,12 +401,7 @@ protected async Task<TryCatch> TryInitializeAsync(
if (failureResponse.HasValue)
{
return TryCatch.FromException(
new CosmosException(
statusCode: failureResponse.Value.StatusCode,
subStatusCode: (int)failureResponse.Value.SubStatusCode.GetValueOrDefault(0),
message: failureResponse.Value.ErrorMessage,
activityId: failureResponse.Value.ActivityId,
requestCharge: failureResponse.Value.RequestCharge));
failureResponse.Value.CosmosException);
}

if (!movedToNextPage)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ namespace Microsoft.Azure.Cosmos.Query.Core.ExecutionContext.ItemProducers
using Microsoft.Azure.Cosmos.Query.Core.Collections;
using Microsoft.Azure.Cosmos.Query.Core.Metrics;
using Microsoft.Azure.Cosmos.Query.Core.QueryClient;
using Microsoft.Azure.Cosmos.Resource.CosmosExceptions;
using Microsoft.Azure.Documents;
using PartitionKeyRange = Documents.PartitionKeyRange;
using PartitionKeyRangeIdentity = Documents.PartitionKeyRangeIdentity;

Expand Down Expand Up @@ -289,7 +291,7 @@ public async Task BufferMoreDocumentsAsync(CancellationToken token)
feedResponse = QueryResponseCore.CreateFailure(
statusCode: (System.Net.HttpStatusCode)429,
subStatusCodes: null,
errorMessage: "Request Rate Too Large",
cosmosException: CosmosExceptionFactory.CreateThrottledException("Request Rate Too Large"),
requestCharge: 0,
activityId: QueryResponseCore.EmptyGuidString,
diagnostics: QueryResponseCore.EmptyDiagnostics);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -641,12 +641,7 @@ private async Task<TryCatch> TryFilterAsync(
if (failureResponse.HasValue)
{
return TryCatch.FromException(
new CosmosException(
statusCode: failureResponse.Value.StatusCode,
subStatusCode: (int)failureResponse.Value.SubStatusCode.GetValueOrDefault(0),
message: failureResponse.Value.ErrorMessage,
activityId: failureResponse.Value.ActivityId,
requestCharge: 0));
failureResponse.Value.CosmosException);
}

break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ private QueryResponseCore(
long responseLengthBytes,
string disallowContinuationTokenMessage,
string continuationToken,
string errorMessage,
CosmosException cosmosException,
SubStatusCodes? subStatusCode)
{
this.IsSuccess = isSuccess;
Expand All @@ -47,13 +47,13 @@ private QueryResponseCore(
this.RequestCharge = requestCharge;
this.DisallowContinuationTokenMessage = disallowContinuationTokenMessage;
this.ContinuationToken = continuationToken;
this.ErrorMessage = errorMessage;
this.CosmosException = cosmosException;
this.SubStatusCode = subStatusCode;
}

internal IReadOnlyList<CosmosElement> CosmosElements { get; }

internal string ErrorMessage { get; }
internal CosmosException CosmosException { get; }

internal SubStatusCodes? SubStatusCode { get; }

Expand Down Expand Up @@ -92,7 +92,7 @@ internal static QueryResponseCore CreateSuccess(
responseLengthBytes: responseLengthBytes,
disallowContinuationTokenMessage: disallowContinuationTokenMessage,
continuationToken: continuationToken,
errorMessage: null,
cosmosException: null,
subStatusCode: null);

return cosmosQueryResponse;
Expand All @@ -101,7 +101,7 @@ internal static QueryResponseCore CreateSuccess(
internal static QueryResponseCore CreateFailure(
HttpStatusCode statusCode,
SubStatusCodes? subStatusCodes,
string errorMessage,
CosmosException cosmosException,
double requestCharge,
string activityId,
IReadOnlyCollection<QueryPageDiagnostics> diagnostics)
Expand All @@ -116,7 +116,7 @@ internal static QueryResponseCore CreateFailure(
responseLengthBytes: 0,
disallowContinuationTokenMessage: null,
continuationToken: null,
errorMessage: errorMessage,
cosmosException: cosmosException,
subStatusCode: subStatusCodes);

return cosmosQueryResponse;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace Microsoft.Azure.Cosmos.Query.Core.QueryPlan
using System.Threading.Tasks;
using Microsoft.Azure.Cosmos.Query.Core.Monads;
using Microsoft.Azure.Cosmos.Query.Core.QueryClient;
using Microsoft.Azure.Cosmos.Resource.CosmosExceptions;
using OperationType = Documents.OperationType;
using PartitionKeyDefinition = Documents.PartitionKeyDefinition;
using ResourceType = Documents.ResourceType;
Expand Down Expand Up @@ -62,9 +63,14 @@ public static async Task<PartitionedQueryExecutionInfo> GetQueryPlanWithServiceI

if (!tryGetQueryPlan.Succeeded)
{
throw new CosmosException(
System.Net.HttpStatusCode.BadRequest,
tryGetQueryPlan.Exception.ToString());
if (tryGetQueryPlan.Exception is CosmosException)
{
throw tryGetQueryPlan.Exception;
}

throw CosmosExceptionFactory.CreateBadRequestException(
message: tryGetQueryPlan.Exception.ToString(),
stackTrace: tryGetQueryPlan.Exception.StackTrace);
}

return tryGetQueryPlan.Result;
Expand Down
Loading

0 comments on commit 1a999ff

Please sign in to comment.