From cf24aeb7e4085f83b84c37a02ae2e9dc53dc2f29 Mon Sep 17 00:00:00 2001 From: Sourabh Jain Date: Fri, 8 Nov 2024 08:35:32 +0530 Subject: [PATCH] first drfat uogeated dimensio name refactor code --- Microsoft.Azure.Cosmos/src/DocumentClient.cs | 1 + .../src/Resource/ClientContextCore.cs | 18 +- .../OpenTelemetry/CosmosDbClientMetrics.cs | 116 ++++++++- .../OpenTelemetry/CosmosNetworkMeter.cs | 238 ++++++++++++++++++ .../OpenTelemetryAttributeKeys.cs | 17 +- .../OpenTelemetry/OpenTelemetryAttributes.cs | 1 + .../ClientSideRequestStatisticsTraceDatum.cs | 30 ++- .../Metrics/CustomMetricExporter.cs | 3 + .../Metrics/OpenTelemetryMetricsTest.cs | 15 +- .../Telemetry/NetworkDataRecorderTest.cs | 6 +- .../Tracing/TraceTests.cs | 3 +- .../Tracing/TraceWriterBaselineTests.cs | 14 +- 12 files changed, 435 insertions(+), 27 deletions(-) create mode 100644 Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/CosmosNetworkMeter.cs diff --git a/Microsoft.Azure.Cosmos/src/DocumentClient.cs b/Microsoft.Azure.Cosmos/src/DocumentClient.cs index 144957de21..4b8536f9e0 100644 --- a/Microsoft.Azure.Cosmos/src/DocumentClient.cs +++ b/Microsoft.Azure.Cosmos/src/DocumentClient.cs @@ -958,6 +958,7 @@ internal virtual void Initialize(Uri serviceEndpoint, if (this.cosmosClientTelemetryOptions.IsClientMetricsEnabled) { CosmosOperationMeter.Initialize(); + CosmosNetworkMeter.Initialize(); CosmosOperationMeter.AddInstanceCount(this.ServiceEndpoint); } diff --git a/Microsoft.Azure.Cosmos/src/Resource/ClientContextCore.cs b/Microsoft.Azure.Cosmos/src/Resource/ClientContextCore.cs index 79a47158e9..df8da3c56a 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/ClientContextCore.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/ClientContextCore.cs @@ -533,17 +533,23 @@ private async Task RunWithDiagnosticsHelperAsync( && (!this.ClientOptions.CosmosClientTelemetryOptions.DisableDistributedTracing || this.ClientOptions.CosmosClientTelemetryOptions.IsClientMetricsEnabled)) { // Extracts and records telemetry data from the result of the operation. - OpenTelemetryAttributes response = openTelemetry?.Item2(result); + OpenTelemetryAttributes otelAttributes = openTelemetry?.Item2(result); // Records the telemetry attributes for Distributed Tracing (if enabled) - recorder.Record(response); + recorder.Record(otelAttributes); // Records metrics such as request units, latency, and item count for the operation. CosmosOperationMeter.RecordTelemetry(getOperationName: getOperationName, accountName: this.client.Endpoint, containerName: containerName, databaseName: databaseName, - attributes: response); + attributes: otelAttributes); + + CosmosNetworkMeter.RecordTelemetry(getOperationName: getOperationName, + accountName: this.client.Endpoint, + containerName: containerName, + databaseName: databaseName, + attributes: otelAttributes); } return result; } @@ -584,6 +590,12 @@ private async Task RunWithDiagnosticsHelperAsync( containerName: containerName, databaseName: databaseName, ex: cosmosException); + + CosmosNetworkMeter.RecordTelemetry(getOperationName: getOperationName, + accountName: this.client.Endpoint, + containerName: containerName, + databaseName: databaseName, + ex: cosmosException); } throw; diff --git a/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/CosmosDbClientMetrics.cs b/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/CosmosDbClientMetrics.cs index f295022504..bcaf0818fe 100644 --- a/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/CosmosDbClientMetrics.cs +++ b/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/CosmosDbClientMetrics.cs @@ -12,7 +12,7 @@ namespace Microsoft.Azure.Cosmos public sealed class CosmosDbClientMetrics { /// - /// OperationMetrics + /// Operation Metrics /// public static class OperationMetrics { @@ -101,6 +101,120 @@ public static class Description } } + /// + /// Network Metrics + /// + public static class NetworkMetrics + { + /// + /// the name of the operation meter + /// + public const string MeterName = "Azure.Cosmos.Client.Request"; + + /// + /// Version of the operation meter + /// + public const string Version = "1.0.0"; + + /// + /// Metric Names + /// + public static class Name + { + /// + /// Network Call Latency + /// + public const string Latency = "db.client.cosmosdb.request.duration"; + + /// + /// Request Payload Size + /// + public const string RequestBodySize = "db.client.cosmosdb.request.body.size"; + + /// + /// Request Payload Size + /// + public const string ResponseBodySize = "db.client.cosmosdb.response.body.size"; + + /// + /// Channel Aquisition Latency + /// + public const string ChannelAquisitionLatency = "db.client.cosmosdb.request.channel_aquisition.duration"; + + /// + /// Backend Server Latency + /// + public const string BackendLatency = "db.server.cosmosdb.request.duration"; + + /// + /// Transit Time Latency + /// + public const string TransitTimeLatency = "db.client.cosmosdb.request.transit.duration"; + + /// + /// Received Time Latency + /// + public const string ReceivedTimeLatency = "db.client.cosmosdb.request.received.duration"; + } + + /// + /// Unit for metrics + /// + public static class Unit + { + /// + /// Unit representing bytes + /// + public const string Bytes = "bytes"; + + /// + /// Unit representing time in seconds + /// + public const string Sec = "s"; + } + + /// + /// Provides descriptions for metrics. + /// + public static class Description + { + /// + /// Network Call Latency + /// + public const string Latency = "Duration of client requests."; + + /// + /// Request Payload Size + /// + public const string RequestBodySize = "Size of client request body."; + + /// + /// Request Payload Size + /// + public const string ResponseBodySize = "Size of client response body."; + + /// + /// Channel Aquisition Latency + /// + public const string ChannelAquisitionLatency = "The duration of the successfully established outbound TCP connections. i.e. Channel Aquisition Time (for direct mode)."; + + /// + /// Backend Server Latency + /// + public const string BackendLatency = "Backend Latency (for direct mode)."; + + /// + /// Transit Time Latency + /// + public const string TransitTimeLatency = "Time spent on the wire (for direct mode)."; + + /// + /// Received Time Latency + /// + public const string ReceivedTimeLatency = "Time spent on 'Received' stage (for direct mode)."; + } + } + /// /// Buckets /// diff --git a/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/CosmosNetworkMeter.cs b/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/CosmosNetworkMeter.cs new file mode 100644 index 0000000000..e9a18b5b64 --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/CosmosNetworkMeter.cs @@ -0,0 +1,238 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// ------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos.Telemetry +{ + using System; + using System.Collections.Generic; + using System.Diagnostics.Metrics; + using System.Linq; + using Microsoft.Azure.Cosmos.Diagnostics; + using Microsoft.Azure.Cosmos.Tracing.TraceData; + + internal static class CosmosNetworkMeter + { + /// + /// Meter instance for capturing various metrics related to Cosmos DB operations. + /// + internal static Meter NetworkMeter = new Meter(CosmosDbClientMetrics.NetworkMetrics.MeterName, CosmosDbClientMetrics.NetworkMetrics.Version); + + internal static Histogram RequestLatencyHistogram = null; + + internal static Histogram RequestBodySizeHistogram = null; + + internal static Histogram ResponseBodySizeHistogram = null; + + internal static Histogram ChannelAquisitionLatencyHistogram = null; + + internal static Histogram BackendLatencyHistogram = null; + + internal static Histogram TransitLatencyHistogram = null; + + internal static Histogram ReceivedLatencyHistogram = null; + + private static bool IsEnabled = false; + + /// + /// Initializes the histograms and counters for capturing Cosmos DB metrics. + /// + internal static void Initialize() + { + // If already initialized, do not initialize again + if (IsEnabled) + { + return; + } + + CosmosNetworkMeter.RequestLatencyHistogram ??= NetworkMeter.CreateHistogram(name: CosmosDbClientMetrics.NetworkMetrics.Name.Latency, + unit: CosmosDbClientMetrics.NetworkMetrics.Unit.Sec, + description: CosmosDbClientMetrics.NetworkMetrics.Description.Latency); + + CosmosNetworkMeter.RequestBodySizeHistogram ??= NetworkMeter.CreateHistogram(name: CosmosDbClientMetrics.NetworkMetrics.Name.RequestBodySize, + unit: CosmosDbClientMetrics.NetworkMetrics.Unit.Bytes, + description: CosmosDbClientMetrics.NetworkMetrics.Description.RequestBodySize); + + CosmosNetworkMeter.ResponseBodySizeHistogram ??= NetworkMeter.CreateHistogram(name: CosmosDbClientMetrics.NetworkMetrics.Name.ResponseBodySize, + unit: CosmosDbClientMetrics.NetworkMetrics.Unit.Bytes, + description: CosmosDbClientMetrics.NetworkMetrics.Description.ResponseBodySize); + + CosmosNetworkMeter.ChannelAquisitionLatencyHistogram ??= NetworkMeter.CreateHistogram(name: CosmosDbClientMetrics.NetworkMetrics.Name.ChannelAquisitionLatency, + unit: CosmosDbClientMetrics.NetworkMetrics.Unit.Sec, + description: CosmosDbClientMetrics.NetworkMetrics.Description.ChannelAquisitionLatency); + + CosmosNetworkMeter.BackendLatencyHistogram ??= NetworkMeter.CreateHistogram(name: CosmosDbClientMetrics.NetworkMetrics.Name.BackendLatency, + unit: CosmosDbClientMetrics.NetworkMetrics.Unit.Sec, + description: CosmosDbClientMetrics.NetworkMetrics.Description.BackendLatency); + + CosmosNetworkMeter.TransitLatencyHistogram ??= NetworkMeter.CreateHistogram(name: CosmosDbClientMetrics.NetworkMetrics.Name.TransitTimeLatency, + unit: CosmosDbClientMetrics.NetworkMetrics.Unit.Sec, + description: CosmosDbClientMetrics.NetworkMetrics.Description.TransitTimeLatency); + + CosmosNetworkMeter.ReceivedLatencyHistogram ??= NetworkMeter.CreateHistogram(name: CosmosDbClientMetrics.NetworkMetrics.Name.ReceivedTimeLatency, + unit: CosmosDbClientMetrics.NetworkMetrics.Unit.Sec, + description: CosmosDbClientMetrics.NetworkMetrics.Description.ReceivedTimeLatency); + + IsEnabled = true; + } + + internal static void RecordTelemetry(Func getOperationName, + Uri accountName, + string containerName, + string databaseName, + OpenTelemetryAttributes attributes = null, + CosmosException ex = null) + { + if (!IsEnabled) + { + return; + } + + SummaryDiagnostics summaryDiagnostics + = new SummaryDiagnostics(((CosmosTraceDiagnostics)(ex?.Diagnostics ?? attributes?.Diagnostics)).Value); + summaryDiagnostics.StoreResponseStatistics.Value.ForEach(stat => + { + if (stat?.StoreResult == null) + { + return; + } + + KeyValuePair[] dimension = GetDimensions( + getOperationName, accountName, containerName, databaseName, attributes, ex, tcpStats: stat); + + CosmosNetworkMeter.RecordRequestLatency(stat.RequestLatency.TotalSeconds, dimension); + CosmosNetworkMeter.RecordRequestBodySize(stat?.StoreResult?.TransportRequestStats?.RequestBodySizeInBytes, dimension); + CosmosNetworkMeter.RecordResponseBodySize(stat?.StoreResult?.TransportRequestStats?.ResponseBodySizeInBytes, dimension); + CosmosNetworkMeter.RecordBackendLatency(stat?.StoreResult?.BackendRequestDurationInMs, dimension); + }); + + summaryDiagnostics.HttpResponseStatistics.Value.ForEach(stat => + { + KeyValuePair[] dimension = GetDimensions( + getOperationName, accountName, containerName, databaseName, attributes, ex, httpStats: stat); + + CosmosNetworkMeter.RecordRequestLatency(stat.Duration.TotalSeconds, dimension); + CosmosNetworkMeter.RecordRequestBodySize(stat.HttpResponseMessage?.RequestMessage?.Content?.Headers?.ContentLength, dimension); + CosmosNetworkMeter.RecordResponseBodySize(stat.ResponseContentLength, dimension); + }); + } + + private static KeyValuePair[] GetDimensions(Func getOperationName, + Uri accountName, + string containerName, + string databaseName, + OpenTelemetryAttributes attributes, + CosmosException ex, + ClientSideRequestStatisticsTraceDatum.StoreResponseStatistics tcpStats = null, + ClientSideRequestStatisticsTraceDatum.HttpResponseStatistics? httpStats = null) + { + return new KeyValuePair[] + { + new KeyValuePair(OpenTelemetryAttributeKeys.DbSystemName, OpenTelemetryCoreRecorder.CosmosDb), + new KeyValuePair(OpenTelemetryAttributeKeys.ContainerName, containerName), + new KeyValuePair(OpenTelemetryAttributeKeys.DbName, databaseName), + new KeyValuePair(OpenTelemetryAttributeKeys.ServerAddress, accountName?.Host), + new KeyValuePair(OpenTelemetryAttributeKeys.ServerPort, accountName?.Port), + new KeyValuePair(OpenTelemetryAttributeKeys.DbOperation, getOperationName()), + new KeyValuePair(OpenTelemetryAttributeKeys.StatusCode, (int)(attributes?.StatusCode ?? ex?.StatusCode)), + new KeyValuePair(OpenTelemetryAttributeKeys.SubStatusCode, attributes?.SubStatusCode ?? ex?.SubStatusCode), + new KeyValuePair(OpenTelemetryAttributeKeys.ConsistencyLevel, attributes?.ConsistencyLevel ?? ex?.Headers?.ConsistencyLevel), + new KeyValuePair(OpenTelemetryAttributeKeys.NetworkProtocolName, GetEndpoint(tcpStats, httpStats).Scheme), + new KeyValuePair(OpenTelemetryAttributeKeys.ServiceEndpointHost, GetEndpoint(tcpStats, httpStats).Host), + new KeyValuePair(OpenTelemetryAttributeKeys.ServiceEndPointPort, GetEndpoint(tcpStats, httpStats).Port), + new KeyValuePair(OpenTelemetryAttributeKeys.ServiceEndpointResourceId, GetEndpoint(tcpStats, httpStats).PathAndQuery), + new KeyValuePair(OpenTelemetryAttributeKeys.ServiceEndpointStatusCode, GetStatusCode(tcpStats, httpStats)), + new KeyValuePair(OpenTelemetryAttributeKeys.ServiceEndpointSubStatusCode, GetSubStatusCode(tcpStats, httpStats)), + new KeyValuePair(OpenTelemetryAttributeKeys.ServiceEndpointRegion, GetRegion(tcpStats, httpStats)), + new KeyValuePair(OpenTelemetryAttributeKeys.ErrorType, GetException(tcpStats, httpStats)) + }; + } + + private static string GetRegion(ClientSideRequestStatisticsTraceDatum.StoreResponseStatistics tcpStats, ClientSideRequestStatisticsTraceDatum.HttpResponseStatistics? httpStats) + { + return httpStats?.Region ?? tcpStats.Region; + } + + private static string GetException(ClientSideRequestStatisticsTraceDatum.StoreResponseStatistics tcpStats, ClientSideRequestStatisticsTraceDatum.HttpResponseStatistics? httpStats) + { + return httpStats?.Exception?.Message ?? tcpStats?.StoreResult?.Exception?.Message; + } + + private static int GetSubStatusCode(ClientSideRequestStatisticsTraceDatum.StoreResponseStatistics tcpStats, ClientSideRequestStatisticsTraceDatum.HttpResponseStatistics? httpStats) + { + int? subStatuscode = null; + if (httpStats.HasValue && + httpStats.Value.HttpResponseMessage?.Headers != null && + httpStats.Value + .HttpResponseMessage + .Headers + .TryGetValues(Documents.WFConstants.BackendHeaders.SubStatus, out IEnumerable statuscodes)) + { + subStatuscode = Convert.ToInt32(statuscodes.FirstOrDefault()); + } + + return subStatuscode ?? Convert.ToInt32(tcpStats?.StoreResult?.SubStatusCode); + } + + private static int? GetStatusCode(ClientSideRequestStatisticsTraceDatum.StoreResponseStatistics tcpStats, ClientSideRequestStatisticsTraceDatum.HttpResponseStatistics? httpStats) + { + return (int?)httpStats?.HttpResponseMessage?.StatusCode ?? (int?)tcpStats?.StoreResult?.StatusCode; + } + + private static Uri GetEndpoint(ClientSideRequestStatisticsTraceDatum.StoreResponseStatistics tcpStats, ClientSideRequestStatisticsTraceDatum.HttpResponseStatistics? httpStats) + { + return httpStats?.RequestUri ?? tcpStats?.LocationEndpoint; + } + + internal static void RecordRequestLatency(double latency, KeyValuePair[] dimension) + { + if (!IsEnabled || CosmosNetworkMeter.RequestLatencyHistogram == null || + !CosmosNetworkMeter.RequestLatencyHistogram.Enabled) + { + return; + } + + CosmosNetworkMeter.RequestLatencyHistogram.Record(latency, dimension); + } + + internal static void RecordRequestBodySize(long? bodySize, KeyValuePair[] dimension) + { + if (!IsEnabled || + CosmosNetworkMeter.RequestBodySizeHistogram == null || + !bodySize.HasValue || + !CosmosNetworkMeter.ResponseBodySizeHistogram.Enabled) + { + return; + } + + CosmosNetworkMeter.RequestBodySizeHistogram.Record(bodySize.Value, dimension); + } + + internal static void RecordResponseBodySize(long? bodySize, KeyValuePair[] dimension) + { + if (!IsEnabled || + CosmosNetworkMeter.ResponseBodySizeHistogram == null || + !bodySize.HasValue || + !CosmosNetworkMeter.ResponseBodySizeHistogram.Enabled) + { + return; + } + + CosmosNetworkMeter.ResponseBodySizeHistogram.Record(bodySize.Value, dimension); + } + + internal static void RecordBackendLatency(string latencyInMs, KeyValuePair[] dimension) + { + if (!IsEnabled || + CosmosNetworkMeter.BackendLatencyHistogram == null || + string.IsNullOrEmpty(latencyInMs) || + !CosmosNetworkMeter.BackendLatencyHistogram.Enabled) + { + return; + } + + CosmosNetworkMeter.BackendLatencyHistogram.Record(Convert.ToDouble(latencyInMs) / 1000, dimension); + } + + } +} diff --git a/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryAttributeKeys.cs b/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryAttributeKeys.cs index 8c6e97b428..02c2f7e574 100644 --- a/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryAttributeKeys.cs +++ b/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryAttributeKeys.cs @@ -168,11 +168,22 @@ internal sealed class OpenTelemetryAttributeKeys : IActivityAttributePopulator /// public const string ExceptionStacktrace = "exception.stacktrace"; - /// - /// Represents the type of error. - /// public const string ErrorType = "error.type"; + public const string NetworkProtocolName = "network.protocol.name"; + + public const string ServiceEndpointHost = "network.protocol.host"; + + public const string ServiceEndPointPort = "network.protocol.port"; + + public const string ServiceEndpointStatusCode = "db.cosmosdb.network.response.status_code"; + + public const string ServiceEndpointSubStatusCode = "db.cosmosdb.network.response.sub_status_code"; + + public const string ServiceEndpointRegion = "cloud.region"; + + public const string ServiceEndpointResourceId = "db.cosmosdb.network.routing_id "; + public void PopulateAttributes(DiagnosticScope scope, string operationName, string databaseName, diff --git a/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryAttributes.cs b/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryAttributes.cs index 3206266291..f5b491c65f 100644 --- a/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryAttributes.cs +++ b/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryAttributes.cs @@ -5,6 +5,7 @@ namespace Microsoft.Azure.Cosmos.Telemetry { using System.Net; + using Microsoft.Azure.Cosmos.Diagnostics; using Microsoft.Azure.Cosmos.Query.Core; internal class OpenTelemetryAttributes diff --git a/Microsoft.Azure.Cosmos/src/Tracing/TraceData/ClientSideRequestStatisticsTraceDatum.cs b/Microsoft.Azure.Cosmos/src/Tracing/TraceData/ClientSideRequestStatisticsTraceDatum.cs index 829899c4b1..5c45957081 100644 --- a/Microsoft.Azure.Cosmos/src/Tracing/TraceData/ClientSideRequestStatisticsTraceDatum.cs +++ b/Microsoft.Azure.Cosmos/src/Tracing/TraceData/ClientSideRequestStatisticsTraceDatum.cs @@ -12,6 +12,7 @@ namespace Microsoft.Azure.Cosmos.Tracing.TraceData using System.Text; using Microsoft.Azure.Cosmos.Handler; using Microsoft.Azure.Cosmos.Json; + using Microsoft.Azure.Cosmos.Pagination; using Microsoft.Azure.Documents; using Microsoft.Azure.Documents.Rntbd; @@ -260,7 +261,8 @@ public void RecordResponse( request.ResourceType, request.OperationType, request.Headers[HttpConstants.HttpHeaders.SessionToken], - locationEndpoint); + locationEndpoint, + regionName); lock (this.storeResponseStatistics) { @@ -352,8 +354,9 @@ public void RecordHttpResponse(HttpRequestMessage request, lock (this.httpResponseStatistics) { Uri locationEndpoint = request.RequestUri; + object regionName = null; if (request.Properties != null && - request.Properties.TryGetValue(HttpRequestRegionNameProperty, out object regionName)) + request.Properties.TryGetValue(HttpRequestRegionNameProperty, out regionName)) { this.TraceSummary.AddRegionContacted(Convert.ToString(regionName), locationEndpoint); } @@ -365,7 +368,8 @@ public void RecordHttpResponse(HttpRequestMessage request, request.Method, resourceType, response, - exception: null)); + exception: null, + region: Convert.ToString(regionName))); } } @@ -380,8 +384,10 @@ public void RecordHttpException(HttpRequestMessage request, lock (this.httpResponseStatistics) { Uri locationEndpoint = request.RequestUri; + + object regionName = null; if (request.Properties != null && - request.Properties.TryGetValue(HttpRequestRegionNameProperty, out object regionName)) + request.Properties.TryGetValue(HttpRequestRegionNameProperty, out regionName)) { this.TraceSummary.AddRegionContacted(Convert.ToString(regionName), locationEndpoint); } @@ -393,7 +399,8 @@ public void RecordHttpException(HttpRequestMessage request, request.Method, resourceType, responseMessage: null, - exception: exception)); + exception: exception, + region: Convert.ToString(regionName))); } } @@ -460,7 +467,8 @@ public StoreResponseStatistics( ResourceType resourceType, OperationType operationType, string requestSessionToken, - Uri locationEndpoint) + Uri locationEndpoint, + string region) { this.RequestStartTime = requestStartTime; this.RequestResponseTime = requestResponseTime; @@ -470,8 +478,10 @@ public StoreResponseStatistics( this.RequestSessionToken = requestSessionToken; this.LocationEndpoint = locationEndpoint; this.IsSupplementalResponse = operationType == OperationType.Head || operationType == OperationType.HeadFeed; + this.Region = region; } + public string Region { get; } public DateTime? RequestStartTime { get; } public DateTime RequestResponseTime { get; } public StoreResult StoreResult { get; } @@ -492,7 +502,8 @@ public HttpResponseStatistics( HttpMethod httpMethod, ResourceType resourceType, HttpResponseMessage responseMessage, - Exception exception) + Exception exception, + string region) { this.RequestStartTime = requestStartTime; this.Duration = requestEndTime - requestStartTime; @@ -501,7 +512,8 @@ public HttpResponseStatistics( this.ResourceType = resourceType; this.HttpMethod = httpMethod; this.RequestUri = requestUri; - + this.Region = region; + this.ResponseContentLength = responseMessage?.Content?.Headers?.ContentLength; if (responseMessage != null) { Headers headers = new Headers(GatewayStoreClient.ExtractResponseHeaders(responseMessage)); @@ -513,6 +525,7 @@ public HttpResponseStatistics( } } + public string Region { get; } public DateTime RequestStartTime { get; } public TimeSpan Duration { get; } public HttpResponseMessage HttpResponseMessage { get; } @@ -521,6 +534,7 @@ public HttpResponseStatistics( public HttpMethod HttpMethod { get; } public Uri RequestUri { get; } public string ActivityId { get; } + public long? ResponseContentLength { get; } } } } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Metrics/CustomMetricExporter.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Metrics/CustomMetricExporter.cs index c8b2dc0d21..be0977fade 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Metrics/CustomMetricExporter.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Metrics/CustomMetricExporter.cs @@ -8,6 +8,7 @@ namespace Microsoft.Azure.Cosmos.SDK.EmulatorTests.Metrics using OpenTelemetry; using System.Collections.Generic; using System.Threading; + using System; public class CustomMetricExporter : BaseExporter { @@ -23,8 +24,10 @@ public CustomMetricExporter(ManualResetEventSlim manualResetEventSlim) // This method will be called periodically by OpenTelemetry SDK public override ExportResult Export(in Batch batch) { + Console.WriteLine(); foreach (Metric metric in batch) { + Console.WriteLine(metric.Name + " " + metric.MetricType); ActualMetrics.TryAdd(metric.Name, metric.MetricType); } diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Metrics/OpenTelemetryMetricsTest.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Metrics/OpenTelemetryMetricsTest.cs index 4c614ddc47..1f2829adfb 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Metrics/OpenTelemetryMetricsTest.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Metrics/OpenTelemetryMetricsTest.cs @@ -24,7 +24,14 @@ public class OpenTelemetryMetricsTest : BaseCosmosClientHelper { "db.client.operation.duration", MetricType.Histogram }, { "db.client.response.row_count", MetricType.Histogram}, { "db.client.cosmosdb.operation.request_charge", MetricType.Histogram }, - { "db.client.cosmosdb.active_instance.count", MetricType.LongSumNonMonotonic } + { "db.client.cosmosdb.active_instance.count", MetricType.LongSumNonMonotonic }, + { "db.client.cosmosdb.request.duration", MetricType.Histogram}, + { "db.client.cosmosdb.request.body.size", MetricType.Histogram}, + { "db.client.cosmosdb.response.body.size", MetricType.Histogram}, + { "db.server.cosmosdb.request.duration", MetricType.Histogram}, + /* { "db.client.cosmosdb.request.channel_aquisition.duration", MetricType.Histogram}, + { "db.client.cosmosdb.request.transit.duration", MetricType.Histogram}, + { "db.client.cosmosdb.request.received.duration", MetricType.Histogram}*/ }; private MeterProvider meterProvider; @@ -34,8 +41,8 @@ public async Task Init() // Initialize OpenTelemetry MeterProvider this.meterProvider = Sdk .CreateMeterProviderBuilder() - .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("Azure Cosmos DB Operation Level Metrics")) - .AddMeter(CosmosDbClientMetrics.OperationMetrics.MeterName) + .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService("Azure Cosmos DB Metrics")) + .AddMeter(CosmosDbClientMetrics.OperationMetrics.MeterName, CosmosDbClientMetrics.NetworkMetrics.MeterName) .AddView( instrumentName: CosmosDbClientMetrics.OperationMetrics.Name.RequestCharge, metricStreamConfiguration: new ExplicitBucketHistogramConfiguration @@ -72,7 +79,7 @@ public async Task Cleanup() } [TestMethod] - public async Task OperationLevelMetricsGenerationTest() + public async Task MetricsGenerationTest() { Container container = await this.database.CreateContainerIfNotExistsAsync(Guid.NewGuid().ToString(), "/pk", throughput: 10000); for (int count = 0; count < 10; count++) diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Telemetry/NetworkDataRecorderTest.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Telemetry/NetworkDataRecorderTest.cs index fc6c8a25c3..be2600c31d 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Telemetry/NetworkDataRecorderTest.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Telemetry/NetworkDataRecorderTest.cs @@ -36,7 +36,8 @@ public void TestRecordWithErroredAndHighLatencyRequests() resourceType: Documents.ResourceType.Document, operationType: OperationType.Create, requestSessionToken: default, - locationEndpoint: new Uri("https://dummy.url")), + locationEndpoint: new Uri("https://dummy.url"), + "region1"), new StoreResponseStatistics( requestStartTime: DateTime.Now, @@ -48,7 +49,8 @@ public void TestRecordWithErroredAndHighLatencyRequests() resourceType: Documents.ResourceType.Document, operationType: OperationType.Create, requestSessionToken: default, - locationEndpoint: new Uri("https://dummy.url")) + locationEndpoint: new Uri("https://dummy.url"), + "region1") }; recorder.Record(stats, "databaseId", "containerId"); diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Tracing/TraceTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Tracing/TraceTests.cs index 6e8b42b552..07472a3a44 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Tracing/TraceTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Tracing/TraceTests.cs @@ -109,7 +109,8 @@ public void ValidateStoreResultSerialization() ResourceType.Document, OperationType.Query, "42", - new Uri("http://someUri1.com")); + new Uri("http://someUri1.com"), + "region1"); ((List)datum.GetType().GetField("storeResponseStatistics", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(datum)).Add(storeResponseStatistics); diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Tracing/TraceWriterBaselineTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Tracing/TraceWriterBaselineTests.cs index cefb82338d..9488d2373a 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Tracing/TraceWriterBaselineTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Tracing/TraceWriterBaselineTests.cs @@ -367,7 +367,8 @@ public void TraceData() ResourceType.Document, OperationType.Query, "42", - uri1.Uri); + uri1.Uri, + "region1"); TraceWriterBaselineTests.GetPrivateField>(datum, "storeResponseStatistics").Add(storeResponseStatistics); rootTrace.AddDatum("Client Side Request Stats", datum); @@ -401,7 +402,8 @@ public void TraceData() resourceType: default, operationType: default, requestSessionToken: default, - locationEndpoint: default); + locationEndpoint: default, + "region1"); TraceWriterBaselineTests.GetPrivateField>(datum, "storeResponseStatistics").Add(storeResponseStatistics); rootTrace.AddDatum("Client Side Request Stats Default", datum); @@ -426,7 +428,8 @@ public void TraceData() HttpMethod.Get, ResourceType.Document, new HttpResponseMessage(System.Net.HttpStatusCode.OK) { ReasonPhrase = "Success" }, - exception: null); + exception: null, + "region1"); TraceWriterBaselineTests.GetPrivateField>(datum, "httpResponseStatistics").Add(httpResponseStatistics); @@ -437,7 +440,8 @@ public void TraceData() HttpMethod.Get, ResourceType.Document, responseMessage: null, - exception: new OperationCanceledException()); + exception: new OperationCanceledException(), + "region1"); TraceWriterBaselineTests.GetPrivateField>(datum, "httpResponseStatistics").Add(httpResponseStatisticsException); rootTrace.AddDatum("Client Side Request Stats", datum); @@ -939,7 +943,7 @@ public static TraceForBaselineTesting GetRootTrace() return new TraceForBaselineTesting("Trace For Baseline Testing", TraceLevel.Info, TraceComponent.Unknown, parent: null); } - public void UpdateRegionContacted(TraceDatum traceDatum) + public void UpdateRegionContacted(TraceDatum _) { //NoImplementation }