public class CustomDatabaseLogFormatter : IDbCommandInterceptor, IDbConnectionInterceptor { private readonly Action _writeAction; private readonly Stopwatch _stopwatch = new Stopwatch(); /// /// Creates a formatter that will log every command from any context and also commands that do not originate from a context. /// /// /// This constructor is not used when a delegate is set on . Instead it can be /// used by setting the formatter directly using . /// /// The delegate to which output will be sent. public CustomDatabaseLogFormatter(Action writeAction) { Check.NotNull(writeAction, "writeAction"); _writeAction = writeAction; } internal Action WriteAction { get { return _writeAction; } } /// /// Writes the given string to the underlying write delegate. /// /// The string to write. protected virtual void Write(string output) { _writeAction(output); } /// /// The stopwatch used to time executions. This stopwatch is started at the end of /// , , and /// methods and is stopped at the beginning of the , , /// and methods. If these methods are overridden and the stopwatch is being used /// then the overrides should either call the base method or start/stop the stopwatch themselves. /// /// The stopwatch. protected internal Stopwatch Stopwatch { get { return _stopwatch; } } private void RestartStopwatch() { Stopwatch.Restart(); } private void StopStopwatch() { Stopwatch.Stop(); } #region IDbCommandInterceptor /// /// This method is called before a call to or /// one of its async counterparts is made. /// Starts the stopwatch returned from . /// /// The command being executed. /// Contextual information associated with the call. public virtual void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext interceptionContext) { Check.NotNull(command, "command"); Check.NotNull(interceptionContext, "interceptionContext"); RestartStopwatch(); } /// /// This method is called after a call to or /// one of its async counterparts is made. /// Stops the stopwatch returned from and calls . /// /// The command being executed. /// Contextual information associated with the call. public virtual void NonQueryExecuted(DbCommand command, DbCommandInterceptionContext interceptionContext) { Check.NotNull(command, "command"); Check.NotNull(interceptionContext, "interceptionContext"); StopStopwatch(); Executed(command, interceptionContext); } /// /// This method is called before a call to or /// one of its async counterparts is made. /// Starts the stopwatch returned from . /// /// The command being executed. /// Contextual information associated with the call. public virtual void ReaderExecuting(DbCommand command, DbCommandInterceptionContext interceptionContext) { Check.NotNull(command, "command"); Check.NotNull(interceptionContext, "interceptionContext"); RestartStopwatch(); } /// /// This method is called after a call to or /// one of its async counterparts is made. /// Stops the stopwatch returned from and calls . /// /// The command being executed. /// Contextual information associated with the call. public virtual void ReaderExecuted(DbCommand command, DbCommandInterceptionContext interceptionContext) { Check.NotNull(command, "command"); Check.NotNull(interceptionContext, "interceptionContext"); StopStopwatch(); Executed(command, interceptionContext); } /// /// This method is called before a call to or /// one of its async counterparts is made. /// Starts the stopwatch returned from . /// /// The command being executed. /// Contextual information associated with the call. public virtual void ScalarExecuting(DbCommand command, DbCommandInterceptionContext interceptionContext) { Check.NotNull(command, "command"); Check.NotNull(interceptionContext, "interceptionContext"); RestartStopwatch(); } /// /// This method is called after a call to or /// one of its async counterparts is made. /// Stops the stopwatch returned from and calls /// . /// /// The command being executed. /// Contextual information associated with the call. public virtual void ScalarExecuted(DbCommand command, DbCommandInterceptionContext interceptionContext) { Check.NotNull(command, "command"); Check.NotNull(interceptionContext, "interceptionContext"); StopStopwatch(); Executed(command, interceptionContext); } /// /// Called whenever a command has completed executing. Calls . /// /// The type of the operation's results. /// The command that was executed. /// Contextual information associated with the command. public virtual void Executed(DbCommand command, DbCommandInterceptionContext interceptionContext) { Check.NotNull(command, "command"); Check.NotNull(interceptionContext, "interceptionContext"); LogResult(command, interceptionContext); } /// /// Called to log the result of executing a command. /// /// The type of the operation's results. /// The command being logged. /// Contextual information associated with the command. public virtual void LogResult(DbCommand command, DbCommandInterceptionContext interceptionContext) { Check.NotNull(command, "command"); Check.NotNull(interceptionContext, "interceptionContext"); var stopwatch = Stopwatch; if (interceptionContext.Exception != null) { Write( String.Format(StringResources.CommandLogFailed, stopwatch.ElapsedMilliseconds, interceptionContext.Exception.Message) ); } else if (interceptionContext.TaskStatus.HasFlag(TaskStatus.Canceled)) { Write(String.Format(StringResources.CommandLogCanceled, stopwatch.ElapsedMilliseconds)); } else { var result = interceptionContext.Result; var resultString = (object)result == null ? "null" : (result is DbDataReader) ? result.GetType().Name : result.ToString(); Write(String.Format(StringResources.CommandLogComplete, stopwatch.ElapsedMilliseconds, resultString)); } } #endregion #region IDbConnectionInterceptor public void BeginningTransaction(DbConnection connection, BeginTransactionInterceptionContext interceptionContext) { } public void BeganTransaction(DbConnection connection, BeginTransactionInterceptionContext interceptionContext) { } public void Closing(DbConnection connection, DbConnectionInterceptionContext interceptionContext) { } /// /// Called after is invoked. /// /// The connection that was closed. /// Contextual information associated with the call. public void Closed(DbConnection connection, DbConnectionInterceptionContext interceptionContext) { Check.NotNull(connection, "connection"); Check.NotNull(interceptionContext, "interceptionContext"); if (interceptionContext.Exception != null) { Write(String.Format(StringResources.ConnectionCloseErrorLog, DateTimeOffset.UtcNow, interceptionContext.Exception.Message)); } else { Write(String.Format(StringResources.ConnectionClosedLog, DateTimeOffset.UtcNow)); } } public void ConnectionStringGetting(DbConnection connection, DbConnectionInterceptionContext interceptionContext) { } public void ConnectionStringGot(DbConnection connection, DbConnectionInterceptionContext interceptionContext) { } public void ConnectionStringSetting(DbConnection connection, DbConnectionPropertyInterceptionContext interceptionContext) { } public void ConnectionStringSet(DbConnection connection, DbConnectionPropertyInterceptionContext interceptionContext) { } public void ConnectionTimeoutGetting(DbConnection connection, DbConnectionInterceptionContext interceptionContext) { } public void ConnectionTimeoutGot(DbConnection connection, DbConnectionInterceptionContext interceptionContext) { } public void DatabaseGetting(DbConnection connection, DbConnectionInterceptionContext interceptionContext) { } public void DatabaseGot(DbConnection connection, DbConnectionInterceptionContext interceptionContext) { } public void DataSourceGetting(DbConnection connection, DbConnectionInterceptionContext interceptionContext) { } public void DataSourceGot(DbConnection connection, DbConnectionInterceptionContext interceptionContext) { } public void Disposing(DbConnection connection, DbConnectionInterceptionContext interceptionContext) { } public void Disposed(DbConnection connection, DbConnectionInterceptionContext interceptionContext) { } public void EnlistingTransaction(DbConnection connection, EnlistTransactionInterceptionContext interceptionContext) { } public void EnlistedTransaction(DbConnection connection, EnlistTransactionInterceptionContext interceptionContext) { } public void Opening(DbConnection connection, DbConnectionInterceptionContext interceptionContext) { } /// /// Called after or its async counterpart is invoked. /// /// The connection that was opened. /// Contextual information associated with the call. public void Opened(DbConnection connection, DbConnectionInterceptionContext interceptionContext) { Check.NotNull(connection, "connection"); Check.NotNull(interceptionContext, "interceptionContext"); if (interceptionContext.Exception != null) { Write( interceptionContext.IsAsync ? String.Format(StringResources.ConnectionOpenErrorLogAsync, DateTimeOffset.UtcNow, interceptionContext.Exception.Message) : String.Format(StringResources.ConnectionOpenErrorLog, DateTimeOffset.UtcNow, interceptionContext.Exception.Message)); } else if (interceptionContext.TaskStatus.HasFlag(TaskStatus.Canceled)) { Write(String.Format(StringResources.ConnectionOpenCanceledLog, DateTimeOffset.UtcNow)); } else { Write( interceptionContext.IsAsync ? String.Format(StringResources.ConnectionOpenedLogAsync, DateTimeOffset.UtcNow) : String.Format(StringResources.ConnectionOpenedLog, DateTimeOffset.UtcNow)); } } public void ServerVersionGetting(DbConnection connection, DbConnectionInterceptionContext interceptionContext) { } public void ServerVersionGot(DbConnection connection, DbConnectionInterceptionContext interceptionContext) { } public void StateGetting(DbConnection connection, DbConnectionInterceptionContext interceptionContext) { } public void StateGot(DbConnection connection, DbConnectionInterceptionContext interceptionContext) { } #endregion }
internal class Check { public static T NotNull(T value, string parameterName) where T : class { if (value == null) { throw new ArgumentNullException(parameterName); } return value; } }
StringResources.resx: CommandLogCanceled Canceled in {0} ms{1} CommandLogComplete Completed in {0} ms with result: {1} CommandLogFailed Failed in {0} ms with error: {1} ConnectionClosedLog Closed connection at {0} ConnectionCloseErrorLog Failed to close connection at {0} with error: {1} ConnectionOpenCanceledLog Cancelled open connection at {0} ConnectionOpenedLog Opened connection at {0} ConnectionOpenedLogAsync Opened connection asynchronously at {0} ConnectionOpenErrorLog Failed to open connection at {0} with error: {1} ConnectionOpenErrorLogAsync Failed to open connection asynchronously at {0} with error: {1}