diff --git a/Microsoft.Azure.Cosmos/src/ChangeFeed/ChangeFeedIteratorCore.cs b/Microsoft.Azure.Cosmos/src/ChangeFeed/ChangeFeedIteratorCore.cs index 5f722e16ed..a6418ab33f 100644 --- a/Microsoft.Azure.Cosmos/src/ChangeFeed/ChangeFeedIteratorCore.cs +++ b/Microsoft.Azure.Cosmos/src/ChangeFeed/ChangeFeedIteratorCore.cs @@ -229,7 +229,7 @@ public override async Task ReadNextAsync(CancellationToken canc operationType: OperationType.ReadFeed, requestOptions: this.changeFeedRequestOptions, task: (trace) => this.ReadNextInternalAsync(trace, cancellationToken), - openTelemetry: new (OpenTelemetryConstants.Operations.QueryChangeFeed, (response) => new OpenTelemetryResponse(responseMessage: response)), + openTelemetry: new (OpenTelemetryConstants.Operations.QueryChangeFeed, (response) => new OpenTelemetryResponse(responseMessage: response, querySpecFunc: () => this.changeFeedQuerySpec?.ToSqlQuerySpec())), traceComponent: TraceComponent.ChangeFeed, traceLevel: TraceLevel.Info); } diff --git a/Microsoft.Azure.Cosmos/src/ChangeFeed/ChangeFeedQuerySpec.cs b/Microsoft.Azure.Cosmos/src/ChangeFeed/ChangeFeedQuerySpec.cs index 1a1c970c23..4099181ee3 100644 --- a/Microsoft.Azure.Cosmos/src/ChangeFeed/ChangeFeedQuerySpec.cs +++ b/Microsoft.Azure.Cosmos/src/ChangeFeed/ChangeFeedQuerySpec.cs @@ -54,5 +54,13 @@ internal bool ShouldSerializeQueryText() { return this.QueryText.Length > 0; } + + /// + /// Converts to SQL Query Specs + /// + internal SqlQuerySpec ToSqlQuerySpec() + { + return new SqlQuerySpec(this.QueryText); + } } } diff --git a/Microsoft.Azure.Cosmos/src/ChangeFeedProcessor/ChangeFeedEstimatorIterator.cs b/Microsoft.Azure.Cosmos/src/ChangeFeedProcessor/ChangeFeedEstimatorIterator.cs index 4c38ee3b21..18cbda9acb 100644 --- a/Microsoft.Azure.Cosmos/src/ChangeFeedProcessor/ChangeFeedEstimatorIterator.cs +++ b/Microsoft.Azure.Cosmos/src/ChangeFeedProcessor/ChangeFeedEstimatorIterator.cs @@ -122,7 +122,7 @@ public override Task> ReadNextAsync(Cance operationType: Documents.OperationType.ReadFeed, requestOptions: null, task: (trace) => this.ReadNextAsync(trace, cancellationToken), - openTelemetry: new (OpenTelemetryConstants.Operations.QueryChangeFeedEstimator, (response) => new OpenTelemetryResponse(responseMessage: response)), + openTelemetry: new (OpenTelemetryConstants.Operations.QueryChangeFeedEstimator, (response) => new OpenTelemetryResponse(responseMessage: response, querySpec: this.querySpec)), traceComponent: TraceComponent.ChangeFeed, traceLevel: TraceLevel.Info); } diff --git a/Microsoft.Azure.Cosmos/src/CosmosClientTelemetryOptions.cs b/Microsoft.Azure.Cosmos/src/CosmosClientTelemetryOptions.cs index a8bf4d44d7..d05a840f0e 100644 --- a/Microsoft.Azure.Cosmos/src/CosmosClientTelemetryOptions.cs +++ b/Microsoft.Azure.Cosmos/src/CosmosClientTelemetryOptions.cs @@ -46,5 +46,12 @@ public class CosmosClientTelemetryOptions /// These values decides whether to generate an with request diagnostics or not. /// public CosmosThresholdOptions CosmosThresholdOptions { get; set; } = new CosmosThresholdOptions(); + + /// + /// Enables printing query in Traces db.query.text attribute. By default, query is not printed. + /// Users have the option to enable printing parameterized or all queries, + /// but has to beware that customer data may be shown when the later option is chosen. It's the user's responsibility to sanitize the queries if necessary. + /// + public QueryTextMode QueryTextMode { get; set; } = QueryTextMode.None; } } \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/src/Query/v3Query/QueryIterator.cs b/Microsoft.Azure.Cosmos/src/Query/v3Query/QueryIterator.cs index ae1a0d6c22..e165b2cc24 100644 --- a/Microsoft.Azure.Cosmos/src/Query/v3Query/QueryIterator.cs +++ b/Microsoft.Azure.Cosmos/src/Query/v3Query/QueryIterator.cs @@ -43,7 +43,8 @@ private QueryIterator( RequestOptions requestOptions, CosmosClientContext clientContext, Guid correlatedActivityId, - ContainerInternal container) + ContainerInternal container, + SqlQuerySpec sqlQuerySpec) { this.cosmosQueryContext = cosmosQueryContext ?? throw new ArgumentNullException(nameof(cosmosQueryContext)); this.queryPipelineStage = cosmosQueryExecutionContext ?? throw new ArgumentNullException(nameof(cosmosQueryExecutionContext)); @@ -53,6 +54,7 @@ private QueryIterator( this.hasMoreResults = true; this.correlatedActivityId = correlatedActivityId; + this.querySpec = sqlQuerySpec; this.container = container; this.operationName = OpenTelemetryConstants.Operations.QueryItems; this.operationType = Documents.OperationType.Query; @@ -119,7 +121,8 @@ public static QueryIterator Create( queryRequestOptions, clientContext, correlatedActivityId, - containerCore); + containerCore, + sqlQuerySpec); } requestContinuationToken = tryParse.Result; @@ -152,7 +155,8 @@ public static QueryIterator Create( queryRequestOptions, clientContext, correlatedActivityId, - containerCore); + containerCore, + sqlQuerySpec); } public override bool HasMoreResults => this.hasMoreResults; diff --git a/Microsoft.Azure.Cosmos/src/RequestOptions/ChangeFeedRequestOptions.cs b/Microsoft.Azure.Cosmos/src/RequestOptions/ChangeFeedRequestOptions.cs index 7bfd256820..527a1e4b52 100644 --- a/Microsoft.Azure.Cosmos/src/RequestOptions/ChangeFeedRequestOptions.cs +++ b/Microsoft.Azure.Cosmos/src/RequestOptions/ChangeFeedRequestOptions.cs @@ -79,6 +79,13 @@ internal override void PopulateRequestOptions(RequestMessage request) #endif JsonSerializationFormatOptions JsonSerializationFormatOptions { get; set; } + /// + /// Enables printing query in Traces db.query.text attribute. By default, query is not printed. + /// Users have the option to enable printing parameterized or all queries, + /// but has to beware that customer data may be shown when the later option is chosen. It's the user's responsibility to sanitize the queries if necessary. + /// + internal QueryTextMode QueryTextMode { get; set; } = Cosmos.QueryTextMode.None; + internal ChangeFeedRequestOptions Clone() { return new ChangeFeedRequestOptions() diff --git a/Microsoft.Azure.Cosmos/src/RequestOptions/QueryRequestOptions.cs b/Microsoft.Azure.Cosmos/src/RequestOptions/QueryRequestOptions.cs index 7b1359c27b..6f5b817685 100644 --- a/Microsoft.Azure.Cosmos/src/RequestOptions/QueryRequestOptions.cs +++ b/Microsoft.Azure.Cosmos/src/RequestOptions/QueryRequestOptions.cs @@ -170,6 +170,13 @@ public ConsistencyLevel? ConsistencyLevel /// These options have no effect otherwise. /// public DedicatedGatewayRequestOptions DedicatedGatewayRequestOptions { get; set; } + + /// + /// Enables printing query in Traces db.query.text attribute. By default, query is not printed. + /// Users have the option to enable printing parameterized or all queries, + /// but has to beware that customer data may be shown when the later option is chosen. It's the user's responsibility to sanitize the queries if necessary. + /// + public QueryTextMode QueryTextMode { get; set; } = QueryTextMode.None; internal CosmosElement CosmosElementContinuationToken { get; set; } diff --git a/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIterator.cs b/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIterator.cs index 16226e6591..55fec68ed9 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIterator.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIterator.cs @@ -7,6 +7,7 @@ namespace Microsoft.Azure.Cosmos using System; using System.Threading; using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.Query.Core; /// /// Cosmos Result set iterator that keeps track of the continuation token when retrieving results form a query. @@ -145,5 +146,10 @@ public void Dispose() /// Operation Type used for open telemetry traces, it will set optionally, otherwise default operation type will be used in traces /// internal Documents.OperationType? operationType; + + /// + /// collect SQL query Specs for tracing + /// + internal SqlQuerySpec querySpec; } } \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIteratorCore.cs b/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIteratorCore.cs index 8e59fc26f7..7c4c3688b0 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIteratorCore.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIteratorCore.cs @@ -27,7 +27,6 @@ internal sealed class FeedIteratorCore : FeedIteratorInternal private readonly CosmosClientContext clientContext; private readonly string resourceLink; private readonly ResourceType resourceType; - private readonly SqlQuerySpec querySpec; private bool hasMoreResultsInternal; public FeedIteratorCore( diff --git a/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIteratorInlineCore.cs b/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIteratorInlineCore.cs index 9d0fbd6caf..6656fa2ee5 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIteratorInlineCore.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIteratorInlineCore.cs @@ -28,6 +28,7 @@ internal FeedIteratorInlineCore( this.feedIteratorInternal = feedIteratorInternal; this.clientContext = clientContext; + this.querySpec = feedIteratorInternal.querySpec; this.container = feedIteratorInternal.container; this.databaseName = feedIteratorInternal.databaseName; @@ -62,7 +63,7 @@ public override Task ReadNextAsync(CancellationToken cancellati task: (trace) => this.feedIteratorInternal.ReadNextAsync(trace, cancellationToken), openTelemetry: new (this.operationName, (response) => { - OpenTelemetryResponse openTelemetryResponse = new OpenTelemetryResponse(responseMessage: response); + OpenTelemetryResponse openTelemetryResponse = new OpenTelemetryResponse(responseMessage: response, querySpecFunc: () => this.querySpec); if (this.operationType.HasValue) { diff --git a/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIteratorInlineCore{T}.cs b/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIteratorInlineCore{T}.cs index 605e293ae1..66e20b4d00 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIteratorInlineCore{T}.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIteratorInlineCore{T}.cs @@ -28,6 +28,7 @@ internal FeedIteratorInlineCore( this.feedIteratorInternal = feedIteratorInternal; this.clientContext = clientContext; + this.querySpec = feedIterator.querySpec; this.container = feedIteratorInternal.container; this.databaseName = feedIteratorInternal.databaseName; @@ -62,7 +63,7 @@ public override Task> ReadNextAsync(CancellationToken cancellati task: trace => this.feedIteratorInternal.ReadNextAsync(trace, cancellationToken), openTelemetry: new (this.feedIteratorInternal.operationName, (response) => { - OpenTelemetryResponse openTelemetryResponse = new OpenTelemetryResponse(responseMessage: response); + OpenTelemetryResponse openTelemetryResponse = new OpenTelemetryResponse(responseMessage: response, querySpec: this.querySpec); if (this.operationType.HasValue) { diff --git a/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIterator{T}.cs b/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIterator{T}.cs index cf389ee846..3d75f23810 100644 --- a/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIterator{T}.cs +++ b/Microsoft.Azure.Cosmos/src/Resource/FeedIterators/FeedIterator{T}.cs @@ -7,6 +7,7 @@ namespace Microsoft.Azure.Cosmos using System; using System.Threading; using System.Threading.Tasks; + using Microsoft.Azure.Cosmos.Query.Core; /// /// Cosmos Result set iterator that keeps track of the continuation token when retrieving results form a query. @@ -133,5 +134,10 @@ public void Dispose() /// Operation Type used for open telemetry traces /// internal Documents.OperationType? operationType; + + /// + /// collect SQL query Specs for tracing + /// + internal SqlQuerySpec querySpec; } } \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryAttributeKeys.cs b/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryAttributeKeys.cs index a0fdccb233..bbd40cf428 100644 --- a/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryAttributeKeys.cs +++ b/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryAttributeKeys.cs @@ -137,6 +137,11 @@ internal sealed class OpenTelemetryAttributeKeys /// public const string CorrelatedActivityId = "db.cosmosdb.correlated_activity_id"; + /// + /// Represents the Azure Cosmos DB SQL Query. + /// + public const string QueryText = "db.query.text"; + /// /// Represents the size of the batch operation. /// diff --git a/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryAttributes.cs b/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryAttributes.cs index 2e927f53a1..0204f9f4d4 100644 --- a/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryAttributes.cs +++ b/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryAttributes.cs @@ -6,7 +6,7 @@ namespace Microsoft.Azure.Cosmos.Telemetry { using System; using System.Net; - using System.Security.AccessControl; + using Microsoft.Azure.Cosmos.Query.Core; internal class OpenTelemetryAttributes { @@ -90,5 +90,10 @@ internal OpenTelemetryAttributes(RequestMessage requestMessage) /// Batch Size /// internal int? BatchSize { get; set; } + + /// + /// Query Spec with Query Text and Parameters + /// + internal SqlQuerySpec QuerySpec { get; set; } } } diff --git a/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryCoreRecorder.cs b/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryCoreRecorder.cs index ff35a94054..bcd652ae82 100644 --- a/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryCoreRecorder.cs +++ b/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryCoreRecorder.cs @@ -26,6 +26,7 @@ internal struct OpenTelemetryCoreRecorder : IDisposable private readonly OperationType operationType = OperationType.Invalid; private readonly string connectionModeCache = null; + private readonly QueryTextMode? queryTextMode = null; private OpenTelemetryAttributes response = null; /// @@ -59,13 +60,15 @@ private OpenTelemetryCoreRecorder( string databaseName, OperationType operationType, CosmosClientContext clientContext, - CosmosThresholdOptions config) + CosmosThresholdOptions config, + QueryTextMode queryTextMode) { this.scope = scope; this.config = config; this.operationType = operationType; this.connectionModeCache = Enum.GetName(typeof(ConnectionMode), clientContext.ClientOptions.ConnectionMode); + this.queryTextMode = queryTextMode; if (scope.IsEnabled) { @@ -107,7 +110,8 @@ public static OpenTelemetryCoreRecorder CreateOperationLevelParentActivity( string databaseName, Documents.OperationType operationType, CosmosClientContext clientContext, - CosmosThresholdOptions config) + CosmosThresholdOptions config, + QueryTextMode queryTextMode) { return new OpenTelemetryCoreRecorder( operationScope, @@ -116,7 +120,8 @@ public static OpenTelemetryCoreRecorder CreateOperationLevelParentActivity( databaseName, operationType, clientContext, - config); + config, + queryTextMode); } public bool IsEnabled => this.scope.IsEnabled; @@ -245,6 +250,15 @@ OperationType operationType this.scope.AddAttribute(OpenTelemetryAttributeKeys.ActivityId, this.response.ActivityId); this.scope.AddAttribute(OpenTelemetryAttributeKeys.CorrelatedActivityId, this.response.CorrelatedActivityId); + 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())); diff --git a/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryRecorderFactory.cs b/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryRecorderFactory.cs index e83846d287..6df14a8c9e 100644 --- a/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryRecorderFactory.cs +++ b/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryRecorderFactory.cs @@ -56,6 +56,8 @@ public static OpenTelemetryCoreRecorder CreateRecorder(Func getOperation { scope.SetDisplayName($"{operationName} {containerName}"); + QueryTextMode queryTextMode = GetQueryTextMode(requestOptions, clientContext); + openTelemetryRecorder = OpenTelemetryCoreRecorder.CreateOperationLevelParentActivity( operationScope: scope, operationName: operationName, @@ -63,7 +65,8 @@ public static OpenTelemetryCoreRecorder CreateRecorder(Func getOperation databaseName: databaseName, operationType: operationType, clientContext: clientContext, - config: requestOptions?.CosmosThresholdOptions ?? clientContext.ClientOptions?.CosmosClientTelemetryOptions.CosmosThresholdOptions); + config: requestOptions?.CosmosThresholdOptions ?? clientContext.ClientOptions?.CosmosClientTelemetryOptions.CosmosThresholdOptions, + queryTextMode: queryTextMode); } #if !INTERNAL // If there are no listeners at operation level and no parent activity created. @@ -83,5 +86,21 @@ public static OpenTelemetryCoreRecorder CreateRecorder(Func getOperation } return openTelemetryRecorder; } + + private static QueryTextMode GetQueryTextMode(RequestOptions requestOptions, CosmosClientContext clientContext) + { + QueryTextMode? queryTextMode = null; + if (requestOptions is QueryRequestOptions queryRequestOptions) + { + queryTextMode = queryRequestOptions.QueryTextMode; + } + else if (requestOptions is ChangeFeedRequestOptions changeFeedRequestOptions) + { + queryTextMode = changeFeedRequestOptions.QueryTextMode; + } + + queryTextMode ??= clientContext.ClientOptions?.CosmosClientTelemetryOptions?.QueryTextMode ?? QueryTextMode.None; + return queryTextMode.Value; + } } } diff --git a/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryResponse.cs b/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryResponse.cs index ed55a9812e..e22c836a51 100644 --- a/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryResponse.cs +++ b/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryResponse.cs @@ -8,6 +8,8 @@ namespace Microsoft.Azure.Cosmos using System.IO; using System.Net; using Microsoft.Azure.Cosmos.Core.Trace; + using Microsoft.Azure.Cosmos.Query.Core; + using Microsoft.Azure.Documents; using Telemetry; internal sealed class OpenTelemetryResponse : OpenTelemetryAttributes @@ -27,7 +29,7 @@ internal OpenTelemetryResponse(TransactionalBatchResponse responseMessage) { } - internal OpenTelemetryResponse(ResponseMessage responseMessage) + internal OpenTelemetryResponse(ResponseMessage responseMessage, Func querySpecFunc = null) : this( statusCode: responseMessage.StatusCode, requestCharge: OpenTelemetryResponse.GetHeader(responseMessage)?.RequestCharge, @@ -37,7 +39,8 @@ internal OpenTelemetryResponse(ResponseMessage responseMessage) requestMessage: responseMessage.RequestMessage, subStatusCode: OpenTelemetryResponse.GetHeader(responseMessage)?.SubStatusCode, activityId: OpenTelemetryResponse.GetHeader(responseMessage)?.ActivityId, - correlationId: OpenTelemetryResponse.GetHeader(responseMessage)?.CorrelatedActivityId) + correlationId: OpenTelemetryResponse.GetHeader(responseMessage)?.CorrelatedActivityId, + querySpecFunc: querySpecFunc) { } @@ -51,7 +54,8 @@ private OpenTelemetryResponse( Documents.SubStatusCodes? subStatusCode, string activityId, string correlationId, - int? batchSize = null) + int? batchSize = null, + Func querySpecFunc = null) : base(requestMessage) { this.StatusCode = statusCode; @@ -63,6 +67,10 @@ private OpenTelemetryResponse( this.ActivityId = activityId; this.CorrelatedActivityId = correlationId; this.BatchSize = batchSize; + if (querySpecFunc != null) + { + this.QuerySpec = querySpecFunc(); + } } private static string GetPayloadSize(ResponseMessage response) diff --git a/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryResponse{T}.cs b/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryResponse{T}.cs index a6ffa18dc1..a6f3e73380 100644 --- a/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryResponse{T}.cs +++ b/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/OpenTelemetryResponse{T}.cs @@ -7,12 +7,12 @@ namespace Microsoft.Azure.Cosmos using System; using System.Net; using Microsoft.Azure.Cosmos.Core.Trace; - using Microsoft.Azure.Documents; + using Microsoft.Azure.Cosmos.Query.Core; using Telemetry; internal sealed class OpenTelemetryResponse : OpenTelemetryAttributes { - internal OpenTelemetryResponse(FeedResponse responseMessage) + internal OpenTelemetryResponse(FeedResponse responseMessage, SqlQuerySpec querySpec = null) : this( statusCode: responseMessage.StatusCode, requestCharge: OpenTelemetryResponse.GetHeader(responseMessage)?.RequestCharge, @@ -22,11 +22,12 @@ internal OpenTelemetryResponse(FeedResponse responseMessage) requestMessage: responseMessage.RequestMessage, subStatusCode: OpenTelemetryResponse.GetHeader(responseMessage)?.SubStatusCode, activityId: OpenTelemetryResponse.GetHeader(responseMessage)?.ActivityId, - correlatedActivityId: OpenTelemetryResponse.GetHeader(responseMessage)?.CorrelatedActivityId) + correlatedActivityId: OpenTelemetryResponse.GetHeader(responseMessage)?.CorrelatedActivityId, + querySpec: querySpec) { } - internal OpenTelemetryResponse(Response responseMessage) + internal OpenTelemetryResponse(Response responseMessage, SqlQuerySpec querySpec = null) : this( statusCode: responseMessage.StatusCode, requestCharge: OpenTelemetryResponse.GetHeader(responseMessage)?.RequestCharge, @@ -36,7 +37,8 @@ internal OpenTelemetryResponse(Response responseMessage) requestMessage: responseMessage.RequestMessage, subStatusCode: OpenTelemetryResponse.GetHeader(responseMessage)?.SubStatusCode, activityId: OpenTelemetryResponse.GetHeader(responseMessage)?.ActivityId, - correlatedActivityId: OpenTelemetryResponse.GetHeader(responseMessage)?.CorrelatedActivityId) + correlatedActivityId: OpenTelemetryResponse.GetHeader(responseMessage)?.CorrelatedActivityId, + querySpec: querySpec) { } @@ -49,7 +51,8 @@ private OpenTelemetryResponse( RequestMessage requestMessage, Documents.SubStatusCodes? subStatusCode, string activityId, - string correlatedActivityId) + string correlatedActivityId, + SqlQuerySpec querySpec) : base(requestMessage) { this.StatusCode = statusCode; @@ -60,6 +63,7 @@ private OpenTelemetryResponse( this.SubStatusCode = (int)(subStatusCode ?? Documents.SubStatusCodes.Unknown); this.ActivityId = activityId; this.CorrelatedActivityId = correlatedActivityId; + this.QuerySpec = querySpec; } private static Headers GetHeader(FeedResponse responseMessage) diff --git a/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/QueryTextMode.cs b/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/QueryTextMode.cs new file mode 100644 index 0000000000..5c7f77af35 --- /dev/null +++ b/Microsoft.Azure.Cosmos/src/Telemetry/OpenTelemetry/QueryTextMode.cs @@ -0,0 +1,27 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// ------------------------------------------------------------ + +namespace Microsoft.Azure.Cosmos +{ + /// + /// Defines whether to print query text in tracing attributes. + /// + public enum QueryTextMode + { + /// + /// Do not show query text. + /// + None = 0, + + /// + /// Only print query text that is parameterized. Parameter values won't be captured. + /// + ParameterizedOnly = 1, + + /// + /// Print query text from both parameterized and non parameterized queries. Parameter values won't be captured. + /// + All = 2 + } +} diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/EndToEndTraceWriterBaselineTests.QueryAsync.xml b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/EndToEndTraceWriterBaselineTests.QueryAsync.xml index 9c45187931..6f941a7312 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/EndToEndTraceWriterBaselineTests.QueryAsync.xml +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/EndToEndTraceWriterBaselineTests.QueryAsync.xml @@ -629,6 +629,7 @@ Some Value Some Value Some Value + SELECT * FROM c South Central US @@ -650,6 +651,7 @@ Some Value Some Value Some Value + SELECT * FROM c South Central US @@ -671,6 +673,7 @@ Some Value Some Value Some Value + SELECT * FROM c South Central US @@ -692,6 +695,7 @@ Some Value Some Value Some Value + SELECT * FROM c South Central US @@ -2052,6 +2056,7 @@ Some Value Some Value Some Value + SELECT * FROM c South Central US @@ -2073,6 +2078,7 @@ Some Value Some Value Some Value + SELECT * FROM c South Central US @@ -2094,6 +2100,7 @@ Some Value Some Value Some Value + SELECT * FROM c South Central US @@ -2115,6 +2122,7 @@ Some Value Some Value Some Value + SELECT * FROM c South Central US @@ -2128,7 +2136,7 @@ Query Public API Typed feedIterator = container.GetItemQueryIterator( - queryText: "SELECT * FROM c", + queryDefinition: parameterizedQuery, requestOptions: requestOptions); List traces = new List(); @@ -4281,6 +4289,7 @@ Some Value Some Value Some Value + SELECT * FROM c South Central US @@ -4302,6 +4311,7 @@ Some Value Some Value Some Value + SELECT * FROM c South Central US @@ -4323,6 +4333,7 @@ Some Value Some Value Some Value + SELECT * FROM c South Central US @@ -4344,6 +4355,7 @@ Some Value Some Value Some Value + SELECT * FROM c South Central US diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Tracing/AssertActivity.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Tracing/AssertActivity.cs index 71bd1947f0..ef392c2a8f 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Tracing/AssertActivity.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Tracing/AssertActivity.cs @@ -60,6 +60,7 @@ public static void IsValidOperationActivity(Activity activity) "exception.type", "exception.message", "exception.stacktrace", + "db.query.text", "error.type" }; diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Tracing/CustomListener.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Tracing/CustomListener.cs index e1fbb1690a..b1b54c5838 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Tracing/CustomListener.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Tracing/CustomListener.cs @@ -41,6 +41,7 @@ internal class CustomListener : "db.cosmosdb.operation_type", "db.cosmosdb.regions_contacted", "db.operation.batch_size", + "db.query.text", "error.type" }; diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Tracing/EndToEndTraceWriterBaselineTests.cs b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Tracing/EndToEndTraceWriterBaselineTests.cs index b0c7c58c1b..92957a7513 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Tracing/EndToEndTraceWriterBaselineTests.cs +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/Tracing/EndToEndTraceWriterBaselineTests.cs @@ -16,6 +16,7 @@ namespace Microsoft.Azure.Cosmos.EmulatorTests.Tracing using System.Xml; using System.Xml.Linq; using global::Azure; + using Microsoft.Azure.Cosmos.ChangeFeed; using Microsoft.Azure.Cosmos.CosmosElements; using Microsoft.Azure.Cosmos.Diagnostics; using Microsoft.Azure.Cosmos.SDK.EmulatorTests; @@ -61,7 +62,8 @@ public static async Task ClassInitAsync(TestContext _) { PointOperationLatencyThreshold = TimeSpan.Zero, NonPointOperationLatencyThreshold = TimeSpan.Zero - } + }, + QueryTextMode = QueryTextMode.All })); bulkClient = TestCommon.CreateCosmosClient(builder => builder @@ -73,8 +75,9 @@ public static async Task ClassInitAsync(TestContext _) { PointOperationLatencyThreshold = TimeSpan.Zero, NonPointOperationLatencyThreshold = TimeSpan.Zero - } - })); + }, + QueryTextMode = QueryTextMode.All + })); // Set a small retry count to reduce test time miscCosmosClient = TestCommon.CreateCosmosClient(builder => @@ -87,8 +90,9 @@ public static async Task ClassInitAsync(TestContext _) { PointOperationLatencyThreshold = TimeSpan.Zero, NonPointOperationLatencyThreshold = TimeSpan.Zero - } - })); + }, + QueryTextMode = QueryTextMode.All + })); EndToEndTraceWriterBaselineTests.database = await client.CreateDatabaseAsync( "databaseName", @@ -520,6 +524,8 @@ public async Task QueryAsync() // Query Typed //---------------------------------------------------------------- { + requestOptions.QueryTextMode = QueryTextMode.None; + startLineNumber = GetLineNumber(); FeedIteratorInternal feedIterator = (FeedIteratorInternal)container.GetItemQueryIterator( queryText: "SELECT * FROM c", @@ -539,6 +545,8 @@ public async Task QueryAsync() inputs.Add(new Input("Query Typed", traceForest, startLineNumber, endLineNumber, EndToEndTraceWriterBaselineTests.testListener?.GetRecordedAttributes())); EndToEndTraceWriterBaselineTests.AssertAndResetActivityInformation(); + + requestOptions.QueryTextMode = QueryTextMode.None; //Reset request Option } //---------------------------------------------------------------- @@ -546,6 +554,8 @@ public async Task QueryAsync() // Query Public API //---------------------------------------------------------------- { + requestOptions.QueryTextMode = QueryTextMode.ParameterizedOnly; + startLineNumber = GetLineNumber(); FeedIterator feedIterator = container.GetItemQueryStreamIterator( queryText: "SELECT * FROM c", @@ -566,6 +576,8 @@ public async Task QueryAsync() inputs.Add(new Input("Query Public API", traceForest, startLineNumber, endLineNumber, EndToEndTraceWriterBaselineTests.testListener?.GetRecordedAttributes())); EndToEndTraceWriterBaselineTests.AssertAndResetActivityInformation(); + + requestOptions.QueryTextMode = QueryTextMode.None; //Reset request Option } //---------------------------------------------------------------- @@ -573,9 +585,16 @@ public async Task QueryAsync() // Query Public API Typed //---------------------------------------------------------------- { + requestOptions.QueryTextMode = QueryTextMode.ParameterizedOnly; + + QueryDefinition parameterizedQuery = new QueryDefinition( + query: "SELECT * FROM c WHERE c.id != @customIdentifier" + ) + .WithParameter("@customIdentifier", "anyRandomId"); + startLineNumber = GetLineNumber(); FeedIterator feedIterator = container.GetItemQueryIterator( - queryText: "SELECT * FROM c", + queryDefinition: parameterizedQuery, requestOptions: requestOptions); List traces = new List(); diff --git a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetSDKAPI.json b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetSDKAPI.json index c6a6817f1d..03ea64e93e 100644 --- a/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetSDKAPI.json +++ b/Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/Contracts/DotNetSDKAPI.json @@ -3237,6 +3237,18 @@ ], "MethodInfo": "Microsoft.Azure.Cosmos.CosmosThresholdOptions get_CosmosThresholdOptions();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, + "Microsoft.Azure.Cosmos.QueryTextMode get_QueryTextMode()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.QueryTextMode get_QueryTextMode();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.QueryTextMode QueryTextMode": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.QueryTextMode QueryTextMode;CanRead:True;CanWrite:True;Microsoft.Azure.Cosmos.QueryTextMode get_QueryTextMode();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_QueryTextMode(Microsoft.Azure.Cosmos.QueryTextMode);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, "Void .ctor()": { "Type": "Constructor", "Attributes": [], @@ -3262,6 +3274,13 @@ "CompilerGeneratedAttribute" ], "MethodInfo": "Void set_DisableSendingMetricsToService(Boolean);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Void set_QueryTextMode(Microsoft.Azure.Cosmos.QueryTextMode)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_QueryTextMode(Microsoft.Azure.Cosmos.QueryTextMode);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" } }, "NestedTypes": {} @@ -6649,6 +6668,18 @@ ], "MethodInfo": "Microsoft.Azure.Cosmos.DedicatedGatewayRequestOptions get_DedicatedGatewayRequestOptions();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, + "Microsoft.Azure.Cosmos.QueryTextMode get_QueryTextMode()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.QueryTextMode get_QueryTextMode();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.QueryTextMode QueryTextMode": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.QueryTextMode QueryTextMode;CanRead:True;CanWrite:True;Microsoft.Azure.Cosmos.QueryTextMode get_QueryTextMode();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_QueryTextMode(Microsoft.Azure.Cosmos.QueryTextMode);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, "System.Nullable`1[Microsoft.Azure.Cosmos.ConsistencyLevel] ConsistencyLevel": { "Type": "Property", "Attributes": [], @@ -6840,6 +6871,13 @@ ], "MethodInfo": "Void set_PopulateIndexMetrics(System.Nullable`1[System.Boolean]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, + "Void set_QueryTextMode(Microsoft.Azure.Cosmos.QueryTextMode)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_QueryTextMode(Microsoft.Azure.Cosmos.QueryTextMode);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, "Void set_ResponseContinuationTokenLimitInKb(System.Nullable`1[System.Int32])[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", "Attributes": [ @@ -6857,6 +6895,32 @@ }, "NestedTypes": {} }, + "Microsoft.Azure.Cosmos.QueryTextMode;System.Enum;IsAbstract:False;IsSealed:True;IsInterface:False;IsEnum:True;IsClass:False;IsValueType:True;IsNested:False;IsGenericType:False;IsSerializable:True": { + "Subclasses": {}, + "Members": { + "Int32 value__": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "Int32 value__;IsInitOnly:False;IsStatic:False;" + }, + "Microsoft.Azure.Cosmos.QueryTextMode All": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.QueryTextMode All;IsInitOnly:False;IsStatic:True;" + }, + "Microsoft.Azure.Cosmos.QueryTextMode None": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.QueryTextMode None;IsInitOnly:False;IsStatic:True;" + }, + "Microsoft.Azure.Cosmos.QueryTextMode ParameterizedOnly": { + "Type": "Field", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.QueryTextMode ParameterizedOnly;IsInitOnly:False;IsStatic:True;" + } + }, + "NestedTypes": {} + }, "Microsoft.Azure.Cosmos.ReadManyRequestOptions;Microsoft.Azure.Cosmos.RequestOptions;IsAbstract:False;IsSealed:False;IsInterface:False;IsEnum:False;IsClass:True;IsValueType:False;IsNested:False;IsGenericType:False;IsSerializable:False": { "Subclasses": {}, "Members": { @@ -7725,6 +7789,18 @@ ], "MethodInfo": "Microsoft.Azure.Cosmos.DedicatedGatewayRequestOptions get_DedicatedGatewayRequestOptions();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, + "Microsoft.Azure.Cosmos.QueryTextMode get_QueryTextMode()[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Microsoft.Azure.Cosmos.QueryTextMode get_QueryTextMode();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, + "Microsoft.Azure.Cosmos.QueryTextMode QueryTextMode": { + "Type": "Property", + "Attributes": [], + "MethodInfo": "Microsoft.Azure.Cosmos.QueryTextMode QueryTextMode;CanRead:True;CanWrite:True;Microsoft.Azure.Cosmos.QueryTextMode get_QueryTextMode();IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;Void set_QueryTextMode(Microsoft.Azure.Cosmos.QueryTextMode);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, "System.Nullable`1[Microsoft.Azure.Cosmos.ConsistencyLevel] ConsistencyLevel": { "Type": "Property", "Attributes": [], @@ -7916,6 +7992,13 @@ ], "MethodInfo": "Void set_PopulateIndexMetrics(System.Nullable`1[System.Boolean]);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" }, + "Void set_QueryTextMode(Microsoft.Azure.Cosmos.QueryTextMode)[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { + "Type": "Method", + "Attributes": [ + "CompilerGeneratedAttribute" + ], + "MethodInfo": "Void set_QueryTextMode(Microsoft.Azure.Cosmos.QueryTextMode);IsAbstract:False;IsStatic:False;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;" + }, "Void set_ResponseContinuationTokenLimitInKb(System.Nullable`1[System.Int32])[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]": { "Type": "Method", "Attributes": [