巧用ActionFilterAttribute實(shí)現(xiàn)API日志的記錄
本文轉(zhuǎn)載自微信公眾號「UP技術(shù)控」,作者conan5566。轉(zhuǎn)載本文請聯(lián)系UP技術(shù)控公眾號。
背景
上回提到開發(fā)web api的時候,一般是需要記錄api的輸入輸出信息,方便后續(xù)排查問題;使用的是委托的形式進(jìn)行記錄日志。
使用Func
這次我們使用另外一種方式,F(xiàn)ilter來記錄輸入輸出日志。
實(shí)現(xiàn)方式
1、首先在進(jìn)入action的時候,定義OnActionExecuting。
- public override void OnActionExecuting(ActionExecutingContext context)
 - {
 - base.OnActionExecuting(context);
 - // 后續(xù)添加了獲取請求的請求體,如果在實(shí)際項(xiàng)目中不需要刪除即可
 - long contentLen = context.HttpContext.Request.ContentLength == null ? 0 : context.HttpContext.Request.ContentLength.Value;
 - if (contentLen > 0)
 - {
 - // 讀取請求體中所有內(nèi)容
 - System.IO.Stream stream = context.HttpContext.Request.Body;
 - if (context.HttpContext.Request.Method == "POST")
 - {
 - stream.Position = 0;
 - }
 - byte[] buffer = new byte[contentLen];
 - stream.Read(buffer, 0, buffer.Length);
 - // 轉(zhuǎn)化為字符串
 - RequestBody = System.Text.Encoding.UTF8.GetString(buffer);
 - }
 - ActionArguments = Newtonsoft.Json.JsonConvert.SerializeObject(context.ActionArguments);
 - Stopwatch = new Stopwatch();
 - Stopwatch.Start();
 - }
 
2、定義Stopwatch ,計算方法的耗時。
- private string ActionArguments { get; set; }
 - /// <summary>
 - /// 請求體中的所有值
 - /// </summary>
 - private string RequestBody { get; set; }
 - private Stopwatch Stopwatch { get; set; }
 
3、結(jié)束的時候,把信息打印出來OnActionExecuted。
- public override void OnActionExecuted(ActionExecutedContext context)
 - {
 - base.OnActionExecuted(context);
 - Stopwatch.Stop();
 - string url = context.HttpContext.Request.Host + context.HttpContext.Request.Path + context.HttpContext.Request.QueryString;
 - string method = context.HttpContext.Request.Method;
 - string controller = context.Controller.ToString();
 - string action = context.ActionDescriptor.DisplayName;
 - string token = "";
 - if (context.HttpContext.Request != null && context.HttpContext.Request.Headers != null && context.HttpContext.Request.Headers["Authorization"].Count > 0)
 - {
 - token = context.HttpContext.Request.Headers["Authorization"];
 - }
 - string qs = ActionArguments;
 - dynamic result = context?.Result?.GetType()?.Name == "EmptyResult" ? new { Value = "無返回結(jié)果" } : context?.Result as dynamic;
 - string res = "在返回結(jié)果前發(fā)生了異常";
 - try
 - {
 - if (result != null)
 - {
 - res = Newtonsoft.Json.JsonConvert.SerializeObject(result.Value);
 - }
 - }
 - catch (System.Exception)
 - {
 - res = "日志未獲取到結(jié)果,返回的數(shù)據(jù)無法序列化";
 - }
 - NLogger.Info(
 - $"地址:{url} \n " +
 - $"controller:{controller} \n " +
 - $"action:{action} \n " +
 - $"token:{token} \n " +
 - $"方式:{method} \n " +
 - $"請求體:{RequestBody} \n " +
 - $"參數(shù):{qs}\n " +
 - $"結(jié)果:{res}\n " +
 - $"耗時:{Stopwatch.Elapsed.TotalMilliseconds} 毫秒(指控制器內(nèi)對應(yīng)方法執(zhí)行完畢的時間)");
 - }
 
4、控制器調(diào)用LogAttribute。
- /// <summary>
 - ///
 - /// </summary>
 - [Produces("application/json")]
 - [LogAttribute]
 - [CustomExceptionFilterAttribute]
 - public class DefaultController : Controller
 - {
 - }
 
完整代碼
- using CompanyName.ProjectName.Core;
 - using Microsoft.AspNetCore.Mvc.Filters;
 - using System.Diagnostics;
 - namespace CompanyName.ProjectName.HttpApi.Host.Code
 - {
 - /// <summary>
 - /// 攔截器
 - /// </summary>
 - public class LogAttribute : ActionFilterAttribute
 - {
 - private string ActionArguments { get; set; }
 - /// <summary>
 - /// 請求體中的所有值
 - /// </summary>
 - private string RequestBody { get; set; }
 - private Stopwatch Stopwatch { get; set; }
 - /// <summary>
 - ///
 - /// </summary>
 - /// <param name="context"></param>
 - public override void OnActionExecuting(ActionExecutingContext context)
 - {
 - base.OnActionExecuting(context);
 - // 后續(xù)添加了獲取請求的請求體,如果在實(shí)際項(xiàng)目中不需要刪除即可
 - long contentLen = context.HttpContext.Request.ContentLength == null ? 0 : context.HttpContext.Request.ContentLength.Value;
 - if (contentLen > 0)
 - {
 - // 讀取請求體中所有內(nèi)容
 - System.IO.Stream stream = context.HttpContext.Request.Body;
 - if (context.HttpContext.Request.Method == "POST")
 - {
 - stream.Position = 0;
 - }
 - byte[] buffer = new byte[contentLen];
 - stream.Read(buffer, 0, buffer.Length);
 - // 轉(zhuǎn)化為字符串
 - RequestBody = System.Text.Encoding.UTF8.GetString(buffer);
 - }
 - ActionArguments = Newtonsoft.Json.JsonConvert.SerializeObject(context.ActionArguments);
 - Stopwatch = new Stopwatch();
 - Stopwatch.Start();
 - }
 - /// <summary>
 - ///
 - /// </summary>
 - /// <param name="context"></param>
 - public override void OnActionExecuted(ActionExecutedContext context)
 - {
 - base.OnActionExecuted(context);
 - Stopwatch.Stop();
 - string url = context.HttpContext.Request.Host + context.HttpContext.Request.Path + context.HttpContext.Request.QueryString;
 - string method = context.HttpContext.Request.Method;
 - string controller = context.Controller.ToString();
 - string action = context.ActionDescriptor.DisplayName;
 - string token = "";
 - if (context.HttpContext.Request != null && context.HttpContext.Request.Headers != null && context.HttpContext.Request.Headers["Authorization"].Count > 0)
 - {
 - token = context.HttpContext.Request.Headers["Authorization"];
 - }
 - string qs = ActionArguments;
 - dynamic result = context?.Result?.GetType()?.Name == "EmptyResult" ? new { Value = "無返回結(jié)果" } : context?.Result as dynamic;
 - string res = "在返回結(jié)果前發(fā)生了異常";
 - try
 - {
 - if (result != null)
 - {
 - res = Newtonsoft.Json.JsonConvert.SerializeObject(result.Value);
 - }
 - }
 - catch (System.Exception)
 - {
 - res = "日志未獲取到結(jié)果,返回的數(shù)據(jù)無法序列化";
 - }
 - NLogger.Info(
 - $"地址:{url} \n " +
 - $"controller:{controller} \n " +
 - $"action:{action} \n " +
 - $"token:{token} \n " +
 - $"方式:{method} \n " +
 - $"請求體:{RequestBody} \n " +
 - $"參數(shù):{qs}\n " +
 - $"結(jié)果:{res}\n " +
 - $"耗時:{Stopwatch.Elapsed.TotalMilliseconds} 毫秒(指控制器內(nèi)對應(yīng)方法執(zhí)行完畢的時間)");
 - }
 - }
 - }
 















 
 
 











 
 
 
 