由於MVC自身的特點,可以讓我們記錄每一個Controller下Action的執行時間以及View視圖渲染完成的時間,本文采用log4net記錄MVC每個Action的執行時間和View視圖渲染完成時間,以及請求Action時post或get的數據。這樣通過日志記錄的時間方便我們定位哪一個Action和View執行的時間過長,進而采取優化的手段。
監控日志監控的指標如下圖

監控程序實現
改監控程序主要繼承ActionFilterAttribute類,並重寫其中的OnActionExecuted、OnActionExecuting、OnResultExecuted、OnResultExecuting幾個方法實現。
1、監控日志對象
/// <summary> /// 監控日志對象 /// </summary> public class MonitorLog { public string ControllerName { get; set; } public string ActionName { get; set; } public DateTime ExecuteStartTime { get; set; } public DateTime ExecuteEndTime { get; set; } /// <summary> /// Form 表單數據 /// </summary> public NameValueCollection FormCollections { get; set; } /// <summary> /// URL 參數 /// </summary> public NameValueCollection QueryCollections { get; set; } /// <summary> /// 監控類型 /// </summary> public enum MonitorType { Action = 1, View = 2 } /// <summary> /// 獲取監控指標日志 /// </summary> /// <param name="mtype"></param> /// <returns></returns> public string GetLoginfo(MonitorType mtype = MonitorType.Action) { string ActionView = "Action執行時間監控:"; string Name = "Action"; if (mtype == MonitorType.View) { ActionView = "View視圖生成時間監控:"; Name = "View"; } string Msg = @" {0} ControllerName:{1}Controller {8}Name:{2} 開始時間:{3} 結束時間:{4} 總 時 間:{5}秒 Form表單數據:{6} URL參數:{7} "; return string.Format(Msg, ActionView, ControllerName, ActionName, ExecuteStartTime, ExecuteEndTime, (ExecuteEndTime - ExecuteStartTime).TotalSeconds, GetCollections(FormCollections), GetCollections(QueryCollections), Name); } /// <summary> /// 獲取Post 或Get 參數 /// </summary> /// <param name="Collections"></param> /// <returns></returns> public string GetCollections(NameValueCollection Collections) { string Parameters = string.Empty; if (Collections == null || Collections.Count == 0) { return Parameters; } foreach (string key in Collections.Keys) { Parameters += string.Format("{0}={1}&", key, Collections[key]); } if (!string.IsNullOrWhiteSpace(Parameters) && Parameters.EndsWith("&")) { Parameters = Parameters.Substring(0, Parameters.Length - 1); } return Parameters; } }
2、監控類
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)] public class StatisticsTrackerAttribute : ActionFilterAttribute,IExceptionFilter { private readonly string Key = "_thisOnActionMonitorLog_"; #region Action時間監控 public override void OnActionExecuting(ActionExecutingContext filterContext) { MonitorLog MonLog = new MonitorLog(); MonLog.ExecuteStartTime = Convert.ToDateTime(DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss.ffff", DateTimeFormatInfo.InvariantInfo)); MonLog.ControllerName = filterContext.RouteData.Values["controller"] as string; MonLog.ActionName = filterContext.RouteData.Values["action"] as string; filterContext.Controller.ViewData[Key] = MonLog; } public override void OnActionExecuted(ActionExecutedContext filterContext) { MonitorLog MonLog = filterContext.Controller.ViewData[Key] as MonitorLog; MonLog.ExecuteEndTime = DateTime.Now; MonLog.FormCollections = filterContext.HttpContext.Request.Form;//form表單提交的數據 MonLog.QueryCollections = filterContext.HttpContext.Request.QueryString;//Url 參數 LoggerHelper.Monitor(MonLog.GetLoginfo()); } #endregion #region View 視圖生成時間監控 public override void OnResultExecuting(ResultExecutingContext filterContext) { MonitorLog MonLog = filterContext.Controller.ViewData[Key] as MonitorLog; MonLog.ExecuteStartTime = DateTime.Now; } public override void OnResultExecuted(ResultExecutedContext filterContext) { MonitorLog MonLog = filterContext.Controller.ViewData[Key] as MonitorLog; MonLog.ExecuteEndTime = DateTime.Now; LoggerHelper.Monitor(MonLog.GetLoginfo(MonitorLog.MonitorType.View)); filterContext.Controller.ViewData.Remove(Key); } #endregion #region 錯誤日志 public void OnException(ExceptionContext filterContext) { if (!filterContext.ExceptionHandled) { string ControllerName =string.Format("{0}Controller",filterContext.RouteData.Values["controller"] as string); string ActionName = filterContext.RouteData.Values["action"] as string; string ErrorMsg = string.Format("在執行 controller[{0}] 的 action[{1}] 時產生異常", ControllerName, ActionName); LoggerHelper.Error(ErrorMsg, filterContext.Exception); } } #endregion }
3、引用監控
我們可以在每個Controller類上或Action上直接引用 [StatisticsTracker]即可完成對該Controller或Action的監控。
我們也可以在FilterConfig.cs中注冊全局監控,這樣我們就可以監控每一個Controller中的Action,代碼如下:
public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new HandleErrorAttribute()); //監控引用 filters.Add(new StatisticsTrackerAttribute()); } }
LoggerHelper
log文件的記錄采用log4net,log4net是.Net下一個非常優秀的開源日志記錄組件。log4net記錄日志的功能非常強大。具體配置如下。
1、log4net配置文件
log4Net的配置文件名稱為log4net.config,具體配置如下。
<?xml version="1.0"?> <configuration> <configSections> <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/> </configSections> <log4net> <!--錯誤日志--> <appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender"> <file value="log\\LogError\\"/> <appendToFile value="true"/> <rollingStyle value="Date"/> <datePattern value="yyyy\\yyyyMM\\yyyyMMdd'.txt'"/> <staticLogFileName value="false"/> <param name="MaxSizeRollBackups" value="100"/> <layout type="log4net.Layout.PatternLayout"> <!--每條日志末尾的文字說明--> <!--輸出格式--> <!--樣例:2008-03-26 13:42:32,111 [10] INFO Log4NetDemo.MainClass [(null)] - info--> <conversionPattern value="%newline %n記錄時間:%date %n線程ID:[%thread] %n日志級別: %-5level %n錯誤描述:%message%newline %n"/> </layout> </appender> <!--Info日志--> <appender name="InfoAppender" type="log4net.Appender.RollingFileAppender"> <param name="File" value="Log\\LogInfo\\" /> <param name="AppendToFile" value="true" /> <param name="MaxFileSize" value="10240" /> <param name="MaxSizeRollBackups" value="100" /> <param name="StaticLogFileName" value="false" /> <param name="DatePattern" value="yyyy\\yyyyMM\\yyyyMMdd'.txt'" /> <param name="RollingStyle" value="Date" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%newline %n記錄時間:%date %n線程ID:[%thread] %n日志級別: %-5level %n日志描述:%message%newline %n"/> </layout> </appender> <!--監控日志--> <appender name="MonitorAppender" type="log4net.Appender.RollingFileAppender"> <param name="File" value="Log\\LogMonitor\\" /> <param name="AppendToFile" value="true" /> <param name="MaxFileSize" value="10240" /> <param name="MaxSizeRollBackups" value="100" /> <param name="StaticLogFileName" value="false" /> <param name="DatePattern" value="yyyy\\yyyyMM\\yyyyMMdd'.txt'" /> <param name="RollingStyle" value="Date" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%newline %n記錄時間:%date %n線程ID:[%thread] %n日志級別: %-5level %n跟蹤描述:%message%newline %n"/> </layout> </appender> <!--Error日志--> <logger name="logerror"> <level value="ERROR" /> <appender-ref ref="RollingLogFileAppender" /> </logger> <!--Info日志--> <logger name="loginfo"> <level value="INFO" /> <appender-ref ref="InfoAppender" /> </logger> <!--監控日志--> <logger name="logmonitor"> <level value="Monitor" /> <appender-ref ref="MonitorAppender" /> </logger> </log4net> </configuration>
2、注冊log4net配置文件
在Global.asax中注冊log4net配置文件,代碼如下
protected void Application_Start() { //注冊 log4net log4net.Config.XmlConfigurator.Configure( new System.IO.FileInfo(AppDomain.CurrentDomain.BaseDirectory + "\\Config\\log4net.config") ); AreaRegistration.RegisterAllAreas(); WebApiConfig.Register(GlobalConfiguration.Configuration); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); BundleConfig.RegisterBundles(BundleTable.Bundles); }
3、LoggerHelper.cs
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace Monitor.Models.ActionFilters { public class LoggerHelper { static readonly log4net.ILog loginfo = log4net.LogManager.GetLogger("loginfo"); static readonly log4net.ILog logerror = log4net.LogManager.GetLogger("logerror"); static readonly log4net.ILog logmonitor = log4net.LogManager.GetLogger("logmonitor"); public static void Error(string ErrorMsg, Exception ex = null) { if (ex != null) { logerror.Error(ErrorMsg, ex); } else { logerror.Error(ErrorMsg); } } public static void Info(string Msg) { loginfo.Info(Msg); } public static void Monitor(string Msg) { logmonitor.Info(Msg); } } }
4.log4net日志生成的文件目錄結構如下圖

目錄結構我們區分開了錯誤日志、Info日志、監控日志,並且會按照日期生成日志,方便我們查看。
