AB#5820 - Added simple controller and view to test the miniprofiler
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Umbraco.Composing;
|
||||
|
||||
namespace Umbraco.Web.BackOffice.Controllers
|
||||
{
|
||||
public class BackOfficeController : Controller
|
||||
{
|
||||
// GET
|
||||
public IActionResult Index()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ namespace Umbraco.Web.Common.AspNetCore
|
||||
GetAbsoluteUrl = globalSettings.UmbracoPath;
|
||||
}
|
||||
|
||||
public string GetAbsoluteUrl { get; }
|
||||
public string GetAbsoluteUrl { get; } // TODO make absolute
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,7 +169,7 @@ namespace Umbraco.Web.Common.AspNetCore
|
||||
}
|
||||
|
||||
var webProfiler = new WebProfiler(httpContextAccessor);
|
||||
webProfiler.Start();
|
||||
webProfiler.StartBoot();
|
||||
|
||||
return webProfiler;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using StackExchange.Profiling;
|
||||
using Umbraco.Web.Common.Middleware;
|
||||
|
||||
namespace Umbraco.Web.Common.Extensions
|
||||
@@ -13,9 +14,11 @@ namespace Umbraco.Web.Common.Extensions
|
||||
throw new ArgumentNullException(nameof(app));
|
||||
}
|
||||
|
||||
app.UseMiddleware<UmbracoRequestMiddleware>();
|
||||
|
||||
app.UseMiddleware<UmbracoRequestMiddleware>();
|
||||
app.UseMiddleware<MiniProfilerMiddleware>();
|
||||
return app;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,76 +1,118 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Extensions;
|
||||
using StackExchange.Profiling;
|
||||
using StackExchange.Profiling.Internal;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Web.Common.Middleware;
|
||||
|
||||
namespace Umbraco.Web.Common.Runtime.Profiler
|
||||
{
|
||||
public class WebProfiler : IProfiler
|
||||
{
|
||||
private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim();
|
||||
private volatile BootPhase _bootPhase;
|
||||
private MiniProfiler _startupProfiler;
|
||||
|
||||
|
||||
private const string BootRequestItemKey = "Umbraco.Core.Logging.WebProfiler__isBootRequest";
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly WebProfilerProvider _provider;
|
||||
private int _first;
|
||||
|
||||
public WebProfiler(IHttpContextAccessor httpContextAccessor)
|
||||
{
|
||||
// create our own provider, which can provide a profiler even during boot
|
||||
_provider = new WebProfilerProvider();
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
|
||||
MiniProfiler.DefaultOptions.ProfilerProvider = _provider;
|
||||
_bootPhase = BootPhase.Boot;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Normally we would call MiniProfiler.Current.RenderIncludes(...), but because the requeststate is not set, this method does not work.
|
||||
/// We fake the requestIds from the RequestState here.
|
||||
/// </remarks>
|
||||
public string Render()
|
||||
{
|
||||
return MiniProfiler.Current
|
||||
.RenderIncludes(_httpContextAccessor.HttpContext, RenderPosition.Right).ToString();
|
||||
|
||||
var profiler = MiniProfiler.Current;
|
||||
if (profiler == null) return string.Empty;
|
||||
|
||||
var context = _httpContextAccessor.HttpContext;
|
||||
|
||||
var path = (profiler.Options as MiniProfilerOptions)?.RouteBasePath.Value.EnsureTrailingSlash();
|
||||
|
||||
var result = StackExchange.Profiling.Internal.Render.Includes(
|
||||
profiler,
|
||||
path: context.Request.PathBase + path,
|
||||
isAuthorized: true,
|
||||
requestIDs: new List<Guid>{ profiler.Id },
|
||||
position: RenderPosition.Right,
|
||||
showTrivial: profiler.Options.PopupShowTrivial,
|
||||
showTimeWithChildren: profiler.Options.PopupShowTimeWithChildren,
|
||||
maxTracesToShow: profiler.Options.PopupMaxTracesToShow,
|
||||
showControls:profiler.Options.ShowControls,
|
||||
startHidden: profiler.Options.PopupStartHidden);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public IDisposable Step(string name) => MiniProfiler.Current?.Step(name);
|
||||
public IDisposable Step(string name)
|
||||
{
|
||||
return MiniProfiler.Current?.Step(name);
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
MiniProfiler.StartNew();
|
||||
}
|
||||
|
||||
public void StartBoot()
|
||||
{
|
||||
_startupProfiler = MiniProfiler.StartNew("Startup Profiler");
|
||||
}
|
||||
|
||||
public void StopBoot()
|
||||
{
|
||||
_startupProfiler.Stop();
|
||||
}
|
||||
|
||||
public void Stop(bool discardResults = false)
|
||||
{
|
||||
MiniProfiler.Current?.Stop(discardResults);
|
||||
}
|
||||
|
||||
|
||||
public void UmbracoApplicationBeginRequest(HttpContext context)
|
||||
{
|
||||
// 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)
|
||||
if (ShouldProfile(context.Request))
|
||||
{
|
||||
_provider.BeginBootRequest();
|
||||
context.Items[BootRequestItemKey] = true;
|
||||
// and no need to start anything, profiler is already there
|
||||
}
|
||||
// else start a profiler, the normal way
|
||||
else if (ShouldProfile(context.Request))
|
||||
Start();
|
||||
}
|
||||
}
|
||||
|
||||
public void UmbracoApplicationEndRequest(HttpContext context)
|
||||
{
|
||||
// if this is the boot request, or if we should profile this request, stop
|
||||
// (the boot request is always profiled, no matter what)
|
||||
if (context.Items.TryGetValue(BootRequestItemKey, out var isBoot) && isBoot.Equals(true))
|
||||
{
|
||||
_provider.EndBootRequest();
|
||||
Stop();
|
||||
}
|
||||
else if (ShouldProfile(context.Request))
|
||||
if (ShouldProfile(context.Request))
|
||||
{
|
||||
Stop();
|
||||
|
||||
// if this is the first request, append the startup profiler
|
||||
var first = Interlocked.Exchange(ref _first, 1) == 0;
|
||||
if (first)
|
||||
{
|
||||
|
||||
var startupDuration = _startupProfiler.Root.DurationMilliseconds.GetValueOrDefault();
|
||||
MiniProfiler.Current.DurationMilliseconds += startupDuration;
|
||||
MiniProfiler.Current.GetTimingHierarchy().First().DurationMilliseconds += startupDuration;
|
||||
MiniProfiler.Current.Root.AddChild(_startupProfiler.Root);
|
||||
|
||||
_startupProfiler = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,5 +124,15 @@ namespace Umbraco.Web.Common.Runtime.Profiler
|
||||
if (bool.TryParse(request.Cookies["UMB-DEBUG"], out var cUmbDebug)) return cUmbDebug;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,9 @@ namespace Umbraco.Web.Common.Runtime.Profiler
|
||||
_umbracoRequestLifetime.RequestStart +=
|
||||
(sender, context) => _profiler.UmbracoApplicationBeginRequest(context);
|
||||
_umbracoRequestLifetime.RequestEnd += (sender, context) => _profiler.UmbracoApplicationEndRequest(context);
|
||||
|
||||
// Stop the profiling of the booting process
|
||||
_profiler.StopBoot();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using StackExchange.Profiling;
|
||||
using StackExchange.Profiling.Internal;
|
||||
using Umbraco.Core.Cache;
|
||||
|
||||
namespace Umbraco.Web.Common.Runtime.Profiler
|
||||
{
|
||||
public class WebProfilerProvider : DefaultProfilerProvider
|
||||
{
|
||||
private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim();
|
||||
private volatile BootPhase _bootPhase;
|
||||
private int _first;
|
||||
private MiniProfiler _startupProfiler;
|
||||
|
||||
public WebProfilerProvider()
|
||||
{
|
||||
// booting...
|
||||
_bootPhase = BootPhase.Boot;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current profiler.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the boot phase is not Booted, then this will return the startup profiler (this), otherwise
|
||||
/// returns the base class
|
||||
/// </remarks>
|
||||
public override MiniProfiler CurrentProfiler
|
||||
{
|
||||
get
|
||||
{
|
||||
// if not booting then just use base (fast)
|
||||
// no lock, _bootPhase is volatile
|
||||
if (_bootPhase == BootPhase.Booted)
|
||||
return base.CurrentProfiler;
|
||||
|
||||
// else
|
||||
try
|
||||
{
|
||||
var current = base.CurrentProfiler;
|
||||
return current ?? _startupProfiler;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return _startupProfiler;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
CurrentProfiler = _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>
|
||||
/// Starts a new MiniProfiler.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// 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.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public override MiniProfiler Start(string profilerName, MiniProfilerBaseOptions options)
|
||||
{
|
||||
var first = Interlocked.Exchange(ref _first, 1) == 0;
|
||||
if (first == false) return base.Start(profilerName, options);
|
||||
|
||||
_startupProfiler = new MiniProfiler("StartupProfiler", options);
|
||||
CurrentProfiler = _startupProfiler;
|
||||
return _startupProfiler;
|
||||
}
|
||||
|
||||
/// <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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:36804",
|
||||
"sslPort": 44354
|
||||
@@ -15,10 +15,11 @@
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"Umbraco.Web.UI.BackOffice": {
|
||||
"Umbraco.Web.UI.NetCore": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"applicationUrl": "https://localhost:5001;http://localhost:5000",
|
||||
"launchBrowser": false,
|
||||
"applicationUrl": "https://localhost:44354;http://localhost:9000",
|
||||
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
|
||||
@@ -5,13 +5,19 @@ using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Extensions;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using StackExchange.Profiling;
|
||||
using Umbraco.Composing;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Web.BackOffice.AspNetCore;
|
||||
using Umbraco.Web.Common.AspNetCore;
|
||||
using Umbraco.Web.Common.Extensions;
|
||||
using Umbraco.Web.Common.Runtime.Profiler;
|
||||
using Umbraco.Web.Website.AspNetCore;
|
||||
|
||||
|
||||
@@ -43,11 +49,19 @@ namespace Umbraco.Web.UI.BackOffice
|
||||
services.AddUmbracoConfiguration(_config);
|
||||
services.AddUmbracoCore(_webHostEnvironment);
|
||||
services.AddUmbracoWebsite();
|
||||
|
||||
services.AddMvc();
|
||||
services.AddMiniProfiler(options =>
|
||||
{
|
||||
options.ShouldProfile = request => false; // WebProfiler determine and start profiling. We should not use the MiniProfilerMiddleware to also profile
|
||||
});
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
{
|
||||
|
||||
// app.UseMiniProfiler();
|
||||
app.UseUmbracoRequest();
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
@@ -59,9 +73,17 @@ namespace Umbraco.Web.UI.BackOffice
|
||||
app.UseUmbracoBackOffice();
|
||||
|
||||
app.UseRouting();
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapControllerRoute(
|
||||
"backoffice-spa",
|
||||
"/umbraco",
|
||||
new
|
||||
{
|
||||
Controller = "BackOffice",
|
||||
Action = "Index"
|
||||
}
|
||||
);
|
||||
endpoints.MapGet("/", async context =>
|
||||
{
|
||||
await context.Response.WriteAsync($"<html><body>Hello World!{Current.Profiler.Render()}</body></html>");
|
||||
@@ -69,4 +91,25 @@ namespace Umbraco.Web.UI.BackOffice
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public class UmbracoMiniProfilerOptions : MiniProfilerOptions
|
||||
{
|
||||
public UmbracoMiniProfilerOptions()
|
||||
{
|
||||
}
|
||||
|
||||
public new MiniProfiler StartProfiler(string profilerName = null)
|
||||
{
|
||||
return base.StartProfiler();
|
||||
}
|
||||
|
||||
public new Func<HttpRequest, bool> ShouldProfile => (request) =>
|
||||
{
|
||||
if (new Uri(request.GetEncodedUrl(), UriKind.RelativeOrAbsolute).IsClientSideRequest()) return false;
|
||||
if (bool.TryParse(request.Query["umbDebug"], out var umbDebug)) return umbDebug;
|
||||
if (bool.TryParse(request.Headers["X-UMB-DEBUG"], out var xUmbDebug)) return xUmbDebug;
|
||||
if (bool.TryParse(request.Cookies["UMB-DEBUG"], out var cUmbDebug)) return cUmbDebug;
|
||||
return false;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
@using Umbraco.Composing
|
||||
|
||||
@{
|
||||
Layout = null;
|
||||
}
|
||||
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>title</title>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
Hello World
|
||||
@Html.Raw(Current.Profiler.Render())
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -104,8 +104,11 @@ namespace Umbraco.Web.Editors
|
||||
public async Task<ActionResult> Default()
|
||||
{
|
||||
return await RenderDefaultOrProcessExternalLoginAsync(
|
||||
() => View(_ioHelper.BackOfficePath.EnsureEndsWith('/') + "Views/Default.cshtml", new BackOfficeModel(_features, GlobalSettings, _umbracoVersion, _contentSettings,_ioHelper, _treeCollection, _httpContextAccessor, _hostingEnvironment, _runtimeSettings, _securitySettings)),
|
||||
() => View(_ioHelper.BackOfficePath.EnsureEndsWith('/') + "Views/Default.cshtml", new BackOfficeModel(_features, GlobalSettings, _umbracoVersion, _contentSettings, _ioHelper, _treeCollection, _httpContextAccessor, _hostingEnvironment, _runtimeSettings, _securitySettings)));
|
||||
() =>
|
||||
View(_ioHelper.BackOfficePath.EnsureEndsWith('/') + "Views/Default.cshtml", new BackOfficeModel(_features, GlobalSettings, _umbracoVersion, _contentSettings,_ioHelper, _treeCollection, _httpContextAccessor, _hostingEnvironment, _runtimeSettings, _securitySettings)),
|
||||
() =>
|
||||
View(_ioHelper.BackOfficePath.EnsureEndsWith('/') + "Views/Default.cshtml", new BackOfficeModel(_features, GlobalSettings, _umbracoVersion, _contentSettings, _ioHelper, _treeCollection, _httpContextAccessor, _hostingEnvironment, _runtimeSettings, _securitySettings))
|
||||
);
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
|
||||
Reference in New Issue
Block a user