From c891648afa65256c3fb5ba43081189497bf5f71b Mon Sep 17 00:00:00 2001 From: liuhaoyang Date: Sun, 29 Apr 2018 23:10:48 +0800 Subject: [PATCH 1/4] Add NpgsqlDiagnosticListener for NpgsqlCommand --- src/Npgsql/Npgsql.csproj | 13 +- src/Npgsql/NpgsqlCommand.cs | 117 ++++++++++++++++-- .../NpgsqlDiagnosticListenerExtensions.cs | 74 +++++++++++ 3 files changed, 186 insertions(+), 18 deletions(-) create mode 100644 src/Npgsql/NpgsqlDiagnosticListenerExtensions.cs diff --git a/src/Npgsql/Npgsql.csproj b/src/Npgsql/Npgsql.csproj index db51080ed4..11b6f99aa7 100644 --- a/src/Npgsql/Npgsql.csproj +++ b/src/Npgsql/Npgsql.csproj @@ -1,5 +1,4 @@  - Npgsql is the open source .NET data provider for PostgreSQL. Shay Rojansky;Emil Lenngren;Francisco Figueiredo Jr.;Kenji Uno @@ -24,39 +23,33 @@ IOperation - - + - - - - $(DefineConstants);RELEASE;TRACE;OPTIMIZED_CRYPTOGRAPHY true - $(DefineConstants);DEBUG;TRACE;OPTIMIZED_CRYPTOGRAPHY true false - - + \ No newline at end of file diff --git a/src/Npgsql/NpgsqlCommand.cs b/src/Npgsql/NpgsqlCommand.cs index 6ad0d9fb79..199484cce5 100644 --- a/src/Npgsql/NpgsqlCommand.cs +++ b/src/Npgsql/NpgsqlCommand.cs @@ -84,6 +84,8 @@ public sealed class NpgsqlCommand : DbCommand, ICloneable static readonly SingleThreadSynchronizationContext SingleThreadSynchronizationContext = new SingleThreadSynchronizationContext("NpgsqlRemainingAsyncSendWorker"); static readonly NpgsqlLogger Log = NpgsqlLogManager.GetCurrentClassLogger(); + + static readonly DiagnosticListener NpgsqlDiagnosticListener = new DiagnosticListener(NpgsqlDiagnosticListenerExtensions.DiagnosticListenerName); #endregion Fields @@ -1041,11 +1043,35 @@ public override Task ExecuteNonQueryAsync(CancellationToken cancellationTok [MethodImpl(MethodImplOptions.AggressiveInlining)] async Task ExecuteNonQuery(bool async, CancellationToken cancellationToken) { - using (var reader = await ExecuteDbDataReader(CommandBehavior.Default, async, cancellationToken)) + var operationId = NpgsqlDiagnosticListener.WriteCommandBefore(this); + + Exception e = null; + + try + { + using (var reader = await ExecuteDbDataReader(CommandBehavior.Default, async, cancellationToken)) + { + while (async ? await reader.NextResultAsync(cancellationToken) : reader.NextResult()) {} + reader.Close(); + return reader.RecordsAffected; + } + } + catch (Exception exception) { - while (async ? await reader.NextResultAsync(cancellationToken) : reader.NextResult()) {} - reader.Close(); - return reader.RecordsAffected; + e = exception; + throw; + } + finally + { + + if (e != null) + { + NpgsqlDiagnosticListener.WriteCommandError(operationId, this, e); + } + else + { + NpgsqlDiagnosticListener.WriteCommandAfter(operationId, this); + } } } @@ -1083,8 +1109,33 @@ async ValueTask ExecuteScalar(bool async, CancellationToken cancellation var behavior = CommandBehavior.SingleRow; if (!Parameters.HasOutputParameters) behavior |= CommandBehavior.SequentialAccess; - using (var reader = await ExecuteDbDataReader(behavior, async, cancellationToken)) - return reader.Read() && reader.FieldCount != 0 ? reader.GetValue(0) : null; + + var operationId = NpgsqlDiagnosticListener.WriteCommandBefore(this); + + Exception e = null; + + try + { + using (var reader = await ExecuteDbDataReader(behavior, async, cancellationToken)) + return reader.Read() && reader.FieldCount != 0 ? reader.GetValue(0) : null; + } + catch (Exception ex) + { + e = ex; + throw; + } + finally + { + if (e != null) + { + NpgsqlDiagnosticListener.WriteCommandError(operationId, this, e); + } + else + { + NpgsqlDiagnosticListener.WriteCommandAfter(operationId, this); + } + } + } #endregion Execute Scalar @@ -1099,7 +1150,32 @@ async ValueTask ExecuteScalar(bool async, CancellationToken cancellation /// DataReader. /// /// A DbDataReader object. - public new NpgsqlDataReader ExecuteReader() => (NpgsqlDataReader) base.ExecuteReader(); + public new NpgsqlDataReader ExecuteReader() + { + var operationId = NpgsqlDiagnosticListener.WriteCommandBefore(this); + + Exception e = null; + try + { + return (NpgsqlDataReader) base.ExecuteReader(); + } + catch (Exception exception) + { + e = exception; + throw; + } + finally + { + if (e != null) + { + NpgsqlDiagnosticListener.WriteCommandError(operationId, this, e); + } + else + { + NpgsqlDiagnosticListener.WriteCommandAfter(operationId, this); + } + } + } /// /// Executes the CommandText against the Connection, and returns an DbDataReader using one @@ -1110,7 +1186,32 @@ async ValueTask ExecuteScalar(bool async, CancellationToken cancellation /// DataReader. /// /// A DbDataReader object. - public new NpgsqlDataReader ExecuteReader(CommandBehavior behavior) => (NpgsqlDataReader) base.ExecuteReader(behavior); + public new NpgsqlDataReader ExecuteReader(CommandBehavior behavior) + { + var operationId = NpgsqlDiagnosticListener.WriteCommandBefore(this); + + Exception e = null; + try + { + return (NpgsqlDataReader) base.ExecuteReader(behavior); + } + catch (Exception exception) + { + e = exception; + throw; + } + finally + { + if (e != null) + { + NpgsqlDiagnosticListener.WriteCommandError(operationId, this, e); + } + else + { + NpgsqlDiagnosticListener.WriteCommandAfter(operationId, this); + } + } + } /// /// Executes the command text against the connection. diff --git a/src/Npgsql/NpgsqlDiagnosticListenerExtensions.cs b/src/Npgsql/NpgsqlDiagnosticListenerExtensions.cs new file mode 100644 index 0000000000..47e1ecaae3 --- /dev/null +++ b/src/Npgsql/NpgsqlDiagnosticListenerExtensions.cs @@ -0,0 +1,74 @@ +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace Npgsql +{ + internal static class NpgsqlDiagnosticListenerExtensions + { + public const string DiagnosticListenerName = "NpgsqlDiagnosticListener"; + + private const string NpgsqlClientPrefix = "Npgsql."; + + public const string NpgsqlBeforeExecuteCommand = NpgsqlClientPrefix + nameof(WriteCommandBefore); + public const string NpgsqlAfterExecuteCommand = NpgsqlClientPrefix + nameof(WriteCommandAfter); + public const string NpgsqlErrorExecuteCommand = NpgsqlClientPrefix + nameof(WriteCommandError); + + public static Guid WriteCommandBefore(this DiagnosticListener @this, NpgsqlCommand command, [CallerMemberName] string operation = "") + { + if (!@this.IsEnabled(NpgsqlBeforeExecuteCommand)) + { + return Guid.Empty; + } + + var operationId = Guid.NewGuid(); + + @this.Write( + NpgsqlBeforeExecuteCommand, + new + { + OperationId = operationId, + Operation = operation, + ConnectionId = command.Connection?.Connector?.Id, + Command = command + }); + + return operationId; + } + + public static void WriteCommandAfter(this DiagnosticListener @this, Guid operationId, NpgsqlCommand command, [CallerMemberName] string operation = "") + { + if (@this.IsEnabled(NpgsqlAfterExecuteCommand)) + { + @this.Write( + NpgsqlAfterExecuteCommand, + new + { + OperationId = operationId, + Operation = operation, + ConnectionId = command.Connection?.Connector?.Id, + Command = command, + Timestamp = Stopwatch.GetTimestamp() + }); + } + } + + public static void WriteCommandError(this DiagnosticListener @this, Guid operationId, NpgsqlCommand command, Exception ex, [CallerMemberName] string operation = "") + { + if (@this.IsEnabled(NpgsqlErrorExecuteCommand)) + { + @this.Write( + NpgsqlErrorExecuteCommand, + new + { + OperationId = operationId, + Operation = operation, + ConnectionId = command.Connection?.Connector?.Id, + Command = command, + Exception = ex, + Timestamp = Stopwatch.GetTimestamp() + }); + } + } + } +} From 5a2a76289d71e2dfdc2b0ca58d20fcb498aba624 Mon Sep 17 00:00:00 2001 From: liuhaoyang Date: Thu, 3 May 2018 08:41:23 +0800 Subject: [PATCH 2/4] Support DiagnosticListener for NpgsqlConnection & NpgsqlTransaction --- src/Npgsql/NpgsqlConnection.cs | 135 ++++++--- .../NpgsqlDiagnosticListenerExtensions.cs | 270 +++++++++++++++++- src/Npgsql/NpgsqlTransaction.cs | 70 ++++- 3 files changed, 412 insertions(+), 63 deletions(-) diff --git a/src/Npgsql/NpgsqlConnection.cs b/src/Npgsql/NpgsqlConnection.cs index 4e91f10802..23b73fc9a5 100644 --- a/src/Npgsql/NpgsqlConnection.cs +++ b/src/Npgsql/NpgsqlConnection.cs @@ -83,6 +83,8 @@ public sealed class NpgsqlConnection : DbConnection, ICloneable static readonly NpgsqlConnectionStringBuilder DefaultSettings = new NpgsqlConnectionStringBuilder(); + static readonly DiagnosticListener NpgsqlDiagnosticListener = new DiagnosticListener(NpgsqlDiagnosticListenerExtensions.DiagnosticListenerName); + [CanBeNull] ConnectorPool _pool; @@ -222,28 +224,49 @@ Task Open(bool async, CancellationToken cancellationToken) { // This is an optimized path for when a connection can be taken from the pool // with no waiting or I/O + var operationId = NpgsqlDiagnosticListener.WriteConnectionOpenBefore(this); + Exception e = null; + try + { + CheckConnectionClosed(); - CheckConnectionClosed(); - - Log.Trace("Opening connection..."); + Log.Trace("Opening connection..."); - if (_pool == null || Settings.Enlist || !_pool.TryAllocateFast(this, out Connector)) - return OpenLong(async, cancellationToken); + if (_pool == null || Settings.Enlist || !_pool.TryAllocateFast(this, out Connector)) + return OpenLong(async, cancellationToken); - _userFacingConnectionString = _pool.UserFacingConnectionString; + _userFacingConnectionString = _pool.UserFacingConnectionString; - Counters.SoftConnectsPerSecond.Increment(); + Counters.SoftConnectsPerSecond.Increment(); - // Since this pooled connector was opened, global mappings may have - // changed. Bring this up to date if needed. - var mapper = Connector.TypeMapper; - if (mapper.ChangeCounter != TypeMapping.GlobalTypeMapper.Instance.ChangeCounter) - mapper.Reset(); + // Since this pooled connector was opened, global mappings may have + // changed. Bring this up to date if needed. + var mapper = Connector.TypeMapper; + if (mapper.ChangeCounter != TypeMapping.GlobalTypeMapper.Instance.ChangeCounter) + mapper.Reset(); - Debug.Assert(Connector.Connection != null, "Open done but connector not set on Connection"); - Log.Debug("Connection opened", Connector.Id); - OnStateChange(new StateChangeEventArgs(ConnectionState.Closed, ConnectionState.Open)); - return PGUtil.CompletedTask; + Debug.Assert(Connector.Connection != null, "Open done but connector not set on Connection"); + Log.Debug("Connection opened", Connector.Id); + OnStateChange(new StateChangeEventArgs(ConnectionState.Closed, ConnectionState.Open)); + return PGUtil.CompletedTask; + } + catch (Exception ex) + { + e = ex; + throw; + } + finally + { + if (e != null) + { + NpgsqlDiagnosticListener.WriteConnectionOpenError(operationId, this, e); + } + else + { + NpgsqlDiagnosticListener.WriteConnectionOpenAfter(operationId, this); + } + } + } async Task OpenLong(bool async, CancellationToken cancellationToken) @@ -605,44 +628,66 @@ public override void EnlistTransaction(Transaction transaction) internal void Close(bool wasBroken) { - if (Connector == null) - return; - var connectorId = Connector.Id; - Log.Trace("Closing connection...", connectorId); - _wasBroken = wasBroken; + var operationId = NpgsqlDiagnosticListener.WriteConnectionCloseBefore(this); + + Exception e = null; + try + { + if (Connector == null) + return; + var connectorId = Connector.Id; + Log.Trace("Closing connection...", connectorId); + _wasBroken = wasBroken; - Connector.CloseOngoingOperations(); + Connector.CloseOngoingOperations(); - if (Settings.Pooling) - { - if (EnlistedTransaction == null) - _pool.Release(Connector); - else + if (Settings.Pooling) + { + if (EnlistedTransaction == null) + _pool.Release(Connector); + else + { + // A System.Transactions transaction is still in progress, we need to wait for it to complete. + // Close the connection and disconnect it from the resource manager but leave the connector + // in a enlisted pending list in the pool. + _pool.AddPendingEnlistedConnector(Connector, EnlistedTransaction); + Connector.Connection = null; + EnlistedTransaction = null; + } + } + else // Non-pooled connection { - // A System.Transactions transaction is still in progress, we need to wait for it to complete. - // Close the connection and disconnect it from the resource manager but leave the connector - // in a enlisted pending list in the pool. - _pool.AddPendingEnlistedConnector(Connector, EnlistedTransaction); + if (EnlistedTransaction == null) + Connector.Close(); + // If a non-pooled connection is being closed but is enlisted in an ongoing + // TransactionScope, simply detach the connector from the connection and leave + // it open. It will be closed when the TransactionScope is disposed. Connector.Connection = null; EnlistedTransaction = null; } - } - else // Non-pooled connection - { - if (EnlistedTransaction == null) - Connector.Close(); - // If a non-pooled connection is being closed but is enlisted in an ongoing - // TransactionScope, simply detach the connector from the connection and leave - // it open. It will be closed when the TransactionScope is disposed. - Connector.Connection = null; - EnlistedTransaction = null; - } - Log.Debug("Connection closed", connectorId); + Log.Debug("Connection closed", connectorId); - Connector = null; + Connector = null; - OnStateChange(OpenToClosedEventArgs); + OnStateChange(OpenToClosedEventArgs); + } + catch (Exception exception) + { + e = exception; + throw; + } + finally + { + if (e != null) + { + NpgsqlDiagnosticListener.WriteConnectionCloseError(operationId, this, e); + } + else + { + NpgsqlDiagnosticListener.WriteConnectionCloseAfter(operationId, this); + } + } } /// diff --git a/src/Npgsql/NpgsqlDiagnosticListenerExtensions.cs b/src/Npgsql/NpgsqlDiagnosticListenerExtensions.cs index 47e1ecaae3..b8ca168bfa 100644 --- a/src/Npgsql/NpgsqlDiagnosticListenerExtensions.cs +++ b/src/Npgsql/NpgsqlDiagnosticListenerExtensions.cs @@ -1,6 +1,7 @@ using System; using System.Diagnostics; using System.Runtime.CompilerServices; +using System.Data; namespace Npgsql { @@ -8,19 +9,35 @@ internal static class NpgsqlDiagnosticListenerExtensions { public const string DiagnosticListenerName = "NpgsqlDiagnosticListener"; - private const string NpgsqlClientPrefix = "Npgsql."; - + const string NpgsqlClientPrefix = "Npgsql."; + public const string NpgsqlBeforeExecuteCommand = NpgsqlClientPrefix + nameof(WriteCommandBefore); public const string NpgsqlAfterExecuteCommand = NpgsqlClientPrefix + nameof(WriteCommandAfter); public const string NpgsqlErrorExecuteCommand = NpgsqlClientPrefix + nameof(WriteCommandError); - + + public const string NpgsqlBeforeOpenConnection = NpgsqlClientPrefix + nameof(WriteConnectionOpenBefore); + public const string NpgsqlAfterOpenConnection = NpgsqlClientPrefix + nameof(WriteConnectionOpenAfter); + public const string NpgsqlErrorOpenConnection = NpgsqlClientPrefix + nameof(WriteConnectionOpenError); + + public const string NpgsqlBeforeCloseConnection = NpgsqlClientPrefix + nameof(WriteConnectionCloseBefore); + public const string NpgsqlAfterCloseConnection = NpgsqlClientPrefix + nameof(WriteConnectionCloseAfter); + public const string NpgsqlErrorCloseConnection = NpgsqlClientPrefix + nameof(WriteConnectionCloseError); + + public const string NpgsqlBeforeCommitTransaction = NpgsqlClientPrefix + nameof(WriteTransactionCommitBefore); + public const string NpgsqlAfterCommitTransaction = NpgsqlClientPrefix + nameof(WriteTransactionCommitAfter); + public const string NpgsqlErrorCommitTransaction = NpgsqlClientPrefix + nameof(WriteTransactionCommitError); + + public const string NpgsqlBeforeRollbackTransaction = NpgsqlClientPrefix + nameof(WriteTransactionRollbackBefore); + public const string NpgsqlAfterRollbackTransaction = NpgsqlClientPrefix + nameof(WriteTransactionRollbackAfter); + public const string NpgsqlErrorRollbackTransaction = NpgsqlClientPrefix + nameof(WriteTransactionRollbackError); + public static Guid WriteCommandBefore(this DiagnosticListener @this, NpgsqlCommand command, [CallerMemberName] string operation = "") { if (!@this.IsEnabled(NpgsqlBeforeExecuteCommand)) { return Guid.Empty; } - + var operationId = Guid.NewGuid(); @this.Write( @@ -35,7 +52,7 @@ public static Guid WriteCommandBefore(this DiagnosticListener @this, NpgsqlComma return operationId; } - + public static void WriteCommandAfter(this DiagnosticListener @this, Guid operationId, NpgsqlCommand command, [CallerMemberName] string operation = "") { if (@this.IsEnabled(NpgsqlAfterExecuteCommand)) @@ -52,7 +69,7 @@ public static void WriteCommandAfter(this DiagnosticListener @this, Guid operati }); } } - + public static void WriteCommandError(this DiagnosticListener @this, Guid operationId, NpgsqlCommand command, Exception ex, [CallerMemberName] string operation = "") { if (@this.IsEnabled(NpgsqlErrorExecuteCommand)) @@ -70,5 +87,246 @@ public static void WriteCommandError(this DiagnosticListener @this, Guid operati }); } } + + public static Guid WriteConnectionOpenBefore(this DiagnosticListener @this, NpgsqlConnection connection, [CallerMemberName] string operation = "") + { + if (@this.IsEnabled(NpgsqlBeforeOpenConnection)) + { + Guid operationId = Guid.NewGuid(); + + @this.Write( + NpgsqlBeforeOpenConnection, + new + { + OperationId = operationId, + Operation = operation, + Connection = connection, + Timestamp = Stopwatch.GetTimestamp() + }); + + return operationId; + } + else + return Guid.Empty; + } + + public static void WriteConnectionOpenAfter(this DiagnosticListener @this, Guid operationId, NpgsqlConnection connection, [CallerMemberName] string operation = "") + { + if (@this.IsEnabled(NpgsqlAfterOpenConnection)) + { + @this.Write( + NpgsqlAfterOpenConnection, + new + { + OperationId = operationId, + Operation = operation, + ConnectionId = connection.Connector.Id, + Connection = connection, + Timestamp = Stopwatch.GetTimestamp() + }); + } + } + + public static void WriteConnectionOpenError(this DiagnosticListener @this, Guid operationId, NpgsqlConnection sqlConnection, Exception ex, + [CallerMemberName] string operation = "") + { + if (@this.IsEnabled(NpgsqlErrorOpenConnection)) + { + @this.Write( + NpgsqlErrorOpenConnection, + new + { + OperationId = operationId, + Operation = operation, + ConnectionId = sqlConnection.Connector.Id, + Connection = sqlConnection, + Exception = ex, + Timestamp = Stopwatch.GetTimestamp() + }); + } + } + + public static Guid WriteConnectionCloseBefore(this DiagnosticListener @this, NpgsqlConnection connection, [CallerMemberName] string operation = "") + { + if (@this.IsEnabled(NpgsqlBeforeCloseConnection)) + { + Guid operationId = Guid.NewGuid(); + + @this.Write( + NpgsqlBeforeCloseConnection, + new + { + OperationId = operationId, + Operation = operation, + ConnectionId = connection.Connector.Id, + Connection = connection, + Timestamp = Stopwatch.GetTimestamp() + }); + + return operationId; + } + else + return Guid.Empty; + } + + public static void WriteConnectionCloseAfter(this DiagnosticListener @this, Guid operationId, NpgsqlConnection connection, [CallerMemberName] string operation = "") + { + if (@this.IsEnabled(NpgsqlAfterCloseConnection)) + { + @this.Write( + NpgsqlAfterCloseConnection, + new + { + OperationId = operationId, + Operation = operation, + Connection = connection, + Timestamp = Stopwatch.GetTimestamp() + }); + } + } + + public static void WriteConnectionCloseError(this DiagnosticListener @this, Guid operationId, NpgsqlConnection connection, Exception ex, + [CallerMemberName] string operation = "") + { + if (@this.IsEnabled(NpgsqlErrorCloseConnection)) + { + @this.Write( + NpgsqlErrorCloseConnection, + new + { + OperationId = operationId, + Operation = operation, + Connection = connection, + Exception = ex, + Timestamp = Stopwatch.GetTimestamp() + }); + } + } + + public static Guid WriteTransactionCommitBefore(this DiagnosticListener @this, IsolationLevel isolationLevel, NpgsqlConnection connection, + [CallerMemberName] string operation = "") + { + if (@this.IsEnabled(NpgsqlBeforeCommitTransaction)) + { + Guid operationId = Guid.NewGuid(); + + @this.Write( + NpgsqlBeforeCommitTransaction, + new + { + OperationId = operationId, + Operation = operation, + IsolationLevel = isolationLevel, + Connection = connection, + Timestamp = Stopwatch.GetTimestamp() + }); + + return operationId; + } + else + return Guid.Empty; + } + + public static void WriteTransactionCommitAfter(this DiagnosticListener @this, Guid operationId, IsolationLevel isolationLevel, NpgsqlConnection connection, + [CallerMemberName] string operation = "") + { + if (@this.IsEnabled(NpgsqlAfterCommitTransaction)) + { + @this.Write( + NpgsqlAfterCommitTransaction, + new + { + OperationId = operationId, + Operation = operation, + IsolationLevel = isolationLevel, + Connection = connection, + Timestamp = Stopwatch.GetTimestamp() + }); + } + } + + public static void WriteTransactionCommitError(this DiagnosticListener @this, Guid operationId, IsolationLevel isolationLevel, NpgsqlConnection connection, Exception ex, + [CallerMemberName] string operation = "") + { + if (@this.IsEnabled(NpgsqlErrorCommitTransaction)) + { + @this.Write( + NpgsqlErrorCommitTransaction, + new + { + OperationId = operationId, + Operation = operation, + IsolationLevel = isolationLevel, + Connection = connection, + Exception = ex, + Timestamp = Stopwatch.GetTimestamp() + }); + } + } + + public static Guid WriteTransactionRollbackBefore(this DiagnosticListener @this, IsolationLevel isolationLevel, NpgsqlConnection connection, string transactionName, + [CallerMemberName] string operation = "") + { + if (@this.IsEnabled(NpgsqlBeforeRollbackTransaction)) + { + Guid operationId = Guid.NewGuid(); + + @this.Write( + NpgsqlBeforeRollbackTransaction, + new + { + OperationId = operationId, + Operation = operation, + IsolationLevel = isolationLevel, + Connection = connection, + TransactionName = transactionName, + Timestamp = Stopwatch.GetTimestamp() + }); + + return operationId; + } + else + return Guid.Empty; + } + + public static void WriteTransactionRollbackAfter(this DiagnosticListener @this, Guid operationId, IsolationLevel isolationLevel, NpgsqlConnection connection, + string transactionName, [CallerMemberName] string operation = "") + { + if (@this.IsEnabled(NpgsqlAfterRollbackTransaction)) + { + @this.Write( + NpgsqlAfterRollbackTransaction, + new + { + OperationId = operationId, + Operation = operation, + IsolationLevel = isolationLevel, + Connection = connection, + TransactionName = transactionName, + Timestamp = Stopwatch.GetTimestamp() + }); + } + } + + public static void WriteTransactionRollbackError(this DiagnosticListener @this, Guid operationId, IsolationLevel isolationLevel, NpgsqlConnection connection, + string transactionName, Exception ex, [CallerMemberName] string operation = "") + { + if (@this.IsEnabled(NpgsqlErrorRollbackTransaction)) + { + @this.Write( + NpgsqlErrorRollbackTransaction, + new + { + OperationId = operationId, + Operation = operation, + IsolationLevel = isolationLevel, + Connection = connection, + TransactionName = transactionName, + Exception = ex, + Timestamp = Stopwatch.GetTimestamp() + }); + } + } } } + diff --git a/src/Npgsql/NpgsqlTransaction.cs b/src/Npgsql/NpgsqlTransaction.cs index e51ed4c456..df7d77de45 100644 --- a/src/Npgsql/NpgsqlTransaction.cs +++ b/src/Npgsql/NpgsqlTransaction.cs @@ -83,6 +83,8 @@ public override IsolationLevel IsolationLevel static readonly NpgsqlLogger Log = NpgsqlLogManager.GetCurrentClassLogger(); + static readonly DiagnosticListener NpgsqlDiagnosticListener = new DiagnosticListener(NpgsqlDiagnosticListenerExtensions.DiagnosticListenerName); + const IsolationLevel DefaultIsolationLevel = IsolationLevel.ReadCommitted; #endregion @@ -145,16 +147,38 @@ internal NpgsqlTransaction(NpgsqlConnection conn, IsolationLevel isolationLevel async Task Commit(bool async) { - CheckReady(); + Exception e = null; + var operationId = NpgsqlDiagnosticListener.WriteTransactionCommitBefore(_isolationLevel, _connector.Connection); - if (!_connector.DatabaseInfo.SupportsTransactions) - return; + try + { + CheckReady(); - using (_connector.StartUserAction()) + if (!_connector.DatabaseInfo.SupportsTransactions) + return; + + using (_connector.StartUserAction()) + { + Log.Debug("Committing transaction", _connector.Id); + await _connector.ExecuteInternalCommand(PregeneratedMessage.CommitTransaction, async); + Clear(); + } + } + catch (Exception exception) { - Log.Debug("Committing transaction", _connector.Id); - await _connector.ExecuteInternalCommand(PregeneratedMessage.CommitTransaction, async); - Clear(); + e = exception; + throw; + } + finally + { + if (e != null) + { + NpgsqlDiagnosticListener.WriteTransactionCommitError(operationId, _isolationLevel, _connector.Connection, e); + } + else + { + NpgsqlDiagnosticListener.WriteTransactionCommitAfter(operationId, _isolationLevel, _connector.Connection); + } } } @@ -186,11 +210,33 @@ public Task CommitAsync(CancellationToken cancellationToken) async Task Rollback(bool async) { - CheckReady(); - if (!_connector.DatabaseInfo.SupportsTransactions) - return; - await _connector.Rollback(async); - Clear(); + Exception e = null; + var operationId = NpgsqlDiagnosticListener.WriteTransactionRollbackBefore(_isolationLevel, _connector.Connection, null); + + try + { + CheckReady(); + if (!_connector.DatabaseInfo.SupportsTransactions) + return; + await _connector.Rollback(async); + Clear(); + } + catch (Exception exception) + { + e = exception; + throw; + } + finally + { + if (e != null) + { + NpgsqlDiagnosticListener.WriteTransactionRollbackError(operationId, _isolationLevel, _connector.Connection, null, e); + } + else + { + NpgsqlDiagnosticListener.WriteTransactionRollbackAfter(operationId, _isolationLevel, _connector.Connection, null); + } + } } /// From fde4c731b48997bf16333fce2596ebcd7c0b232a Mon Sep 17 00:00:00 2001 From: liuhaoyang Date: Thu, 3 May 2018 10:26:40 +0800 Subject: [PATCH 3/4] Fix ci --- src/Npgsql/NpgsqlTransaction.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Npgsql/NpgsqlTransaction.cs b/src/Npgsql/NpgsqlTransaction.cs index df7d77de45..056147d73c 100644 --- a/src/Npgsql/NpgsqlTransaction.cs +++ b/src/Npgsql/NpgsqlTransaction.cs @@ -148,7 +148,7 @@ internal NpgsqlTransaction(NpgsqlConnection conn, IsolationLevel isolationLevel async Task Commit(bool async) { Exception e = null; - var operationId = NpgsqlDiagnosticListener.WriteTransactionCommitBefore(_isolationLevel, _connector.Connection); + var operationId = NpgsqlDiagnosticListener.WriteTransactionCommitBefore(_isolationLevel, Connection); try { @@ -173,11 +173,11 @@ async Task Commit(bool async) { if (e != null) { - NpgsqlDiagnosticListener.WriteTransactionCommitError(operationId, _isolationLevel, _connector.Connection, e); + NpgsqlDiagnosticListener.WriteTransactionCommitError(operationId, _isolationLevel, Connection, e); } else { - NpgsqlDiagnosticListener.WriteTransactionCommitAfter(operationId, _isolationLevel, _connector.Connection); + NpgsqlDiagnosticListener.WriteTransactionCommitAfter(operationId, _isolationLevel, Connection); } } } @@ -211,7 +211,7 @@ public Task CommitAsync(CancellationToken cancellationToken) async Task Rollback(bool async) { Exception e = null; - var operationId = NpgsqlDiagnosticListener.WriteTransactionRollbackBefore(_isolationLevel, _connector.Connection, null); + var operationId = NpgsqlDiagnosticListener.WriteTransactionRollbackBefore(_isolationLevel, Connection, null); try { @@ -230,11 +230,11 @@ async Task Rollback(bool async) { if (e != null) { - NpgsqlDiagnosticListener.WriteTransactionRollbackError(operationId, _isolationLevel, _connector.Connection, null, e); + NpgsqlDiagnosticListener.WriteTransactionRollbackError(operationId, _isolationLevel, Connection, null, e); } else { - NpgsqlDiagnosticListener.WriteTransactionRollbackAfter(operationId, _isolationLevel, _connector.Connection, null); + NpgsqlDiagnosticListener.WriteTransactionRollbackAfter(operationId, _isolationLevel, Connection, null); } } } From 0e3c914dc5c53cf5c7bdd83f14bbb32feb647c27 Mon Sep 17 00:00:00 2001 From: liuhaoyang Date: Fri, 4 May 2018 14:41:35 +0800 Subject: [PATCH 4/4] Revise according to the guideline. --- src/Npgsql/NpgsqlCommand.cs | 115 +------ src/Npgsql/NpgsqlConnection.cs | 40 +-- .../NpgsqlDiagnosticListenerExtensions.cs | 288 +++++------------- src/Npgsql/NpgsqlTransaction.cs | 42 +-- 4 files changed, 106 insertions(+), 379 deletions(-) diff --git a/src/Npgsql/NpgsqlCommand.cs b/src/Npgsql/NpgsqlCommand.cs index f368d5aaa7..28cd2cd25c 100644 --- a/src/Npgsql/NpgsqlCommand.cs +++ b/src/Npgsql/NpgsqlCommand.cs @@ -85,7 +85,7 @@ public sealed class NpgsqlCommand : DbCommand, ICloneable static readonly NpgsqlLogger Log = NpgsqlLogManager.GetCurrentClassLogger(); - static readonly DiagnosticListener NpgsqlDiagnosticListener = new DiagnosticListener(NpgsqlDiagnosticListenerExtensions.DiagnosticListenerName); + static readonly DiagnosticListener NpgsqlDiagnosticListener = new DiagnosticListener(NpgsqlDiagnosticListenerExtensions.CommandDiagnosticListenerName); #endregion Fields @@ -1043,35 +1043,11 @@ public override Task ExecuteNonQueryAsync(CancellationToken cancellationTok [MethodImpl(MethodImplOptions.AggressiveInlining)] async Task ExecuteNonQuery(bool async, CancellationToken cancellationToken) { - var operationId = NpgsqlDiagnosticListener.WriteCommandBefore(this); - - Exception e = null; - - try - { - using (var reader = await ExecuteDbDataReader(CommandBehavior.Default, async, cancellationToken)) - { - while (async ? await reader.NextResultAsync(cancellationToken) : reader.NextResult()) {} - reader.Close(); - return reader.RecordsAffected; - } - } - catch (Exception exception) - { - e = exception; - throw; - } - finally + using (var reader = await ExecuteDbDataReader(CommandBehavior.Default, async, cancellationToken)) { - - if (e != null) - { - NpgsqlDiagnosticListener.WriteCommandError(operationId, this, e); - } - else - { - NpgsqlDiagnosticListener.WriteCommandAfter(operationId, this); - } + while (async ? await reader.NextResultAsync(cancellationToken) : reader.NextResult()){ } + reader.Close(); + return reader.RecordsAffected; } } @@ -1110,32 +1086,8 @@ async ValueTask ExecuteScalar(bool async, CancellationToken cancellation if (!Parameters.HasOutputParameters) behavior |= CommandBehavior.SequentialAccess; - var operationId = NpgsqlDiagnosticListener.WriteCommandBefore(this); - - Exception e = null; - - try - { - using (var reader = await ExecuteDbDataReader(behavior, async, cancellationToken)) - return reader.Read() && reader.FieldCount != 0 ? reader.GetValue(0) : null; - } - catch (Exception ex) - { - e = ex; - throw; - } - finally - { - if (e != null) - { - NpgsqlDiagnosticListener.WriteCommandError(operationId, this, e); - } - else - { - NpgsqlDiagnosticListener.WriteCommandAfter(operationId, this); - } - } - + using (var reader = await ExecuteDbDataReader(behavior, async, cancellationToken)) + return reader.Read() && reader.FieldCount != 0 ? reader.GetValue(0) : null; } #endregion Execute Scalar @@ -1152,29 +1104,7 @@ async ValueTask ExecuteScalar(bool async, CancellationToken cancellation /// A DbDataReader object. public new NpgsqlDataReader ExecuteReader() { - var operationId = NpgsqlDiagnosticListener.WriteCommandBefore(this); - - Exception e = null; - try - { - return (NpgsqlDataReader) base.ExecuteReader(); - } - catch (Exception exception) - { - e = exception; - throw; - } - finally - { - if (e != null) - { - NpgsqlDiagnosticListener.WriteCommandError(operationId, this, e); - } - else - { - NpgsqlDiagnosticListener.WriteCommandAfter(operationId, this); - } - } + return (NpgsqlDataReader)base.ExecuteReader(); } /// @@ -1188,29 +1118,7 @@ async ValueTask ExecuteScalar(bool async, CancellationToken cancellation /// A DbDataReader object. public new NpgsqlDataReader ExecuteReader(CommandBehavior behavior) { - var operationId = NpgsqlDiagnosticListener.WriteCommandBefore(this); - - Exception e = null; - try - { - return (NpgsqlDataReader) base.ExecuteReader(behavior); - } - catch (Exception exception) - { - e = exception; - throw; - } - finally - { - if (e != null) - { - NpgsqlDiagnosticListener.WriteCommandError(operationId, this, e); - } - else - { - NpgsqlDiagnosticListener.WriteCommandAfter(operationId, this); - } - } + return (NpgsqlDataReader)base.ExecuteReader(behavior); } /// @@ -1238,6 +1146,7 @@ async ValueTask ExecuteDbDataReader(CommandBehavior behavior, bool connector.StartUserAction(this); try { + NpgsqlDiagnosticListener.ExecuteCommandStart(this); using (cancellationToken.Register(cmd => ((NpgsqlCommand)cmd).Cancel(), this)) { ValidateParameters(); @@ -1320,11 +1229,13 @@ async ValueTask ExecuteDbDataReader(CommandBehavior behavior, bool await reader.NextResultAsync(cancellationToken); else reader.NextResult(); + NpgsqlDiagnosticListener.ExecuteCommandStop(this); return reader; } } - catch + catch(Exception exception) { + NpgsqlDiagnosticListener.ExecuteCommandError(this, exception); State = CommandState.Idle; Connection.Connector?.EndUserAction(); diff --git a/src/Npgsql/NpgsqlConnection.cs b/src/Npgsql/NpgsqlConnection.cs index 9eb358e5de..2a300aa9b2 100644 --- a/src/Npgsql/NpgsqlConnection.cs +++ b/src/Npgsql/NpgsqlConnection.cs @@ -83,7 +83,7 @@ public sealed class NpgsqlConnection : DbConnection, ICloneable static readonly NpgsqlConnectionStringBuilder DefaultSettings = new NpgsqlConnectionStringBuilder(); - static readonly DiagnosticListener NpgsqlDiagnosticListener = new DiagnosticListener(NpgsqlDiagnosticListenerExtensions.DiagnosticListenerName); + static readonly DiagnosticListener NpgsqlDiagnosticListener = new DiagnosticListener(NpgsqlDiagnosticListenerExtensions.ConnectionDiagnosticListenerName); [CanBeNull] ConnectorPool _pool; @@ -224,10 +224,10 @@ Task Open(bool async, CancellationToken cancellationToken) { // This is an optimized path for when a connection can be taken from the pool // with no waiting or I/O - var operationId = NpgsqlDiagnosticListener.WriteConnectionOpenBefore(this); - Exception e = null; try { + NpgsqlDiagnosticListener.OpenConnectionStart(this); + CheckConnectionClosed(); Log.Trace("Opening connection..."); @@ -248,25 +248,15 @@ Task Open(bool async, CancellationToken cancellationToken) Debug.Assert(Connector.Connection != null, "Open done but connector not set on Connection"); Log.Debug("Connection opened", Connector.Id); OnStateChange(new StateChangeEventArgs(ConnectionState.Closed, ConnectionState.Open)); + NpgsqlDiagnosticListener.OpenConnectionStop(this); return PGUtil.CompletedTask; } catch (Exception ex) { - e = ex; + NpgsqlDiagnosticListener.WriteConnectionOpenError(this, ex); throw; } - finally - { - if (e != null) - { - NpgsqlDiagnosticListener.WriteConnectionOpenError(operationId, this, e); - } - else - { - NpgsqlDiagnosticListener.WriteConnectionOpenAfter(operationId, this); - } - } - + async Task OpenLong() { CheckConnectionClosed(); @@ -629,13 +619,12 @@ public override void EnlistTransaction(Transaction transaction) internal void Close(bool wasBroken) { - var operationId = NpgsqlDiagnosticListener.WriteConnectionCloseBefore(this); - Exception e = null; try { if (Connector == null) return; + NpgsqlDiagnosticListener.CloseConnectionStart(this); var connectorId = Connector.Id; Log.Trace("Closing connection...", connectorId); _wasBroken = wasBroken; @@ -672,23 +661,14 @@ internal void Close(bool wasBroken) Connector = null; OnStateChange(OpenToClosedEventArgs); + + NpgsqlDiagnosticListener.CloseConnectionStop(this); } catch (Exception exception) { - e = exception; + NpgsqlDiagnosticListener.CloseConnectionError(this, exception); throw; } - finally - { - if (e != null) - { - NpgsqlDiagnosticListener.WriteConnectionCloseError(operationId, this, e); - } - else - { - NpgsqlDiagnosticListener.WriteConnectionCloseAfter(operationId, this); - } - } } /// diff --git a/src/Npgsql/NpgsqlDiagnosticListenerExtensions.cs b/src/Npgsql/NpgsqlDiagnosticListenerExtensions.cs index b8ca168bfa..693682d88f 100644 --- a/src/Npgsql/NpgsqlDiagnosticListenerExtensions.cs +++ b/src/Npgsql/NpgsqlDiagnosticListenerExtensions.cs @@ -5,325 +5,181 @@ namespace Npgsql { - internal static class NpgsqlDiagnosticListenerExtensions + static class NpgsqlDiagnosticListenerExtensions { - public const string DiagnosticListenerName = "NpgsqlDiagnosticListener"; + public const string CommandDiagnosticListenerName = "Npgsql.Command"; + public const string ConnectionDiagnosticListenerName = "Npgsql.Connection"; + public const string TransactionDiagnosticListenerName = "Npgsql.Transaction"; - const string NpgsqlClientPrefix = "Npgsql."; + public const string NpgsqlExecuteCommandStart = nameof(ExecuteCommandStart); + public const string NpgsqlExecuteCommandStop = nameof(ExecuteCommandStop); + public const string NpgsqlExecuteCommandError = nameof(ExecuteCommandError); - public const string NpgsqlBeforeExecuteCommand = NpgsqlClientPrefix + nameof(WriteCommandBefore); - public const string NpgsqlAfterExecuteCommand = NpgsqlClientPrefix + nameof(WriteCommandAfter); - public const string NpgsqlErrorExecuteCommand = NpgsqlClientPrefix + nameof(WriteCommandError); + public const string NpgsqlOpenConnectionStart = nameof(OpenConnectionStart); + public const string NpgsqlOpenConnectionStop = nameof(OpenConnectionStop); + public const string NpgsqlOpenConnectionError = nameof(WriteConnectionOpenError); - public const string NpgsqlBeforeOpenConnection = NpgsqlClientPrefix + nameof(WriteConnectionOpenBefore); - public const string NpgsqlAfterOpenConnection = NpgsqlClientPrefix + nameof(WriteConnectionOpenAfter); - public const string NpgsqlErrorOpenConnection = NpgsqlClientPrefix + nameof(WriteConnectionOpenError); + public const string NpgsqlCloseConnectionStart = nameof(CloseConnectionStart); + public const string NpgsqlCloseConnectionStop = nameof(CloseConnectionStop); + public const string NpgsqlCloseConnectionError = nameof(CloseConnectionError); - public const string NpgsqlBeforeCloseConnection = NpgsqlClientPrefix + nameof(WriteConnectionCloseBefore); - public const string NpgsqlAfterCloseConnection = NpgsqlClientPrefix + nameof(WriteConnectionCloseAfter); - public const string NpgsqlErrorCloseConnection = NpgsqlClientPrefix + nameof(WriteConnectionCloseError); + public const string NpgsqlCommitTransactionStart = nameof(CommitTransactionStart); + public const string NpgsqlCommitTransactionStop = nameof(CommitTransactionStop); + public const string NpgsqlCommitTransactionError = nameof(CommitTransactionError); - public const string NpgsqlBeforeCommitTransaction = NpgsqlClientPrefix + nameof(WriteTransactionCommitBefore); - public const string NpgsqlAfterCommitTransaction = NpgsqlClientPrefix + nameof(WriteTransactionCommitAfter); - public const string NpgsqlErrorCommitTransaction = NpgsqlClientPrefix + nameof(WriteTransactionCommitError); + public const string NpgsqlRollbackTransactionStart = nameof(RollbackTransactionStart); + public const string NpgsqlRollbackTransactionStop = nameof(RollbackTransactionStop); + public const string NpgsqlRollbackTransactionError = nameof(RollbackTransactionError); - public const string NpgsqlBeforeRollbackTransaction = NpgsqlClientPrefix + nameof(WriteTransactionRollbackBefore); - public const string NpgsqlAfterRollbackTransaction = NpgsqlClientPrefix + nameof(WriteTransactionRollbackAfter); - public const string NpgsqlErrorRollbackTransaction = NpgsqlClientPrefix + nameof(WriteTransactionRollbackError); - - public static Guid WriteCommandBefore(this DiagnosticListener @this, NpgsqlCommand command, [CallerMemberName] string operation = "") + public static void ExecuteCommandStart(this DiagnosticListener @this, NpgsqlCommand command) { - if (!@this.IsEnabled(NpgsqlBeforeExecuteCommand)) + if (!@this.IsEnabled(NpgsqlExecuteCommandStart)) { - return Guid.Empty; + return; } - var operationId = Guid.NewGuid(); - - @this.Write( - NpgsqlBeforeExecuteCommand, - new - { - OperationId = operationId, - Operation = operation, - ConnectionId = command.Connection?.Connector?.Id, - Command = command - }); - - return operationId; + @this.Write(NpgsqlExecuteCommandStart, command); } - public static void WriteCommandAfter(this DiagnosticListener @this, Guid operationId, NpgsqlCommand command, [CallerMemberName] string operation = "") + public static void ExecuteCommandStop(this DiagnosticListener @this, NpgsqlCommand command) { - if (@this.IsEnabled(NpgsqlAfterExecuteCommand)) + if (@this.IsEnabled(NpgsqlExecuteCommandStop)) { - @this.Write( - NpgsqlAfterExecuteCommand, - new - { - OperationId = operationId, - Operation = operation, - ConnectionId = command.Connection?.Connector?.Id, - Command = command, - Timestamp = Stopwatch.GetTimestamp() - }); + @this.Write(NpgsqlExecuteCommandStop, command); } } - public static void WriteCommandError(this DiagnosticListener @this, Guid operationId, NpgsqlCommand command, Exception ex, [CallerMemberName] string operation = "") + public static void ExecuteCommandError(this DiagnosticListener @this, NpgsqlCommand command, Exception ex) { - if (@this.IsEnabled(NpgsqlErrorExecuteCommand)) + if (@this.IsEnabled(NpgsqlExecuteCommandError)) { @this.Write( - NpgsqlErrorExecuteCommand, + NpgsqlExecuteCommandError, new { - OperationId = operationId, - Operation = operation, - ConnectionId = command.Connection?.Connector?.Id, Command = command, - Exception = ex, - Timestamp = Stopwatch.GetTimestamp() + Exception = ex }); } } - public static Guid WriteConnectionOpenBefore(this DiagnosticListener @this, NpgsqlConnection connection, [CallerMemberName] string operation = "") + public static void OpenConnectionStart(this DiagnosticListener @this, NpgsqlConnection connection) { - if (@this.IsEnabled(NpgsqlBeforeOpenConnection)) + if (@this.IsEnabled(NpgsqlOpenConnectionStart)) { - Guid operationId = Guid.NewGuid(); - - @this.Write( - NpgsqlBeforeOpenConnection, - new - { - OperationId = operationId, - Operation = operation, - Connection = connection, - Timestamp = Stopwatch.GetTimestamp() - }); - - return operationId; + @this.Write(NpgsqlOpenConnectionStart, connection); } - else - return Guid.Empty; } - public static void WriteConnectionOpenAfter(this DiagnosticListener @this, Guid operationId, NpgsqlConnection connection, [CallerMemberName] string operation = "") + public static void OpenConnectionStop(this DiagnosticListener @this, NpgsqlConnection connection) { - if (@this.IsEnabled(NpgsqlAfterOpenConnection)) + if (@this.IsEnabled(NpgsqlOpenConnectionStop)) { - @this.Write( - NpgsqlAfterOpenConnection, - new - { - OperationId = operationId, - Operation = operation, - ConnectionId = connection.Connector.Id, - Connection = connection, - Timestamp = Stopwatch.GetTimestamp() - }); + @this.Write(NpgsqlOpenConnectionStop, connection); } } - public static void WriteConnectionOpenError(this DiagnosticListener @this, Guid operationId, NpgsqlConnection sqlConnection, Exception ex, - [CallerMemberName] string operation = "") + public static void WriteConnectionOpenError(this DiagnosticListener @this, NpgsqlConnection sqlConnection, Exception ex) { - if (@this.IsEnabled(NpgsqlErrorOpenConnection)) + if (@this.IsEnabled(NpgsqlOpenConnectionError)) { @this.Write( - NpgsqlErrorOpenConnection, + NpgsqlOpenConnectionError, new { - OperationId = operationId, - Operation = operation, - ConnectionId = sqlConnection.Connector.Id, Connection = sqlConnection, - Exception = ex, - Timestamp = Stopwatch.GetTimestamp() + Exception = ex }); } } - public static Guid WriteConnectionCloseBefore(this DiagnosticListener @this, NpgsqlConnection connection, [CallerMemberName] string operation = "") + public static void CloseConnectionStart(this DiagnosticListener @this, NpgsqlConnection connection) { - if (@this.IsEnabled(NpgsqlBeforeCloseConnection)) + if (@this.IsEnabled(NpgsqlCloseConnectionStart)) { - Guid operationId = Guid.NewGuid(); - - @this.Write( - NpgsqlBeforeCloseConnection, - new - { - OperationId = operationId, - Operation = operation, - ConnectionId = connection.Connector.Id, - Connection = connection, - Timestamp = Stopwatch.GetTimestamp() - }); - return operationId; + @this.Write(NpgsqlCloseConnectionStart, connection); } - else - return Guid.Empty; } - public static void WriteConnectionCloseAfter(this DiagnosticListener @this, Guid operationId, NpgsqlConnection connection, [CallerMemberName] string operation = "") + public static void CloseConnectionStop(this DiagnosticListener @this, NpgsqlConnection connection) { - if (@this.IsEnabled(NpgsqlAfterCloseConnection)) + if (@this.IsEnabled(NpgsqlCloseConnectionStop)) { - @this.Write( - NpgsqlAfterCloseConnection, - new - { - OperationId = operationId, - Operation = operation, - Connection = connection, - Timestamp = Stopwatch.GetTimestamp() - }); + @this.Write(NpgsqlCloseConnectionStop, connection); } } - public static void WriteConnectionCloseError(this DiagnosticListener @this, Guid operationId, NpgsqlConnection connection, Exception ex, - [CallerMemberName] string operation = "") + public static void CloseConnectionError(this DiagnosticListener @this, NpgsqlConnection connection, Exception ex) { - if (@this.IsEnabled(NpgsqlErrorCloseConnection)) + if (@this.IsEnabled(NpgsqlCloseConnectionError)) { @this.Write( - NpgsqlErrorCloseConnection, + NpgsqlCloseConnectionError, new { - OperationId = operationId, - Operation = operation, Connection = connection, - Exception = ex, - Timestamp = Stopwatch.GetTimestamp() + Exception = ex }); } } - public static Guid WriteTransactionCommitBefore(this DiagnosticListener @this, IsolationLevel isolationLevel, NpgsqlConnection connection, - [CallerMemberName] string operation = "") + public static void CommitTransactionStart(this DiagnosticListener @this, NpgsqlTransaction transaction) { - if (@this.IsEnabled(NpgsqlBeforeCommitTransaction)) + if (@this.IsEnabled(NpgsqlCommitTransactionStart)) { - Guid operationId = Guid.NewGuid(); - - @this.Write( - NpgsqlBeforeCommitTransaction, - new - { - OperationId = operationId, - Operation = operation, - IsolationLevel = isolationLevel, - Connection = connection, - Timestamp = Stopwatch.GetTimestamp() - }); - - return operationId; + @this.Write(NpgsqlCommitTransactionStart, transaction); } - else - return Guid.Empty; } - public static void WriteTransactionCommitAfter(this DiagnosticListener @this, Guid operationId, IsolationLevel isolationLevel, NpgsqlConnection connection, - [CallerMemberName] string operation = "") + public static void CommitTransactionStop(this DiagnosticListener @this, NpgsqlTransaction transaction) { - if (@this.IsEnabled(NpgsqlAfterCommitTransaction)) + if (@this.IsEnabled(NpgsqlCommitTransactionStop)) { - @this.Write( - NpgsqlAfterCommitTransaction, - new - { - OperationId = operationId, - Operation = operation, - IsolationLevel = isolationLevel, - Connection = connection, - Timestamp = Stopwatch.GetTimestamp() - }); + @this.Write(NpgsqlCommitTransactionStop, transaction); } } - public static void WriteTransactionCommitError(this DiagnosticListener @this, Guid operationId, IsolationLevel isolationLevel, NpgsqlConnection connection, Exception ex, - [CallerMemberName] string operation = "") + public static void CommitTransactionError(this DiagnosticListener @this, NpgsqlTransaction transaction, Exception ex) { - if (@this.IsEnabled(NpgsqlErrorCommitTransaction)) + if (@this.IsEnabled(NpgsqlCommitTransactionError)) { @this.Write( - NpgsqlErrorCommitTransaction, + NpgsqlCommitTransactionError, new { - OperationId = operationId, - Operation = operation, - IsolationLevel = isolationLevel, - Connection = connection, - Exception = ex, - Timestamp = Stopwatch.GetTimestamp() + Transaction = transaction, + Exception = ex }); } } - public static Guid WriteTransactionRollbackBefore(this DiagnosticListener @this, IsolationLevel isolationLevel, NpgsqlConnection connection, string transactionName, - [CallerMemberName] string operation = "") + public static void RollbackTransactionStart(this DiagnosticListener @this, NpgsqlTransaction transaction) { - if (@this.IsEnabled(NpgsqlBeforeRollbackTransaction)) + if (@this.IsEnabled(NpgsqlRollbackTransactionStart)) { - Guid operationId = Guid.NewGuid(); - - @this.Write( - NpgsqlBeforeRollbackTransaction, - new - { - OperationId = operationId, - Operation = operation, - IsolationLevel = isolationLevel, - Connection = connection, - TransactionName = transactionName, - Timestamp = Stopwatch.GetTimestamp() - }); - - return operationId; + @this.Write(NpgsqlRollbackTransactionStart, transaction); } - else - return Guid.Empty; } - public static void WriteTransactionRollbackAfter(this DiagnosticListener @this, Guid operationId, IsolationLevel isolationLevel, NpgsqlConnection connection, - string transactionName, [CallerMemberName] string operation = "") + public static void RollbackTransactionStop(this DiagnosticListener @this, NpgsqlTransaction transaction) { - if (@this.IsEnabled(NpgsqlAfterRollbackTransaction)) + if (@this.IsEnabled(NpgsqlRollbackTransactionStop)) { - @this.Write( - NpgsqlAfterRollbackTransaction, - new - { - OperationId = operationId, - Operation = operation, - IsolationLevel = isolationLevel, - Connection = connection, - TransactionName = transactionName, - Timestamp = Stopwatch.GetTimestamp() - }); + @this.Write(NpgsqlRollbackTransactionStop, transaction); } } - public static void WriteTransactionRollbackError(this DiagnosticListener @this, Guid operationId, IsolationLevel isolationLevel, NpgsqlConnection connection, - string transactionName, Exception ex, [CallerMemberName] string operation = "") + public static void RollbackTransactionError(this DiagnosticListener @this, NpgsqlTransaction transaction, Exception ex) { - if (@this.IsEnabled(NpgsqlErrorRollbackTransaction)) + if (@this.IsEnabled(NpgsqlRollbackTransactionError)) { @this.Write( - NpgsqlErrorRollbackTransaction, + NpgsqlRollbackTransactionError, new { - OperationId = operationId, - Operation = operation, - IsolationLevel = isolationLevel, - Connection = connection, - TransactionName = transactionName, - Exception = ex, - Timestamp = Stopwatch.GetTimestamp() + Transaction = transaction, + Exception = ex }); } } diff --git a/src/Npgsql/NpgsqlTransaction.cs b/src/Npgsql/NpgsqlTransaction.cs index 056147d73c..8fcefb44a8 100644 --- a/src/Npgsql/NpgsqlTransaction.cs +++ b/src/Npgsql/NpgsqlTransaction.cs @@ -83,7 +83,7 @@ public override IsolationLevel IsolationLevel static readonly NpgsqlLogger Log = NpgsqlLogManager.GetCurrentClassLogger(); - static readonly DiagnosticListener NpgsqlDiagnosticListener = new DiagnosticListener(NpgsqlDiagnosticListenerExtensions.DiagnosticListenerName); + static readonly DiagnosticListener NpgsqlDiagnosticListener = new DiagnosticListener(NpgsqlDiagnosticListenerExtensions.TransactionDiagnosticListenerName); const IsolationLevel DefaultIsolationLevel = IsolationLevel.ReadCommitted; @@ -147,11 +147,10 @@ internal NpgsqlTransaction(NpgsqlConnection conn, IsolationLevel isolationLevel async Task Commit(bool async) { - Exception e = null; - var operationId = NpgsqlDiagnosticListener.WriteTransactionCommitBefore(_isolationLevel, Connection); - try { + NpgsqlDiagnosticListener.CommitTransactionStart(this); + CheckReady(); if (!_connector.DatabaseInfo.SupportsTransactions) @@ -163,23 +162,14 @@ async Task Commit(bool async) await _connector.ExecuteInternalCommand(PregeneratedMessage.CommitTransaction, async); Clear(); } + + NpgsqlDiagnosticListener.CommitTransactionStop(this); } catch (Exception exception) { - e = exception; + NpgsqlDiagnosticListener.CommitTransactionError(this, exception); throw; } - finally - { - if (e != null) - { - NpgsqlDiagnosticListener.WriteTransactionCommitError(operationId, _isolationLevel, Connection, e); - } - else - { - NpgsqlDiagnosticListener.WriteTransactionCommitAfter(operationId, _isolationLevel, Connection); - } - } } /// @@ -210,33 +200,23 @@ public Task CommitAsync(CancellationToken cancellationToken) async Task Rollback(bool async) { - Exception e = null; - var operationId = NpgsqlDiagnosticListener.WriteTransactionRollbackBefore(_isolationLevel, Connection, null); - try { + NpgsqlDiagnosticListener.RollbackTransactionStart(this); + CheckReady(); if (!_connector.DatabaseInfo.SupportsTransactions) return; await _connector.Rollback(async); Clear(); + + NpgsqlDiagnosticListener.RollbackTransactionStop(this); } catch (Exception exception) { - e = exception; + NpgsqlDiagnosticListener.RollbackTransactionError(this, exception); throw; } - finally - { - if (e != null) - { - NpgsqlDiagnosticListener.WriteTransactionRollbackError(operationId, _isolationLevel, Connection, null, e); - } - else - { - NpgsqlDiagnosticListener.WriteTransactionRollbackAfter(operationId, _isolationLevel, Connection, null); - } - } } ///