diff --git a/Microsoft.Azure.Cosmos/src/Handler/ResponseMessage.cs b/Microsoft.Azure.Cosmos/src/Handler/ResponseMessage.cs
index f788900a73..19c204b799 100644
--- a/Microsoft.Azure.Cosmos/src/Handler/ResponseMessage.cs
+++ b/Microsoft.Azure.Cosmos/src/Handler/ResponseMessage.cs
@@ -8,6 +8,7 @@ namespace Microsoft.Azure.Cosmos
using System.Diagnostics;
using System.IO;
using System.Net;
+ using System.Text;
using Microsoft.Azure.Cosmos.Resource.CosmosExceptions;
using Microsoft.Azure.Documents;
@@ -99,7 +100,7 @@ public virtual Stream Content
///
/// Gets the reason for a failure in the current response.
///
- public virtual string ErrorMessage => this.CosmosException?.ToString(includeDiagnostics: false);
+ public virtual string ErrorMessage => this.CosmosException?.Message;
///
/// Gets the current HTTP headers.
diff --git a/Microsoft.Azure.Cosmos/src/Resource/CosmosExceptions/CosmosException.cs b/Microsoft.Azure.Cosmos/src/Resource/CosmosExceptions/CosmosException.cs
index b6b5b6c09a..e8a940fb39 100644
--- a/Microsoft.Azure.Cosmos/src/Resource/CosmosExceptions/CosmosException.cs
+++ b/Microsoft.Azure.Cosmos/src/Resource/CosmosExceptions/CosmosException.cs
@@ -28,8 +28,13 @@ internal CosmosException(
CosmosDiagnosticsContext diagnosticsContext,
Error error,
Exception innerException)
- : base(message, innerException)
+ : base(CosmosException.GetMessageHelper(
+ statusCodes,
+ subStatusCode,
+ message,
+ activityId), innerException)
{
+ this.ResponseBody = message;
this.stackTrace = stackTrace;
this.ActivityId = activityId;
this.StatusCode = statusCodes;
@@ -162,12 +167,13 @@ public virtual bool TryGetHeader(string headerName, out string value)
/// A string representation of the exception.
public override string ToString()
{
- return this.ToStringHelper(true);
- }
+ StringBuilder stringBuilder = new StringBuilder();
+ stringBuilder.Append(this.GetType().FullName);
+ stringBuilder.Append(" : ");
- internal string ToString(bool includeDiagnostics)
- {
- return this.ToStringHelper(includeDiagnostics);
+ this.ToStringHelper(stringBuilder);
+
+ return stringBuilder.ToString();
}
internal ResponseMessage ToCosmosResponseMessage(RequestMessage request)
@@ -180,35 +186,38 @@ internal ResponseMessage ToCosmosResponseMessage(RequestMessage request)
diagnostics: this.DiagnosticsContext);
}
- private string ToStringHelper(bool includeDiagnostics)
+ private static string GetMessageHelper(
+ HttpStatusCode statusCode,
+ int subStatusCode,
+ string responseBody,
+ string activityId)
{
StringBuilder stringBuilder = new StringBuilder();
- stringBuilder.Append(this.GetType().FullName);
- if (this.Message != null)
- {
- stringBuilder.Append(" : ");
- stringBuilder.Append(this.Message);
- stringBuilder.AppendLine();
- }
-
- stringBuilder.AppendFormat("StatusCode = {0};", this.StatusCode);
- stringBuilder.AppendLine();
-
- stringBuilder.AppendFormat("SubStatusCode = {0};", this.SubStatusCode);
- stringBuilder.AppendLine();
- stringBuilder.AppendFormat("ActivityId = {0};", this.ActivityId ?? Guid.Empty.ToString());
- stringBuilder.AppendLine();
+ stringBuilder.Append($"Response status code does not indicate success: ");
+ stringBuilder.Append($"{statusCode} ({(int)statusCode})");
+ stringBuilder.Append("; Substatus: ");
+ stringBuilder.Append(subStatusCode);
+ stringBuilder.Append("; ActivityId: ");
+ stringBuilder.Append(activityId ?? string.Empty);
+ stringBuilder.Append("; Reason: (");
+ stringBuilder.Append(responseBody ?? string.Empty);
+ stringBuilder.Append(");");
- stringBuilder.AppendFormat("RequestCharge = {0};", this.RequestCharge);
- stringBuilder.AppendLine();
+ return stringBuilder.ToString();
+ }
- if (includeDiagnostics && this.Diagnostics != null)
+ private string ToStringHelper(
+ StringBuilder stringBuilder)
+ {
+ if (stringBuilder == null)
{
- stringBuilder.Append(this.Diagnostics);
- stringBuilder.AppendLine();
+ throw new ArgumentNullException(nameof(stringBuilder));
}
+ stringBuilder.Append(this.Message);
+ stringBuilder.AppendLine();
+
if (this.InnerException != null)
{
stringBuilder.Append(" ---> ");
@@ -219,7 +228,16 @@ private string ToStringHelper(bool includeDiagnostics)
stringBuilder.AppendLine();
}
- stringBuilder.Append(this.StackTrace);
+ if (this.StackTrace != null)
+ {
+ stringBuilder.Append(this.StackTrace);
+ stringBuilder.AppendLine();
+ }
+
+ if (this.Diagnostics != null)
+ {
+ stringBuilder.Append(this.Diagnostics);
+ }
return stringBuilder.ToString();
}
diff --git a/Microsoft.Azure.Cosmos/src/Util/Extensions.cs b/Microsoft.Azure.Cosmos/src/Util/Extensions.cs
index 9ef10d731b..c523b9f181 100644
--- a/Microsoft.Azure.Cosmos/src/Util/Extensions.cs
+++ b/Microsoft.Azure.Cosmos/src/Util/Extensions.cs
@@ -99,7 +99,7 @@ internal static ResponseMessage ToCosmosResponseMessage(this DocumentClientExcep
subStatusCode: (int)SubStatusCodes.Unknown,
responseTimeUtc: DateTime.UtcNow,
requestCharge: cosmosException.Headers.RequestCharge,
- errorMessage: cosmosException.Message,
+ errorMessage: documentClientException.ToString(),
method: requestMessage?.Method,
requestUri: requestMessage?.RequestUri,
requestSessionToken: requestMessage?.Headers?.Session,
diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemTests.cs
index 3311c3fa92..b00dafbfa2 100644
--- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemTests.cs
+++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/CosmosItemTests.cs
@@ -1230,10 +1230,23 @@ public async Task ItemRequestOptionAccessConditionTest()
IfMatchEtag = Guid.NewGuid().ToString(),
};
+ using (ResponseMessage responseMessage = await this.Container.UpsertItemStreamAsync(
+ streamPayload: TestCommon.SerializerCore.ToStream(testItem),
+ partitionKey: new Cosmos.PartitionKey(testItem.status),
+ requestOptions: itemRequestOptions))
+ {
+ Assert.IsNotNull(responseMessage);
+ Assert.IsNull(responseMessage.Content);
+ Assert.AreEqual(HttpStatusCode.PreconditionFailed, responseMessage.StatusCode, responseMessage.ErrorMessage);
+ Assert.AreNotEqual(responseMessage.Headers.ActivityId, Guid.Empty);
+ Assert.IsTrue(responseMessage.Headers.RequestCharge > 0);
+ Assert.IsFalse(string.IsNullOrEmpty(responseMessage.ErrorMessage));
+ Assert.IsTrue(responseMessage.ErrorMessage.Contains("One of the specified pre-condition is not met"));
+ }
+
try
{
- ItemResponse response = await this.Container.ReplaceItemAsync(
- id: testItem.id,
+ ItemResponse response = await this.Container.UpsertItemAsync(
item: testItem,
requestOptions: itemRequestOptions);
Assert.Fail("Access condition should have failed");
@@ -1242,8 +1255,11 @@ public async Task ItemRequestOptionAccessConditionTest()
{
Assert.IsNotNull(e);
Assert.AreEqual(HttpStatusCode.PreconditionFailed, e.StatusCode, e.Message);
- Assert.IsNotNull(e.ActivityId);
+ Assert.AreNotEqual(e.ActivityId, Guid.Empty);
Assert.IsTrue(e.RequestCharge > 0);
+ Assert.AreEqual($"{{{Environment.NewLine} \"Errors\": [{Environment.NewLine} \"One of the specified pre-condition is not met\"{Environment.NewLine} ]{Environment.NewLine}}}", e.ResponseBody);
+ string expectedMessage = $"Response status code does not indicate success: PreconditionFailed (412); Substatus: 0; ActivityId: {e.ActivityId}; Reason: ({{{Environment.NewLine} \"Errors\": [{Environment.NewLine} \"One of the specified pre-condition is not met\"{Environment.NewLine} ]{Environment.NewLine}}});";
+ Assert.AreEqual(expectedMessage, e.Message);
}
finally
{
diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/TransportWrapperTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/TransportWrapperTests.cs
index 87cceb4c79..f5399dc5a5 100644
--- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/TransportWrapperTests.cs
+++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/TransportWrapperTests.cs
@@ -6,6 +6,7 @@ namespace Microsoft.Azure.Cosmos.SDK.EmulatorTests
{
using System;
using System.Diagnostics;
+ using System.Net;
using System.Threading.Tasks;
using Microsoft.Azure.Documents;
using Microsoft.VisualStudio.TestTools.UnitTesting;
@@ -64,10 +65,24 @@ public async Task TransportExceptionValidationTest()
{
this.ValidateTransportException(ce);
}
+
+ using (ResponseMessage responseMessage = await container.CreateItemStreamAsync(
+ TestCommon.SerializerCore.ToStream(new TestPayload { id = "bad" }),
+ new Cosmos.PartitionKey("bad")))
+ {
+ this.ValidateTransportException(responseMessage);
+ }
+
+ FeedIterator streamIterator = container.GetItemQueryStreamIterator("select * from T where T.Random = 19827 ");
+ using (ResponseMessage responseMessage = await streamIterator.ReadNextAsync())
+ {
+ this.ValidateTransportException(responseMessage);
+ }
}
private void ValidateTransportException(CosmosException cosmosException)
{
+ Assert.AreEqual(HttpStatusCode.ServiceUnavailable, cosmosException.StatusCode);
string message = cosmosException.ToString();
Assert.IsTrue(message.Contains("TransportException: A client transport error occurred: The connection failed"), "StoreResult Exception is missing");
string diagnostics = cosmosException.Diagnostics.ToString();
@@ -75,6 +90,17 @@ private void ValidateTransportException(CosmosException cosmosException)
Assert.IsTrue(diagnostics.Contains("TransportException: A client transport error occurred: The connection failed"));
}
+ private void ValidateTransportException(ResponseMessage responseMessage)
+ {
+ Assert.AreEqual(HttpStatusCode.ServiceUnavailable, responseMessage.StatusCode);
+ string message = responseMessage.ErrorMessage;
+ Assert.AreEqual(responseMessage.ErrorMessage, responseMessage.CosmosException.Message);
+ Assert.IsTrue(message.Contains("TransportException: A client transport error occurred: The connection failed"), "StoreResult Exception is missing");
+ string diagnostics = responseMessage.Diagnostics.ToString();
+ Assert.IsNotNull(diagnostics);
+ Assert.IsTrue(diagnostics.Contains("TransportException: A client transport error occurred: The connection failed"));
+ }
+
private static void Interceptor(
Uri physicalAddress,
ResourceOperation resourceOperation,
diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosExceptionTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosExceptionTests.cs
index 29aba6e7a8..b39f13c294 100644
--- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosExceptionTests.cs
+++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosExceptionTests.cs
@@ -111,7 +111,7 @@ public void VerifyDocumentClientExceptionToResponseMessage()
Assert.AreEqual(HttpStatusCode.BadRequest, responseMessage.StatusCode);
Assert.AreEqual(SubStatusCodes.WriteForbidden, responseMessage.Headers.SubStatusCode);
Assert.IsTrue(responseMessage.ErrorMessage.Contains(errorMessage));
- Assert.IsTrue(responseMessage.ErrorMessage.Contains("VerifyDocumentClientExceptionToResponseMessage"), $"Message should have method name for the stack trace {responseMessage.ErrorMessage}");
+ Assert.IsFalse(responseMessage.ErrorMessage.Contains("VerifyDocumentClientExceptionToResponseMessage"), $"Message should not have the stack trace {responseMessage.ErrorMessage}. StackTrace should be in Diagnostics.");
}
[TestMethod]
@@ -143,22 +143,21 @@ public void VerifyTransportExceptionToResponseMessage()
Assert.IsFalse(responseMessage.IsSuccessStatusCode);
Assert.AreEqual(HttpStatusCode.ServiceUnavailable, responseMessage.StatusCode);
Assert.IsTrue(responseMessage.ErrorMessage.Contains(errorMessage));
- Assert.IsTrue(responseMessage.ErrorMessage.Contains(transportException.ToString()));
- Assert.IsTrue(responseMessage.ErrorMessage.Contains("VerifyTransportExceptionToResponseMessage"), $"Message should have method name for the stack trace {responseMessage.ErrorMessage}");
+ Assert.IsFalse(responseMessage.ErrorMessage.Contains(transportException.ToString()), "InnerException tracked in Diagnostics");
}
[TestMethod]
public void EnsureCorrectStatusCode()
{
string testMessage = "Test" + Guid.NewGuid().ToString();
-
+
List<(HttpStatusCode statusCode, CosmosException exception)> exceptionsToStatusCodes = new List<(HttpStatusCode, CosmosException)>()
{
- (HttpStatusCode.NotFound, CosmosExceptionFactory.CreateNotFoundException(testMessage)),
- (HttpStatusCode.InternalServerError, CosmosExceptionFactory.CreateInternalServerErrorException(testMessage)),
- (HttpStatusCode.BadRequest, CosmosExceptionFactory.CreateBadRequestException(testMessage)),
- (HttpStatusCode.RequestTimeout,CosmosExceptionFactory.CreateRequestTimeoutException(testMessage)),
- ((HttpStatusCode)429, CosmosExceptionFactory.CreateThrottledException(testMessage)),
+ (HttpStatusCode.NotFound, CosmosExceptionFactory.CreateNotFoundException(testMessage, activityId: Guid.NewGuid().ToString())),
+ (HttpStatusCode.InternalServerError, CosmosExceptionFactory.CreateInternalServerErrorException(testMessage, activityId: Guid.NewGuid().ToString())),
+ (HttpStatusCode.BadRequest, CosmosExceptionFactory.CreateBadRequestException(testMessage, activityId: Guid.NewGuid().ToString())),
+ (HttpStatusCode.RequestTimeout,CosmosExceptionFactory.CreateRequestTimeoutException(testMessage, activityId: Guid.NewGuid().ToString())),
+ ((HttpStatusCode)429, CosmosExceptionFactory.CreateThrottledException(testMessage, activityId: Guid.NewGuid().ToString())),
};
foreach((HttpStatusCode statusCode, CosmosException exception) item in exceptionsToStatusCodes)
@@ -239,8 +238,13 @@ private void ValidateExceptionInfo(
HttpStatusCode httpStatusCode,
string message)
{
+ exception.DiagnosticsContext.GetOverallScope().Dispose();
+ Assert.AreEqual(message, exception.ResponseBody);
Assert.AreEqual(httpStatusCode, exception.StatusCode);
Assert.IsTrue(exception.ToString().Contains(message));
+ string expectedMessage = $"Response status code does not indicate success: {httpStatusCode} ({(int)httpStatusCode}); Substatus: 0; ActivityId: {exception.ActivityId}; Reason: ({message});";
+
+ Assert.AreEqual(expectedMessage, exception.Message);
}
}
}
diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosQueryUnitTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosQueryUnitTests.cs
index 471faf3250..c92986352a 100644
--- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosQueryUnitTests.cs
+++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/CosmosQueryUnitTests.cs
@@ -40,8 +40,10 @@ public void VerifyNegativeCosmosQueryResponseStream()
string errorMessage = "TestErrorMessage";
string activityId = "TestActivityId";
double requestCharge = 42.42;
- CosmosException cosmosException = CosmosExceptionFactory.CreateBadRequestException(errorMessage);
CosmosDiagnosticsContext diagnostics = new CosmosDiagnosticsContextCore();
+ CosmosException cosmosException = CosmosExceptionFactory.CreateBadRequestException(errorMessage, diagnosticsContext: diagnostics);
+
+ diagnostics.GetOverallScope().Dispose();
QueryResponse queryResponse = QueryResponse.CreateFailure(
statusCode: HttpStatusCode.NotFound,
cosmosException: cosmosException,
@@ -58,7 +60,7 @@ public void VerifyNegativeCosmosQueryResponseStream()
diagnostics: diagnostics);
Assert.AreEqual(HttpStatusCode.NotFound, queryResponse.StatusCode);
- Assert.AreEqual(cosmosException.ToString(includeDiagnostics: false), queryResponse.ErrorMessage);
+ Assert.AreEqual(cosmosException.Message, queryResponse.ErrorMessage);
Assert.AreEqual(requestCharge, queryResponse.Headers.RequestCharge);
Assert.AreEqual(activityId, queryResponse.Headers.ActivityId);
Assert.AreEqual(diagnostics, queryResponse.DiagnosticsContext);
diff --git a/changelog.md b/changelog.md
index 44690ce2fe..e0d365b3b7 100644
--- a/changelog.md
+++ b/changelog.md
@@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [#1213](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/1213) CosmosException now returns the original stack trace.
- [#1213](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/1213) ResponseMessage.ErrorMessage is now always correctly populated. There was bug in some scenarios where the error message was left in the content stream.
+- [#1298](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/1298) CosmosException.Message contains the same information as CosmosException.ToString() to ensure all the information is being tracked
- [#1242](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/1242) Client encryption - Fix bug in read path without encrypted properties
- [#1189](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/1189) Query diagnostics shows correct overall time.
- [#1189](https://github.com/Azure/azure-cosmos-dotnet-v3/pull/1189) Fixed a bug that caused duplicate information in diagnostic context.