首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >ASP.NET核心请求/响应日志记录中间件

ASP.NET核心请求/响应日志记录中间件
EN

Code Review用户
提问于 2019-04-28 17:05:12
回答 1查看 3.2K关注 0票数 4

我正在寻找关于我的ASP.NET核心请求/响应日志记录中间件的反馈,特别是在降低内存占用和字符串分配方面:

代码语言:javascript
运行
复制
public class RequestResponseLoggingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger _logger;
    private readonly RecyclableMemoryStreamManager _recyclableMemoryStreamManager;
    private const int ReadChunkBufferLength = 4096;

    public RequestResponseLoggingMiddleware(RequestDelegate next, ILogger logger)
    {
        _next = next;
        _logger = logger;
        _recyclableMemoryStreamManager = new RecyclableMemoryStreamManager();
    }

    public async Task Invoke(HttpContext context)
    {
        var requestProfilerModel = new RequestProfilerModel
        {
            RequestTime = new DateTimeOffset(),
            Context = context,
            Request = await FormatRequestAsync(context)
        };

        var originalBody = context.Response.Body;

        using (var newResponseBody = _recyclableMemoryStreamManager.GetStream())
        {
            context.Response.Body = newResponseBody;

            await _next(context);

            newResponseBody.Seek(0, SeekOrigin.Begin);
            await newResponseBody.CopyToAsync(originalBody);

            newResponseBody.Seek(0, SeekOrigin.Begin);
            requestProfilerModel.Response = await FormatResponseAsync(context, newResponseBody);
            requestProfilerModel.ResponseTime = new DateTimeOffset();

            _logger.LogInformation(requestProfilerModel.Request);
            _logger.LogInformation(requestProfilerModel.Response);
        }
    }

    private async Task FormatResponseAsync(HttpContext context, Stream newResponseBody)
    {
        var request = context.Request;
        var response = context.Response;

        return $"Http Response Information: {Environment.NewLine}" +
                $"Schema: {request.Scheme} {Environment.NewLine}" +
                $"Host: {request.Host} {Environment.NewLine}" +
                $"Path: {request.Path} {Environment.NewLine}" +
                $"QueryString: {request.QueryString} {Environment.NewLine}" +
                $"Headers: {Environment.NewLine}" + FormatHeaders(response.Headers) +
                $"StatusCode: {response.StatusCode} {Environment.NewLine}" +
                $"Response Body: {await ReadStreamInChunksAsync(newResponseBody)}";
    }

    private async Task FormatRequestAsync(HttpContext context)
    {
        var request = context.Request;

        return $"Http Request Information: {Environment.NewLine}" +
                    $"Schema:{request.Scheme} {Environment.NewLine}" +
                    $"Host: {request.Host} {Environment.NewLine}" +
                    $"Path: {request.Path} {Environment.NewLine}" +
                    $"QueryString: {request.QueryString} {Environment.NewLine}" +
                    $"Headers: {Environment.NewLine}" + FormatHeaders(request.Headers) +
                    $"Request Body: {await GetRequestBodyAsync(request)}";
    }

    private string FormatHeaders(IHeaderDictionary headers)
    {
        var stringBuilder = new StringBuilder();

        foreach (var (key, value) in headers)
        {
            stringBuilder.AppendLine($"- {key}: {value}");
        }

        return stringBuilder.ToString();
    }

    public async Task GetRequestBodyAsync(HttpRequest request)
    {
        request.EnableBuffering();
        request.EnableRewind();
        using (var stream = _recyclableMemoryStreamManager.GetStream())
        {
            await request.Body.CopyToAsync(stream);
            request.Body.Seek(0, SeekOrigin.Begin);
            return await ReadStreamInChunksAsync(stream);
        }
    }

    private static async Task ReadStreamInChunksAsync(Stream stream)
    {
        stream.Seek(0, SeekOrigin.Begin);
        string result;

        using (var stringWriter = new StringWriter())
        using (var streamReader = new StreamReader(stream))
        {
            var readChunk = new char[ReadChunkBufferLength];
            int readChunkLength;
            //do while: is useful for the last iteration in case readChunkLength < chunkLength
            do
            {
                readChunkLength = await streamReader.ReadBlockAsync(readChunk, 0, ReadChunkBufferLength);
                await stringWriter.WriteAsync(readChunk, 0, readChunkLength);
            } while (readChunkLength > 0);

            result = stringWriter.ToString();
        }

        return result;
    }
}
EN

回答 1

Code Review用户

回答已采纳

发布于 2019-04-28 22:22:58

我建议您使用基于工厂的中间件,而不是您在这里共享的副约定方法。然后,您将能够对DI进行范围限定。

对于字符串级联,可以考虑使用StringBuilder,而不是处处使用加号运算符。我可以看到您在一些地方使用它,但字符串连接与插值“看起来”昂贵。请参考备注部分,看看StringBuilder是否适合您的用例。

尝试使用ReadChunkBufferLength,并考虑将其作为一个可配置的值,并将其设置为正常的默认值。这是如此的帖子可能会提供一些见解。

票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/219315

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档