Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
LoggingMiddleware.cs 9.09 KiB
using Coscine.ApiCommons.Utils;
using Coscine.Configuration;
using Coscine.Logging;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Internal;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Primitives;
using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Coscine.ApiCommons.Middleware
{
    public class LoggingMiddleware
    {
        private readonly RequestDelegate _next;
        private readonly CoscineLogger _coscineLogger;

        public LoggingMiddleware(ILogger<LoggingMiddleware> logger, RequestDelegate next)
        {
            _next = next;
            _coscineLogger = new CoscineLogger(logger);
        }

        public async Task Invoke(HttpContext context)
        {
            string _uri = "";
            string _request = "";
            string _requestBody = "";
            string _responseBody = "";
            string _statusCode = "";

            using (MemoryStream requestBodyStream = new MemoryStream())
            {
                using (MemoryStream responseBodyStream = new MemoryStream())
                {
                    Stream originalRequestBody = context.Request.Body;
                    Stream originalResponseBody = context.Response.Body;

                    var debugLevelIsActive = CoscineLoggerConfiguration.IsLogLevelActivated(LogType.Debug);

                    if (debugLevelIsActive)
                    {
                        context.Request.EnableRewind();
                    }
                    try
                    {
                        /*******************************************
                         * logging process
                         ******************************************/

                        // reading of the request header
                        _uri = $"{context.Request.Scheme}://{context.Request.Host}{context.Request.Path}{context.Request.QueryString}";
                        _request = $"Uri: {_uri} Method: {context.Request.Method}";

                        CoscineLoggerMetadata.SetUri(_uri);

                        // Get the User Id
                        var authorization = context.Request.Headers["Authorization"].ToArray();
                        string bearer = null;
                        foreach (var line in authorization)
                        {
                            if (line.Contains("Bearer"))
                            {
                                bearer = line;
                            }
                        }
                        if (!string.IsNullOrWhiteSpace(bearer))
                        {
                            bearer = bearer.Replace("Bearer", "").Trim();
                            JWTHandler jwtHandler = new JWTHandler(new ConsulConfiguration());
                            var claims = jwtHandler.GetContents(bearer);
                            var userClaim = (from claimObj in claims
                                             where claimObj.Type == "UserId"
                                             select claimObj).First();
                            CoscineLoggerMetadata.SetUserId(userClaim.Value);
                        }

                        // Get the corrolation Id
                        context.Request.Headers.TryGetValue("X-Coscine-Logging-CorrelationId", out StringValues clientCorrolationId);
                        if (clientCorrolationId.Count == 1)
                        {
                            CoscineLoggerMetadata.SetClientCorrolationId(clientCorrolationId.ToString());
                        }
                        CoscineLoggerMetadata.SetCorrolationId();                        

                        /*******************************************
                         * preparation of the logging process
                         ******************************************/
                        if (debugLevelIsActive)
                        {
                            // write a log entry if fields are missing
                            if (!(context.Request.Method.Equals("GET") || context.Request.Method.Equals("DELETE")) && (context.Request.ContentType == null || context.Request.ContentType == ""))
                            {
                                _coscineLogger.Log(LogType.Debug, $"{_request} No MimeType specified for Request.");
                            }
                            // only log short requests or request that contain json
                            if (context.Request.ContentLength < 500000 || (context.Request.ContentType != null && context.Request.ContentType.Contains("json")))
                            {
                                context.Request.Body.CopyTo(requestBodyStream);
                                requestBodyStream.Seek(0, SeekOrigin.Begin);

                                _requestBody = new StreamReader(requestBodyStream).ReadToEnd();

                                requestBodyStream.Seek(0, SeekOrigin.Begin);
                                context.Request.Body = requestBodyStream;
                            }
                            else
                            {
                                _requestBody = "No Request Body or Request Body to long.";
                            }

                            _coscineLogger.Log(LogType.Debug, $"{_request} RequestBody: {_requestBody}");
                            _responseBody = "Default Response Body";
                            context.Response.Body = responseBodyStream;
                        }

                        /*******************************************
                         * processing of the request
                         ******************************************/

                        await _next(context);

                        /*******************************************
                         * processing of the response
                         ******************************************/

                        _statusCode = $"{context.Response.StatusCode}";
                        CoscineLoggerMetadata.SetStatus(_statusCode);

                        // log every message on high that has a non 200-399 StatusCode
                        if (context.Response.StatusCode < StatusCodes.Status200OK || context.Response.StatusCode >= StatusCodes.Status400BadRequest)
                        {
                            _coscineLogger.Log(LogType.High, $"{_request} StatusCode: {_statusCode}");
                        }

                        if (debugLevelIsActive && context.Response.StatusCode != StatusCodes.Status204NoContent)
                        {
                            // Write a log entry if fields are missing
                            if (context.Response.ContentType == null || context.Response.ContentType == "")
                            {
                                _coscineLogger.Log(LogType.Debug, $"{_request} No MimeType specified for Response.");
                            }

                            // only log short responses or responses that return json
                            if (context.Response.ContentLength < 500000 || (context.Response.ContentType != null && context.Response.ContentType.Contains("json")))
                            {
                                responseBodyStream.Seek(0, SeekOrigin.Begin);
                                _responseBody = new StreamReader(responseBodyStream).ReadToEnd();
                            }
                            else
                            {
                                _responseBody = "No Response Body or Response Body to long.";
                            }
                            
                            responseBodyStream.Seek(0, SeekOrigin.Begin);
                            await responseBodyStream.CopyToAsync(originalResponseBody);
                        }
                        _coscineLogger.Log(LogType.Debug, $"{_request} RequestBody: {_requestBody} StatusCode: {_statusCode} ResponseBody: {_responseBody}");

                    }
                    catch (Exception ex)
                    {
                        _coscineLogger.Log(LogType.Critical, $"An uncaught Exception occured while processing the request: {_request}", ex);
                        if (!context.Response.HasStarted)
                        {
                            context.Response.StatusCode = StatusCodes.Status500InternalServerError;
                            byte[] data = System.Text.Encoding.UTF8.GetBytes("Unhandled Error occured. Please, try again in a while.");
                            context.Response.ContentLength = data.Length;
                            originalResponseBody.Write(data, 0, data.Length);
                        }
                    }
                    finally
                    {
                        context.Request.Body = originalRequestBody;
                        if (context.Response.StatusCode != StatusCodes.Status204NoContent)
                        {
                            context.Response.Body = originalResponseBody;
                        }
                    }
                }
            }
        }
    }
}