Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MiniProfiler: How do I profile an AngularJS + WebAPI app?

Cross-posted on the MiniProfiler community.

I'm trying to throw MiniProfiler into my current stack. I think I'm mostly setup, but am missing the UI approach and would like recommendations on the best way to proceed.

Current Stack

  • SQL for DB (including MiniProfiler tables)
  • EF 6
  • WebAPI 2 API app
  • Angular 1.x. app for the UI (separate app, no MVC backing it) -- I think it's 1.5.x at this point.

So, the current method of RenderIncludes() isn't available to me.

What's the best method to include the JS files and set them up to retrieve the information from the SQL Server storage? I know that the files are included in the UI repo, but I didn't see docs for explicit configuration.

What I've Tried So Far -- Web API App

  • Installed the MiniProfiler and MiniProfiler.EF6 packages.

Web.Config -- Added Handler

(not sure if this is necessary):

<add name="MiniProfiler" path="mini-profiler-resources/*" verb="*" type="System.Web.Routing.UrlRoutingModule" resourceType="Unspecified" preCondition="integratedMode" />

Added a CORS filter to expose the MiniProfiler IDs to my client app:

public class AddMiniProfilerCORSHeaderFilter : ActionFilterAttribute
{
    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        actionExecutedContext.Response.Headers.Add("Access-Control-Expose-Headers", "X-MiniProfiler-Ids");
    }
}

Add that filter as part of startup:

config.Filters.Add(new AddMiniProfilerCORSHeaderFilter());`

In Global.asax, added to Application_Start():

var connectionString = ConfigurationReader.GetConnectionString(Constants.ConfigSettings.CONNECTION_STRING_NAME);

MiniProfiler.Settings.Storage = new SqlServerStorage(connectionString);
MiniProfilerEF6.Initialize();

Update the begin/end requests:

   protected void Application_BeginRequest()
    {
        if (Request.IsLocal || ConfigurationReader.GetAppSetting(Constants.ConfigSettings.USE_PROFILER, false))
        {
            var sessionId = Guid.NewGuid().ToString();
            MiniProfiler.Start(sessionId);
        }
    }

    protected void Application_EndRequest()
    {
        MiniProfiler.Stop();
    }

What I've tried so far -- client (Angular) App

  • Snagged the UI files from the Github Repo
  • Copied the UI directory to my project's output

Reference the CSS:

<link rel="stylesheet" href="js/lib/miniprofiler/includes.css" />

Call the JavaScript

  <script async type="text/javascript" 
    id="mini-profiler" 
    src="js/lib/miniprofiler/includes.js?v=1.0.0.0" 
    data-current-id="" 
    data-path="https://localhost:44378/api/profiler/" 
    data-children="true" 
    data-ids="" 
    data-version="1.0.0.0" 
    data-controls="true" 
    data-start-hidden="false" 
    data-trivial-milliseconds="5">
  </script>

Current Status

When I do these things, it looks like it just can't find the appropriate WebAPI controller to render the result. If I can figure out where that controller is or replicate it (as I'm attempting to do currently) I think I'll be in business.

like image 728
SeanKilleen Avatar asked Jul 07 '16 15:07

SeanKilleen


People also ask

How do I add MiniProfiler to my project?

MiniProfiler provides tons of packages you can use to profile your code: for example, you can profile Entity Framework, Redis, PostgreSql, and more. Once you've installed it, we can add it to our project by updating the Startup class. In the Configure method, you can simply add MiniProfiler to the ASP.NET pipeline:

Is there a good profiling tool for WebAPI projects?

All Posts All Tags Profiling WebApi projects August 06, 2017 Reading time ~8 minutes Introduction In the last few weeks I’ve been working on a couple of WebApi projects and one thing I have been missing, was a good profiling tool to keep an eye on the performance of the controller actions.

Can I use MiniProfiler or glimpse with WebAPI?

Sadly both MiniProfiler and Glimpse don’t work out of the box with WebApi for two main reasons: although WebApi and MVC share similar patterns, they are not exactly the same and run on different namespaces and dlls; secondly WebApi does not usually have a GUI to interact with.

Does MiniProfiler work with MVC websites?

Luckily for us MiniProfiler is designed to also work with ajax calls in mvc websites, so with a few more hacks we can get it to work. Scanning through the MiniProfiler js code we find that it can listen to the xhr object of the page if its an angular app.


1 Answers

The RenderIncludes function results in a <script> tag being output to the page. It is defined in the UI Repo as include.partial.html and currently looks like this:

<script async type="text/javascript" id="mini-profiler" 
        src="{path}includes.js?v={version}" data-version="{version}" 
        data-path="{path}" data-current-id="{currentId}" 
        data-ids="{ids}" data-position="{position}" 
        data-trivial="{showTrivial}" data-children="{showChildren}" 
        data-max-traces="{maxTracesToShow}" data-controls="{showControls}"
        data-authorized="{authorized}" data-toggle-shortcut="{toggleShortcut}" 
        data-start-hidden="{startHidden}" data-trivial-milliseconds="{trivialMilliseconds}">
</script>

This is the piece of Javascript that runs the rendering.

The RenderIncludes function is defined here. It does the following:

  1. Makes sure that you have storage set up
  2. Checks that current request is authorized to view results
  3. Gets the Ids of the unviewed profiles for the current user
  4. Takes the Ids along with any other params that you passed into the function and inserts them into the placeholders in the script defined in include.partial.html
  5. Outputs this <script>

So if you cannot call RenderIncludes, there should be no reason why you cannot just put the script file in place, retrieve the unviewed Ids, but them along with any other setup values you want into the <script> tag, and output the tag.

The key lines of code for retrieving the Id values are:

var ids = authorized 
            ? MiniProfiler.Settings.Storage.GetUnviewedIds(profiler.User) 
            : new List<Guid>();
ids.Add(profiler.Id);

where profiler is the current instance of MiniProfiler (run on the current request.

You will also probably need to make sure that you can handle the call that the script will make to /mini-profiler-resources/results (passing in the id of the profiler as a param). The guts of this is located here in the GetSingleProfilerResult(HttpContext context) function

like image 167
Yaakov Ellis Avatar answered Nov 11 '22 08:11

Yaakov Ellis