2018-06-29 19:52:40 +02:00
using System ;
using System.Diagnostics ;
2018-08-14 13:22:44 +01:00
namespace Umbraco.Core.Logging
2018-06-29 19:52:40 +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 : DisposableObjectSlim
{
private readonly ILogger _logger ;
2018-08-30 19:08:55 +02:00
private readonly LogLevel _level ;
2018-06-29 19:52:40 +02:00
private readonly Type _loggerType ;
private readonly int _thresholdMilliseconds ;
private readonly IDisposable _profilerStep ;
private readonly string _endMessage ;
private string _failMessage ;
private Exception _failException ;
private bool _failed ;
2018-08-28 15:40:20 +10:00
private readonly string _timingId ;
2018-06-29 19:52:40 +02:00
// internal - created by profiling logger
2018-08-30 19:08:55 +02:00
internal DisposableTimer ( ILogger logger , LogLevel level , IProfiler profiler , Type loggerType ,
2018-06-29 19:52:40 +02:00
string startMessage , string endMessage , string failMessage = null ,
int thresholdMilliseconds = 0 )
{
2018-08-30 19:08:55 +02:00
_logger = logger ? ? throw new ArgumentNullException ( nameof ( logger ) ) ;
_level = level ;
_loggerType = loggerType ? ? throw new ArgumentNullException ( nameof ( loggerType ) ) ;
2018-06-29 19:52:40 +02:00
_endMessage = endMessage ;
_failMessage = failMessage ;
_thresholdMilliseconds = thresholdMilliseconds < 0 ? 0 : thresholdMilliseconds ;
2018-09-20 08:49:19 +02:00
_timingId = Guid . NewGuid ( ) . ToString ( "N" ) . Substring ( 0 , 7 ) ; // keep it short-ish
2018-06-29 19:52:40 +02:00
if ( thresholdMilliseconds = = 0 )
{
2018-08-30 19:08:55 +02:00
switch ( _level )
2018-06-29 19:52:40 +02:00
{
2018-08-30 19:08:55 +02:00
case LogLevel . Debug :
2021-02-22 11:22:49 +13:00
logger . Debug < string , string > ( loggerType , "{StartMessage} [Timing {TimingId}]" , startMessage , _timingId ) ;
2018-06-29 19:52:40 +02:00
break ;
2018-08-30 19:08:55 +02:00
case LogLevel . Information :
2021-02-22 11:22:49 +13:00
logger . Info < string , string > ( loggerType , "{StartMessage} [Timing {TimingId}]" , startMessage , _timingId ) ;
2018-06-29 19:52:40 +02:00
break ;
default :
2018-08-30 19:08:55 +02:00
throw new ArgumentOutOfRangeException ( nameof ( level ) ) ;
2018-06-29 19:52:40 +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.
_profilerStep = profiler ? . Step ( loggerType , startMessage ) ;
}
/// <summary>
/// Reports a failure.
/// </summary>
/// <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 )
{
_failed = true ;
_failMessage = failMessage ? ? _failMessage ? ? "Failed." ;
_failException = exception ;
}
public Stopwatch Stopwatch { get ; } = Stopwatch . StartNew ( ) ;
/// <summary>
///Disposes resources.
/// </summary>
/// <remarks>Overrides abstract class <see cref="DisposableObject"/> which handles required locking.</remarks>
protected override void DisposeResources ( )
{
Stopwatch . Stop ( ) ;
_profilerStep ? . Dispose ( ) ;
if ( ( Stopwatch . ElapsedMilliseconds > = _thresholdMilliseconds | | _failed )
2018-08-30 19:08:55 +02:00
& & _loggerType ! = null & & _logger ! = null
2018-06-29 19:52:40 +02:00
& & ( _endMessage . IsNullOrWhiteSpace ( ) = = false | | _failed ) )
{
if ( _failed )
{
2021-02-22 11:22:49 +13:00
_logger . Error < string , long , string > ( _loggerType , _failException , "{FailMessage} ({Duration}ms) [Timing {TimingId}]" , _failMessage , Stopwatch . ElapsedMilliseconds , _timingId ) ;
2018-06-29 19:52:40 +02:00
}
2018-08-30 19:08:55 +02:00
else switch ( _level )
2018-06-29 19:52:40 +02:00
{
2018-08-30 19:08:55 +02:00
case LogLevel . Debug :
2021-02-22 11:22:49 +13:00
_logger . Debug < string , long , string > ( _loggerType , "{EndMessage} ({Duration}ms) [Timing {TimingId}]" , _endMessage , Stopwatch . ElapsedMilliseconds , _timingId ) ;
2018-06-29 19:52:40 +02:00
break ;
2018-08-30 19:08:55 +02:00
case LogLevel . Information :
2021-02-22 11:22:49 +13:00
_logger . Info < string , long , string > ( _loggerType , "{EndMessage} ({Duration}ms) [Timing {TimingId}]" , _endMessage , Stopwatch . ElapsedMilliseconds , _timingId ) ;
2018-06-29 19:52:40 +02:00
break ;
// filtered in the ctor
//default:
// throw new Exception();
}
}
}
}
}