天天看點

Web APi之異常處理(Exception)以及日志記錄(NLog)(十六)

前言

上一篇文章我們介紹了關于日志記錄用的是Log4net,确實也很挺強大,但是别忘了我們.NET有專屬于我們的日志架構,那就是NLog,相對于Log4net而言,NLog可以說也是一個很好的記錄日志的架構,并且其中的異步日志等都有非常大的改善,本文借此用了最新的NLog來在Web APi中進行記錄日志。

NLog

第一步則是下載下傳我們需要的程式包,包括程式集以及配置檔案

Web APi之異常處理(Exception)以及日志記錄(NLog)(十六)

利用NLog記錄日志同樣可以實作如我們上篇文章利用Log4net來實作的那樣,是以在這裡就不多說,下面我們來講另外一種方式,那就是利用.NET内置的跟蹤級别類來進行記錄日志。進而達到我們所需。

在NLog.config配置檔案中,我們添加如下進行日志的記錄【注意:隻是簡單的利用了NLog,它還是比較強大,更多的詳細内容請到官網或通過其他途徑進行學習】

<targets>         <target name="logfile" xsi:type="File" fileName="${basedir}/WebAPiNLog/${date:format=yyyyMMdd}.log" /> //在根目錄下的WebAPiNlog檔案下生成日志       </targets>       <rules>         <logger name="*" minlevel="Trace" writeTo="logfile" />       </rules>      

第二步

既然是利用.NET内置的跟蹤級别類來實作,那麼我們就需要實作其接口 ITraceWriter ,該接口需要實作如下方法

// 摘要:              //     當且僅當在給定 category 和 level 允許跟蹤時,調用指定的 traceAction 以允許在新的 System.Web.Http.Tracing.TraceRecord             //     中設定值。             //             // 參數:              //   request:             //     目前 System.Net.Http.HttpRequestMessage。它可以為 null,但這樣做将阻止後續跟蹤分析将跟蹤與特定請求關聯。             //             //   category:             //     跟蹤的邏輯類别。使用者可以定義自己的跟蹤。             //             //   level:             //     寫入此跟蹤時所在的 System.Web.Http.Tracing.TraceLevel。             //             //   traceAction:             //     啟用了跟蹤時要調用的操作。在此操作中,調用方應填充給定 System.Web.Http.Tracing.TraceRecord 的各個字段。             void Trace(HttpRequestMessage request, string category, TraceLevel level, Action<TraceRecord> traceAction);      

【注意】利用内置的跟蹤級别則需要引用如下命名空間

using System.Web.Http.Tracing;      

首先建立一個NLogger類并實作如上接口,當然我們也得建立NLog執行個體并利用其執行個體進行級别處理,如下:

private static readonly Logger NlogLogger = LogManager.GetCurrentClassLogger();      

接着我們該如何做呢?我們需要利用 TraceLevel 跟蹤級别,同時結合NLog得到相應的級别資訊,可能建立對象執行個體的過程比較長,我們可以利用Lazy<>來實作延遲加載,代碼如下:

private static readonly Lazy<Dictionary<TraceLevel, Action<string>>> LoggingMap = new Lazy<Dictionary<TraceLevel, Action<string>>>                 (() => new Dictionary<TraceLevel, Action<string>>                  {{ TraceLevel.Info, NlogLogger.Info },                  { TraceLevel.Debug, NlogLogger.Debug },                 { TraceLevel.Error, NlogLogger.Error },                  { TraceLevel.Fatal, NlogLogger.Fatal },                  { TraceLevel.Warn, NlogLogger.Warn }                  });      

然後,我們定義一個屬性來傳回其執行個體的值,如下

private Dictionary<TraceLevel, Action<string>> Logger      {            get { return LoggingMap.Value; }      }      

緊接着我們就是實作上述ITraceWriter接口

public void Trace(System.Net.Http.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);                     Log(record);                 }      }      

我們記錄請求的參數,URL,以及Token、傳回的JSON等等,上述Log方法則如下

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);     }      

第三步 

我們自定義日志特性取名為 NLogFilterAttribute ,我們在通路Action時來進行記載日志也就是需要繼承于 ActionFilterAttribute ,簡單來說代碼如下:

public class NLogFilterAttribute : ActionFilterAttribute      {             public override void OnActionExecuting(HttpActionContext filterContext)             {......}      }      

那麼問題來了,.NET預設确确實實是走得内置的跟蹤級别,我們如何讓其實作我們上述實作的接口的級别呢?

此時我們之前所學就派上了一點用場,我們将其服務容器的關于ITraceWriter接口實作進行替換我們自定義實作的接口即可,用如下一句即可

GlobalConfiguration.Configuration.Services.Replace(typeof(ITraceWriter), new NLogHelper());      

接下來就是從服務容器中擷取我們自定義實作的跟蹤級别

var trace = GlobalConfiguration.Configuration.Services.GetTraceWriter();      

然後調用比如說隔離級别中的Info,擷取其通路的控制器名、Action名稱以及JSON等資料,如下:

trace.Info(filterContext.Request,                      "Controller : " + filterContext.ControllerContext.ControllerDescriptor.ControllerType.FullName + Environment.NewLine +                      "Action : " + filterContext.ActionDescriptor.ActionName,                      "JSON", filterContext.ActionArguments);      

我們簡單個給出所請求的控制器以及需要傳回的資料,如下:

public class AboutController : ApiController         {             [POST("about")]             public string about()             {                 var test = new                 {                     name = "xpy0928",                     gender = "男",                     age = 12                 };                 return Newtonsoft.Json.JsonConvert.SerializeObject(test);             }         }      

接下來我們利用WebAPiTestOnHelpPage進行測試,看結果是否如我們所期望

Web APi之異常處理(Exception)以及日志記錄(NLog)(十六)

結果如我們所期待的那樣

Web APi之異常處理(Exception)以及日志記錄(NLog)(十六)

但是我們的重點是是否生成了相應的日志,我們一起來看下:

Web APi之異常處理(Exception)以及日志記錄(NLog)(十六)

到此,關于利用.NET内置的跟蹤級别結合NLog來實作日志的記錄也就告一段落,接下來我們來看看異常處理

Exception

既然異常處理,那麼我們當然就得利用自定義異常特性實作 ExceptionFilterAttribute ,其基本實作如下:

public class CustomExceptionAttribute:ExceptionFilterAttribute      {             public override void OnException(HttpActionExecutedContext actionExecutedContext)             {....}      }      

關于其實作和上面大同小異,如下

GlobalConfiguration.Configuration.Services.Replace(typeof(ITraceWriter), new NLogHelper());                 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));                 }      

此時我們還需要在日志NLogger類中添加如下一句

//處理異常                 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);                 }      

如此就基本實作了利用NLog記錄異常,當然我們可以自定義個異常類來更好的管理異常例如,如下:

public class ApiExceptions:Exception         {             int ErrorCode { get; set; }             string ErrorDescription { get; set; }             HttpStatusCode HttpStatus { get; set; }         }      

總結

本節我們學習了利用NLog來實作記錄異常通過集合内置的跟蹤級别。NLog是屬于.NET是以就單獨拿來講講,其強大也是不可言喻的。

所有的選擇不過是為了下一次選擇做準備