using System; using System.Diagnostics; using System.Web; using Umbraco.Core.Logging; using Umbraco.Core.Profiling; namespace Umbraco.Core { /// /// Starts the timer and invokes a callback upon disposal. Provides a simple way of timing an operation by wrapping it in a using (C#) statement. /// /// /// /// /// using (DisposableTimer.TraceDuration{MyType}("starting", "finished")) /// { /// Thread.Sleep(567); /// } /// /// Console.WriteLine("Testing Stopwatchdisposable, should be 567:"); /// using (var timer = new DisposableTimer(result => Console.WriteLine("Took {0}ms", result))) /// { /// Thread.Sleep(567); /// } /// /// public class DisposableTimer : DisposableObject { private readonly Stopwatch _stopwatch = Stopwatch.StartNew(); private readonly Action _callback; protected DisposableTimer(Action callback) { _callback = callback; } public Stopwatch Stopwatch { get { return _stopwatch; } } /// /// Starts the timer and invokes the specified callback upon disposal. /// /// The callback. /// [Obsolete("Use either TraceDuration or DebugDuration instead of using Start")] public static DisposableTimer Start(Action callback) { return new DisposableTimer(callback); } #region TraceDuration public static DisposableTimer TraceDuration(Func startMessage, Func completeMessage) { return TraceDuration(typeof(T), startMessage, completeMessage); } public static DisposableTimer TraceDuration(Type loggerType, Func startMessage, Func completeMessage) { var startMsg = startMessage(); LogHelper.Info(loggerType, startMsg); if (HttpContext.Current != null) HttpContext.Current.Trace.Write("Start: " + startMsg); var profiler = ActivateProfiler(loggerType, startMsg); return new DisposableTimer(x => { profiler.DisposeIfDisposable(); LogHelper.Info(loggerType, () => completeMessage() + " (took " + x + "ms)"); if (HttpContext.Current != null) HttpContext.Current.Trace.Write("End: " + startMsg); }); } /// /// Adds a start and end log entry as Info and tracks how long it takes until disposed. /// /// /// /// /// public static DisposableTimer TraceDuration(string startMessage, string completeMessage) { return TraceDuration(typeof(T), startMessage, completeMessage); } public static DisposableTimer TraceDuration(string startMessage) { return TraceDuration(typeof(T), startMessage, "Complete"); } /// /// Adds a start and end log entry as Info and tracks how long it takes until disposed. /// /// /// /// /// public static DisposableTimer TraceDuration(Type loggerType, string startMessage, string completeMessage) { LogHelper.Info(loggerType, startMessage); if (HttpContext.Current != null) HttpContext.Current.Trace.Write("Start: " + startMessage); var profiler = ActivateProfiler(loggerType, startMessage); return new DisposableTimer(x => { profiler.DisposeIfDisposable(); LogHelper.Info(loggerType, () => completeMessage + " (took " + x + "ms)"); if (HttpContext.Current != null) HttpContext.Current.Trace.Write("End: " + startMessage); }); } #endregion #region DebugDuration /// /// Adds a start and end log entry as Debug and tracks how long it takes until disposed. /// /// /// /// /// public static DisposableTimer DebugDuration(string startMessage, string completeMessage) { return DebugDuration(typeof(T), startMessage, completeMessage); } public static DisposableTimer DebugDuration(string startMessage) { return DebugDuration(typeof(T), startMessage, "Complete"); } /// /// Adds a start and end log entry as Debug and tracks how long it takes until disposed. /// /// /// /// /// public static DisposableTimer DebugDuration(Func startMessage, Func completeMessage) { return DebugDuration(typeof(T), startMessage, completeMessage); } /// /// Adds a start and end log entry as Debug and tracks how long it takes until disposed. /// /// /// /// /// public static DisposableTimer DebugDuration(Type loggerType, string startMessage, string completeMessage) { LogHelper.Debug(loggerType, startMessage); if (HttpContext.Current != null) HttpContext.Current.Trace.Write("Start: " + startMessage); var profiler = ActivateProfiler(loggerType, startMessage); return new DisposableTimer(x => { profiler.DisposeIfDisposable(); LogHelper.Debug(loggerType, () => completeMessage + " (took " + x + "ms)"); if (HttpContext.Current != null) HttpContext.Current.Trace.Write("End: " + startMessage); }); } /// /// Adds a start and end log entry as Debug and tracks how long it takes until disposed. /// /// /// /// /// public static DisposableTimer DebugDuration(Type loggerType, Func startMessage, Func completeMessage) { var msg = startMessage(); LogHelper.Debug(loggerType, msg); if (HttpContext.Current != null) HttpContext.Current.Trace.Write("Start: " + startMessage); var profiler = ActivateProfiler(loggerType, msg); return new DisposableTimer(x => { profiler.DisposeIfDisposable(); LogHelper.Debug(loggerType, () => completeMessage() + " (took " + x + "ms)"); if (HttpContext.Current != null) HttpContext.Current.Trace.Write("End: " + startMessage); }); } #endregion /// /// Handles the disposal of resources. Derived from abstract class which handles common required locking logic. /// protected override void DisposeResources() { _callback.Invoke(Stopwatch.ElapsedMilliseconds); } private static IDisposable ActivateProfiler(Type loggerType, string profileName) { try { return ProfilerResolver.Current.Profiler.Step(loggerType, profileName); } catch (InvalidOperationException) { //swallow this exception, it will occur if the ProfilerResolver is not initialized... generally only in // unit tests. } return null; } } }