【问题标题】:How to trace all HTTP requests in .net core 2.1 globally?如何在全球范围内跟踪 .net core 2.1 中的所有 HTTP 请求?
【发布时间】:2019-11-18 18:11:12
【问题描述】:

我想在 dotnet core 2.1 应用程序中记录所有 HTTP 请求。日志记录应包括 HTTP 标头、正文和主机地址。我需要在不更改现有代码的情况下全局绑定我的日志记录代码。

我试过这个例子https://www.azurefromthetrenches.com/capturing-and-tracing-all-http-requests-in-c-and-net/,但没有HTTP事件到达监听器。

有没有办法在全球范围内监听 dotnet core 2.1 上的 HTTP 事件?

【问题讨论】:

  • 您是否在 IIS 上托管?你能在那里启用日志记录吗?
  • @ste-fu 它适用于我无权访问的容器
  • 您可以使用Middleware
  • 您必须创建示例中引用的 HttpEventListener 类的实例。我过去使用过这种方法,效果很好,也适用于 .NET Core。
  • @bartbje 我做了,但是 OnEventWritten 方法没有发生任何事件。你用的是哪个版本? .NET core 2.1 是否可能缺少此功能? (这里提到medium.com/criteo-labs/…

标签: c# .net-core dotnet-httpclient system.diagnostics


【解决方案1】:

This is a good blog post This is a good blog post 由 Steve Gordon 登录 .Net Core 2.1。

本质上,您需要将 System.Net.Http.HttpClient 的日志记录级别设置为 Trace 以获取有关请求和响应的详细信息。

您的 appsettings.json 中所需部分的示例如下:

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning",
      "System.Net.Http.HttpClient": "Trace"
    }
  }

这将显示所有 HttpClient 请求和响应的所有跟踪日志记录。

【讨论】:

【解决方案2】:

您可以在中间件中记录所有的http请求信息。看看下面的例子

1.创建一个类RequestHandlerMiddleware.cs

using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System.IO;
using System.Threading.Tasks;

namespace Onsolve.ONE.WebApi.Middlewares
{
    public sealed class RequestHandlerMiddleware
    {
        private readonly RequestDelegate next;
        private readonly ILogger logger;

        public RequestHandlerMiddleware(ILogger<RequestHandlerMiddleware> logger, RequestDelegate next)
        {
            this.next = next;
            this.logger = logger;
        }

        public async Task Invoke(HttpContext context)
        {
            logger.LogInformation($"Header: {JsonConvert.SerializeObject(context.Request.Headers, Formatting.Indented)}");

            context.Request.EnableBuffering();
            var body = await new StreamReader(context.Request.Body).ReadToEndAsync();
            logger.LogInformation($"Body: {body}");
            context.Request.Body.Position = 0;

            logger.LogInformation($"Host: {context.Request.Host.Host}");
            logger.LogInformation($"Client IP: {context.Connection.RemoteIpAddress}");
            await next(context);
        }

    }
}

2.在Startup.cs中添加RequestHandlerMiddlewareConfigure方法

app.UseMiddleware<RequestHandlerMiddleware>();

或更简单

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILogger<Startup> logger)
{
    app.Use(async (context, next) =>
    {
        logger.LogInformation($"Header: {JsonConvert.SerializeObject(context.Request.Headers, Formatting.Indented)}");

        context.Request.EnableBuffering();
        var body = await new StreamReader(context.Request.Body).ReadToEndAsync();
        logger.LogInformation($"Body: {body}");
        context.Request.Body.Position = 0;

        logger.LogInformation($"Host: {context.Request.Host.Host}");
        logger.LogInformation($"Client IP: {context.Connection.RemoteIpAddress}");
        await next.Invoke();
    });
}

参考:

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?view=aspnetcore-2.2

https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/write?view=aspnetcore-2.2

【讨论】:

  • 那么这会记录来自他的应用程序的传出http调用吗?中间件如何插入客户端,例如 HttpClient?
  • @LasseVågsætherKarlsen 它只记录传入的请求。你想记录来自 HttpClient 的调用,对吧?
  • OP 在 cmets 中声明他想要记录传出流量,而不是传入流量。
【解决方案3】:

尝试了以下更简单的解决方案,该解决方案不会篡改最终响应流程

创建中间件类来拦截所有请求和响应。在 startup.cs 中启用中间件类关联

app.UseMiddleware<HttpRequestResponseLogger>();

实现一个中间件类来拦截请求和响应。您可以选择将这些日志存储在数据库中。我忽略了秘密和不必要的标头值

 public class HttpRequestResponseLogger
{
    RequestDelegate next;

    public HttpRequestResponseLogger(RequestDelegate next)
    {
        this.next = next;
    }
    //can not inject as a constructor parameter in Middleware because only Singleton services can be resolved
    //by constructor injection in Middleware. Moved the dependency to the Invoke method
    public async Task InvokeAsync(HttpContext context, IHttpLogRepository repoLogs)
    {
        HttpLog logEntry = new HttpLog();
        await RequestLogger(context, logEntry);
        
        await next.Invoke(context);

        await ResponseLogger(context, logEntry);

        //store log to database repository
        repoLogs.SaveLog(logEntry);
    }

    // Handle web request values
    public async Task RequestLogger(HttpContext context, HttpLog log)
    {
        string requestHeaders = string.Empty;

        log.RequestedOn = DateTime.Now;
        log.Method = context.Request.Method;
        log.Path = context.Request.Path;
        log.QueryString = context.Request.QueryString.ToString();
        log.ContentType = context.Request.ContentType;

        foreach (var headerDictionary in context.Request.Headers)
        {
            //ignore secrets and unnecessary header values
            if (headerDictionary.Key != "Authorization" && headerDictionary.Key != "Connection" &&
                headerDictionary.Key != "User-Agent" && headerDictionary.Key != "Postman-Token" &&
                headerDictionary.Key != "Accept-Encoding")
            {
                requestHeaders += headerDictionary.Key + "=" + headerDictionary.Value + ", ";
            }
        }

        if (requestHeaders != string.Empty)
            log.Headers = requestHeaders;

        //Request handling. Check if the Request is a POST call 
        if (context.Request.Method == "POST")
        {
            context.Request.EnableBuffering();
            var body = await new StreamReader(context.Request.Body).ReadToEndAsync();
            context.Request.Body.Position = 0;
            log.Payload = body;
        }
    }

    //handle response values
    public async Task ResponseLogger(HttpContext context, HttpLog log)
    {
        using (Stream originalRequest = context.Response.Body)
        {
            try
            {
                using (var memStream = new MemoryStream())
                {
                    context.Response.Body = memStream;
                    // All the Request processing as described above 
                    // happens from here.
                    // Response handling starts from here
                    // set the pointer to the beginning of the 
                    // memory stream to read
                    memStream.Position = 0;
                    // read the memory stream till the end
                    var response = await new StreamReader(memStream)
                        .ReadToEndAsync();
                    // write the response to the log object
                    log.Response = response;
                    log.ResponseCode = context.Response.StatusCode.ToString();
                    log.IsSuccessStatusCode = (
                        context.Response.StatusCode == 200 ||
                        context.Response.StatusCode == 201);
                    log.RespondedOn = DateTime.Now;

                    // since we have read till the end of the stream, 
                    // reset it onto the first position
                    memStream.Position = 0;

                    // now copy the content of the temporary memory 
                    // stream we have passed to the actual response body 
                    // which will carry the response out.
                    await memStream.CopyToAsync(originalRequest);
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
            finally
            {
                // assign the response body to the actual context
                context.Response.Body = originalRequest;
            }
        }

    }

【讨论】:

    猜你喜欢
    • 2017-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-17
    • 2018-08-09
    • 2012-03-18
    • 2020-01-21
    • 1970-01-01
    相关资源
    最近更新 更多