Skip to content

Commit

Permalink
Use loggging infrastructure on Cosmos (#23517)
Browse files Browse the repository at this point in the history
Fixes #19063
  • Loading branch information
ajcvickers committed Nov 28, 2020
1 parent 42cdfb8 commit 691eaac
Show file tree
Hide file tree
Showing 14 changed files with 501 additions and 133 deletions.
17 changes: 9 additions & 8 deletions src/EFCore.Cosmos/Diagnostics/CosmosEventId.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,33 +26,34 @@ private enum Id
// Update events

// Query events
// These events are actually in Event in `DbLoggerCategory.Database.Command`.
// Leaving the ID unchanged to avoid changing it after release.
ExecutingSqlQuery = CoreEventId.ProviderBaseId + 100,
ExecutingReadItem
}

private static readonly string _queryPrefix = DbLoggerCategory.Query.Name + ".";

private static EventId MakeQueryId(Id id)
=> new EventId((int)id, _queryPrefix + id);
private static readonly string _commandPrefix = DbLoggerCategory.Database.Command.Name + ".";

/// <summary>
/// <para>
/// A SQL query was executed.
/// </para>
/// <para>
/// This event is in the <see cref="DbLoggerCategory.Query" /> category.
/// This event is in the <see cref="DbLoggerCategory.Database.Command" /> category.
/// </para>
/// </summary>
public static readonly EventId ExecutingSqlQuery = MakeQueryId(Id.ExecutingSqlQuery);
public static readonly EventId ExecutingSqlQuery
= new EventId((int)Id.ExecutingSqlQuery, _commandPrefix + Id.ExecutingSqlQuery);

/// <summary>
/// <para>
/// ReadItem was executed.
/// </para>
/// <para>
/// This event is in the <see cref="DbLoggerCategory.Query" /> category.
/// This event is in the <see cref="DbLoggerCategory.Database.Command" /> category.
/// </para>
/// </summary>
public static readonly EventId ExecutingReadItem = MakeQueryId(Id.ExecutingReadItem);
public static readonly EventId ExecutingReadItem
= new EventId((int)Id.ExecutingReadItem, _commandPrefix + Id.ExecutingReadItem);
}
}
68 changes: 68 additions & 0 deletions src/EFCore.Cosmos/Diagnostics/CosmosQueryEventData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
// 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.Collections.Generic;
using System.Diagnostics;
using JetBrains.Annotations;

namespace Microsoft.EntityFrameworkCore.Diagnostics
{
/// <summary>
/// A <see cref="DiagnosticSource" /> event payload class for Cosmos query events.
/// </summary>
public class CosmosQueryEventData : EventData
{
/// <summary>
/// Constructs the event payload.
/// </summary>
/// <param name="eventDefinition"> The event definition. </param>
/// <param name="messageGenerator"> A delegate that generates a log message for this event. </param>
/// <param name="containerId"> The ID of the Cosmos container being queried. </param>
/// <param name="partitionKey"> The key of the Cosmos partition that the query is using. </param>
/// <param name="parameters"> Name/values for each parameter in the Cosmos Query. </param>
/// <param name="querySql"> The SQL representing the query. </param>
/// <param name="logSensitiveData"> Indicates whether or not the application allows logging of sensitive data. </param>
public CosmosQueryEventData(
[NotNull] EventDefinitionBase eventDefinition,
[NotNull] Func<EventDefinitionBase, EventData, string> messageGenerator,
[NotNull] string containerId,
[CanBeNull] string partitionKey,
[NotNull] IReadOnlyList<(string Name, object Value)> parameters,
[NotNull] string querySql,
bool logSensitiveData)
: base(eventDefinition, messageGenerator)
{
ContainerId = containerId;
PartitionKey = partitionKey;
Parameters = parameters;
QuerySql = querySql;
LogSensitiveData = logSensitiveData;
}

/// <summary>
/// The ID of the Cosmos container being queried.
/// </summary>
public virtual string ContainerId { get; }

/// <summary>
/// The key of the Cosmos partition that the query is using.
/// </summary>
public virtual string PartitionKey { get; }

/// <summary>
/// Name/values for each parameter in the Cosmos Query.
/// </summary>
public virtual IReadOnlyList<(string Name, object Value)> Parameters { get; }

/// <summary>
/// The SQL representing the query.
/// </summary>
public virtual string QuerySql { get; }

/// <summary>
/// Indicates whether or not the application allows logging of sensitive data.
/// </summary>
public virtual bool LogSensitiveData { get; }
}
}
59 changes: 59 additions & 0 deletions src/EFCore.Cosmos/Diagnostics/CosmosReadItemEventData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// 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.Diagnostics;
using JetBrains.Annotations;

namespace Microsoft.EntityFrameworkCore.Diagnostics
{
/// <summary>
/// A <see cref="DiagnosticSource" /> event payload class for Cosmos read-item events.
/// </summary>
public class CosmosReadItemEventData : EventData
{
/// <summary>
/// Constructs the event payload.
/// </summary>
/// <param name="eventDefinition"> The event definition. </param>
/// <param name="messageGenerator"> A delegate that generates a log message for this event. </param>
/// <param name="resourceId"> The ID of the resource being read. </param>
/// <param name="containerId"> The ID of the Cosmos container being queried. </param>
/// <param name="partitionKey"> The key of the Cosmos partition that the query is using. </param>
/// <param name="logSensitiveData"> Indicates whether or not the application allows logging of sensitive data. </param>
public CosmosReadItemEventData(
[NotNull] EventDefinitionBase eventDefinition,
[NotNull] Func<EventDefinitionBase, EventData, string> messageGenerator,
[NotNull] string resourceId,
[NotNull] string containerId,
[CanBeNull] string partitionKey,
bool logSensitiveData)
: base(eventDefinition, messageGenerator)
{
ResourceId = resourceId;
ContainerId = containerId;
PartitionKey = partitionKey;
LogSensitiveData = logSensitiveData;
}

/// <summary>
/// The ID of the Cosmos container being queried.
/// </summary>
public virtual string ContainerId { get; }

/// <summary>
/// The ID of the resource being read.
/// </summary>
public virtual string ResourceId { get; }

/// <summary>
/// The key of the Cosmos partition that the query is using.
/// </summary>
public virtual string PartitionKey { get; }

/// <summary>
/// Indicates whether or not the application allows logging of sensitive data.
/// </summary>
public virtual bool LogSensitiveData { get; }
}
}
117 changes: 77 additions & 40 deletions src/EFCore.Cosmos/Diagnostics/Internal/CosmosLoggerExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
using System.Linq;
using System.Text;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Cosmos.Internal;
using Microsoft.EntityFrameworkCore.Cosmos.Storage.Internal;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

Expand All @@ -31,31 +31,52 @@ public static class CosmosLoggerExtensions
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public static void ExecutingSqlQuery(
[NotNull] this IDiagnosticsLogger<DbLoggerCategory.Database.Command> diagnosticsLogger,
[NotNull] this IDiagnosticsLogger<DbLoggerCategory.Database.Command> diagnostics,
[NotNull] string containerId,
[CanBeNull] string partitionKey,
[NotNull] CosmosSqlQuery cosmosSqlQuery)
{
var definition = new EventDefinition<string, string, string>(
diagnosticsLogger.Options,
CosmosEventId.ExecutingSqlQuery,
LogLevel.Debug,
"CosmosEventId.ExecutingSqlQuery",
level => LoggerMessage.Define<string, string, string>(
level,
CosmosEventId.ExecutingSqlQuery,
"Executing Sql Query [Parameters=[{parameters}]]{newLine}{commandText}"));

definition.Log(
diagnosticsLogger,
FormatParameters(cosmosSqlQuery.Parameters, ShouldLogParameterValues(diagnosticsLogger, cosmosSqlQuery)),
Environment.NewLine,
cosmosSqlQuery.Query);
var definition = CosmosResources.LogExecutingSqlQuery(diagnostics);

if (diagnostics.ShouldLog(definition))
{
var logSensitiveData = diagnostics.ShouldLogSensitiveData();

definition.Log(
diagnostics,
containerId,
logSensitiveData ? partitionKey : "?",
FormatParameters(cosmosSqlQuery.Parameters, logSensitiveData && cosmosSqlQuery.Parameters.Count > 0),
Environment.NewLine,
cosmosSqlQuery.Query);
}

if (diagnostics.NeedsEventData(definition, out var diagnosticSourceEnabled, out var simpleLogEnabled))
{
var eventData = new CosmosQueryEventData(
definition,
ExecutingSqlQuery,
containerId,
partitionKey,
cosmosSqlQuery.Parameters.Select(p => (p.Name, p.Value)).ToList(),
cosmosSqlQuery.Query,
diagnostics.ShouldLogSensitiveData());

diagnostics.DispatchEventData(definition, eventData, diagnosticSourceEnabled, simpleLogEnabled);
}
}

private static bool ShouldLogParameterValues(
IDiagnosticsLogger<DbLoggerCategory.Database.Command> diagnostics,
CosmosSqlQuery cosmosSqlQuery)
=> cosmosSqlQuery.Parameters.Count > 0
&& diagnostics.ShouldLogSensitiveData();
private static string ExecutingSqlQuery(EventDefinitionBase definition, EventData payload)
{
var d = (EventDefinition<string, string, string, string, string>)definition;
var p = (CosmosQueryEventData)payload;
return d.GenerateMessage(
p.ContainerId,
p.LogSensitiveData ? p.PartitionKey : "?",
FormatParameters(p.Parameters, p.LogSensitiveData && p.Parameters.Count > 0),
Environment.NewLine,
p.QuerySql);
}

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand All @@ -64,31 +85,47 @@ public static class CosmosLoggerExtensions
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
public static void ExecutingReadItem(
[NotNull] this IDiagnosticsLogger<DbLoggerCategory.Database.Command> diagnosticsLogger,
[NotNull] string partitionKey,
[NotNull] this IDiagnosticsLogger<DbLoggerCategory.Database.Command> diagnostics,
[NotNull] string containerId,
[CanBeNull] string partitionKey,
[NotNull] string resourceId)
{
var definition = new EventDefinition<string>(
diagnosticsLogger.Options,
CosmosEventId.ExecutingReadItem,
LogLevel.Debug,
"CosmosEventId.ExecutingReadItem",
level => LoggerMessage.Define<string>(
level,
CosmosEventId.ExecutingReadItem,
"Executing Read Item [Partition Key, Resource Id=[{parameters}]]"));

definition.Log(
diagnosticsLogger,
$"{partitionKey}, {resourceId}");
var definition = CosmosResources.LogExecutingReadItem(diagnostics);

if (diagnostics.ShouldLog(definition))
{
var logSensitiveData = diagnostics.ShouldLogSensitiveData();
definition.Log(diagnostics, logSensitiveData ? resourceId : "?", containerId, logSensitiveData ? partitionKey : "?");
}

if (diagnostics.NeedsEventData(definition, out var diagnosticSourceEnabled, out var simpleLogEnabled))
{
var eventData = new CosmosReadItemEventData(
definition,
ExecutingReadItem,
resourceId,
containerId,
partitionKey,
diagnostics.ShouldLogSensitiveData());

diagnostics.DispatchEventData(definition, eventData, diagnosticSourceEnabled, simpleLogEnabled);
}
}

private static string ExecutingReadItem(EventDefinitionBase definition, EventData payload)
{
var d = (EventDefinition<string, string, string>)definition;
var p = (CosmosReadItemEventData)payload;
return d.GenerateMessage(p.LogSensitiveData ? p.ResourceId : "?", p.ContainerId, p.LogSensitiveData ? p.PartitionKey : "?");
}

private static string FormatParameters(IReadOnlyList<(string Name, object Value)> parameters, bool shouldLogParameterValues)
=> FormatParameters(parameters.Select(p => new SqlParameter(p.Name, p.Value)).ToList(), shouldLogParameterValues);

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

private static string FormatParameter(SqlParameter parameter, bool shouldLogParameterValue)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@ public class CosmosLoggingDefinitions : LoggingDefinitions
/// 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>
public EventDefinitionBase LogSavedChanges;
public EventDefinitionBase LogExecutingSqlQuery;

/// <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>
public EventDefinitionBase LogTransactionsNotSupported;
public EventDefinitionBase LogExecutingReadItem;
}
}
Loading

0 comments on commit 691eaac

Please sign in to comment.