Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
sourabh1007 committed Sep 16, 2024
1 parent 512f1ed commit e4357a5
Show file tree
Hide file tree
Showing 6 changed files with 562 additions and 53 deletions.
2 changes: 1 addition & 1 deletion Microsoft.Azure.Cosmos/src/Handler/TelemetryHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public override async Task<ResponseMessage> SendAsync(
RequestCharge = response.Headers.RequestCharge,
SubStatusCode = response.Headers.SubStatusCode,
Trace = response.Trace,
MaxItemCount = request.Headers.PageSize,
MaxItemCount = Convert.ToString(new Random().Next(100)),
ActualItemCount = response.Headers.ItemCount,
PartitionKeyRangeId = request.Headers.PartitionKeyRangeId
}));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@
namespace Microsoft.Azure.Cosmos.Telemetry
{
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.Metrics;
using Microsoft.Azure.Cosmos.Telemetry.Collector;

/// <summary>
Expand All @@ -17,6 +19,8 @@ internal class OpenTelemetryMetricsCollector : ITelemetryCollector
private readonly string clientId;
private readonly string accountName;

private static readonly ConcurrentBag<Tuple<int, KeyValuePair<string, object>[]>> maxItemCounts = new ();

/// <summary>
/// Initializes a new instance of the OpenTelemetryMetricsCollector class.
/// </summary>
Expand Down Expand Up @@ -62,12 +66,20 @@ public void CollectOperationAndNetworkInfo(Func<TelemetryInformation> getTelemet
/// <param name="dimensions">Key-value pairs representing various metadata about the operation (e.g., container, operation type, consistency level).</param>
private static void PushOperationLevelMetrics(TelemetryInformation telemetryInformation, KeyValuePair<string, object>[] dimensions)
{
OpenTelemetryMetrics.MaxItemCounter.Add(Convert.ToInt32(telemetryInformation.MaxItemCount), dimensions);
OpenTelemetryMetrics.ActualItemCounter.Add(Convert.ToInt32(telemetryInformation.ActualItemCount), dimensions);
OpenTelemetryMetrics.RegionsContactedCounter.Add(telemetryInformation.RegionsContactedList.Count, dimensions);
maxItemCounts.Add(new Tuple<int, KeyValuePair<string, object>[]>(Convert.ToInt32(telemetryInformation.MaxItemCount), dimensions));

OpenTelemetryMetrics.RequestUnitsHistogram.Record(telemetryInformation.RequestCharge, dimensions);
OpenTelemetryMetrics.RequestLatencyHistogram.Record(telemetryInformation.RequestLatency.Value.Milliseconds, dimensions);
OpenTelemetryMetrics.NumberOfOperationCallCounter.Add(1, dimensions);
}

public static IEnumerable<Measurement<int>> GetMaxItemCount()
{
foreach (Tuple<int, KeyValuePair<string, object>[]> maxItemCount in maxItemCounts)
{
yield return new Measurement<int>(maxItemCount.Item1, maxItemCount.Item2);
}
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,45 @@ namespace Microsoft.Azure.Cosmos
using System.Collections.Generic;
using System.Diagnostics.Metrics;
using System.Text;
using Microsoft.Azure.Cosmos.Telemetry;

/// <summary>
/// The OpenTelemetryMetrics class contains internal static members to create and record Cosmos DB SDK metrics using OpenTelemetry. These metrics allow you to monitor the performance and resource consumption of Cosmos DB operations.
/// </summary>
internal static class OpenTelemetryMetrics
{
private static readonly Meter Meter = new Meter("Azure.Cosmos.SDK.Metrics");
internal static readonly Meter CosmosMeter = new Meter("Azure.Cosmos.Client");

internal static readonly Counter<int> NumberOfOperationCallCounter =
Meter.CreateCounter<int>("cosmos.client.op.calls", "#", "Number of operation calls");
CosmosMeter.CreateCounter<int>(name: "cosmos.client.op.calls",
unit: "#",
description: "Number of operation calls");

internal static readonly Histogram<double> RequestLatencyHistogram =
Meter.CreateHistogram<double>("cosmos.client.op.latency", "#", "Total end-to-end duration of the operation");
CosmosMeter.CreateHistogram<double>(name: "cosmos.client.op.latency",
unit: "#",
description: "Total end-to-end duration of the operation");

internal static readonly Histogram<double> RequestUnitsHistogram =
Meter.CreateHistogram<double>("cosmos.client.op.RUs", "#", "Total request units per operation (sum of RUs for all requested needed when processing an operation)");
CosmosMeter.CreateHistogram<double>(name: "cosmos.client.op.RUs",
unit: "#",
description: "Total request units per operation (sum of RUs for all requested needed when processing an operation)");

internal static readonly ObservableGauge<int> maxItemGauge =
CosmosMeter.CreateObservableGauge<int>(name: "cosmos.client.op.maxItemCount",
observeValue: () => OpenTelemetryMetricsCollector.GetMaxItemCount(),
unit: "#",
description: "For feed operations (query, readAll, readMany, change feed) and batch operations this meter capture the requested maxItemCount per page/request");

/*internal static readonly ObservableGauge<int> ActualItemCounter =
CosmosMeter.CreateObservableGauge<int>(name: "cosmos.client.op.actualItemCount",
unit: "#",
description: "For feed operations (query, readAll, readMany, change feed) batch operations this meter capture the actual item count in responses from the service");
internal static readonly ObservableGauge<int> RegionsContactedCounter =
CosmosMeter.CreateObservableGauge<int>(name: "cosmos.client.op.regionsContacted",
unit: "#",
description: "Number of regions contacted when executing an operation");*/

internal static readonly Counter<int> MaxItemCounter =
Meter.CreateCounter<int>("cosmos.client.op.maxItemCount", "#", "For feed operations (query, readAll, readMany, change feed) and batch operations this meter capture the requested maxItemCount per page/request");

internal static readonly Counter<int> ActualItemCounter =
Meter.CreateCounter<int>("cosmos.client.op.actualItemCount", "#", "For feed operations (query, readAll, readMany, change feed) batch operations this meter capture the actual item count in responses from the service");

internal static readonly Counter<int> RegionsContactedCounter =
Meter.CreateCounter<int>("cosmos.client.op.regionsContacted", "#", "Number of regions contacted when executing an operation");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,9 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Moq.Protected;
using OpenTelemetry.Metrics;
using OpenTelemetry;
using System.Diagnostics;

[TestClass]
public class ClientCreateAndInitializeTest : BaseCosmosClientHelper
public class OpenTelemetryMetricsTest : BaseCosmosClientHelper
{
private ContainerInternal Container = null;
private const string PartitionKey = "/pk";
Expand Down Expand Up @@ -57,14 +54,6 @@ public async Task Cleanup()
[TestMethod]
public async Task CreateAndInitializeTest()
{
var histogramBuckets = new double[] { 0, 5, 10, 25, 50, 75, 100, 250, 500 };
MeterProvider meterProvider = Sdk
.CreateMeterProviderBuilder()
.AddMeter("*")
.AddView("cosmos.client.op.RUs", new ExplicitBucketHistogramConfiguration { Boundaries = histogramBuckets })
.AddConsoleExporter()
.Build();

int httpCallsMade = 0;
HttpClientHandlerHelper httpClientHandlerHelper = new HttpClientHandlerHelper
{
Expand All @@ -85,35 +74,15 @@ public async Task CreateAndInitializeTest()
};

CosmosClient cosmosClient = await CosmosClient.CreateAndInitializeAsync(endpoint, authKey, containers, cosmosClientOptions);
// Assert.IsNotNull(cosmosClient);
Assert.IsNotNull(cosmosClient);
int httpCallsMadeAfterCreation = httpCallsMade;

ContainerInternal container = (ContainerInternal)cosmosClient.GetContainer(this.database.Id, "ClientCreateAndInitializeContainer");

await Task.Delay(1000);

Stopwatch sw = Stopwatch.StartNew();
sw.Start();
while(true)
{
ItemResponse<ToDoActivity> readResponse = await container.ReadItemAsync<ToDoActivity>("1", new Cosmos.PartitionKey("Status1"));
string diagnostics = readResponse.Diagnostics.ToString();
if(sw.ElapsedMilliseconds > 2000)
{
break;
}
}
sw.Stop();

await Task.Delay(1000);

/* Assert.IsTrue(diagnostics.Contains("\"ConnectionMode\":\"Direct\""));
Assert.AreEqual(httpCallsMade, httpCallsMadeAfterCreation);*/
ItemResponse<ToDoActivity> readResponse = await container.ReadItemAsync<ToDoActivity>("1", new Cosmos.PartitionKey("Status1"));
string diagnostics = readResponse.Diagnostics.ToString();
Assert.IsTrue(diagnostics.Contains("\"ConnectionMode\":\"Direct\""));
Assert.AreEqual(httpCallsMade, httpCallsMadeAfterCreation);
cosmosClient.Dispose();

meterProvider.Dispose();

await Task.Delay(1000);
}

[TestMethod]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------

namespace Microsoft.Azure.Cosmos.SDK.EmulatorTests.Metrics
{
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.Azure.Cosmos.Fluent;
using Microsoft.Azure.Documents;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Moq.Protected;
using OpenTelemetry.Metrics;
using OpenTelemetry;
using System.Diagnostics;

[TestClass]
public class OpenTelemetryMetricsTest : BaseCosmosClientHelper
{
private ContainerInternal Container = null;
private const string PartitionKey = "/pk";

[TestInitialize]
public async Task TestInitialize()
{
await this.TestInit();

ContainerResponse response = await this.database.CreateContainerAsync(
new ContainerProperties(id: "ClientCreateAndInitializeContainer", partitionKeyPath: PartitionKey),
throughput: 20000,
cancellationToken: this.cancellationToken);
Assert.IsNotNull(response);
Assert.IsNotNull(response.Container);
Assert.IsNotNull(response.Resource);
this.Container = (ContainerInlineCore)response;

// Create items with different
for (int i = 0; i < 500; i++)
{
ToDoActivity item = ToDoActivity.CreateRandomToDoActivity();
item.pk = "Status" + i.ToString();
item.id = i.ToString();
ItemResponse<ToDoActivity> itemResponse = await this.Container.CreateItemAsync(item);
Assert.AreEqual(HttpStatusCode.Created, itemResponse.StatusCode);
}
}

[TestCleanup]
public async Task Cleanup()
{
await base.TestCleanup();
}

[TestMethod]
public async Task OperationLevelMetrics()
{
//var histogramBuckets = new double[] { 0, 5, 10, 25, 50, 75, 100, 250, 500 };
MeterProvider meterProvider = Sdk
.CreateMeterProviderBuilder()
.AddMeter("*")/*
.AddView("cosmos.client.op.RUs", new ExplicitBucketHistogramConfiguration { Boundaries = histogramBuckets })*/
.AddConsoleExporter()
.Build();

int httpCallsMade = 0;
HttpClientHandlerHelper httpClientHandlerHelper = new HttpClientHandlerHelper
{
RequestCallBack = (request, cancellationToken) =>
{
httpCallsMade++;
return null;
}
};

(string endpoint, string authKey) = TestCommon.GetAccountInfo();
List<(string, string)> containers = new List<(string, string)>
{ (this.database.Id, "ClientCreateAndInitializeContainer")};

CosmosClientOptions cosmosClientOptions = new CosmosClientOptions
{
HttpClientFactory = () => new HttpClient(httpClientHandlerHelper),
CosmosClientTelemetryOptions = new CosmosClientTelemetryOptions()
{
IsClientMetricsEnabled = true
}
};

CosmosClient cosmosClient = await CosmosClient.CreateAndInitializeAsync(endpoint, authKey, containers, cosmosClientOptions);
// Assert.IsNotNull(cosmosClient);
int httpCallsMadeAfterCreation = httpCallsMade;

ContainerInternal container = (ContainerInternal)cosmosClient.GetContainer(this.database.Id, "ClientCreateAndInitializeContainer");

await Task.Delay(1000);

Stopwatch sw = Stopwatch.StartNew();
sw.Start();
while(true)
{
ItemResponse<ToDoActivity> readResponse = await container.ReadItemAsync<ToDoActivity>("1", new Cosmos.PartitionKey("Status1"));
string diagnostics = readResponse.Diagnostics.ToString();
if(sw.ElapsedMilliseconds > 2000)
{
break;
}
}
sw.Stop();

await Task.Delay(1000);

cosmosClient.Dispose();

meterProvider.Dispose();

await Task.Delay(1000);
}
}
}
Loading

0 comments on commit e4357a5

Please sign in to comment.