Using MiniProfiler with FSharp
What is MiniProfiler, and why should you care?
According to it’s website:
Well, “performance is a feature”, and MiniProfiler helps a lot with that: Profile you code, see calls to your storage (including SQL, and there are others)…
The interesting part for F# developers is that is runs on .NET Core too, therefore should work with Giraffe and Saturn as well. In fact, it does work!
MiniProfiler traces can help with debugging on your machine too. You see exactly what did happen, not just guessing…
Also, if you save the traces in production, you can get the current profile’s
idfrom code, and save that info in your exception log. So profiling info is immediately available during troubleshooting!
But there are other performance and crash monitoring tools?
Yes, but this was working for me for years, and stores all data locally. Not tied to any third-party services. You won’t be leaking sensitive personal, financial, business etc information accidentally!
Getting it running is pretty easy despite that there is no official support for Giraffe / Saturn / SAFE-Stack app.
- Add the required nuget package
- Configure profiling
Ok, there were some gotchas, but it does work! Let’s get to the details!
First, add nuget dependency
This package contains Asp Net Core bits, and pulls in the core MiniProfiler package as dependency.
This was straight forward, just followed the samples in the docs. OK, syntax is slightly different, but was pretty easy to figure out:
At this point if you run your app, you should start seeing MiniProfiler’s trace ids in the response headers:
So far so good, but you still can’t see it’s UI popup, let’s do that!
Because the profiler itself works, and you can check your result at http://localhost:8080/mini-profiler-resources/results-index. That’s the default path, but it is configurable.
But you lose two things:
- Seeing the results right away, in the same browser tab, without clicking around
- Client timings
MiniProfiler reports back how long the request took from the client’s point of view. To look up DNS, send the request, network latency, parse everything and render the DOM! This tells a lot more than just server processing time!
Or look around in the source, and find the part that does the real job! It helps that I used MiniProfiler for years, so I knew that there was a non-tag-helper method to render the includes, called
All that needs is an
HttpContext, and returns a string. So you can use it in Giraffe’s view engine, just pass in the
HttpContext as a parameter, and you are good to go!
And you are done:
You can profile “ajax” (is that still called that way, or it’s me being ancient?) calls as well. How does it work? Actually the includes monkey-patch the global XHR, fetch objects to enable profiling. Monkey-patching global objects is not something you should, but since those APIs weren’t built with intercepting and profiling in mind, I guess it’s OK, it works, and had no problems with it for several years.
One thing to note: If you want the profiler to catch your ajax / XHR / fetch request, add the includes before your main JS bundle. If the result doesn’t show up, just open the console, and send and empty
$.get()to your server. Previous results should show up.
What about other type of apps?
Console .NET Core apps are supported by MiniProfiler, here are the docs.
Freya and WebSharper
Honestly? Don’t know for sure. But as far as I know, both do run on top of Asp.NET Core and Kestrel, so should work.
Suave is it’s own webserver, there is no
HttpContext, so you would have to do all the plumbing to properly get it running and profiling your Http requests.
Conclusions / next steps
Yeyy I have profiling for my F# SAFE-app, I’m happy with that!
If you are using it in production, don’t forget to set up authorization, otherwise anybody can see it! Optionally, you can conditionally include the
RenderIncludespart, if you don’t want client timing information.
Also, by default it stores results in an in-memory cache, so change storage to something else (there are storage providers available as NuGet packages).
Is something missing?
Depends on :)
I’m using the SAFE-Stack for SPA application, so pretty much my only server rendered view is the index, in other cases, YMMV, but should work…
Currently, there is no support for view render profiling, doesn’t know about Saturn’s
plugs, so those things won’t show up in the traces. But you can create wrappers for those things!
Also, depends on the whole MVC package, that means more dlls to deploy.
Still, for me, the benefits outweigh those issues and megabytes :)