Skip to content
Open
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
fe73a70
Diagnostics: Adds DiagnosticsVerbosity Summary mode for compacted dia…
NaluTripician Mar 17, 2026
a2ba0d4
Diagnostics: Fixes review issues for DiagnosticsVerbosity feature
NaluTripician Mar 17, 2026
ee2ef58
Diagnostics: Adds env var support, builder API, baseline and emulator…
NaluTripician Mar 17, 2026
f984756
Merge branch 'master' into users/nalutripician/diagnostics-compaction
NaluTripician Mar 18, 2026
e1d2218
Diagnostics: Fixes test failures from DiagnosticsVerbosity constructo…
NaluTripician Mar 18, 2026
2b43505
Merge branch 'master' into users/nalutripician/diagnostics-compaction
NaluTripician Mar 18, 2026
341986f
Merge branch 'master' into users/nalutripician/diagnostics-compaction
NaluTripician Mar 23, 2026
becef8e
Diagnostics: Fixes Debug.Assert crash by calling SetWalkingStateRecur…
NaluTripician Mar 24, 2026
9272187
Merge branch 'master' into users/nalutripician/diagnostics-compaction
NaluTripician Mar 24, 2026
9f3be1c
Add diagnostics-compaction spec from PR #5644
NaluTripician Mar 31, 2026
7e01c2d
Merge branch 'master' into users/nalutripician/diagnostics-compaction
NaluTripician Mar 31, 2026
6ac6075
Merge branch 'master' into users/nalutripician/diagnostics-compaction
NaluTripician Mar 31, 2026
e9fc8fe
Diagnostics: Addresses PR review feedback for diagnostics compaction
NaluTripician Apr 1, 2026
060781b
Merge branch 'master' into users/nalutripician/diagnostics-compaction
NaluTripician Apr 6, 2026
2bc57b6
Merge remote-tracking branch 'origin/master' into users/nalutripician…
NaluTripician Apr 8, 2026
5c1180c
Diagnostics: Fixes review feedback for Summary mode
NaluTripician Apr 8, 2026
57a1a18
Merge branch 'master' into users/nalutripician/diagnostics-compaction
NaluTripician Apr 9, 2026
b165fb7
Merge branch 'master' into users/nalutripician/diagnostics-compaction
NaluTripician Apr 14, 2026
b8fba34
Encryption: Fixes SA1202 member ordering in EncryptionCosmosDiagnostics
NaluTripician Apr 14, 2026
0eab24a
Merge branch 'master' into users/nalutripician/diagnostics-compaction
NaluTripician Apr 14, 2026
db53e52
Merge branch 'master' into users/nalutripician/diagnostics-compaction
NaluTripician Apr 16, 2026
3131db6
Merge branch 'master' into users/nalutripician/diagnostics-compaction
NaluTripician Apr 21, 2026
2384581
Merge branch 'master' into users/nalutripician/diagnostics-compaction
NaluTripician Apr 28, 2026
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,9 @@ internal sealed class EncryptionCosmosDiagnostics : CosmosDiagnostics
private readonly JObject encryptContent;
private readonly JObject decryptContent;
private readonly TimeSpan processingDuration;
#if SDKPROJECTREF
private string cachedSummaryDiagnostics;
Comment thread
NaluTripician marked this conversation as resolved.
Outdated
#endif

public EncryptionCosmosDiagnostics(
CosmosDiagnostics coreDiagnostics,
Expand Down Expand Up @@ -88,6 +91,52 @@ public override string ToString()
}

#if SDKPROJECTREF
public override string ToString(DiagnosticsVerbosity verbosity)
Comment thread
NaluTripician marked this conversation as resolved.
{
switch (verbosity)
{
case DiagnosticsVerbosity.Summary:
if (this.cachedSummaryDiagnostics != null)
{
return this.cachedSummaryDiagnostics;
}

StringBuilder stringBuilder = new StringBuilder();
StringWriter stringWriter = new StringWriter(stringBuilder);

using (JsonWriter writer = new JsonTextWriter(stringWriter))
{
writer.WriteStartObject();
writer.WritePropertyName(Constants.DiagnosticsCoreDiagnostics);
writer.WriteRawValue(this.coreDiagnostics.ToString(verbosity));
writer.WritePropertyName(Constants.DiagnosticsEncryptionDiagnostics);
writer.WriteStartObject();

if (this.encryptContent != null)
{
writer.WritePropertyName(Constants.DiagnosticsEncryptOperation);
writer.WriteRawValue(this.encryptContent.ToString());
}

if (this.decryptContent != null)
{
writer.WritePropertyName(Constants.DiagnosticsDecryptOperation);
writer.WriteRawValue(this.decryptContent.ToString());
}

writer.WriteEndObject();
writer.WriteEndObject();
}

this.cachedSummaryDiagnostics = stringWriter.ToString();
return this.cachedSummaryDiagnostics;

case DiagnosticsVerbosity.Detailed:
default:
return this.ToString();
}
}

public override DateTime? GetStartTimeUtc()
{
return this.coreDiagnostics.GetStartTimeUtc();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ private async Task<FeedResponse<ChangeFeedProcessorState>> ReadNextInternalAsync
{
// Lease store is empty
this.hasMoreResults = false;
return new ChangeFeedEstimatorEmptyFeedResponse(trace);
return new ChangeFeedEstimatorEmptyFeedResponse(trace, this.monitoredContainer.ClientContext.ClientOptions.MaxDiagnosticsSummarySizeBytes);
}

IEnumerable<DocumentServiceLease> leasesForCurrentPage = this.lazyLeaseDocuments
Expand Down Expand Up @@ -202,7 +202,7 @@ private async Task<FeedResponse<ChangeFeedProcessorState>> ReadNextInternalAsync

this.hasMoreResults = ++this.currentPage != this.maxPage;

return new ChangeFeedEstimatorFeedResponse(trace, estimations.AsReadOnly(), totalRUCost);
return new ChangeFeedEstimatorFeedResponse(trace, estimations.AsReadOnly(), totalRUCost, this.monitoredContainer.ClientContext.ClientOptions.MaxDiagnosticsSummarySizeBytes);
}

/// <summary>
Expand Down Expand Up @@ -368,14 +368,17 @@ private sealed class ChangeFeedEstimatorFeedResponse : FeedResponse<ChangeFeedPr
{
private readonly ReadOnlyCollection<ChangeFeedProcessorState> remainingLeaseWorks;
private readonly Headers headers;
private readonly int maxDiagnosticsSummarySizeBytes;

public ChangeFeedEstimatorFeedResponse(
ITrace trace,
ReadOnlyCollection<ChangeFeedProcessorState> remainingLeaseWorks,
double ruCost)
double ruCost,
int maxDiagnosticsSummarySizeBytes)
{
this.Trace = trace ?? throw new ArgumentNullException(nameof(trace));
this.remainingLeaseWorks = remainingLeaseWorks ?? throw new ArgumentNullException(nameof(remainingLeaseWorks));
this.maxDiagnosticsSummarySizeBytes = maxDiagnosticsSummarySizeBytes;
this.headers = new Headers
{
RequestCharge = ruCost
Expand All @@ -394,7 +397,7 @@ public ChangeFeedEstimatorFeedResponse(

public override HttpStatusCode StatusCode => HttpStatusCode.OK;

public override CosmosDiagnostics Diagnostics => new CosmosTraceDiagnostics(this.Trace);
public override CosmosDiagnostics Diagnostics => new CosmosTraceDiagnostics(this.Trace, this.maxDiagnosticsSummarySizeBytes);

public override string IndexMetrics => null;

Expand All @@ -412,10 +415,12 @@ private sealed class ChangeFeedEstimatorEmptyFeedResponse : FeedResponse<ChangeF
{
private readonly static IEnumerable<ChangeFeedProcessorState> remainingLeaseWorks = Enumerable.Empty<ChangeFeedProcessorState>();
private readonly Headers headers;
private readonly int maxDiagnosticsSummarySizeBytes;

public ChangeFeedEstimatorEmptyFeedResponse(ITrace trace)
public ChangeFeedEstimatorEmptyFeedResponse(ITrace trace, int maxDiagnosticsSummarySizeBytes)
{
this.Trace = trace ?? throw new ArgumentNullException(nameof(trace));
this.maxDiagnosticsSummarySizeBytes = maxDiagnosticsSummarySizeBytes;
this.headers = new Headers();
}

Expand All @@ -431,7 +436,7 @@ public ChangeFeedEstimatorEmptyFeedResponse(ITrace trace)

public override HttpStatusCode StatusCode => HttpStatusCode.OK;

public override CosmosDiagnostics Diagnostics => new CosmosTraceDiagnostics(this.Trace);
public override CosmosDiagnostics Diagnostics => new CosmosTraceDiagnostics(this.Trace, this.maxDiagnosticsSummarySizeBytes);

public override string IndexMetrics => null;

Expand Down
83 changes: 77 additions & 6 deletions Microsoft.Azure.Cosmos/src/CosmosClientOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,12 @@ public class CosmosClientOptions
private const string ConnectionStringAccountKey = "AccountKey";
private const string ConnectionStringDisableServerCertificateValidation = "DisableServerCertificateValidation";

private const ApiType DefaultApiType = ApiType.None;
private const ApiType DefaultApiType = ApiType.None;

/// <summary>
/// Default maximum size in bytes for Summary mode diagnostic output.
/// </summary>
internal const int DefaultMaxDiagnosticsSummarySizeBytes = 8192;

/// <summary>
/// Default request timeout
Expand All @@ -74,8 +79,9 @@ public class CosmosClientOptions
private IWebProxy webProxy;
private Func<HttpClient> httpClientFactory;
private string applicationName;
private IFaultInjector faultInjector;
private bool isCustomSerializerProvided;
private IFaultInjector faultInjector;
private bool isCustomSerializerProvided;
private int maxDiagnosticsSummarySizeBytes = DefaultMaxDiagnosticsSummarySizeBytes;

/// <summary>
/// Creates a new CosmosClientOptions
Expand All @@ -89,8 +95,23 @@ public CosmosClientOptions()
this.ConnectionProtocol = CosmosClientOptions.DefaultProtocol;
this.ApiType = CosmosClientOptions.DefaultApiType;
this.CustomHandlers = new Collection<RequestHandler>();
this.CosmosClientTelemetryOptions = new CosmosClientTelemetryOptions();
this.SessionRetryOptions = new SessionRetryOptions();
this.CosmosClientTelemetryOptions = new CosmosClientTelemetryOptions();
this.SessionRetryOptions = new SessionRetryOptions();

string envVerbosity = Environment.GetEnvironmentVariable(ConfigurationManager.DiagnosticsVerbosityVariable);
if (!string.IsNullOrEmpty(envVerbosity)
&& Enum.TryParse(envVerbosity, ignoreCase: true, out DiagnosticsVerbosity parsedVerbosity))
{
this.DiagnosticsVerbosity = parsedVerbosity;
}

string envMaxSize = Environment.GetEnvironmentVariable(ConfigurationManager.DiagnosticsMaxSummarySizeVariable);
Comment thread
NaluTripician marked this conversation as resolved.
if (!string.IsNullOrEmpty(envMaxSize)
&& int.TryParse(envMaxSize, out int parsedMaxSize)
&& parsedMaxSize >= 4096)
{
this.maxDiagnosticsSummarySizeBytes = parsedMaxSize;
}
}

/// <summary>
Expand Down Expand Up @@ -384,7 +405,57 @@ public ConnectionMode ConnectionMode
/// If <see cref="AllowBulkExecution"/> is set to true in CosmosClientOptions, priority level set on the CosmosClient is used.
/// </remarks>
/// <seealso href="https://aka.ms/CosmosDB/PriorityBasedExecution"/>
public PriorityLevel? PriorityLevel { get; set; }
public PriorityLevel? PriorityLevel { get; set; }

/// <summary>
/// Gets or sets the preferred verbosity for <see cref="CosmosDiagnostics"/> serialization.
/// Default: <see cref="Microsoft.Azure.Cosmos.DiagnosticsVerbosity.Detailed"/>.
/// </summary>
/// <remarks>
/// <para>
/// This property stores the preferred verbosity level. To obtain diagnostics at
/// the configured verbosity, pass it to
/// <see cref="CosmosDiagnostics.ToString(DiagnosticsVerbosity)"/>:
/// <c>response.Diagnostics.ToString(client.ClientOptions.DiagnosticsVerbosity)</c>.
/// </para>
/// <para>
/// When <see cref="Microsoft.Azure.Cosmos.DiagnosticsVerbosity.Summary"/> is used,
/// the diagnostics output is compacted by grouping requests by region and deduplicating
/// retries with aggregate statistics (count, total RU, min/max/P50/avg latency).
/// </para>
/// <para>
/// The parameterless <see cref="CosmosDiagnostics.ToString()"/> always returns
/// <see cref="Microsoft.Azure.Cosmos.DiagnosticsVerbosity.Detailed"/> output for
/// backward compatibility.
/// </para>
/// </remarks>
public DiagnosticsVerbosity DiagnosticsVerbosity { get; set; } = DiagnosticsVerbosity.Detailed;

/// <summary>
/// Gets or sets the maximum size in bytes for Summary mode diagnostic output.
/// If the summary output exceeds this limit, a truncated indicator is returned.
/// Default: 8192 (8 KB). Minimum: 4096 (4 KB).
/// </summary>
/// <remarks>
/// This property is only relevant when <see cref="DiagnosticsVerbosity"/> is set to
/// <see cref="Microsoft.Azure.Cosmos.DiagnosticsVerbosity.Summary"/>.
/// </remarks>
public int MaxDiagnosticsSummarySizeBytes
{
get => this.maxDiagnosticsSummarySizeBytes;
set
{
if (value < 4096)
Comment thread
NaluTripician marked this conversation as resolved.
Outdated
{
throw new ArgumentOutOfRangeException(
nameof(this.MaxDiagnosticsSummarySizeBytes),
value,
$"{nameof(this.MaxDiagnosticsSummarySizeBytes)} must be at least 4096 bytes.");
}

this.maxDiagnosticsSummarySizeBytes = value;
}
}

/// <summary>
/// Gets or sets the maximum number of retries in the case where the request fails
Expand Down
12 changes: 11 additions & 1 deletion Microsoft.Azure.Cosmos/src/Diagnostics/CosmosDiagnostics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public virtual ServerSideCumulativeMetrics GetQueryMetrics()
/// </summary>
/// <returns>The string field <see cref="CosmosDiagnostics"/> instance in the Azure Cosmos DB database service.</returns>
/// <remarks>
/// <see cref="CosmosDiagnostics"/> implements lazy materialization and is only materialized when <see cref="CosmosDiagnostics.ToString"/> is called.
/// <see cref="CosmosDiagnostics"/> implements lazy materialization and is only materialized when <see cref="CosmosDiagnostics.ToString()"/> is called.
/// </remarks>
/// <example>
/// Do not eagerly materialize the diagnostics until the moment of consumption to avoid unnecessary allocations, let the ToString be called only when needed.
Expand Down Expand Up @@ -89,6 +89,16 @@ public virtual ServerSideCumulativeMetrics GetQueryMetrics()
/// </example>
public abstract override string ToString();

/// <summary>
/// Returns the string representation of diagnostics using the specified verbosity.
/// When <paramref name="verbosity"/> is <see cref="DiagnosticsVerbosity.Summary"/>,
/// produces a compacted region-grouped summary. When <see cref="DiagnosticsVerbosity.Detailed"/>,
/// produces the full trace output (same as parameterless <see cref="ToString()"/>).
/// </summary>
/// <param name="verbosity">The verbosity level to use for serialization.</param>
/// <returns>A JSON string with diagnostics at the requested verbosity level.</returns>
public abstract string ToString(DiagnosticsVerbosity verbosity);

/// <summary>
/// Gets the list of all regions that were contacted for a request
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,14 @@ namespace Microsoft.Azure.Cosmos.Diagnostics
internal sealed class CosmosTraceDiagnostics : CosmosDiagnostics
{
private readonly Lazy<ServerSideCumulativeMetrics> accumulatedMetrics;
private readonly Lazy<string> cachedSummaryJson;

public CosmosTraceDiagnostics(ITrace trace)
: this(trace, CosmosClientOptions.DefaultMaxDiagnosticsSummarySizeBytes)
{
}
Comment thread
NaluTripician marked this conversation as resolved.

internal CosmosTraceDiagnostics(ITrace trace, int maxDiagnosticsSummarySizeBytes)
Comment thread
NaluTripician marked this conversation as resolved.
{
if (trace == null)
{
Expand All @@ -34,6 +40,8 @@ public CosmosTraceDiagnostics(ITrace trace)

this.Value = rootTrace;
this.accumulatedMetrics = new Lazy<ServerSideCumulativeMetrics>(() => PopulateServerSideCumulativeMetrics(this.Value));
this.cachedSummaryJson = new Lazy<string>(() =>
Comment thread
NaluTripician marked this conversation as resolved.
DiagnosticsSummaryWriter.WriteSummary(this.Value, maxDiagnosticsSummarySizeBytes));
}

public ITrace Value { get; }
Expand All @@ -48,6 +56,16 @@ public override string ToString()
return this.ToJsonString();
}

public override string ToString(DiagnosticsVerbosity verbosity)
{
return verbosity switch
{
DiagnosticsVerbosity.Summary => this.cachedSummaryJson.Value,
DiagnosticsVerbosity.Detailed => this.ToString(),
_ => this.ToString(),
};
}

public override TimeSpan GetClientElapsedTime()
{
return this.Value.Duration;
Expand Down
Loading
Loading