How to auto log every request in .NET Core WebAPI?

Viewed 36518

I'd like to have every request logged automatically. In previous .Net Framwork WebAPI project, I used to register a delegateHandler to do so.

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;
            });
    }
}

an example of the log content:

------------------------------------------------------
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"}
------------------------------------------------------

But in .NET Core WebAPI project, there is no WebApiConfig , or the register function at Global.asax GlobalConfiguration.Configure(WebApiConfig.Register);

So is there any way to achieve that in .NET Core WebAPI?

5 Answers

For someone that wants a quick and (very) dirty solution for debugging purposes (that works on .Net Core 3), here's an expansion of this answer that's all you need...

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;
    }
});

This is a complete Log component for .NET Core 2.2 Web API. It will log Requests and Responses, both Headers and Bodies. Just make sure you have a "Logs" folder.

AutoLogMiddleWare.cs (New file)

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 (existing file)

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();
}
Related