Skip to content

Commit bd17f6d

Browse files
authored
Report debug log for connection cancellation (#35793)
Fixes #35708
1 parent 4ee7d30 commit bd17f6d

15 files changed

+347
-45
lines changed

src/EFCore.Relational/Diagnostics/DbConnectionInterceptor.cs

+12
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,16 @@ public virtual Task ConnectionFailedAsync(
9797
ConnectionErrorEventData eventData,
9898
CancellationToken cancellationToken = default)
9999
=> Task.CompletedTask;
100+
101+
/// <inheritdoc />
102+
public virtual void ConnectionCanceled(DbConnection connection, ConnectionEndEventData eventData)
103+
{
104+
}
105+
106+
/// <inheritdoc />
107+
public virtual Task ConnectionCanceledAsync(
108+
DbConnection connection,
109+
ConnectionEndEventData eventData,
110+
CancellationToken cancellationToken = default)
111+
=> Task.CompletedTask;
100112
}

src/EFCore.Relational/Diagnostics/IDbConnectionInterceptor.cs

+22-2
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ Task ConnectionDisposedAsync(DbConnection connection, ConnectionEndEventData eve
261261
=> Task.CompletedTask;
262262

263263
/// <summary>
264-
/// Called when closing of a connection has failed with an exception.
264+
/// Called when a connection operation (e.g. open) has failed with an exception.
265265
/// </summary>
266266
/// <param name="connection">The connection.</param>
267267
/// <param name="eventData">Contextual information about the connection.</param>
@@ -270,7 +270,7 @@ void ConnectionFailed(DbConnection connection, ConnectionErrorEventData eventDat
270270
}
271271

272272
/// <summary>
273-
/// Called when closing of a connection has failed with an exception.
273+
/// Called when a connection operation (e.g. open) has failed with an exception.
274274
/// </summary>
275275
/// <param name="connection">The connection.</param>
276276
/// <param name="eventData">Contextual information about the connection.</param>
@@ -279,4 +279,24 @@ void ConnectionFailed(DbConnection connection, ConnectionErrorEventData eventDat
279279
/// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
280280
Task ConnectionFailedAsync(DbConnection connection, ConnectionErrorEventData eventData, CancellationToken cancellationToken = default)
281281
=> Task.CompletedTask;
282+
283+
/// <summary>
284+
/// Called when the opening of a connection was canceled.
285+
/// </summary>
286+
/// <param name="connection">The connection.</param>
287+
/// <param name="eventData">Contextual information about the connection.</param>
288+
void ConnectionCanceled(DbConnection connection, ConnectionEndEventData eventData)
289+
{
290+
}
291+
292+
/// <summary>
293+
/// Called when the opening of a connection was canceled.
294+
/// </summary>
295+
/// <param name="connection">The connection.</param>
296+
/// <param name="eventData">Contextual information about the connection.</param>
297+
/// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
298+
/// <returns>A <see cref="Task" /> representing the asynchronous operation.</returns>
299+
/// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
300+
Task ConnectionCanceledAsync(DbConnection connection, ConnectionEndEventData eventData, CancellationToken cancellationToken = default)
301+
=> Task.CompletedTask;
282302
}

src/EFCore.Relational/Diagnostics/IRelationalCommandDiagnosticsLogger.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,7 @@ Task CommandErrorAsync(
463463
/// <param name="commandId">The correlation ID associated with the given <see cref="DbCommand" />.</param>
464464
/// <param name="connectionId">The correlation ID associated with the <see cref="DbConnection" /> being used.</param>
465465
/// <param name="startTime">The time that execution began.</param>
466-
/// <param name="duration">The amount of time that passed until the exception was raised.</param>
466+
/// <param name="duration">The amount of time that passed until the command was canceled.</param>
467467
/// <param name="commandSource">Source of the command.</param>
468468
void CommandCanceled(
469469
IRelationalConnection connection,
@@ -488,7 +488,7 @@ void CommandCanceled(
488488
/// <param name="commandId">The correlation ID associated with the given <see cref="DbCommand" />.</param>
489489
/// <param name="connectionId">The correlation ID associated with the <see cref="DbConnection" /> being used.</param>
490490
/// <param name="startTime">The time that execution began.</param>
491-
/// <param name="duration">The amount of time that passed until the exception was raised.</param>
491+
/// <param name="duration">The amount of time that passed until the command was canceled.</param>
492492
/// <param name="commandSource">Source of the command.</param>
493493
/// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
494494
/// <returns>A <see cref="Task" /> representing the async operation.</returns>

src/EFCore.Relational/Diagnostics/IRelationalConnectionDiagnosticsLogger.cs

+26
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,32 @@ Task ConnectionErrorAsync(
210210
bool logErrorAsDebug,
211211
CancellationToken cancellationToken = default);
212212

213+
/// <summary>
214+
/// Logs for the <see cref="RelationalEventId.ConnectionCanceled" /> event.
215+
/// </summary>
216+
/// <param name="connection">The connection.</param>
217+
/// <param name="startTime">The time that the operation was started.</param>
218+
/// <param name="duration">The amount of time that passed until the command was canceled.</param>
219+
void ConnectionCanceled(
220+
IRelationalConnection connection,
221+
DateTimeOffset startTime,
222+
TimeSpan duration);
223+
224+
/// <summary>
225+
/// Logs for the <see cref="RelationalEventId.ConnectionCanceled" /> event.
226+
/// </summary>
227+
/// <param name="connection">The connection.</param>
228+
/// <param name="startTime">The time that the operation was started.</param>
229+
/// <param name="duration">The amount of time that passed until the command was canceled.</param>
230+
/// <param name="cancellationToken">A <see cref="CancellationToken" /> to observe while waiting for the task to complete.</param>
231+
/// <returns>A <see cref="Task" /> representing the async operation.</returns>
232+
/// <exception cref="OperationCanceledException">If the <see cref="CancellationToken" /> is canceled.</exception>
233+
Task ConnectionCanceledAsync(
234+
IRelationalConnection connection,
235+
DateTimeOffset startTime,
236+
TimeSpan duration,
237+
CancellationToken cancellationToken = default);
238+
213239
/// <summary>
214240
/// Whether <see cref="RelationalEventId.ConnectionCreating" /> or <see cref="RelationalEventId.ConnectionCreated" /> need
215241
/// to be logged.

src/EFCore.Relational/Diagnostics/Internal/RelationalConnectionDiagnosticsLogger.cs

+115
Original file line numberDiff line numberDiff line change
@@ -892,6 +892,121 @@ private void LogConnectionError(
892892

893893
#endregion ConnectionError
894894

895+
#region ConnectionCanceled
896+
897+
/// <summary>
898+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
899+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
900+
/// any release. You should only use it directly in your code with extreme caution and knowing that
901+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
902+
/// </summary>
903+
public virtual void ConnectionCanceled(
904+
IRelationalConnection connection,
905+
DateTimeOffset startTime,
906+
TimeSpan duration)
907+
{
908+
var definition = RelationalResources.LogConnectionCanceled(this);
909+
910+
LogConnectionCanceled(connection, definition);
911+
912+
if (NeedsEventData<IDbConnectionInterceptor>(
913+
definition, out var interceptor, out var diagnosticSourceEnabled, out var simpleLogEnabled))
914+
{
915+
var eventData = BroadcastConnectionCanceled(
916+
connection,
917+
startTime,
918+
duration,
919+
async: false,
920+
definition,
921+
diagnosticSourceEnabled,
922+
simpleLogEnabled);
923+
924+
interceptor?.ConnectionCanceled(connection.DbConnection, eventData);
925+
}
926+
}
927+
928+
/// <summary>
929+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
930+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
931+
/// any release. You should only use it directly in your code with extreme caution and knowing that
932+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
933+
/// </summary>
934+
public virtual Task ConnectionCanceledAsync(
935+
IRelationalConnection connection,
936+
DateTimeOffset startTime,
937+
TimeSpan duration,
938+
CancellationToken cancellationToken = default)
939+
{
940+
var definition = RelationalResources.LogConnectionCanceled(this);
941+
942+
LogConnectionCanceled(connection, definition);
943+
944+
if (NeedsEventData<IDbConnectionInterceptor>(
945+
definition, out var interceptor, out var diagnosticSourceEnabled, out var simpleLogEnabled))
946+
{
947+
var eventData = BroadcastConnectionCanceled(
948+
connection,
949+
startTime,
950+
duration,
951+
async: true,
952+
definition,
953+
diagnosticSourceEnabled,
954+
simpleLogEnabled);
955+
956+
interceptor?.ConnectionCanceled(connection.DbConnection, eventData);
957+
}
958+
959+
return Task.CompletedTask;
960+
}
961+
962+
private ConnectionEndEventData BroadcastConnectionCanceled(
963+
IRelationalConnection connection,
964+
DateTimeOffset startTime,
965+
TimeSpan duration,
966+
bool async,
967+
EventDefinition<string, string> definition,
968+
bool diagnosticSourceEnabled,
969+
bool simpleLogEnabled)
970+
{
971+
var eventData = new ConnectionEndEventData(
972+
definition,
973+
ConnectionCanceled,
974+
connection.DbConnection,
975+
connection.Context,
976+
connection.ConnectionId,
977+
async,
978+
startTime,
979+
duration);
980+
981+
DispatchEventData(definition, eventData, diagnosticSourceEnabled, simpleLogEnabled);
982+
983+
return eventData;
984+
985+
static string ConnectionCanceled(EventDefinitionBase definition, EventData payload)
986+
{
987+
var d = (EventDefinition<string, string>)definition;
988+
var p = (ConnectionEndEventData)payload;
989+
return d.GenerateMessage(
990+
p.Connection.Database,
991+
p.Connection.DataSource);
992+
}
993+
}
994+
995+
private void LogConnectionCanceled(
996+
IRelationalConnection connection,
997+
EventDefinition<string, string> definition)
998+
{
999+
if (ShouldLog(definition))
1000+
{
1001+
definition.Log(
1002+
this,
1003+
connection.DbConnection.Database,
1004+
connection.DbConnection.DataSource);
1005+
}
1006+
}
1007+
1008+
#endregion ConnectionCanceled
1009+
8951010
#region ConnectionCreating
8961011

8971012
/// <summary>

src/EFCore.Relational/Diagnostics/RelationalEventId.cs

+14
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ private enum Id
3333
ConnectionCreated,
3434
ConnectionDisposing,
3535
ConnectionDisposed,
36+
ConnectionCanceled,
3637

3738
// Command events
3839
CommandExecuting = CoreEventId.RelationalBaseId + 100,
@@ -217,6 +218,19 @@ private static EventId MakeConnectionId(Id id)
217218
/// </remarks>
218219
public static readonly EventId ConnectionError = MakeConnectionId(Id.ConnectionError);
219220

221+
/// <summary>
222+
/// A <see cref="DbConnection" /> open operation has been canceled.
223+
/// </summary>
224+
/// <remarks>
225+
/// <para>
226+
/// This event is in the <see cref="DbLoggerCategory.Database.Connection" /> category.
227+
/// </para>
228+
/// <para>
229+
/// This event uses the <see cref="ConnectionEndEventData" /> payload when used with a <see cref="DiagnosticSource" />.
230+
/// </para>
231+
/// </remarks>
232+
public static readonly EventId ConnectionCanceled = MakeConnectionId(Id.ConnectionCanceled);
233+
220234
/// <summary>
221235
/// A <see cref="DbConnection" /> is about to be created by EF.
222236
/// </summary>

src/EFCore.Relational/Diagnostics/RelationalLoggingDefinitions.cs

+9
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,15 @@ public abstract class RelationalLoggingDefinitions : LoggingDefinitions
133133
[EntityFrameworkInternal]
134134
public EventDefinitionBase? LogConnectionError;
135135

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

src/EFCore.Relational/Properties/RelationalStrings.Designer.cs

+26-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/EFCore.Relational/Properties/RelationalStrings.resx

+4
Original file line numberDiff line numberDiff line change
@@ -716,6 +716,10 @@
716716
<value>An error occurred using the connection to database '{database}' on server '{server}'.</value>
717717
<comment>Debug RelationalEventId.ConnectionError string string</comment>
718718
</data>
719+
<data name="LogConnectionCanceled" xml:space="preserve">
720+
<value>A connection open was canceled to database '{database}' on server '{server}'.</value>
721+
<comment>Debug RelationalEventId.ConnectionCanceled string string string</comment>
722+
</data>
719723
<data name="LogCreatedTransactionSavepoint" xml:space="preserve">
720724
<value>Created transaction savepoint.</value>
721725
<comment>Debug RelationalEventId.CreatedTransactionSavepoint</comment>

src/EFCore.Relational/Storage/RelationalConnection.cs

+21-9
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ public abstract class RelationalConnection : IRelationalConnection, ITransaction
4242
private readonly ConcurrentStack<Transaction> _ambientTransactions = new();
4343
private DbConnection? _connection;
4444
private readonly IRelationalCommandBuilder _relationalCommandBuilder;
45+
private readonly IExceptionDetector _exceptionDetector;
4546
private IRelationalCommand? _cachedRelationalCommand;
4647

4748
/// <summary>
@@ -74,6 +75,8 @@ protected RelationalConnection(RelationalConnectionDependencies dependencies)
7475
{
7576
_connectionOwned = true;
7677
}
78+
79+
_exceptionDetector = dependencies.ExceptionDetector;
7780
}
7881

7982
/// <summary>
@@ -726,7 +729,14 @@ private void OpenInternal(bool errorsExpected)
726729
}
727730
catch (Exception e)
728731
{
729-
logger.ConnectionError(this, e, startTime, stopwatch.Elapsed, errorsExpected);
732+
if (_exceptionDetector.IsCancellation(e, CancellationToken.None))
733+
{
734+
logger.ConnectionCanceled(this, startTime, stopwatch.Elapsed);
735+
}
736+
else
737+
{
738+
logger.ConnectionError(this, e, startTime, stopwatch.Elapsed, errorsExpected);
739+
}
730740

731741
throw;
732742
}
@@ -773,14 +783,16 @@ await logger.ConnectionOpenedAsync(this, startTime, stopwatch.Elapsed, cancellat
773783
}
774784
catch (Exception e)
775785
{
776-
await logger.ConnectionErrorAsync(
777-
this,
778-
e,
779-
startTime,
780-
stopwatch.Elapsed,
781-
errorsExpected,
782-
cancellationToken)
783-
.ConfigureAwait(false);
786+
if (_exceptionDetector.IsCancellation(e, CancellationToken.None))
787+
{
788+
await logger.ConnectionCanceledAsync(this, startTime, stopwatch.Elapsed, cancellationToken)
789+
.ConfigureAwait(false);
790+
}
791+
else
792+
{
793+
await logger.ConnectionErrorAsync(this, e, startTime, stopwatch.Elapsed, errorsExpected, cancellationToken)
794+
.ConfigureAwait(false);
795+
}
784796

785797
throw;
786798
}

0 commit comments

Comments
 (0)