【问题标题】:how to log http request message(not body) and response message(not body) in asp.net core(Kestrel)如何在asp.net核心(Kestrel)中记录http请求消息(不是正文)和响应消息(不是正文)
【发布时间】:2018-09-06 18:46:35
【问题描述】:

我的请求示例

GET http://localhost:5000/api/values HTTP/1.1
cache-control: no-cache
User-Agent: PostmanRuntime/6.4.1
Accept: */*
Host: localhost:5000
accept-encoding: gzip, deflate
Connection: keep-alive

和回应

HTTP/1.1 200 OK
Date: Wed, 28 Mar 2018 07:18:08 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 19

["value1","value2"]

我想获取所有的 http 日志消息

我应该如何获取请求消息和响应消息

因为我可以找到任何错误的日志(例如错误请求消息(自定义 TCP))

【问题讨论】:

    标签: c# .net-core asp.net-core-mvc asp.net-core-webapi


    【解决方案1】:

    我编写了一个中间件来以 nginx 样式记录(几乎)访问日志。 您可以做大致相同的操作,在 LogData 类中添加和填充您自己的字段,然后更改 DefaultFormatter 函数。

    public class RequestLogMiddleware
    {
    
        public class LogData {
            public IPAddress RemoteAddr {get;set;}
            public string User {get; set;}
            public int ResponseStatus {get; set;}
    
            public string RequestMethod {get;set;}
            public string RequestTimestamp {get;set;}
            public string RequestPath {get;set;}
    
            public string RequestProtocol {get;set;}
            public string UserAgent {get;set;}
    
            public long DurationMs {get;set;}
        }
    
        private readonly RequestDelegate _next;
        private readonly ILogger _logger;
    
        public RequestLogMiddleware(RequestDelegate next, ILoggerFactory factory)
        {
            _next = next;
            _logger = factory.CreateLogger("RequestLog");
    
        }
    
        private Func<LogData, string> _logLineFormatter;
        private Func<LogData,string> logLineFormatter{
            get  {
    
                if (this._logLineFormatter != null) {
                    return this._logLineFormatter;
                } 
                return this.DefaultFormatter();
            }
            set {
                this._logLineFormatter = value;
            }
        }
    
        /// <summary>
        /// Override this to set the default formatter if none was supplied
        /// </summary>
        /// <returns></returns>
        protected Func<LogData, string> DefaultFormatter() {
                return (logData => $"{logData.RemoteAddr} - {logData.User} {logData.RequestTimestamp} \"{logData.RequestMethod} {logData.RequestPath} {logData.RequestProtocol}\" {logData.ResponseStatus} \"{logData.UserAgent}\" {logData.DurationMs}ms");
        }
    
        /// <summary>
        /// Used to set a custom formatter for this instance
        /// </summary>
        /// <param name="formatter"></param>
        public void SetLogLineFormat(Func<LogData, string> formatter) {
            this._logLineFormatter = formatter;
        }
    
        public async Task Invoke(HttpContext context)
        {
    
            var now = DateTime.Now;
            var watch = Stopwatch.StartNew();
            await _next.Invoke(context);
            watch.Stop();
    
            var nowString = now.ToString("yyyy-MM-dd'T'HH:mm:ss.fffzzz", DateTimeFormatInfo.InvariantInfo);
            var user = context.User.Identity.Name ?? "-";
            var request = context.Request.Path +  (string.IsNullOrEmpty(context.Request.QueryString.ToString()) ? "" : context.Request.QueryString.ToString());
            var responseStatus = context.Response.StatusCode;
            var userAgent = context.Request.Headers.ContainsKey("User-Agent") ? context.Request.Headers["User-Agent"].ToString() : "-";
            var protocol = context.Request.Protocol;
            var duration = watch.ElapsedMilliseconds;
            var remoteAddr = context.Connection.RemoteIpAddress;    
            var method = context.Request.Method;
    
            var logData = new LogData {
                RemoteAddr = remoteAddr,
                RequestMethod = method,
                RequestPath = request,
                RequestProtocol = protocol,
                RequestTimestamp = nowString,
                ResponseStatus = responseStatus,
                User = user,
                UserAgent = userAgent,
                DurationMs = duration,
            };
    
            _logger.LogInformation(this.logLineFormatter(logData));
    
        }
    }
    

    只需在Startup.csConfigure 方法中连接它:

    app.UseMiddleware(typeof(RequestLogMiddleware));
    

    确保它是您添加的第一个中间件。

    示例输出:

    info: RequestLog[0]
          ::1 - - 2018-08-10T14:23:04.183+02:00 "GET /api/ping HTTP/1.1" 200 "-" 266ms
    info: RequestLog[0]
          ::1 - - 2018-08-10T14:23:04.569+02:00 "POST /api/search HTTP/1.1" 401 "-" 31ms
    info: RequestLog[0]
          ::1 - - 2018-08-10T14:23:04.375+02:00 "POST /api/search HTTP/1.1" 200 "-" 3453ms
    info: RequestLog[0]
          ::1 - - 2018-08-10T14:23:04.375+02:00 "POST /api/search HTTP/1.1" 200 "-" 4341ms
    info: RequestLog[0]
          ::1 - - 2018-08-10T14:23:08.932+02:00 "POST /api/search HTTP/1.1" 400 "-" 16ms
    

    记录器可以在配置中开启/关闭:

    "Logging": {
        "LogLevel": {
          "Default": "Warning",
          "RequestLog": "Information"
        }
    },
    

    【讨论】:

      【解决方案2】:

      一种选择是创建自定义中间件,然后您可以在其中检查请求/响应以构建所需的日志。

      https://docs.microsoft.com/en-us/aspnet/core/fundamentals/middleware/?tabs=aspnetcore2x#writing-middleware

      中间件允许您在输入时获取请求并在输出时获取响应。

      在上下文(HttpContext)上你应该能够得到你期望的数据——https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.http.httpcontext?view=aspnetcore-2.0

      【讨论】:

      • 谢谢,但请求/响应和 HttpContext 不是原始数据。我想在处理之前在 webService(Kestrel) 中获取数据。因为日志我可以找到任何错误(如错误请求消息(自定义 TCP))-
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-03-12
      • 2017-01-22
      • 1970-01-01
      • 1970-01-01
      • 2017-01-27
      • 2012-12-07
      • 2022-11-11
      相关资源
      最近更新 更多