2017-07-20 11:21:28 +02:00
using System ;
2012-07-28 23:08:02 +06:00
using System.Diagnostics ;
using Umbraco.Core.Logging ;
namespace Umbraco.Core
{
2017-07-20 11:21:28 +02:00
/// <summary>
/// Starts the timer and invokes a callback upon disposal. Provides a simple way of timing an operation by wrapping it in a <code>using</code> (C#) statement.
/// </summary>
public class DisposableTimer : DisposableObject
{
private readonly ILogger _logger ;
private readonly LogType ? _logType ;
private readonly Type _loggerType ;
private readonly int _thresholdMilliseconds ;
private readonly IDisposable _profilerStep ;
2016-09-11 19:57:33 +02:00
private readonly string _endMessage ;
private string _failMessage ;
2017-07-20 11:21:28 +02:00
private Exception _failException ;
private bool _failed ;
2012-07-28 23:08:02 +06:00
2016-09-11 19:57:33 +02:00
internal enum LogType
2017-07-20 11:21:28 +02:00
{
Debug , Info
}
2015-01-16 12:07:29 +11:00
2016-09-11 19:57:33 +02:00
// internal - created by profiling logger
2017-07-20 11:21:28 +02:00
internal DisposableTimer ( ILogger logger , LogType logType , IProfiler profiler , Type loggerType ,
2016-09-11 19:57:33 +02:00
string startMessage , string endMessage , string failMessage = null ,
int thresholdMilliseconds = 0 )
2017-07-20 11:21:28 +02:00
{
2016-09-11 19:57:33 +02:00
if ( logger = = null ) throw new ArgumentNullException ( nameof ( logger ) ) ;
if ( loggerType = = null ) throw new ArgumentNullException ( nameof ( loggerType ) ) ;
2016-04-29 00:50:26 +02:00
_logger = logger ;
_logType = logType ;
_loggerType = loggerType ;
_endMessage = endMessage ;
2017-07-20 11:21:28 +02:00
_failMessage = failMessage ;
_thresholdMilliseconds = thresholdMilliseconds < 0 ? 0 : thresholdMilliseconds ;
2016-04-29 00:50:26 +02:00
2016-09-11 19:57:33 +02:00
if ( thresholdMilliseconds = = 0 )
2016-04-29 00:50:26 +02:00
{
2016-09-11 19:57:33 +02:00
switch ( logType )
{
case LogType . Debug :
logger . Debug ( loggerType , startMessage ) ;
break ;
case LogType . Info :
logger . Info ( loggerType , startMessage ) ;
break ;
default :
throw new ArgumentOutOfRangeException ( nameof ( logType ) ) ;
}
2015-01-16 12:07:29 +11:00
}
2012-07-28 23:08:02 +06:00
2016-09-11 19:57:33 +02:00
// else aren't logging the start message, this is output to the profiler but not the log,
// we just want the log to contain the result if it's more than the minimum ms threshold.
2015-01-16 12:07:29 +11:00
2016-09-11 19:57:33 +02:00
_profilerStep = profiler ? . Step ( loggerType , startMessage ) ;
2013-05-13 21:11:03 -10:00
}
2012-07-28 23:08:02 +06:00
2013-05-13 21:11:03 -10:00
/// <summary>
2016-09-11 19:57:33 +02:00
/// Reports a failure.
2013-05-13 21:11:03 -10:00
/// </summary>
2016-09-11 19:57:33 +02:00
/// <param name="failMessage">The fail message.</param>
/// <param name="exception">The exception.</param>
/// <remarks>Completion of the timer will be reported as an error, with the specified message and exception.</remarks>
public void Fail ( string failMessage = null , Exception exception = null )
2013-05-13 21:11:03 -10:00
{
2016-09-11 19:57:33 +02:00
_failed = true ;
_failMessage = failMessage ? ? _failMessage ? ? "Failed." ;
_failException = exception ;
2013-05-13 21:11:03 -10:00
}
2012-07-28 23:08:02 +06:00
2017-07-20 11:21:28 +02:00
public Stopwatch Stopwatch { get ; } = Stopwatch . StartNew ( ) ;
2012-07-30 22:52:59 +06:00
2013-05-13 21:11:03 -10:00
/// <summary>
2016-09-11 19:57:33 +02:00
///Disposes resources.
2013-05-13 21:11:03 -10:00
/// </summary>
2016-09-11 19:57:33 +02:00
/// <remarks>Overrides abstract class <see cref="DisposableObject"/> which handles required locking.</remarks>
protected override void DisposeResources ( )
2017-07-20 11:21:28 +02:00
{
2016-04-29 00:50:26 +02:00
Stopwatch . Stop ( ) ;
2017-07-20 11:21:28 +02:00
_profilerStep ? . Dispose ( ) ;
2016-01-22 17:27:27 +01:00
2017-07-20 11:21:28 +02:00
if ( ( Stopwatch . ElapsedMilliseconds > = _thresholdMilliseconds | | _failed )
2016-09-11 19:57:33 +02:00
& & _logType . HasValue & & _loggerType ! = null & & _logger ! = null
& & ( _endMessage . IsNullOrWhiteSpace ( ) = = false | | _failed ) )
2017-07-20 11:21:28 +02:00
{
if ( _failed )
{
_logger . Error ( _loggerType , $"{_failMessage} ({Stopwatch.ElapsedMilliseconds}ms)" , _failException ) ;
}
2016-09-11 19:57:33 +02:00
else switch ( _logType )
2016-01-22 17:27:27 +01:00
{
case LogType . Debug :
2016-09-11 19:57:33 +02:00
_logger . Debug ( _loggerType , ( ) = > $"{_endMessage} ({Stopwatch.ElapsedMilliseconds}ms)" ) ;
2016-01-22 17:27:27 +01:00
break ;
case LogType . Info :
2016-09-11 19:57:33 +02:00
_logger . Info ( _loggerType , ( ) = > $"{_endMessage} ({Stopwatch.ElapsedMilliseconds}ms)" ) ;
2016-01-22 17:27:27 +01:00
break ;
2016-09-11 19:57:33 +02:00
// filtered in the ctor
2017-07-20 11:21:28 +02:00
//default:
2016-09-11 19:57:33 +02:00
// throw new Exception();
2016-01-22 17:27:27 +01:00
}
}
2017-07-20 11:21:28 +02:00
}
}
}