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


前言

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

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進行測試,看結果是否如我們所期望

結果如我們所期待的那樣

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

到此,關於利用.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所以就單獨拿來講講,其強大也是不可言喻的。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM