File: Internal\HostingRequestFinishedLog.cs
Web Access
Project: src\src\Hosting\Hosting\src\Microsoft.AspNetCore.Hosting.csproj (Microsoft.AspNetCore.Hosting)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Collections;
using System.Diagnostics;
using System.Globalization;
 
namespace Microsoft.AspNetCore.Hosting;
 
using static HostingRequestStartingLog;
 
internal sealed class HostingRequestFinishedLog : IReadOnlyList<KeyValuePair<string, object?>>
{
    internal static readonly Func<object, Exception?, string> Callback = (state, exception) => ((HostingRequestFinishedLog)state).ToString();
 
    private const string OriginalFormat = "Request finished {Protocol} {Method} {Scheme}://{Host}{PathBase}{Path}{QueryString} - {StatusCode} {ContentLength} {ContentType} {ElapsedMilliseconds}ms";
    private readonly HostingApplication.Context _context;
 
    private string? _cachedToString;
    public TimeSpan Elapsed { get; }
 
    public int Count => 12;
 
    public KeyValuePair<string, object?> this[int index]
    {
        get
        {
            Debug.Assert(_context.HttpContext != null);
 
            var request = _context.HttpContext.Request;
            var response = _context.HttpContext.Response;
 
            return index switch
            {
                0 => new KeyValuePair<string, object?>("ElapsedMilliseconds", Elapsed.TotalMilliseconds),
                1 => new KeyValuePair<string, object?>(nameof(response.StatusCode), response.StatusCode),
                2 => new KeyValuePair<string, object?>(nameof(response.ContentType), response.ContentType),
                3 => new KeyValuePair<string, object?>(nameof(response.ContentLength), response.ContentLength),
                4 => new KeyValuePair<string, object?>(nameof(request.Protocol), request.Protocol),
                5 => new KeyValuePair<string, object?>(nameof(request.Method), request.Method),
                6 => new KeyValuePair<string, object?>(nameof(request.Scheme), request.Scheme),
                7 => new KeyValuePair<string, object?>(nameof(request.Host), request.Host.Value),
                8 => new KeyValuePair<string, object?>(nameof(request.PathBase), request.PathBase.Value),
                9 => new KeyValuePair<string, object?>(nameof(request.Path), request.Path.Value),
                10 => new KeyValuePair<string, object?>(nameof(request.QueryString), request.QueryString.Value),
                11 => new KeyValuePair<string, object?>("{OriginalFormat}", OriginalFormat),
                _ => throw new ArgumentOutOfRangeException(nameof(index)),
            };
        }
    }
 
    public HostingRequestFinishedLog(HostingApplication.Context context, TimeSpan elapsed)
    {
        _context = context;
        Elapsed = elapsed;
    }
 
    public override string ToString()
    {
        if (_cachedToString == null)
        {
            Debug.Assert(_context.HttpContext != null);
 
            var request = _context.HttpContext.Request;
            var response = _context.HttpContext.Response;
            _cachedToString = $"Request finished {request.Protocol} {request.Method} {request.Scheme}://{request.Host.Value}{request.PathBase.Value}{request.Path.Value}{request.QueryString.Value} - {response.StatusCode.ToString(CultureInfo.InvariantCulture)} {ValueOrEmptyMarker(response.ContentLength)} {EscapedValueOrEmptyMarker(response.ContentType)} {Elapsed.TotalMilliseconds.ToString("0.0000", CultureInfo.InvariantCulture)}ms";
        }
 
        return _cachedToString;
    }
 
    public IEnumerator<KeyValuePair<string, object?>> GetEnumerator()
    {
        for (var i = 0; i < Count; i++)
        {
            yield return this[i];
        }
    }
 
    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}