ASP.NET MVC4中加入Log4Net日志记录功能
前言
在之前的.NET中,微软还没有提供过像样的日志框架,目前能用的一些框架比如Log4Net、NLog、CommonLogging等,虽然多多少少使用起来有点费劲,但这里还是简单分享一下Log4Net的初步使用方法。
在项目中的配置
第一步:首先在项目用引用Log4Net.dll.[管理NuGet程序包中直接搜索就可以啦]
第二步:Web.config文件中进行添加confIGsections的节点
<configuration> <configSections> <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" /> </configSections> </configuration>
第三步:在configSections下添加Log4Net配置节点[这里将日志分类了,以便于理解与查看对应的日志信息]
<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>
在框架的体系里,所有的日志对象都是根日志(root logger)的后代。 因此如果一个日志对象没有在配置文件里显式定义,则框架使用根日志中定义的属性。在<root>标签里,可以定义level级别值和Appender的列表。如果没有定义LEVEL的值,则缺省为DEBUG。可以通过<appender-ref>标签定义日志对象使用的Appender对象。<appender-ref>声明了在其他地方定义的Appender对象的一个引用。在一个logger对象中的设置会覆盖根日志的设置。而对Appender属性来说,子日志对象则会继承父日志对象的Appender列表。这种缺省的行为方式也可以通过显式地设定<logger>标签的additivity属性为false而改变。
在项目中建立自定义Log日志
第四步:在文件里配置以下类:ApiTrackerFilter.cs[Api接口日志],TrackerFilter.cs[基本日志类],LoggerHelper.cs[日志信息处理类],MonitorLog.cs[监控日志类](对于监控日志,则根据个人需求是否建立),其中的BookException是自定义的错误,可以省略。
public class LoggerHelper { private static readonly log4net.ILog LogInfo = log4net.LogManager.GetLogger("LogInfo"); private static readonly log4net.ILog LogError = log4net.LogManager.GetLogger("LogError"); private static readonly log4net.ILog LogMonitor = log4net.LogManager.GetLogger("LogMonitor"); /// <summary> /// 记录Error日志 /// </summary> /// <param name="errorMsg"></param> /// <param name="ex"></param> public static void Error(string errorMsg, Exception ex = null) { if (ex != null) { LogError.Error(errorMsg, ex); } else { LogError.Error(errorMsg); } } /// <summary> /// 记录Info日志 /// </summary> /// <param name="msg"></param> /// <param name="ex"></param> public static void Info(string msg, Exception ex = null) { if (ex != null) { LogInfo.Info(msg, ex); } else { LogInfo.Info(msg); } } /// <summary> /// 记录Monitor日志 /// </summary> /// <param name="msg"></param> public static void Monitor(string msg) { LogMonitor.Info(msg); } }
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)] public class TrackerFilter : 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[this.key] = monLog; } public override void OnActionExecuted(ActionExecutedContext filterContext) { MonitorLog monLog = filterContext.Controller.ViewData[this.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[this.key] as MonitorLog; monLog.ExecuteStartTime = DateTime.Now; } public override void OnResultExecuted(ResultExecutedContext filterContext) { MonitorLog monLog = filterContext.Controller.ViewData[this.key] as MonitorLog; monLog.ExecuteEndTime = DateTime.Now; LoggerHelper.Monitor(monLog.GetLogInfo(MonitorLog.MonitorType.View)); filterContext.Controller.ViewData.Remove(this.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 }
/// <summary> /// 监控日志对象 /// </summary> public class MonitorLog { public MonitorLog() { this.Watch = new Stopwatch(); this.Watch.Start(); } /// <summary> /// 监控类型 /// </summary> public enum MonitorType { /// <summary> /// Action /// </summary> Action = 1, /// <summary> /// 视图 /// </summary> View = 2 } public string ControllerName { get; set; } public string ActionName { get; set; } public Stopwatch Watch { 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 string Raw { get; set; } /// <summary> /// 获取监控指标日志 /// </summary> /// <param name="mtype"></param> /// <returns></returns> public string GetLogInfo(MonitorType mtype = MonitorType.Action) { this.Watch.Stop(); string actionView = "Action执行时间监控:"; string action = "Action"; if (mtype == MonitorType.View) { actionView = "View视图生成时间监控:"; action = "View"; } string msgContent = string.Format( @" {0} ControllerName:{1}Controller {2}Name:{3} 开始时间:{4} 结束时间:{5} 总 时 间:{6}秒", actionView, this.ControllerName, action, this.ActionName, this.ExecuteStartTime, this.ExecuteEndTime, this.Watch.ElapsedMilliseconds); if (!string.IsNullOrEmpty(this.Raw)) { msgContent += @" Raw:" + this.Raw; } else if (this.FormCollections != null) { msgContent += @" Form:" + this.GetCollections(this.FormCollections); } else if (this.QueryCollections != null) { msgContent += @" Query:" + this.GetCollections(this.QueryCollections); } return msgContent; } /// <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; } parameters = collections.Keys.Cast<string>() .Aggregate(parameters, (current, key) => current + string.Format("{0}={1}&", key, collections[key])); if (!string.IsNullOrWhiteSpace(parameters) && parameters.EndsWith("&")) { parameters = parameters.Substring(0, parameters.Length - 1); } return parameters; }
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)] public class ApiTrackerFilter : ActionFilterAttribute { private readonly string key = "_thisOnApiActionMonitorLog_"; public override void OnActionExecuting(HttpActionContext actionContext) { var monLog = new MonitorLog(); monLog.ExecuteStartTime = Convert.ToDateTime(DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss.ffff", DateTimeFormatInfo.InvariantInfo)); monLog.ControllerName = actionContext.ActionDescriptor.ControllerDescriptor.ControllerName; monLog.ActionName = actionContext.ActionDescriptor.ActionName; actionContext.Request.Properties[this.key] = monLog; } public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext) { if (actionExecutedContext.Exception != null) { string controllerName = string.Format( "{0}Controller", actionExecutedContext.ActionContext.ActionDescriptor.ControllerDescriptor.ControllerName); string actionName = actionExecutedContext.ActionContext.ActionDescriptor.ActionName; string errorMsg = string.Format("在执行 controller[{0}] 的 action[{1}] 时产生异常", controllerName, actionName); if (actionExecutedContext.Exception is BookException) { LoggerHelper.Info(errorMsg, actionExecutedContext.Exception); } else { LoggerHelper.Error(errorMsg, actionExecutedContext.Exception); } } if (!actionExecutedContext.Request.Properties.ContainsKey(this.key)) { return; } var monLog = actionExecutedContext.Request.Properties[this.key] as MonitorLog; if (monLog != null) { monLog.ExecuteEndTime = DateTime.Now; monLog.Raw = actionExecutedContext.Request.Content.ReadAsStringAsync().Result; LoggerHelper.Monitor(monLog.GetLogInfo()); } } }
在Global.asax文件中配置以及在MVC的FilterConfig过滤器中配置信息
经过这么多的准备工作,终于最后一步:
1.Global.axax中配置
protected void Application_Start(object sender, EventArgs e) { AreaRegistration.RegisterAllAreas(); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); log4net.Config.XmlConfigurator.Configure(); GlobalConfiguration.Configuration.Filters.Add(new Log.ApiTrackerFilter()); BundleConfig.RegisterBundles(BundleTable.Bundles); }
2.在FilterConfig过滤器中配置信息
public static void RegisterGlobalFilters(GlobalFilterCollection filters) { //监控日志 filters.Add(new Log.TrackerFilter()); filters.Add(new HandleErrorAttribute()); }
至此,所有都成功了,最后运行的结果可以在项目根目录下查看相应日期的Log日志信息。