【问题标题】:How to auto log every request in .NET Core WebAPI?如何在 .NET Core WebAPI 中自动记录每个请求?
【发布时间】:2018-01-10 18:09:47
【问题描述】:

我希望自动记录每个请求。在之前的 .Net Framwork WebAPI 项目中,我曾经注册了一个 delegateHandler 来实现。

WebApiConfig.cs

public static void Register(HttpConfiguration config)
{
    config.MessageHandlers.Add(new AutoLogDelegateHandler());
}

AutoLogDelegateHandler.cs

public class AutoLogDelegateHandler : DelegatingHandler
{

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var requestBody = request.Content.ReadAsStringAsync().Result;

        return await base.SendAsync(request, cancellationToken)
            .ContinueWith(task =>
            {
                HttpResponseMessage response = task.Result;

                //Log use log4net
                _LogHandle(request, requestBody, response);

                return response;
            });
    }
}

日志内容示例:

------------------------------------------------------
2017-08-02 19:34:58,840
uri: /emp/register
body: {
    "timeStamp": 1481013427,
    "id": "0322654451",
    "type": "t3",
    "remark": "system auto reg"
}
response: {"msg":"c556f652fc52f94af081a130dc627433","success":"true"}
------------------------------------------------------

但在.NET Core WebAPI项目中,没有WebApiConfig,或者Global.asaxGlobalConfiguration.Configure(WebApiConfig.Register);处的注册功能

那么有什么方法可以在 .NET Core WebAPI 中实现这一点吗?

【问题讨论】:

    标签: c# asp.net-web-api asp.net-core .net-core log4net


    【解决方案1】:

    ActionFilter 将一直工作,直到您需要记录由 MVC 中间件处理的请求(作为控制器操作)。

    如果您需要记录所有传入请求,则需要使用中间件方法。

    良好的视觉效果explanation:

    请注意,中间件顺序很重要,如果您的日志记录应该在管道执行开始时完成,那么您的中间件应该是第一个。

    来自docs的简单示例:

    public void Configure(IApplicationBuilder app)
        {
            app.Use(async (context, next) =>
            {
                // Do loging
                // Do work that doesn't write to the Response.
                await next.Invoke();
                // Do logging or other work that doesn't write to the Response.
            });
    

    【讨论】:

    • 如果我的 api 在 AWS Api Gateway 上运行,这是否有效……这是我的问题,我需要一些帮助……stackoverflow.com/questions/59167427/…
    • 我想你的意思是 ActionFilter 只能用于记录 MVC 中间件处理的请求?
    【解决方案2】:

    您可以创建自己的过滤器属性...

    public class InterceptionAttribute : ActionFilterAttribute
    {
      public override void OnActionExecuting(HttpActionContext actionContext)
      {
        var x = "This is my custom line of code I need executed before any of the controller actions, for example log stuff";
        base.OnActionExecuting(actionContext);
      }
    }
    

    ...您可以在 GlobalFilters 中注册它,但由于您说您使用的是 .NET Core,因此您可以尝试继续...

    来自docs.microsoft.com

    您可以全局注册过滤器(适用于所有控制器和操作) 通过将其添加到 MvcOptions.Filters 集合中 Startup 类中的 ConfigureServices 方法:

    让我们知道它是否有效。

    附: 这是whole tutorial on intercepting requests with WebAPI,以防有人需要更多详细信息。

    【讨论】:

    • 是的,它有效。我创建了一个AutoLogAttribute,并将每个请求记录在OnActionExecuted。然后通过将过滤器添加到 Startup 类的 ConfigureServices 方法中的 MvcOptions.Filters 集合来全局注册过滤器。
    • @Eedoh 你是怎么做到的?
    【解决方案3】:

    对于想要一个快速且(非常)肮脏的解决方案用于调试目的的人(适用于 .Net Core 3),这里是 this answer 的扩展,这就是您所需要的......

    app.Use(async (context, next) =>
    {
        var initialBody = context.Request.Body;
    
        using (var bodyReader = new StreamReader(context.Request.Body))
        {
            string body = await bodyReader.ReadToEndAsync();
            Console.WriteLine(body);
            context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(body));
            await next.Invoke();
            context.Request.Body = initialBody;
        }
    });
    

    【讨论】:

    • 谢谢。您能在这里解释一下正文内容操作背后的原因吗?
    • @GGleGrand 已经有一段时间了,但我猜这是因为代码试图读取流两次——一次是在这里登录,一次是“真正的”。所以我需要在记录后重置流。
    【解决方案4】:

    演示:

    AutologArribute.cs(新文件)

    /// <summary>
    /// <see cref="https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters#Dependency injection"/>
    /// </summary>
    public class AutoLogAttribute : TypeFilterAttribute
        {
            public AutoLogAttribute() : base(typeof(AutoLogActionFilterImpl))
            {
    
            }
    
            private class AutoLogActionFilterImpl : IActionFilter
            {
                private readonly ILogger _logger;
                public AutoLogActionFilterImpl(ILoggerFactory loggerFactory)
                {
                    _logger = loggerFactory.CreateLogger<AutoLogAttribute>();
                }
    
                public void OnActionExecuting(ActionExecutingContext context)
                {
                    // perform some business logic work
                }
    
                public void OnActionExecuted(ActionExecutedContext context)
                {
                    //TODO: log body content and response as well
                    _logger.LogDebug($"path: {context.HttpContext.Request.Path}"); 
                }
            }
        }
    

    StartUp.cs

    public void ConfigureServices(IServiceCollection services)
    {
        //....
    
        // https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/filters#filter-scopes-and-order-of-execution
        services.AddMvc(opts=> {
            opts.Filters.Add(new AutoLogAttribute());
        });
    
        //....
    }
    

    【讨论】:

      【解决方案5】:

      这是 .NET Core 2.2 Web API 的完整日志组件。 它将记录请求和响应,包括标头和正文。 只需确保您有一个“日志”文件夹即可。

      AutoLogMiddleWare.cs(新文件)

      public class AutoLogMiddleWare
      {
          private readonly RequestDelegate _next;
      
          public AutoLogMiddleWare(RequestDelegate next)
          {
              _next = next;
          }
      
          public async Task Invoke(HttpContext context)
          {
              try
              {
                  string route = context.Request.Path.Value;
                  string httpStatus = "0";
      
                  // Log Request
                  var originalRequestBody = context.Request.Body;
                  originalRequestBody.Seek(0, SeekOrigin.Begin);
                  string requestBody = new StreamReader(originalRequestBody).ReadToEnd();
                  originalRequestBody.Seek(0, SeekOrigin.Begin);
      
                  // Log Response
                  string responseBody = string.Empty;
                  using (var swapStream = new MemoryStream())
                  {
      
                      var originalResponseBody = context.Response.Body;
                      context.Response.Body = swapStream;
                      await _next(context);
                      swapStream.Seek(0, SeekOrigin.Begin);
                      responseBody = new StreamReader(swapStream).ReadToEnd();
                      swapStream.Seek(0, SeekOrigin.Begin);
                      await swapStream.CopyToAsync(originalResponseBody);
                      context.Response.Body = originalResponseBody;
                      httpStatus = context.Response.StatusCode.ToString();
                  }
      
                  // Clean route
                  string cleanRoute = route;
                  foreach (var c in Path.GetInvalidFileNameChars())
                  {
                      cleanRoute = cleanRoute.Replace(c, '-');
                  }
      
                  StringBuilder sbRequestHeaders = new StringBuilder();
                  foreach (var item in context.Request.Headers)
                  {
                      sbRequestHeaders.AppendLine(item.Key + ": " + item.Value.ToString());
                  }
      
                  StringBuilder sbResponseHeaders = new StringBuilder();
                  foreach (var item in context.Response.Headers)
                  {
                      sbResponseHeaders.AppendLine(item.Key + ": " + item.Value.ToString());
                  }
      
      
                  string filename = DateTime.Now.ToString("yyyyMMdd.HHmmss.fff") + "_" + httpStatus + "_" + cleanRoute + ".log";
                  StringBuilder sbLog = new StringBuilder();
                  sbLog.AppendLine("Status: " + httpStatus + " - Route: " + route);
                  sbLog.AppendLine("=============");
                  sbLog.AppendLine("Request Headers:");
                  sbLog.AppendLine(sbRequestHeaders.ToString());
                  sbLog.AppendLine("=============");
                  sbLog.AppendLine("Request Body:");
                  sbLog.AppendLine(requestBody);
                  sbLog.AppendLine("=============");
                  sbLog.AppendLine("Response Headers:");
                  sbLog.AppendLine(sbResponseHeaders.ToString());
                  sbLog.AppendLine("=============");
                  sbLog.AppendLine("Response Body:");
                  sbLog.AppendLine(responseBody);
                  sbLog.AppendLine("=============");
      
                  var path = Directory.GetCurrentDirectory();
                  string filepath = ($"{path}\\Logs\\{filename}");
                  File.WriteAllText(filepath, sbLog.ToString());
              }
              catch (Exception ex)
              {
                  // It cannot cause errors no matter what
              }
      
          }
      }
      
      public class EnableRequestRewindMiddleware
      {
          private readonly RequestDelegate _next;
      
          public EnableRequestRewindMiddleware(RequestDelegate next)
          {
              _next = next;
          }
      
          public async Task Invoke(HttpContext context)
          {
              context.Request.EnableRewind();
              await _next(context);
          }
      }
      
      public static class EnableRequestRewindExtension
      {
          public static IApplicationBuilder UseEnableRequestRewind(this IApplicationBuilder builder)
          {
              return builder.UseMiddleware<EnableRequestRewindMiddleware>();
          }
      }
      

      Startup.cs(现有文件)

      public void Configure(IApplicationBuilder app, IHostingEnvironment env, IMapper mapper, ILoggerFactory loggerFactory)
      {
          bool isLogEnabled = true; // Replace it by some setting, Idk
      
          if(isLogEnabled)
              app.UseEnableRequestRewind(); // Add this first
      
          (...)
      
          if(isLogEnabled)
              app.UseMiddleware<AutoLogMiddleWare>(); // Add this just above UseMvc()
      
          app.UseMvc();
      }
      

      【讨论】:

        猜你喜欢
        • 2020-03-11
        • 2021-10-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-03-30
        • 2013-11-19
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多