Web APi之异常处理(NLog)
网上太杂乱,自己重新整理了一下,记录api访问日志,api异常日志和程序异常日志。
NLog
第一步则是下载我们需要的程序包,包括程序集以及配置文件

利用NLog记录日志同样可以实现如我们上篇文章利用Log4net来实现的那样,所以在这里就不多说,下面我们来讲另外一种方式,那就是利用.NET内置的跟踪级别类来进行记录日志。从而达到我们所需。
web.config配置:
<configuration> <configSections> <!--日志--> <section name="nlog" type="NLog.Config.ConfigSectionHandler,NLog" /> </configSections> <nlog> <include file="${basedir}/NLog.config" /><!--包含文件 此处配置为前面引用NLog时添加的NLog.config文件--> </nlog> </configuration> 在NLog.config配置文件中,我们添加如下进行日志的记录【注意:只是简单的利用了NLog,它还是比较强大,更多的详细内容请到官网或通过其他途径进行学习】 <?xml version="1.0" encoding="utf-8" ?> <nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.nlog-project.org/schemas/NLog.xsd NLog.xsd" autoReload="true" throwExceptions="false" internalLogLevel="Off" internalLogFile="c:\temp\nlog-internal.log"> <!-- optional, add some variables https://github.com/nlog/NLog/wiki/Configuration-file#variables --> <variable name="myvar" value="myvalue"/> <!-- See https://github.com/nlog/nlog/wiki/Configuration-file for information on customizing logging rules and outputs. --> <!-- 使用说明 1.一般控制台调式日志(打印到控制台) logger.Trace("GetHotelBrand 操作数据库异常 sqlText : " + sqlText); 2. 一般文本日志,如记录接口,响应值等 请求参数等(记录文本,支持异步), logger.Info("GetHotelBrand 操作数据库异常 sqlText : " + sqlText); 3.错误日志 一般影响到业务流程的正常使用 (记录到DB) logger.ErrorException("GetHotelBrand 操作数据库异常 sqlText : " + sqlText, ex); 4.致命性错误 如金额数据,订单数据操作失败 (发送邮件通知) logger.FatalException("GetHotelBrand 操作数据库异常 sqlText : " + sqlText, ex); --> <targets> <!-- add your targets here See https://github.com/nlog/NLog/wiki/Targets for possible targets. See https://github.com/nlog/NLog/wiki/Layout-Renderers for the possible layout renderers. --> <!--调式打印控制台日志--> <target name="console" xsi:type="ColoredConsole" layout="[${date:format=yyyy-MM-dd HH\:mm\:ss}][${level}] ${message} ${exception}"/> <!-- 记录一般INFO文本日志(启用异步) --> <target name="logfile" xsi:type="AsyncWrapper" queueLimit="5000" overflowAction="Discard"> <target xsi:type="File" fileName="${basedir}/logs/${date:format=yyyyMMdd}.log" layout="${longdate} ${uppercase:${level}} ${message}" maxArchiveFiles="100" /> </target> <!-- 发生错误异常记录数据库日志 --> <target name="database" xsi:type="Database" useTransactions="true" connectionString="Data Source=xxxxxxxx;Initial Catalog=Log;Persist Security Info=True;User ID=sa;Password=123456" commandText="insert into NLogException_HomeinnsInterface([CreateOn],[Origin],[LogLevel], [Message], [Exception],[StackTrace]) values (getdate(), @origin, @logLevel, @message,@exception, @stackTrace);"> <!--日志来源--> <parameter name="@origin" layout="${callsite}"/> <!--日志等级--> <parameter name="@logLevel" layout="${level}"/> <!--日志消息--> <parameter name="@message" layout="${message}"/> <!--异常信息--> <parameter name="@exception" layout="${exception}" /> <!--堆栈信息--> <parameter name="@stackTrace" layout="${stacktrace}"/> </target> <!-- 发生致命错误发送邮件日志 --> <target name="email" xsi:type="Mail" header="-----header------" footer="-----footer-----" layout="${longdate} ${level} ${callsite} ${message} ${exception:format=Message, Type, ShortType, ToString, Method, StackTrace}" html="false" encoding="UTF-8" addNewLines="true" subject="${message}" to="" from="" body="${longdate} ${level} ${callsite} ${message} ${exception:format=Message, Type, ShortType, ToString, Method, StackTrace}" smtpUserName="" enableSsl="false" smtpPassword="" smtpAuthentication="Basic" smtpServer="smtp.163.com" smtpPort="25"> </target> <!-- Write events to a file with the date in the filename. <target xsi:type="File" name="f" fileName="${basedir}/logs/${shortdate}.log" layout="${longdate} ${uppercase:${level}} ${message}" /> --> </targets> <rules> <!-- add your logging rules here --> <logger name="*" minlevel="Trace" writeTo="console" /> <logger name="*" minlevel="Info" writeTo="logfile" /> <!--<logger name="*" minlevel="Error" writeTo="database"/> <logger name="*" minlevel="Fatal" writeTo="email" />--> <!-- Write all events with minimal level of Debug (So Debug, Info, Warn, Error and Fatal, but not Trace) to "f" <logger name="*" minlevel="Debug" writeTo="f" /> --> </rules> </nlog>
第二步
建立Log处理类和异常处理过滤属性
AppLog.cs 异常处理类
public sealed class AppLog : ITraceWriter { //日志写入 private static readonly Logger AppLogger = LogManager.GetCurrentClassLogger(); private static readonly Lazy<Dictionary<TraceLevel, Action<string>>> LoggingMap = new Lazy<Dictionary<TraceLevel, Action<string>>>(() => new Dictionary<TraceLevel, Action<string>> { {TraceLevel.Info,AppLogger.Info }, {TraceLevel.Debug,AppLogger.Debug }, {TraceLevel.Error,AppLogger.Error }, {TraceLevel.Fatal,AppLogger.Fatal }, {TraceLevel.Warn,AppLogger.Warn } }); private Dictionary<TraceLevel, Action<string>> Logger { get { return LoggingMap.Value; } } /// <summary> /// 跟踪编写器接口实现 /// </summary> /// <param name="request"></param> /// <param name="category"></param> /// <param name="level"></param> /// <param name="traceAction"></param> public void Trace(HttpRequestMessage request, string category, TraceLevel level, Action<TraceRecord> traceAction) { if (level != TraceLevel.Off)//未禁用日志跟踪 { if (traceAction != null && traceAction.Target != null) { category = category + Environment.NewLine + "Action Parameters : " + JsonConvert.SerializeObject(traceAction.Target); } var record = new TraceRecord(request, category, level); if (traceAction != null) { traceAction(record); } // traceAction?.Invoke(record); Log(record); } //throw new NotImplementedException(); } /// <summary> /// 日志写入 /// </summary> /// <param name="record"></param> private void Log(TraceRecord record) { var message = new StringBuilder(); /**************************运行日志****************************/ if (!string.IsNullOrWhiteSpace(record.Message)) { message.Append("").Append(record.Message + Environment.NewLine); } if (record.Request != null) { if (record.Request.Method != null) { message.Append("Method : " + record.Request.Method + Environment.NewLine); } if (record.Request.RequestUri != null) { message.Append("").Append("URL : " + record.Request.RequestUri + Environment.NewLine); } if (record.Request.Headers != null && record.Request.Headers.Contains("Token") && record.Request.Headers.GetValues("Token") != null && record.Request.Headers.GetValues("Token").FirstOrDefault() != null) { message.Append("").Append("Token : " + record.Request.Headers.GetValues("Token").FirstOrDefault() + Environment.NewLine); } } if (!string.IsNullOrWhiteSpace(record.Category)) { message.Append("").Append(record.Category); } if (!string.IsNullOrWhiteSpace(record.Operator)) { message.Append(" ").Append(record.Operator).Append(" ").Append(record.Operation); } //***************************异常日志***********************************// if (record.Exception != null && !string.IsNullOrWhiteSpace(record.Exception.GetBaseException().Message)) { var exceptionType = record.Exception.GetType(); message.Append(Environment.NewLine); message.Append("").Append("Error : " + record.Exception.GetBaseException().Message + Environment.NewLine); } //日志写入本地文件 Logger[record.Level](Convert.ToString(message) + Environment.NewLine); } }
AbnormalFilterAttribute.cs 记录调用api产生的异常日志
public class AbnormalFilterAttribute : ExceptionFilterAttribute { public override void OnException(HttpActionExecutedContext actionExecutedContext) { GlobalConfiguration.Configuration.Services.Replace(typeof(ITraceWriter), new AppLog()); var trace = GlobalConfiguration.Configuration.Services.GetTraceWriter(); trace.Error(actionExecutedContext.Request, "Controller : " + actionExecutedContext.ActionContext.ControllerContext.ControllerDescriptor.ControllerType.FullName + Environment.NewLine + "Action : " + actionExecutedContext.ActionContext.ActionDescriptor.ActionName, actionExecutedContext.Exception); var exceptionType = actionExecutedContext.Exception.GetType(); if (exceptionType == typeof(ValidationException)) { var resp = new HttpResponseMessage(HttpStatusCode.BadRequest) { Content = new StringContent(actionExecutedContext.Exception.Message), ReasonPhrase = "ValidationException" }; throw new HttpResponseException(resp); } else if (exceptionType == typeof(UnauthorizedAccessException)) { throw new HttpResponseException(actionExecutedContext.Request.CreateResponse(HttpStatusCode.Unauthorized)); } else { throw new HttpResponseException(actionExecutedContext.Request.CreateResponse(HttpStatusCode.InternalServerError)); } //base.OnException(actionExecutedContext); } }
AppErrorAttribute.cs 记录程序异常信息
public class AppErrorAttribute : HandleErrorAttribute { public override void OnException(ExceptionContext filterContext) { base.OnException(filterContext); if (!filterContext.ExceptionHandled) { string ControllerName = string.Format("{0}Controller", filterContext.RouteData.Values["controller"] as string); string ActionName = filterContext.RouteData.Values["action"] as string; GlobalConfiguration.Configuration.Services.Replace(typeof(ITraceWriter), new AppLog()); var trace = GlobalConfiguration.Configuration.Services.GetTraceWriter(); trace.Error(filterContext.HttpContext.Request.AsHttpRequestMessage(), "Controller : " + ControllerName + Environment.NewLine + "Action : " + ActionName, filterContext.Exception.GetBaseException().Message); var exceptionType = filterContext.Exception.GetType(); if (exceptionType == typeof(ValidationException)) { var resp = new HttpResponseMessage(HttpStatusCode.BadRequest) { Content = new StringContent(filterContext.Exception.Message), ReasonPhrase = "ValidationException" }; throw new HttpResponseException(resp); } else if (exceptionType == typeof(UnauthorizedAccessException)) { throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.Unauthorized)); } else { throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError)); } } } }
LogFilterAttribute.cs Api调用日志记录
public class LogFilterAttribute : ActionFilterAttribute { public override void OnActionExecuting(HttpActionContext actionContext) { GlobalConfiguration.Configuration.Services.Replace(typeof(ITraceWriter), new AppLog()); var trace = GlobalConfiguration.Configuration.Services.GetTraceWriter(); trace.Info( actionContext.Request, "Controller : " + actionContext.ControllerContext.ControllerDescriptor.ControllerType.FullName + Environment.NewLine + "Action : " + actionContext.ActionDescriptor.ActionName, "JSON", actionContext.ActionArguments); //base.OnActionExecuting(actionContext); }
第三步
程序引用配置
App_Data -> FilterConfig.cs
public class FilterConfig { public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new HandleErrorAttribute()); filters.Add(new AppErrorAttribute()); } public static void RegisterHttpFilters(HttpFilterCollection filters) { //记录controler请求日志 //filters.Add(new LogFilterAttribute()); //记录异常 filters.Add(new AbnormalFilterAttribute()); } }
Global.asax.cs
protected void Application_Start() { //日志配置过滤 FilterConfig.RegisterHttpFilters(GlobalConfiguration.Configuration.Filters); }
第四步
使用到的拓展:
HttpRequestBaseExtensions.cs
public static class HttpRequestBaseExtensions { public static HttpRequestMessage AsHttpRequestMessage(this HttpRequestBase request) { if (request == null) { return null; } var httpRequest = new HttpRequestMessage(new HttpMethod(request.HttpMethod), request.Url); request.CopyHeadersTo(httpRequest); if (request.Form != null) { // Avoid a request message that will try to read the request stream twice for already parsed data. httpRequest.Content = new FormUrlEncodedContent(request.Form.AsKeyValuePairs()); } else if (request.InputStream != null) { httpRequest.Content = new StreamContent(request.InputStream); } return httpRequest; } /// <summary> /// Clones an <see cref="HttpWebRequest" /> in order to send it again. /// </summary> /// <param name="message">The message to set headers on.</param> /// <param name="request">The request with headers to clone.</param> public static void CopyHeadersTo(this HttpRequestBase request, HttpRequestMessage message) { if (request == null) { return; } foreach (string headerName in request.Headers) { string[] headerValues = request.Headers.GetValues(headerName); if (!message.Headers.TryAddWithoutValidation(headerName, headerValues)) { message.Content.Headers.TryAddWithoutValidation(headerName, headerValues); } } } }
CollectionExtensions.cs
public static class CollectionExtensions { /// <summary> /// Enumerates all members of the collection as key=value pairs. /// </summary> /// <param name="nvc">The collection to enumerate.</param> /// <returns>A sequence of pairs.</returns> public static IEnumerable<KeyValuePair<string, string>> AsKeyValuePairs(this NameValueCollection nvc) { foreach (string key in nvc) { foreach (string value in nvc.GetValues(key)) { yield return new KeyValuePair<string, string>(key, value); } } } }

浙公网安备 33010602011771号