MiniProfiler,一个.NET简单但有效的微型分析器

背景

MVC MiniProfiler是Stack Overflow团队设计的一款对ASP.NET MVC的性能分析的小程序。可以对一个页面本身,及该页面通过直接引用、Ajax、Iframe形式访问的其它页面进行监控,监控内容包括数据库内容,并可以显示数据库访问的SQL(支持EF、EF CodeFirst等 )。并且以很友好的方式展现在页面上。

该Profiler的一个特别有用的功能是它与数据库框架的集成。除了.NET原生的 DbConnection类,profiler还内置了对实体框架(Entity Framework)以及LINQ to SQL的支持。任何执行的Step都会包括当时查询的次数和所花费的时间。为了检测常见的错误,如N+1反模式,profiler将检测仅有参数值存在差 异的多个查询。

MiniProfiler是以Apache License V2.0协议发布的,你可以在NuGet找到。配置及使用可以看这里:http://code.google.com/p/mvc-mini-profiler

概述

在WebApi中,对其性能进行分析监测是很有必要的。而悲剧的是,MVC项目中可以使用的MiniProfiler或Glimpse等,这些都不支持WebApi项目,而且WebApi项目通常也没有界面,不能进行性能分析的交互。在这篇文章中,我们就来一步步实现为WebApi项目集成Miniprofiler。集成后,我们可以监控EF执行效率,执行语句,页面执行时间等,这些结果将以很友好的方式显示在界面上。

问题

本质上,集成Miniprofiler可以分解为三个问题:

  1. 怎样监测一个WebApi项目的性能。

  2. 将性能分析监测信息从后端发送到UI。

  3. 在UI显示分析监测结果。

实现方式

首先安装Miniprofiler,MiniProfiler.EF6

在Global.asax  加入

using BQoolCommon.Helpers.Model;
using Elmah;
using ResearchManager.Web.App_Start;
using System;
using System.Configuration;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using StackExchange.Profiling;
using StackExchange.Profiling.Mvc;
using StackExchange.Profiling.EntityFramework6;
using System.Web.Optimization;
using NLog;
using ResearchManager.Models.ValidateAttribute;


namespace ResearchManager.Web
{
    public class MvcApplication : HttpApplication
    {
        protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
            //Remove Header X-AspNetMvc-Version
            MvcHandler.DisableMvcResponseHeader = true;
            AutofacConfig.Bootstrapper();
            //在 Controller 之前對 Model 做處理(字串 Trim)
            ModelBinders.Binders.DefaultBinder = new BQoolModelBinder();
            //註冊自訂的 Validation (複寫預設的錯誤訊息)
            CustomerValidation.RegisterCustomerValidation();


            DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(CustomizedRequired), typeof(RequiredAttributeAdapter));


            DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(CustomizedStringLength), typeof(StringLengthAttributeAdapter));


            #region 测试


            //if (bool.Parse(ConfigurationManager.AppSettings["MiniProfiler"] ?? "false"))
            //{
            MiniProfiler.Configure(new MiniProfilerOptions
            {
                RouteBasePath = "~/profiler",
                PopupRenderPosition = RenderPosition.Right,  // defaults to left
                PopupMaxTracesToShow = 10,                   // defaults to 15
                ResultsAuthorize = request => request.IsLocal,
                ResultsListAuthorize = request =>
              {
                  return true; // all requests are legit in this example
              },
                // Stack trace settings
                StackMaxLength = 256, // default is 120 characters
                TrackConnectionOpenClose = true
            }
                .ExcludeType("SessionFactory")  // Ignore any class with the name of SessionFactory)
               .ExcludeAssembly("NHibernate")  // Ignore any assembly named NHibernate
               .ExcludeMethod("Flush")         // Ignore any method with the name of Flush
               .AddViewProfiling()              // Add MVC view profiling (you want this)
                             );
            MiniProfilerEF6.Initialize();
            //}


            #endregion 测试
        }


        protected void Application_BeginRequest(Object source, EventArgs e)
        {
            BundleTable.EnableOptimizations = false;


            #region 测试


            MiniProfiler.StartNew();


            #endregion 测试
        }


        protected void Application_EndRequest()
        {
            #region 测试


            //MiniProfiler.Current?.Stop(); // Be sure to stop the profiler!


            #endregion 测试
        }


        #region Elmah


        private static readonly string _exceptionMsg = "A potentially dangerous Request.Path value was detected from the client";


        protected void Application_Error(object sender, EventArgs e)
        {
            Exception ex = Server.GetLastError();
            Logger nlogger = LogManager.GetCurrentClassLogger();
            nlogger.Error(ex);


            if (BQoolCommon.Helpers.Setting.CommonSetting.IsProd())
            {
                if (e is ExceptionFilterEventArgs exceptionFilter)
                {
                    if (exceptionFilter.Exception is HttpException httpException && httpException.Message.StartsWith(_exceptionMsg))
                    {
                        Response.Redirect("/");
                    }
                }
                Response.Clear();
                Server.ClearError();
                Response.StatusCode = 404;
            }
        }


        /// <summary>
        /// 排除 Elmah 404 寄信通知
        /// </summary>
        public void ErrorMail_Filtering(object sender, ExceptionFilterEventArgs e)
        {
            if (e.Exception is HttpException httpException && (httpException.GetHttpCode() == 404 || httpException.Message.StartsWith(_exceptionMsg)))
            {
                e.Dismiss();
            }
        }


        /// <summary>
        /// 自定 Elmah 發信主旨
        /// </summary>
        private void ErrorMail_Mailing(object sender, Elmah.ErrorMailEventArgs e)
        {
            string machineName = "none server";
            try
            {
                if (Request != null)
                {
                    machineName = Request.ServerVariables["HTTP_HOST"];
                }
            }
            catch
            {
            }


            // 取得 Elamh ErrorMail 的主旨
            // "$MachineName$ at $ErrorTime$ : {0}"
            string elmahSubject = e.Mail.Subject;
            //替換 ErrorMail 的主旨內容
            string emailSubject = string.Format("ResearchManager.Web Error => {0}",
                elmahSubject
                    .Replace("$MachineName$", machineName)
            );


            e.Mail.Subject = emailSubject;
        }


        #endregion Elmah
    }
}

运行效果

运行项目,http://localhost//profiler/results-index  即可看到监测结果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值