Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use loggging infrastructure on Cosmos #23517

Merged
merged 2 commits into from
Nov 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ private static bool ShouldLogParameterValues(
/// 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