Skip to content

Commit

Permalink
Query: Add logging
Browse files Browse the repository at this point in the history
Part of #17498

Added messages
- Starting query compilation
- Navigation included
- Skip/Take without order by (infra is in core but providers use to generate the warning)
- First without order by or predicate  (infra is in core but providers use to generate the warning)

Removed messages
- Include ignored (they get removed due to automatic pruning based on expression tree visitation
- Obsolete exception with aggregate operator in relational. We logged that when doing client eval of aggregate operation
- Optimized query model - there is no more optimization phase.

Other fixes
- Don't log parameter values in Cosmos when sensitive data logging is not enabled.
  • Loading branch information
smitpatel committed Aug 4, 2020
1 parent 797d7f1 commit dd7cf9a
Show file tree
Hide file tree
Showing 20 changed files with 566 additions and 254 deletions.
23 changes: 18 additions & 5 deletions src/EFCore.Cosmos/Diagnostics/Internal/CosmosLoggerExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,17 @@ public static void ExecutingSqlQuery(

definition.Log(
diagnosticsLogger,
FormatParameters(cosmosSqlQuery.Parameters),
FormatParameters(cosmosSqlQuery.Parameters, ShouldLogParameterValues(diagnosticsLogger, cosmosSqlQuery)),
Environment.NewLine,
cosmosSqlQuery.Query);
}

private static bool ShouldLogParameterValues(
IDiagnosticsLogger<DbLoggerCategory.Database.Command> diagnostics,
CosmosSqlQuery cosmosSqlQuery)
=> cosmosSqlQuery.Parameters.Count > 0
&& diagnostics.ShouldLogSensitiveData();

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
Expand All @@ -77,21 +83,28 @@ public static void ExecutingReadItem(
$"{partitionKey}, {resourceId}");
}

private static string FormatParameters(IReadOnlyList<SqlParameter> parameters)
private static string FormatParameters(IReadOnlyList<SqlParameter> parameters, bool shouldLogParameterValues)
{
return parameters.Count == 0
? ""
: string.Join(", ", parameters.Select(FormatParameter));
: string.Join(", ", parameters.Select(e => FormatParameter(e, shouldLogParameterValues)));
}

private static string FormatParameter(SqlParameter parameter)
private static string FormatParameter(SqlParameter parameter, bool shouldLogParameterValue)
{
var builder = new StringBuilder();
builder
.Append(parameter.Name)
.Append("=");

FormatParameterValue(builder, parameter.Value);
if (shouldLogParameterValue)
{
FormatParameterValue(builder, parameter.Value);
}
else
{
builder.Append("?");
}

return builder.ToString();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal
/// </summary>
public class CosmosQueryableMethodTranslatingExpressionVisitor : QueryableMethodTranslatingExpressionVisitor
{
private readonly IModel _model;
private readonly QueryCompilationContext _queryCompilationContext;
private readonly ISqlExpressionFactory _sqlExpressionFactory;
private readonly IMemberTranslatorProvider _memberTranslatorProvider;
private readonly IMethodCallTranslatorProvider _methodCallTranslatorProvider;
Expand All @@ -47,7 +47,7 @@ public CosmosQueryableMethodTranslatingExpressionVisitor(
[NotNull] IMethodCallTranslatorProvider methodCallTranslatorProvider)
: base(dependencies, queryCompilationContext, subquery: false)
{
_model = queryCompilationContext.Model;
_queryCompilationContext = queryCompilationContext;
_sqlExpressionFactory = sqlExpressionFactory;
_memberTranslatorProvider = memberTranslatorProvider;
_methodCallTranslatorProvider = methodCallTranslatorProvider;
Expand All @@ -56,7 +56,7 @@ public CosmosQueryableMethodTranslatingExpressionVisitor(
_sqlExpressionFactory,
_memberTranslatorProvider,
_methodCallTranslatorProvider);
_projectionBindingExpressionVisitor = new CosmosProjectionBindingExpressionVisitor(_model, _sqlTranslator);
_projectionBindingExpressionVisitor = new CosmosProjectionBindingExpressionVisitor(_queryCompilationContext.Model, _sqlTranslator);
}

/// <summary>
Expand All @@ -69,14 +69,14 @@ protected CosmosQueryableMethodTranslatingExpressionVisitor(
[NotNull] CosmosQueryableMethodTranslatingExpressionVisitor parentVisitor)
: base(parentVisitor.Dependencies, parentVisitor.QueryCompilationContext, subquery: true)
{
_model = parentVisitor._model;
_queryCompilationContext = parentVisitor._queryCompilationContext;
_sqlExpressionFactory = parentVisitor._sqlExpressionFactory;
_sqlTranslator = new CosmosSqlTranslatingExpressionVisitor(
QueryCompilationContext,
_sqlExpressionFactory,
_memberTranslatorProvider,
_methodCallTranslatorProvider);
_projectionBindingExpressionVisitor = new CosmosProjectionBindingExpressionVisitor(_model, _sqlTranslator);
_projectionBindingExpressionVisitor = new CosmosProjectionBindingExpressionVisitor(_queryCompilationContext.Model, _sqlTranslator);
}

/// <summary>
Expand Down Expand Up @@ -212,7 +212,7 @@ protected override ShapedQueryExpression CreateShapedQueryExpression(Type elemen
{
Check.NotNull(elementType, nameof(elementType));

var entityType = _model.FindEntityType(elementType);
var entityType = _queryCompilationContext.Model.FindEntityType(elementType);
var selectExpression = _sqlExpressionFactory.Select(entityType);

return new ShapedQueryExpression(
Expand Down Expand Up @@ -471,6 +471,12 @@ protected override ShapedQueryExpression TranslateFirstOrDefault(
}

var selectExpression = (SelectExpression)source.QueryExpression;
if (selectExpression.Predicate == null
&& selectExpression.Orderings.Count == 0)
{
_queryCompilationContext.Logger.FirstWithoutOrderByAndFilterWarning();
}

selectExpression.ApplyLimit(TranslateExpression(Expression.Constant(1)));

return source.ShaperExpression.Type != returnType
Expand Down Expand Up @@ -909,6 +915,11 @@ protected override ShapedQueryExpression TranslateSkip(ShapedQueryExpression sou

if (translation != null)
{
if (selectExpression.Orderings.Count == 0)
{
_queryCompilationContext.Logger.RowLimitingOperationWithoutOrderByWarning();
}

selectExpression.ApplyOffset(translation);

return source;
Expand Down Expand Up @@ -980,6 +991,11 @@ protected override ShapedQueryExpression TranslateTake(ShapedQueryExpression sou

if (translation != null)
{
if (selectExpression.Orderings.Count == 0)
{
_queryCompilationContext.Logger.RowLimitingOperationWithoutOrderByWarning();
}

selectExpression.ApplyLimit(translation);

return source;
Expand Down Expand Up @@ -1144,7 +1160,7 @@ when methodCallExpression.TryGetEFPropertyArguments(out _, out var propertyName)
break;

case MethodCallExpression methodCallExpression
when methodCallExpression.TryGetIndexerArguments(_model, out _, out var propertyName):
when methodCallExpression.TryGetIndexerArguments(_queryCompilationContext.Model, out _, out var propertyName):
property = entityType.FindProperty(propertyName);
break;
}
Expand Down
8 changes: 5 additions & 3 deletions src/EFCore.Relational/Diagnostics/RelationalEventId.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Data.Common;
using System.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
Expand Down Expand Up @@ -75,8 +76,8 @@ private enum Id
// Query events
QueryClientEvaluationWarning = CoreEventId.RelationalBaseId + 500,
QueryPossibleUnintendedUseOfEqualsWarning,
QueryPossibleExceptionWithAggregateOperatorWarning,
ValueConversionSqlLiteralWarning, // This warning has been removed.
Obsolete_QueryPossibleExceptionWithAggregateOperatorWarning,
Obsolete_ValueConversionSqlLiteralWarning,
MultipleCollectionIncludeWarning,

// Model validation events
Expand Down Expand Up @@ -611,8 +612,9 @@ private enum Id
/// This event is in the <see cref="DbLoggerCategory.Query" /> category.
/// </para>
/// </summary>
[Obsolete]
public static readonly EventId QueryPossibleExceptionWithAggregateOperatorWarning =
MakeQueryId(Id.QueryPossibleExceptionWithAggregateOperatorWarning);
MakeQueryId(Id.Obsolete_QueryPossibleExceptionWithAggregateOperatorWarning);

/// <summary>
/// <para>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4163,6 +4163,7 @@ private static string QueryPossibleUnintendedUseOfEqualsWarning(EventDefinitionB
/// Logs for the <see cref="RelationalEventId.QueryPossibleExceptionWithAggregateOperatorWarning" /> event.
/// </summary>
/// <param name="diagnostics"> The diagnostics logger to use. </param>
[Obsolete]
public static void QueryPossibleExceptionWithAggregateOperatorWarning(
[NotNull] this IDiagnosticsLogger<DbLoggerCategory.Query> diagnostics)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using Microsoft.EntityFrameworkCore.Infrastructure;

namespace Microsoft.EntityFrameworkCore.Diagnostics
Expand Down Expand Up @@ -239,6 +240,7 @@ public abstract class RelationalLoggingDefinitions : LoggingDefinitions
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
[EntityFrameworkInternal]
[Obsolete]
public EventDefinitionBase LogQueryPossibleExceptionWithAggregateOperatorWarning;

/// <summary>
Expand Down Expand Up @@ -412,15 +414,6 @@ public abstract class RelationalLoggingDefinitions : LoggingDefinitions
[EntityFrameworkInternal]
public EventDefinitionBase LogMigrationAttributeMissingWarning;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
/// any release. You should only use it directly in your code with extreme caution and knowing that
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
[EntityFrameworkInternal]
public EventDefinitionBase LogValueConversionSqlLiteralWarning;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/EFCore.Relational/Properties/RelationalStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@
</data>
<data name="LogQueryPossibleExceptionWithAggregateOperatorWarning" xml:space="preserve">
<value>Possible unintended use of a potentially throwing aggregate method (Min, Max, Average) in a subquery. Client evaluation will be used and operator will throw if no data exists. Changing the subquery result type to a nullable type will allow full translation.</value>
<comment>Warning RelationalEventId.QueryPossibleExceptionWithAggregateOperatorWarning</comment>
<comment>Obsolete Warning RelationalEventId.QueryPossibleExceptionWithAggregateOperatorWarning</comment>
</data>
<data name="LogGeneratingDown" xml:space="preserve">
<value>Generating down script for migration '{migration}'.</value>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,11 @@ protected override ShapedQueryExpression TranslateFirstOrDefault(
}

var selectExpression = (SelectExpression)source.QueryExpression;
if (selectExpression.Predicate == null
&& selectExpression.Orderings.Count == 0)
{
_queryCompilationContext.Logger.FirstWithoutOrderByAndFilterWarning();
}
selectExpression.ApplyLimit(TranslateExpression(Expression.Constant(1)));

return source.ShaperExpression.Type != returnType
Expand Down Expand Up @@ -1035,6 +1040,11 @@ protected override ShapedQueryExpression TranslateSkip(ShapedQueryExpression sou
return null;
}

if (selectExpression.Orderings.Count == 0)
{
_queryCompilationContext.Logger.RowLimitingOperationWithoutOrderByWarning();
}

selectExpression.ApplyOffset(translation);

return source;
Expand Down Expand Up @@ -1096,6 +1106,11 @@ protected override ShapedQueryExpression TranslateTake(ShapedQueryExpression sou
return null;
}

if (selectExpression.Orderings.Count == 0)
{
_queryCompilationContext.Logger.RowLimitingOperationWithoutOrderByWarning();
}

selectExpression.ApplyLimit(translation);

return source;
Expand Down
57 changes: 54 additions & 3 deletions src/EFCore/Diagnostics/CoreEventId.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,16 @@ private enum Id
// Query events
QueryIterationFailed = CoreBaseId + 100,
Obsolete_QueryModelCompiling,
Obsolete_RowLimitingOperationWithoutOrderByWarning,
Obsolete_FirstWithoutOrderByAndFilterWarning,
RowLimitingOperationWithoutOrderByWarning,
FirstWithoutOrderByAndFilterWarning,
Obsolete_QueryModelOptimized,
Obsolete_NavigationIncluded,
NavigationIncluded,
Obsolete_IncludeIgnoredWarning,
QueryExecutionPlanned,
PossibleUnintendedCollectionNavigationNullComparisonWarning,
PossibleUnintendedReferenceComparisonWarning,
InvalidIncludePathError,
QueryCompilationStarting,

// Infrastructure events
SensitiveDataLoggingEnabledWarning = CoreBaseId + 400,
Expand Down Expand Up @@ -215,6 +216,56 @@ public static readonly EventId PossibleUnintendedReferenceComparisonWarning
public static readonly EventId InvalidIncludePathError
= MakeQueryId(Id.InvalidIncludePathError);

/// <summary>
/// <para>
/// Starting query compilation.
/// </para>
/// <para>
/// This event is in the <see cref="DbLoggerCategory.Query" /> category.
/// </para>
/// <para>
/// This event uses the <see cref="QueryExpressionEventData" /> payload when used with a <see cref="DiagnosticSource" />.
/// </para>
/// </summary>
public static readonly EventId QueryCompilationStarting
= MakeQueryId(Id.QueryCompilationStarting);

/// <summary>
/// <para>
/// A navigation was included in the query.
/// </para>
/// <para>
/// This event is in the <see cref="DbLoggerCategory.Query" /> category.
/// </para>
/// <para>
/// This event uses the <see cref="QueryExpressionEventData" /> payload when used with a <see cref="DiagnosticSource" />.
/// </para>
/// </summary>
public static readonly EventId NavigationIncluded
= MakeQueryId(Id.NavigationIncluded);

/// <summary>
/// <para>
/// A query uses a row limiting operation (Skip/Take) without OrderBy which may lead to unpredictable results.
/// </para>
/// <para>
/// This event is in the <see cref="DbLoggerCategory.Query" /> category.
/// </para>
/// </summary>
public static readonly EventId RowLimitingOperationWithoutOrderByWarning
= MakeQueryId(Id.RowLimitingOperationWithoutOrderByWarning);

/// <summary>
/// <para>
/// A query uses First/FirstOrDefault operation without OrderBy and filter which may lead to unpredictable results.
/// </para>
/// <para>
/// This event is in the <see cref="DbLoggerCategory.Query" /> category.
/// </para>
/// </summary>
public static readonly EventId FirstWithoutOrderByAndFilterWarning
= MakeQueryId(Id.FirstWithoutOrderByAndFilterWarning);

private static readonly string _infraPrefix = DbLoggerCategory.Infrastructure.Name + ".";
private static EventId MakeInfraId(Id id) => new EventId((int)id, _infraPrefix + id);

Expand Down
Loading

0 comments on commit dd7cf9a

Please sign in to comment.