using System; using System.Threading; using System.Web; using StackExchange.Profiling; namespace Umbraco.Core.Logging { /// /// This is a custom MiniProfiler WebRequestProfilerProvider (which is generally the default) that allows /// us to profile items during app startup - before an HttpRequest is created /// /// /// Once the boot phase is changed to BootPhase.BootRequest then the base class (default) provider will handle all /// profiling data and this sub class no longer performs any logic. /// internal class WebProfilerProvider : WebRequestProfilerProvider { private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(); private MiniProfiler _startupProfiler; private int _first; private volatile BootPhase _bootPhase; public WebProfilerProvider() { // booting... _bootPhase = BootPhase.Boot; } /// /// Indicates the boot phase. /// private enum BootPhase { Boot = 0, // boot phase (before the 1st request even begins) BootRequest = 1, // request boot phase (during the 1st request) Booted = 2 // done booting } public void BeginBootRequest() { _locker.EnterWriteLock(); try { if (_bootPhase != BootPhase.Boot) throw new InvalidOperationException("Invalid boot phase."); _bootPhase = BootPhase.BootRequest; // assign the profiler to be the current MiniProfiler for the request // is's already active, starting and all HttpContext.Current.Items[":mini-profiler:"] = _startupProfiler; } finally { _locker.ExitWriteLock(); } } public void EndBootRequest() { _locker.EnterWriteLock(); try { if (_bootPhase != BootPhase.BootRequest) throw new InvalidOperationException("Invalid boot phase."); _bootPhase = BootPhase.Booted; _startupProfiler = null; } finally { _locker.ExitWriteLock(); } } /// /// Starts a new MiniProfiler. /// /// /// This is called when WebProfiler calls MiniProfiler.Start() so, /// - as a result of WebRuntime starting the WebProfiler, and /// - assuming profiling is enabled, on every BeginRequest that should be profiled, /// - except for the very first one which is the boot request. /// public override MiniProfiler Start(string sessionName = null) { var first = Interlocked.Exchange(ref _first, 1) == 0; if (first == false) return base.Start(sessionName); _startupProfiler = new MiniProfiler("http://localhost/umbraco-startup") { Name = "StartupProfiler" }; SetProfilerActive(_startupProfiler); return _startupProfiler; } // obsolete but that's the one that's called ;-( public override MiniProfiler Start(ProfileLevel level, string sessionName = null) { return Start(sessionName); } /// /// Gets the current profiler. /// /// /// If the boot phase is not Booted, then this will return the startup profiler (this), otherwise /// returns the base class /// public override MiniProfiler GetCurrentProfiler() { // if not booting then just use base (fast) // no lock, _bootPhase is volatile if (_bootPhase == BootPhase.Booted) return base.GetCurrentProfiler(); // else try { var current = base.GetCurrentProfiler(); return current ?? _startupProfiler; } catch { return _startupProfiler; } } } }