diff --git a/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/AppInsightClassicAttributeKeys.cs b/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/AppInsightClassicAttributeKeys.cs
index 49d3c4bc3b..1c4dd81d30 100644
--- a/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/AppInsightClassicAttributeKeys.cs
+++ b/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/AppInsightClassicAttributeKeys.cs
@@ -4,6 +4,9 @@
namespace Microsoft.Azure.Cosmos.Telemetry
{
+ using System;
+ using global::Azure.Core;
+
internal sealed class AppInsightClassicAttributeKeys
{
///
@@ -90,5 +93,71 @@ internal sealed class AppInsightClassicAttributeKeys
/// Represents the item count in the operation.
///
public const string ItemCount = "db.cosmosdb.item_count";
+
+ ///
+ /// Represents the type of exception.
+ ///
+ public const string ExceptionType = "exception.type";
+
+ ///
+ /// Represents the message of the exception.
+ ///
+ public const string ExceptionMessage = "exception.message";
+
+ ///
+ /// Represents the stack trace of the exception.
+ ///
+ public const string ExceptionStacktrace = "exception.stacktrace";
+
+ public static void PopulateAttributes(DiagnosticScope scope,
+ string operationName,
+ string databaseName,
+ string containerName,
+ string accountName,
+ string userAgent,
+ string machineId,
+ string clientId,
+ string connectionMode)
+ {
+ scope.AddAttribute(AppInsightClassicAttributeKeys.DbOperation, operationName);
+ scope.AddAttribute(AppInsightClassicAttributeKeys.DbName, databaseName);
+ scope.AddAttribute(AppInsightClassicAttributeKeys.ContainerName, containerName);
+ scope.AddAttribute(AppInsightClassicAttributeKeys.ServerAddress, accountName);
+ scope.AddAttribute(AppInsightClassicAttributeKeys.UserAgent, userAgent);
+ scope.AddAttribute(AppInsightClassicAttributeKeys.MachineId, machineId);
+ scope.AddAttribute(AppInsightClassicAttributeKeys.ClientId, clientId);
+ scope.AddAttribute(AppInsightClassicAttributeKeys.ConnectionMode, connectionMode);
+ }
+
+ public static void PopulateAttributes(DiagnosticScope scope, Exception exception)
+ {
+ scope.AddAttribute(AppInsightClassicAttributeKeys.ExceptionStacktrace, exception.StackTrace);
+ scope.AddAttribute(AppInsightClassicAttributeKeys.ExceptionType, exception.GetType().Name);
+
+ // If Exception is not registered with open Telemetry
+ if (!OpenTelemetryCoreRecorder.IsExceptionRegistered(exception, scope))
+ {
+ scope.AddAttribute(AppInsightClassicAttributeKeys.ExceptionMessage, exception.Message);
+ }
+ }
+
+ public static void PopulateAttributes(DiagnosticScope scope, string operationType, OpenTelemetryAttributes response)
+ {
+ scope.AddAttribute(AppInsightClassicAttributeKeys.OperationType, operationType);
+ if (response != null)
+ {
+ scope.AddAttribute(AppInsightClassicAttributeKeys.RequestContentLength, response.RequestContentLength);
+ scope.AddAttribute(AppInsightClassicAttributeKeys.ResponseContentLength, response.ResponseContentLength);
+ scope.AddIntegerAttribute(AppInsightClassicAttributeKeys.StatusCode, (int)response.StatusCode);
+ scope.AddIntegerAttribute(AppInsightClassicAttributeKeys.SubStatusCode, response.SubStatusCode);
+ scope.AddIntegerAttribute(AppInsightClassicAttributeKeys.RequestCharge, (int)response.RequestCharge);
+ scope.AddAttribute(AppInsightClassicAttributeKeys.ItemCount, response.ItemCount);
+ scope.AddAttribute(AppInsightClassicAttributeKeys.ActivityId, response.ActivityId);
+
+ if (response.Diagnostics != null)
+ {
+ scope.AddAttribute(AppInsightClassicAttributeKeys.Region, ClientTelemetryHelper.GetContactedRegions(response.Diagnostics.GetContactedRegions()));
+ }
+ }
}
}
diff --git a/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/CosmosDbEventSource.cs b/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/CosmosDbEventSource.cs
index 4cd3b8ffa4..0d6aaac76e 100644
--- a/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/CosmosDbEventSource.cs
+++ b/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/CosmosDbEventSource.cs
@@ -35,6 +35,11 @@ public static void RecordDiagnosticsForRequests(
Documents.OperationType operationType,
OpenTelemetryAttributes response)
{
+ if (response.Diagnostics == null)
+ {
+ return;
+ }
+
if (CosmosDbEventSource.IsEnabled(EventLevel.Warning))
{
if (!DiagnosticsFilterHelper.IsSuccessfulResponse(
diff --git a/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryAttributeKeys.cs b/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryAttributeKeys.cs
index 2d01b8864d..0f7f3d38a3 100644
--- a/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryAttributeKeys.cs
+++ b/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryAttributeKeys.cs
@@ -4,6 +4,9 @@
namespace Microsoft.Azure.Cosmos.Telemetry
{
+ using System;
+ using global::Azure.Core;
+
///
/// Contains constant string values representing OpenTelemetry attribute keys for monitoring and tracing Cosmos DB operations.
/// These keys follow the OpenTelemetry conventions and the Cosmos DB semantic conventions as outlined in the OpenTelemetry specification.
@@ -115,7 +118,7 @@ internal sealed class OpenTelemetryAttributeKeys
///
/// Represents the item count in the operation.
///
- public const string ItemCount = "db.cosmosdb.item_count";
+ public const string ItemCount = "db.cosmosdb.row_count";
///
/// Represents the activity ID for the operation.
@@ -158,5 +161,73 @@ internal sealed class OpenTelemetryAttributeKeys
/// Represents the stack trace of the exception.
///
public const string ExceptionStacktrace = "exception.stacktrace";
+
+ public static void PopulateAttributes(DiagnosticScope scope,
+ string operationName,
+ string databaseName,
+ string containerName,
+ string accountName,
+ string userAgent,
+ string clientId,
+ string connectionMode)
+ {
+ scope.AddAttribute(OpenTelemetryAttributeKeys.DbOperation, operationName);
+ scope.AddAttribute(OpenTelemetryAttributeKeys.DbName, databaseName);
+ scope.AddAttribute(OpenTelemetryAttributeKeys.ContainerName, containerName);
+ scope.AddAttribute(OpenTelemetryAttributeKeys.ServerAddress, accountName);
+ scope.AddAttribute(OpenTelemetryAttributeKeys.UserAgent, userAgent);
+ scope.AddAttribute(OpenTelemetryAttributeKeys.ClientId, clientId);
+ scope.AddAttribute(OpenTelemetryAttributeKeys.ConnectionMode, connectionMode);
+ }
+
+ public static void PopulateAttributes(DiagnosticScope scope, Exception exception)
+ {
+ scope.AddAttribute(OpenTelemetryAttributeKeys.ExceptionStacktrace, exception.StackTrace);
+ scope.AddAttribute(OpenTelemetryAttributeKeys.ExceptionType, exception.GetType().Name);
+
+ // If Exception is not registered with open Telemetry
+ if (!OpenTelemetryCoreRecorder.IsExceptionRegistered(exception, scope))
+ {
+ scope.AddAttribute(OpenTelemetryAttributeKeys.ExceptionMessage, exception.Message);
+ }
+ }
+
+ public static void PopulateAttributes(DiagnosticScope scope, QueryTextMode? queryTextMode, OpenTelemetryAttributes response)
+ {
+ if (response == null)
+ {
+ return;
+ }
+
+ if (response.BatchSize is not null)
+ {
+ scope.AddIntegerAttribute(OpenTelemetryAttributeKeys.BatchSize, (int)response.BatchSize);
+ }
+
+ scope.AddIntegerAttribute(OpenTelemetryAttributeKeys.StatusCode, (int)response.StatusCode);
+ scope.AddAttribute(OpenTelemetryAttributeKeys.RequestContentLength, response.RequestContentLength);
+ scope.AddAttribute(OpenTelemetryAttributeKeys.ResponseContentLength, response.ResponseContentLength);
+ scope.AddIntegerAttribute(OpenTelemetryAttributeKeys.SubStatusCode, response.SubStatusCode);
+ scope.AddIntegerAttribute(OpenTelemetryAttributeKeys.RequestCharge, (int)response.RequestCharge);
+ scope.AddAttribute(OpenTelemetryAttributeKeys.ItemCount, response.ItemCount);
+ scope.AddAttribute(OpenTelemetryAttributeKeys.ActivityId, response.ActivityId);
+ scope.AddAttribute(OpenTelemetryAttributeKeys.CorrelatedActivityId, response.CorrelatedActivityId);
+ scope.AddAttribute(OpenTelemetryAttributeKeys.ConsistencyLevel, response.ConsistencyLevel);
+
+ if (response.QuerySpec is not null)
+ {
+ if (queryTextMode == QueryTextMode.All ||
+ (queryTextMode == QueryTextMode.ParameterizedOnly && response.QuerySpec.ShouldSerializeParameters()))
+ {
+ scope.AddAttribute(OpenTelemetryAttributeKeys.QueryText, response.QuerySpec?.QueryText);
+ }
+ }
+
+ if (response.Diagnostics != null)
+ {
+ scope.AddAttribute(OpenTelemetryAttributeKeys.Region, ClientTelemetryHelper.GetContactedRegions(response.Diagnostics.GetContactedRegions()));
+ }
+
+ }
}
}
diff --git a/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryCoreRecorder.cs b/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryCoreRecorder.cs
index f606fef57e..8f917b3612 100644
--- a/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryCoreRecorder.cs
+++ b/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryCoreRecorder.cs
@@ -9,6 +9,7 @@ namespace Microsoft.Azure.Cosmos.Telemetry
using System.Diagnostics;
using global::Azure.Core;
using Microsoft.Azure.Cosmos.Telemetry.Diagnostics;
+ using Microsoft.Azure.Cosmos.Telemetry.OpenTelemetry;
using Microsoft.Azure.Documents;
///
@@ -19,8 +20,6 @@ internal struct OpenTelemetryCoreRecorder : IDisposable
{
private const string CosmosDb = "cosmosdb";
- private static readonly string otelStabilityMode = Environment.GetEnvironmentVariable("OTEL_SEMCONV_STABILITY_OPT_IN");
-
private readonly DiagnosticScope scope = default;
private readonly CosmosThresholdOptions config = null;
private readonly Activity activity = null;
@@ -69,13 +68,21 @@ private OpenTelemetryCoreRecorder(
this.config = config;
this.operationType = operationType;
- this.connectionModeCache = Enum.GetName(typeof(ConnectionMode), clientContext.ClientOptions.ConnectionMode);
+ this.connectionModeCache = clientContext.ClientOptions.ConnectionMode switch
+ {
+ ConnectionMode.Direct => "direct",
+ ConnectionMode.Gateway => "gateway",
+ _ => throw new NotImplementedException()
+ };
+
this.queryTextMode = queryTextMode;
if (scope.IsEnabled)
{
this.scope.Start();
+ this.scope.AddAttribute(OpenTelemetryAttributeKeys.DbSystemName, OpenTelemetryCoreRecorder.CosmosDb);
+
this.Record(
operationName: operationName,
containerName: containerName,
@@ -151,28 +158,15 @@ public void Record(
{
if (this.IsEnabled)
{
- if (otelStabilityMode == OpenTelemetryStablityModes.DatabaseDupe)
- {
- this.scope.AddAttribute(OpenTelemetryAttributeKeys.DbOperation, operationName);
- this.scope.AddAttribute(OpenTelemetryAttributeKeys.DbName, databaseName);
- this.scope.AddAttribute(OpenTelemetryAttributeKeys.ContainerName, containerName);
- this.scope.AddAttribute(OpenTelemetryAttributeKeys.ServerAddress, clientContext.Client?.Endpoint?.Host);
- this.scope.AddAttribute(OpenTelemetryAttributeKeys.UserAgent, clientContext.UserAgent);
- }
- else
- {
- // Classic Appinsights Support
- this.scope.AddAttribute(AppInsightClassicAttributeKeys.DbOperation, operationName);
- this.scope.AddAttribute(AppInsightClassicAttributeKeys.DbName, databaseName);
- this.scope.AddAttribute(AppInsightClassicAttributeKeys.ContainerName, containerName);
- this.scope.AddAttribute(AppInsightClassicAttributeKeys.ServerAddress, clientContext.Client?.Endpoint?.Host);
- this.scope.AddAttribute(AppInsightClassicAttributeKeys.UserAgent, clientContext.UserAgent);
- this.scope.AddAttribute(AppInsightClassicAttributeKeys.MachineId, VmMetadataApiHandler.GetMachineId());
- }
-
- this.scope.AddAttribute(OpenTelemetryAttributeKeys.DbSystemName, OpenTelemetryCoreRecorder.CosmosDb);
- this.scope.AddAttribute(OpenTelemetryAttributeKeys.ClientId, clientContext?.Client?.Id);
- this.scope.AddAttribute(OpenTelemetryAttributeKeys.ConnectionMode, this.connectionModeCache);
+ TracesStabilityFactory.SetAttributes(this.scope,
+ operationName,
+ databaseName,
+ containerName,
+ clientContext.Client?.Endpoint?.Host,
+ clientContext.UserAgent,
+ VmMetadataApiHandler.GetMachineId(),
+ clientContext?.Client?.Id,
+ this.connectionModeCache);
}
}
@@ -196,14 +190,7 @@ public void MarkFailed(Exception exception)
{
if (this.IsEnabled)
{
- this.scope.AddAttribute(OpenTelemetryAttributeKeys.ExceptionStacktrace, exception.StackTrace);
- this.scope.AddAttribute(OpenTelemetryAttributeKeys.ExceptionType, exception.GetType().Name);
-
- // If Exception is not registered with open Telemetry
- if (!OpenTelemetryCoreRecorder.IsExceptionRegistered(exception, this.scope))
- {
- this.scope.AddAttribute(OpenTelemetryAttributeKeys.ExceptionMessage, exception.Message);
- }
+ TracesStabilityFactory.SetAttributes(this.scope, exception);
if (exception is not CosmosException || (exception is CosmosException cosmosException
&& !DiagnosticsFilterHelper
@@ -242,58 +229,15 @@ public void Dispose()
{
OperationType operationType
= (this.response == null || this.response?.OperationType == OperationType.Invalid) ? this.operationType : this.response.OperationType;
- if (otelStabilityMode != OpenTelemetryStablityModes.DatabaseDupe)
- {
- string operationName = Enum.GetName(typeof(OperationType), operationType);
- this.scope.AddAttribute(AppInsightClassicAttributeKeys.OperationType, operationName);
- }
-
- if (this.response != null)
- {
- if (this.response.BatchSize is not null)
- {
- this.scope.AddIntegerAttribute(OpenTelemetryAttributeKeys.BatchSize, (int)this.response.BatchSize);
- }
+ string operationTypeName = Enum.GetName(typeof(OperationType), operationType);
- if (otelStabilityMode == OpenTelemetryStablityModes.DatabaseDupe)
- {
- this.scope.AddIntegerAttribute(OpenTelemetryAttributeKeys.StatusCode, (int)this.response.StatusCode);
- this.scope.AddAttribute(OpenTelemetryAttributeKeys.RequestContentLength, this.response.RequestContentLength);
- this.scope.AddAttribute(OpenTelemetryAttributeKeys.ResponseContentLength, this.response.ResponseContentLength);
- }
- else
- {
- this.scope.AddAttribute(AppInsightClassicAttributeKeys.RequestContentLength, this.response.RequestContentLength);
- this.scope.AddAttribute(AppInsightClassicAttributeKeys.ResponseContentLength, this.response.ResponseContentLength);
- this.scope.AddIntegerAttribute(AppInsightClassicAttributeKeys.StatusCode, (int)this.response.StatusCode);
- }
+ TracesStabilityFactory.SetAttributes(this.scope, operationTypeName, this.queryTextMode, this.response);
- this.scope.AddIntegerAttribute(OpenTelemetryAttributeKeys.SubStatusCode, this.response.SubStatusCode);
- this.scope.AddIntegerAttribute(OpenTelemetryAttributeKeys.RequestCharge, (int)this.response.RequestCharge);
- this.scope.AddAttribute(OpenTelemetryAttributeKeys.ItemCount, this.response.ItemCount);
- this.scope.AddAttribute(OpenTelemetryAttributeKeys.ActivityId, this.response.ActivityId);
- this.scope.AddAttribute(OpenTelemetryAttributeKeys.CorrelatedActivityId, this.response.CorrelatedActivityId);
- this.scope.AddAttribute(OpenTelemetryAttributeKeys.ConsistencyLevel, this.response.ConsistencyLevel);
+ CosmosDbEventSource.RecordDiagnosticsForRequests(this.config, operationType, this.response);
- if (this.response.QuerySpec is not null)
- {
- if (this.queryTextMode == QueryTextMode.All ||
- (this.queryTextMode == QueryTextMode.ParameterizedOnly && this.response.QuerySpec.ShouldSerializeParameters()))
- {
- this.scope.AddAttribute(OpenTelemetryAttributeKeys.QueryText, this.response.QuerySpec?.QueryText);
- }
- }
-
- if (this.response.Diagnostics != null)
- {
- this.scope.AddAttribute(OpenTelemetryAttributeKeys.Region, ClientTelemetryHelper.GetContactedRegions(this.response.Diagnostics.GetContactedRegions()));
- CosmosDbEventSource.RecordDiagnosticsForRequests(this.config, operationType, this.response);
- }
-
- if (!DiagnosticsFilterHelper.IsSuccessfulResponse(this.response.StatusCode, this.response.SubStatusCode))
- {
- this.scope.Failed($"{(int)this.response.StatusCode}/{this.response.SubStatusCode}");
- }
+ if (!DiagnosticsFilterHelper.IsSuccessfulResponse(this.response.StatusCode, this.response.SubStatusCode))
+ {
+ this.scope.Failed($"{(int)this.response.StatusCode}/{this.response.SubStatusCode}");
}
this.scope.Dispose();
diff --git a/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryStablityModes.cs b/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryStablityModes.cs
index b73b40fd00..8b31b1015c 100644
--- a/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryStablityModes.cs
+++ b/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryStablityModes.cs
@@ -18,5 +18,10 @@ internal sealed class OpenTelemetryStablityModes
/// emit both the old and the stable database conventions, allowing for a seamless transition.
///
public const string DatabaseDupe = "database/dup";
+
+ ///
+ /// Environment Variable to support the classic AppInsight conventions
+ ///
+ public const string ClassicAppInsights = "appinsightssdk";
}
}
diff --git a/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/TracesStabilityFactory.cs b/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/TracesStabilityFactory.cs
new file mode 100644
index 0000000000..42faceaaab
--- /dev/null
+++ b/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/TracesStabilityFactory.cs
@@ -0,0 +1,97 @@
+//------------------------------------------------------------
+// Copyright (c) Microsoft Corporation. All rights reserved.
+//------------------------------------------------------------
+
+namespace Microsoft.Azure.Cosmos.Telemetry.OpenTelemetry
+{
+ using System;
+ using global::Azure.Core;
+
+ ///
+ /// Factory for handling telemetry trace stability modes, allowing attribute settings
+ /// based on environment-specified stability mode configurations.
+ ///
+ internal class TracesStabilityFactory
+ {
+ // Specifies the stability mode for telemetry attributes, configured via the OTEL_SEMCONV_STABILITY_OPT_IN environment variable.
+ private static readonly string otelStabilityMode = Environment.GetEnvironmentVariable("OTEL_SEMCONV_STABILITY_OPT_IN");
+
+ ///
+ /// Sets trace attributes based on stability mode for standard telemetry events.
+ ///
+ /// The diagnostic scope to be enriched with attributes.
+ /// The name of the operation being traced.
+ /// The name of the database in context.
+ /// The name of the container in context.
+ /// The account name associated with the operation.
+ /// The user agent performing the operation.
+ /// The machine identifier (optional).
+ /// The client identifier performing the operation.
+ /// The connection mode in use.
+ public static void SetAttributes(DiagnosticScope scope,
+ string operationName,
+ string databaseName,
+ string containerName,
+ string accountName,
+ string userAgent,
+ string machineId,
+ string clientId,
+ string connectionMode)
+ {
+ PopulateAttributesBasedOnMode(
+ () => AppInsightClassicAttributeKeys.PopulateAttributes(scope, operationName, databaseName, containerName, accountName, userAgent, machineId, clientId, connectionMode),
+ () => OpenTelemetryAttributeKeys.PopulateAttributes(scope, operationName, databaseName, containerName, accountName, userAgent, clientId, connectionMode));
+ }
+
+ ///
+ /// Sets trace attributes for telemetry events related to an exception.
+ ///
+ /// The diagnostic scope to be enriched with attributes.
+ /// The exception that occurred.
+ public static void SetAttributes(DiagnosticScope scope, Exception exception)
+ {
+ PopulateAttributesBasedOnMode(
+ () => AppInsightClassicAttributeKeys.PopulateAttributes(scope, exception),
+ () => OpenTelemetryAttributeKeys.PopulateAttributes(scope, exception));
+ }
+
+ ///
+ /// Sets trace attributes for telemetry events
+ ///
+ /// The diagnostic scope to be enriched with attributes.
+ /// The type of operation being traced.
+ /// The query text mode in use.
+ /// The telemetry attributes for the response.
+ public static void SetAttributes(DiagnosticScope scope,
+ string operationType,
+ QueryTextMode? queryTextMode,
+ OpenTelemetryAttributes response)
+ {
+ PopulateAttributesBasedOnMode(
+ () => AppInsightClassicAttributeKeys.PopulateAttributes(scope, operationType, response),
+ () => OpenTelemetryAttributeKeys.PopulateAttributes(scope, queryTextMode, response));
+ }
+
+ ///
+ /// Executes attribute population actions based on the specified stability mode.
+ ///
+ /// Action to populate AppInsightClassic attributes.
+ /// Action to populate OpenTelemetry attributes.
+ private static void PopulateAttributesBasedOnMode(Action populateClassicAttributes, Action populateOpenTelemetryAttributes)
+ {
+ switch (otelStabilityMode)
+ {
+ case OpenTelemetryStablityModes.Database:
+ populateOpenTelemetryAttributes();
+ break;
+ case OpenTelemetryStablityModes.DatabaseDupe:
+ populateClassicAttributes();
+ populateOpenTelemetryAttributes();
+ break;
+ default:
+ populateClassicAttributes();
+ break;
+ }
+ }
+ }
+}