Cleanup WebProfiler from 8.x

This commit is contained in:
Stephan
2016-11-02 09:42:20 +01:00
parent 59ace3d881
commit 07f4da9b00
4 changed files with 154 additions and 200 deletions

View File

@@ -1,159 +0,0 @@
using System.Threading;
using System.Web;
using StackExchange.Profiling;
using Umbraco.Core;
namespace Umbraco.Web.Profiling
{
/// <summary>
/// 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
/// </summary>
/// <remarks>
/// Once the boot phase is changed to StartupPhase.Request then the base class (default) provider will handle all
/// profiling data and this sub class no longer performs any logic.
/// </remarks>
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();
/// <summary>
/// Used to determine which phase the boot process is in
/// </summary>
private enum StartupPhase
{
None = 0,
Boot = 1,
Request = 2
}
private volatile StartupPhase _startupPhase;
/// <summary>
/// Executed once the application boot process is complete and changes the phase to Request
/// </summary>
public void BootComplete()
{
using (new ReadLock(_locker))
{
if (_startupPhase != StartupPhase.Boot) return;
}
using (var l = new UpgradeableReadLock(_locker))
{
if (_startupPhase == StartupPhase.Boot)
{
l.UpgradeToWriteLock();
_startupPhase = StartupPhase.Request;
}
}
}
/// <summary>
/// Executed when a profiling operation is completed
/// </summary>
/// <param name="discardResults"></param>
/// <remarks>
/// This checks if the bootup phase is None, if so, it just calls the base class, otherwise it checks
/// if a profiler is active (i.e. in startup), then sets the phase to None so that the base class will be used
/// for all subsequent calls.
/// </remarks>
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;
//This is required to pass the mini profiling context from before a request
// to the current startup request.
if (HttpContext.Current != null)
{
HttpContext.Current.Items[":mini-profiler:"] = _startupProfiler;
base.Stop(discardResults);
_startupProfiler = null;
}
}
else
{
base.Stop(discardResults);
}
}
}
/// <summary>
/// Executed when a profiling operation is started
/// </summary>
/// <param name="level"></param>
/// <returns></returns>
/// <remarks>
/// This checks if the startup phase is not None, if this is the case and the current profiler is NULL
/// then this sets the startup profiler to be active. Otherwise it just calls the base class Start method.
/// </remarks>
public override MiniProfiler Start(ProfileLevel level)
{
using (new ReadLock(_locker))
{
if (_startupPhase > 0 && base.GetCurrentProfiler() == null)
{
SetProfilerActive(_startupProfiler);
return _startupProfiler;
}
return base.Start(level);
}
}
/// <summary>
/// This returns the current profiler
/// </summary>
/// <returns></returns>
/// <remarks>
/// If the boot phase is not None, then this will return the startup profiler (this), otherwise
/// returns the base class
/// </remarks>
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();
}
}
}
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Threading;
using System.Web;
using StackExchange.Profiling;
using StackExchange.Profiling.SqlFormatters;
@@ -14,22 +15,23 @@ namespace Umbraco.Web.Profiling
/// </summary>
internal class WebProfiler : IProfiler
{
private StartupWebProfilerProvider _startupWebProfilerProvider;
private const string BootRequestItemKey = "Umbraco.Web.Profiling.WebProfiler__isBootRequest";
private WebProfilerProvider _provider;
private int _first;
/// <summary>
/// Constructor
/// </summary>
internal WebProfiler()
{
//setup some defaults
// create our own provider, which can provide a profiler even during boot
// MiniProfiler's default cannot because there's no HttpRequest in HttpContext
_provider = new WebProfilerProvider();
// settings
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;
MiniProfiler.Settings.ProfilerProvider = _provider;
//Binds to application events to enable the MiniProfiler with a real HttpRequest
UmbracoApplicationBase.ApplicationInit += UmbracoApplicationApplicationInit;
@@ -57,43 +59,33 @@ namespace Umbraco.Web.Profiling
}
}
/// <summary>
/// Handle the begin request event
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void UmbracoApplicationEndRequest(object sender, EventArgs e)
{
if (_startupWebProfilerProvider != null)
{
Stop();
_startupWebProfilerProvider = null;
}
else if (CanPerformProfilingAction(sender))
{
Stop();
}
}
/// <summary>
/// Handle the end request event
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void UmbracoApplicationBeginRequest(object sender, EventArgs e)
{
if (_startupWebProfilerProvider != null)
// if this is the first request, notify our own provider that this request is the boot request
var first = Interlocked.Exchange(ref _first, 1) == 0;
if (first)
{
_startupWebProfilerProvider.BootComplete();
_provider.BeginBootRequest();
((HttpApplication)sender).Context.Items[BootRequestItemKey] = true;
// and no need to start anything, profiler is already there
}
if (CanPerformProfilingAction(sender))
{
// else start a profiler, the normal way
else if (ShouldProfile(sender))
Start();
}
}
private bool CanPerformProfilingAction(object sender)
void UmbracoApplicationEndRequest(object sender, EventArgs e)
{
// if this is the boot request, or if we should profile this request, stop
// (the boot request is always profiled, no matter what)
var isBootRequest = ((HttpApplication)sender).Context.Items[BootRequestItemKey] != null; // fixme perfs
if (isBootRequest)
_provider.EndBootRequest();
if (isBootRequest || ShouldProfile(sender))
Stop();
}
private bool ShouldProfile(object sender)
{
if (GlobalSettings.DebugMode == false)
return false;
@@ -108,10 +100,10 @@ namespace Umbraco.Web.Profiling
return false;
if (string.IsNullOrEmpty(request.Result.QueryString["umbDebug"]))
return true;
return false;
if (request.Result.Url.IsBackOfficeRequest(HttpRuntime.AppDomainAppVirtualPath))
return true;
return false;
return true;
}

View File

@@ -0,0 +1,121 @@
using System;
using System.Threading;
using System.Web;
using StackExchange.Profiling;
namespace Umbraco.Web.Profiling
{
/// <summary>
/// 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
/// </summary>
/// <remarks>
/// Once the boot phase is changed to StartupPhase.Request then the base class (default) provider will handle all
/// profiling data and this sub class no longer performs any logic.
/// </remarks>
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;
}
/// <summary>
/// Indicates the boot phase.
/// </summary>
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();
}
}
/// <summary>
/// Executed when a profiling operation is started
/// </summary>
/// <param name="level"></param>
/// <returns></returns>
/// <remarks>
/// This checks if the startup phase is not None, if this is the case and the current profiler is NULL
/// then this sets the startup profiler to be active. Otherwise it just calls the base class Start method.
/// </remarks>
public override MiniProfiler Start(ProfileLevel level)
{
var first = Interlocked.Exchange(ref _first, 1) == 0;
if (first == false) return base.Start(level);
_startupProfiler = new MiniProfiler("http://localhost/umbraco-startup") { Name = "StartupProfiler" };
SetProfilerActive(_startupProfiler);
return _startupProfiler;
}
/// <summary>
/// This returns the current profiler
/// </summary>
/// <returns></returns>
/// <remarks>
/// If the boot phase is not None, then this will return the startup profiler (this), otherwise
/// returns the base class
/// </remarks>
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;
}
}
}
}

View File

@@ -382,7 +382,7 @@
<Compile Include="Mvc\UmbracoRequireHttpsAttribute.cs" />
<Compile Include="Mvc\ValidateMvcAngularAntiForgeryTokenAttribute.cs" />
<Compile Include="OwinMiddlewareConfiguredEventArgs.cs" />
<Compile Include="Profiling\StartupWebProfilerProvider.cs" />
<Compile Include="Profiling\WebProfilerProvider.cs" />
<Compile Include="Profiling\WebProfiler.cs" />
<Compile Include="PropertyEditors\DatePreValueEditor.cs" />
<Compile Include="PropertyEditors\DateTimePreValueEditor.cs" />