diff --git a/src/Umbraco.Core/Profiling/StartupWebProfilerProvider.cs b/src/Umbraco.Core/Profiling/StartupWebProfilerProvider.cs new file mode 100644 index 0000000000..16ce638c0e --- /dev/null +++ b/src/Umbraco.Core/Profiling/StartupWebProfilerProvider.cs @@ -0,0 +1,126 @@ +using System.Threading; +using System.Web; +using StackExchange.Profiling; + +namespace Umbraco.Core.Profiling +{ + /// + /// Allows us to profile items during app startup - before an HttpRequest is created + /// + internal class StartupWebProfilerProvider : WebRequestProfilerProvider + { + public StartupWebProfilerProvider() + { + _startupPhase = StartupPhase.Boot; + //create the startup profiler + _startupProfiler = new MiniProfiler("http://localhost/umbraco-startup", ProfileLevel.Verbose) + { + Name = "StartupProfiler" + }; + } + + private MiniProfiler _startupProfiler; + private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(); + + private enum StartupPhase + { + None = 0, + Boot = 1, + Request = 2 + } + + private volatile StartupPhase _startupPhase; + + public void BootComplete() + { + using (new ReadLock(_locker)) + { + if (_startupPhase != StartupPhase.Boot) return; + } + + using (var l = new UpgradeableReadLock(_locker)) + { + if (_startupPhase == StartupPhase.Boot) + { + l.UpgradeToWriteLock(); + + ////Now we need to transfer some information from our startup phase to the normal + ////web request phase to output the startup profiled information. + ////Stop our internal startup profiler, this will write out it's results to storage. + //StopProfiler(_startupProfiler); + //SaveProfiler(_startupProfiler); + + _startupPhase = StartupPhase.Request; + } + } + } + + public override void Stop(bool discardResults) + { + using (new ReadLock(_locker)) + { + if (_startupPhase == StartupPhase.None) + { + base.Stop(discardResults); + return; + } + } + + using (var l = new UpgradeableReadLock(_locker)) + { + if (_startupPhase > 0 && base.GetCurrentProfiler() == null) + { + l.UpgradeToWriteLock(); + + _startupPhase = StartupPhase.None; + + if (HttpContext.Current != null) + { + HttpContext.Current.Items[":mini-profiler:"] = _startupProfiler; + base.Stop(discardResults); + _startupProfiler = null; + } + } + else + { + base.Stop(discardResults); + } + } + } + + public override MiniProfiler Start(ProfileLevel level) + { + using (new ReadLock(_locker)) + { + if (_startupPhase > 0 && base.GetCurrentProfiler() == null) + { + SetProfilerActive(_startupProfiler); + return _startupProfiler; + } + + return base.Start(level); + } + } + + public override MiniProfiler GetCurrentProfiler() + { + using (new ReadLock(_locker)) + { + if (_startupPhase > 0) + { + try + { + var current = base.GetCurrentProfiler(); + if (current == null) return _startupProfiler; + } + catch + { + return _startupProfiler; + } + } + + return base.GetCurrentProfiler(); + } + } + } +} \ No newline at end of file diff --git a/src/Umbraco.Core/Profiling/WebProfiler.cs b/src/Umbraco.Core/Profiling/WebProfiler.cs index 00d088bca7..7e2cf49313 100644 --- a/src/Umbraco.Core/Profiling/WebProfiler.cs +++ b/src/Umbraco.Core/Profiling/WebProfiler.cs @@ -12,15 +12,24 @@ namespace Umbraco.Core.Profiling /// internal class WebProfiler : IProfiler { + private StartupWebProfilerProvider _startupWebProfilerProvider; /// /// Constructor - /// - /// - /// Binds to application events to enable the MiniProfiler - /// + /// internal WebProfiler() { + //setup some defaults + MiniProfiler.Settings.SqlFormatter = new SqlServerFormatter(); + MiniProfiler.Settings.StackMaxLength = 5000; + + //At this point we know that we've been constructed during app startup, there won't be an HttpRequest in the HttpContext + // since it hasn't started yet. So we need to do some hacking to enable profiling during startup. + _startupWebProfilerProvider = new StartupWebProfilerProvider(); + //this should always be the case during startup, we'll need to set a custom profiler provider + MiniProfiler.Settings.ProfilerProvider = _startupWebProfilerProvider; + + //Binds to application events to enable the MiniProfiler with a real HttpRequest UmbracoApplicationBase.ApplicationInit += UmbracoApplicationApplicationInit; } @@ -53,7 +62,12 @@ namespace Umbraco.Core.Profiling /// void UmbracoApplicationEndRequest(object sender, EventArgs e) { - if (CanPerformProfilingAction(sender)) + if (_startupWebProfilerProvider != null) + { + Stop(); + _startupWebProfilerProvider = null; + } + else if (CanPerformProfilingAction(sender)) { Stop(); } @@ -66,6 +80,11 @@ namespace Umbraco.Core.Profiling /// void UmbracoApplicationBeginRequest(object sender, EventArgs e) { + if (_startupWebProfilerProvider != null) + { + _startupWebProfilerProvider.BootComplete(); + } + if (CanPerformProfilingAction(sender)) { Start(); @@ -124,9 +143,7 @@ namespace Umbraco.Core.Profiling /// Start the profiler /// public void Start() - { - MiniProfiler.Settings.SqlFormatter = new SqlServerFormatter(); - MiniProfiler.Settings.StackMaxLength = 5000; + { MiniProfiler.Start(); } diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 2ef0501f8e..d11270592a 100644 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -469,6 +469,7 @@ + diff --git a/src/Umbraco.Web/WebBootManager.cs b/src/Umbraco.Web/WebBootManager.cs index dcf1e98be4..a9495afa7c 100644 --- a/src/Umbraco.Web/WebBootManager.cs +++ b/src/Umbraco.Web/WebBootManager.cs @@ -183,7 +183,9 @@ namespace Umbraco.Web base.InitializeProfilerResolver(); //Set the profiler to be the web profiler - ProfilerResolver.Current.SetProfiler(new WebProfiler()); + var profiler = new WebProfiler(); + ProfilerResolver.Current.SetProfiler(profiler); + profiler.Start(); } ///