Add project files.
This commit is contained in:
		| @@ -0,0 +1,96 @@ | ||||
| // *********************************************************************** | ||||
| // <copyright file="HttpErrorMiddleware.cs"> | ||||
| //     Heath | ||||
| // </copyright> | ||||
| // *********************************************************************** | ||||
|  | ||||
| using Lib.Common.LoggingAPI.Common.Settings; | ||||
| using Lib.Common.LoggingAPI.Service.Constants; | ||||
| using Lib.Common.LoggingAPI.Service.DataTransferObjects.Error; | ||||
| using Lib.Common.LoggingAPI.Service.Middleware.HttpLogger; | ||||
| using Microsoft.AspNetCore.Http; | ||||
| using Serilog; | ||||
| using System.Text.Json; | ||||
|  | ||||
| namespace Lib.Common.LoggingAPI.Service.Middleware.HttpException | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Handles HTTP logging. | ||||
|     /// </summary> | ||||
|     public class HttpErrorMiddleware | ||||
|     { | ||||
|         private readonly ILogger logger; | ||||
|         private readonly RequestDelegate requestProcess; | ||||
|         public readonly ServiceSettings settings; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Creates a new instrance of <see cref="HttpErrorMiddleware"/>. | ||||
|         /// </summary> | ||||
|         /// <param name="logger">The logger representig an instance of <see cref="ILogger"/>.</param> | ||||
|         /// <param name="requestProcess">The request delegate process.</param> | ||||
|         public HttpErrorMiddleware(ILogger logger, RequestDelegate requestProcess, ServiceSettings settings) | ||||
|         { | ||||
|             this.logger = logger; | ||||
|             this.requestProcess = requestProcess; | ||||
|             this.settings = settings; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Invoke method. | ||||
|         /// </summary> | ||||
|         /// <param name="context">The HTTP context.</param> | ||||
|         /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> | ||||
|         public async Task Invoke(HttpContext context) | ||||
|         { | ||||
|             try | ||||
|             { | ||||
|                 await this.requestProcess(context).ConfigureAwait(false); | ||||
|             } | ||||
|             catch (Extensions.HttpException exception) | ||||
|             { | ||||
|                 await HandleErrorResponse( | ||||
|                     context, | ||||
|                     exception.Message, | ||||
|                     exception.ErrorCode, | ||||
|                     exception.StatusCode).ConfigureAwait(false); | ||||
|             } | ||||
|             catch (Exception defaultException) | ||||
|             { | ||||
|                 await HandleErrorResponse( | ||||
|                     context, | ||||
|                     defaultException.Message, | ||||
|                     ErrorCodes.InternalServerError, | ||||
|                     StatusCodes.Status500InternalServerError).ConfigureAwait(false); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Handles error responses. | ||||
|         /// </summary> | ||||
|         /// <param name="context">The HTTP context.</param> | ||||
|         /// <param name="message">The error message.</param> | ||||
|         /// <param name="errorCode">The error code.</param> | ||||
|         /// <param name="statusCode">The HTTP status code.</param> | ||||
|         /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> | ||||
|         private async Task HandleErrorResponse(HttpContext context, string? message, string? errorCode, int statusCode) | ||||
|         { | ||||
|             var errorMessage = new HttpErrorDto( | ||||
|                 message, | ||||
|                 errorCode, | ||||
|                 string.Format( | ||||
|                     Responses.Target, | ||||
|                     context.Request.Method, | ||||
|                     context.Request.Scheme, | ||||
|                     context.Request.Host.Host, | ||||
|                     context.Request.Path)); | ||||
|  | ||||
|             this.logger.LogError<HttpErrorDto>(context, errorMessage, settings.ServiceId); | ||||
|  | ||||
|             context.Response.ContentType = MimeTypes.ApplicationJson; | ||||
|             context.Response.StatusCode = statusCode; | ||||
|  | ||||
|             await context.Response.WriteAsync(JsonSerializer.Serialize(errorMessage)).ConfigureAwait(false); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -0,0 +1,20 @@ | ||||
| // *********************************************************************** | ||||
| // <copyright file="HttpLoggingMiddlewareExtension.cs"> | ||||
| //     Heath | ||||
| // </copyright> | ||||
| // *********************************************************************** | ||||
|  | ||||
| using Lib.Common.LoggingAPI.Common.Settings; | ||||
| using Microsoft.AspNetCore.Builder; | ||||
|  | ||||
| namespace Lib.Common.LoggingAPI.Service.Middleware.HttpException | ||||
| { | ||||
|     public static class HttpErrorMiddlewareExtension | ||||
|     { | ||||
|         public static IApplicationBuilder UseHttpExceptionHandler(this IApplicationBuilder builder, ServiceSettings settings) | ||||
|         { | ||||
|             return builder.UseMiddleware<HttpErrorMiddleware>(settings); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -0,0 +1,239 @@ | ||||
| // *********************************************************************** | ||||
| // <copyright file="HttpLogger.cs"> | ||||
| //     Heath | ||||
| // </copyright> | ||||
| // *********************************************************************** | ||||
|  | ||||
| using Lib.Common.LoggingAPI.Service.Constants; | ||||
| using Lib.Common.LoggingAPI.Service.DataTransferObjects.Logger; | ||||
| using Microsoft.AspNetCore.Http; | ||||
| using Microsoft.IO; | ||||
| using Serilog; | ||||
| using System.IdentityModel.Tokens.Jwt; | ||||
| using System.Text.Json; | ||||
| using System.Text.Json.Serialization; | ||||
|  | ||||
| namespace Lib.Common.LoggingAPI.Service.Middleware.HttpLogger | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Handles all logging scenarios. | ||||
|     /// </summary> | ||||
|     public static class HttpLogger | ||||
|     { | ||||
|         /// <summary> | ||||
|         /// The JSON serializer options for logging methods. | ||||
|         /// </summary> | ||||
|         public static JsonSerializerOptions serializerOptions = new JsonSerializerOptions | ||||
|         { | ||||
|             DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, | ||||
|             Converters = { | ||||
|                 new JsonStringEnumConverter( JsonNamingPolicy.CamelCase), | ||||
|             }, | ||||
|         }; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Logs an error message. | ||||
|         /// </summary> | ||||
|         /// <typeparam name="TMessage">The generic message parameter.</typeparam> | ||||
|         /// <param name="context">The HTTP context.</param> | ||||
|         /// <param name="message">The message.</param> | ||||
|         /// <param name="serviceId">The service identifier.</param> | ||||
|         public static void LogError<TMessage>(this ILogger logger, HttpContext context, TMessage message, string? serviceId) | ||||
|         { | ||||
|             var logMessage = CreateErrorLog(context, message, serviceId); | ||||
|             logger.Error(logMessage); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Logs an information message. | ||||
|         /// </summary> | ||||
|         /// <typeparam name="TMessage">The generic message parameter.</typeparam> | ||||
|         /// <param name="context">The HTTP context.</param> | ||||
|         /// <param name="message">The message.</param> | ||||
|         /// <param name="serviceId">The service identifier.</param> | ||||
|         public static void LogInfo<TMessage>(this ILogger logger, HttpContext context, TMessage message, string? serviceId) | ||||
|         { | ||||
|             var logMessage = CreateInfoLog(context, message, serviceId); | ||||
|             logger.Information(logMessage); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Logs an incoming HTTP request. | ||||
|         /// </summary> | ||||
|         /// <param name="logger">The logger.</param> | ||||
|         /// <param name="context">The HTTP context.</param> | ||||
|         /// <param name="recyclableMemoryStreamManager">The recyclable mmory stream manager.</param> | ||||
|         /// <param name="serviceId">The service identifier.</param> | ||||
|         /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> | ||||
|         public static async Task LogRequest( | ||||
|             this ILogger logger, | ||||
|             HttpContext context, | ||||
|             RecyclableMemoryStreamManager recyclableMemoryStreamManager, | ||||
|             string? serviceId) | ||||
|         { | ||||
|             context.Request.EnableBuffering(); | ||||
|             await using var requestStream = recyclableMemoryStreamManager.GetStream(); | ||||
|             await context.Request.Body.CopyToAsync(requestStream); | ||||
|  | ||||
|             var logMessage = CreateRequestLog( | ||||
|                 context, | ||||
|                 ReadStream(requestStream), | ||||
|                 serviceId); | ||||
|             logger.Information(logMessage); | ||||
|  | ||||
|             context.Request.Body.Position = 0; | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Logs an outcome HTTP response. | ||||
|         /// </summary> | ||||
|         /// <param name="logger">The logger.</param> | ||||
|         /// <param name="context">The HTTP context.</param> | ||||
|         /// <param name="recyclableMemoryStreamManager">The recyclable mmory stream manager.</param> | ||||
|         /// <param name="requestProcess">The request delegate process.</param> | ||||
|         /// <param name="serviceId">The service identifier.</param> | ||||
|         /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> | ||||
|         ///  | ||||
|         public static async Task LogResponse( | ||||
|             this ILogger logger, | ||||
|             HttpContext context, | ||||
|             RecyclableMemoryStreamManager recyclableMemoryStreamManager, | ||||
|             RequestDelegate requestProcess, | ||||
|             string? serviceId) | ||||
|         { | ||||
|             var originalBodyStream = context.Response.Body; | ||||
|             await using var responseBody = recyclableMemoryStreamManager.GetStream(); | ||||
|             context.Response.Body = responseBody; | ||||
|  | ||||
|             await requestProcess(context); | ||||
|  | ||||
|             context.Response.Body.Seek(0, SeekOrigin.Begin); | ||||
|             var text = await new StreamReader(context.Response.Body).ReadToEndAsync(); | ||||
|             context.Response.Body.Seek(0, SeekOrigin.Begin); | ||||
|  | ||||
|             var logMessage = CreateResponseLog(context, text, serviceId); | ||||
|             logger.Information(logMessage); | ||||
|  | ||||
|             await responseBody.CopyToAsync(originalBodyStream); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Creates an error log. | ||||
|         /// </summary> | ||||
|         /// <typeparam name="TMessage">The generic message.</typeparam> | ||||
|         /// <param name="context">The HTTP context.</param> | ||||
|         /// <param name="message">The error message.</param> | ||||
|         /// <param name="serviceId">The service identifier.</param> | ||||
|         /// <returns>A <see cref="string"/> representig the error log.</returns> | ||||
|         private static string CreateErrorLog<TMessage>(HttpContext context, TMessage message, string? serviceId) | ||||
|             => CreateLog(context, LogSeverity.Error, LogOperation.Error, message, serviceId); | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Creates an info log. | ||||
|         /// </summary> | ||||
|         /// <typeparam name="TMessage">The generic message.</typeparam> | ||||
|         /// <param name="context">The HTTP context.</param> | ||||
|         /// <param name="message">The info message.</param> | ||||
|         /// <param name="serviceId">The service identifier.</param> | ||||
|         /// <returns>A <see cref="string"/> representig the info log.</returns> | ||||
|         private static string CreateInfoLog<TMessage>(HttpContext context, TMessage message, string? serviceId) | ||||
|             => CreateLog(context, LogSeverity.Info, LogOperation.Info, message, serviceId); | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Creates a request log. | ||||
|         /// </summary> | ||||
|         /// <param name="context">The HTTP context.</param> | ||||
|         /// <param name="requestBody">The request body.</param> | ||||
|         /// <param name="serviceId">The service identifier.</param> | ||||
|         /// <returns>A <see cref="string"/> representig the request log.</returns> | ||||
|         private static string CreateRequestLog(HttpContext context, string? requestBody, string? serviceId) | ||||
|             => CreateLog(context, LogSeverity.Info, LogOperation.ClientRequest, requestBody, serviceId); | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Creates a response log. | ||||
|         /// </summary> | ||||
|         /// <param name="context">The HTTP context.</param> | ||||
|         /// <param name="responseBody">The response body.</param> | ||||
|         /// <param name="serviceId">The service identifier.</param> | ||||
|         /// <returns>A <see cref="string"/> representig the response log.</returns> | ||||
|         private static string CreateResponseLog(HttpContext context, string? responseBody, string? serviceId) | ||||
|             => CreateLog(context, LogSeverity.Info, LogOperation.ClientResponse, responseBody, serviceId); | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Creates a generic log. | ||||
|         /// </summary> | ||||
|         /// <param name="context">The HTTP context.</param> | ||||
|         /// <param name="severity">The log severity.</param> | ||||
|         /// <param name="operation">The log operation.</param> | ||||
|         /// <param name="message">The log message</param> | ||||
|         /// <param name="serviceId">The service identifier.</param> | ||||
|         /// <returns>A <see cref="string"/> representing a generic log.</returns> | ||||
|         private static string CreateLog<TMessage>( | ||||
|             HttpContext context, | ||||
|             LogSeverity severity, | ||||
|             LogOperation operation, | ||||
|             TMessage message, | ||||
|             string? serviceId) | ||||
|         { | ||||
|             var tokenHeader = context.Request.Headers[Headers.Authorization].FirstOrDefault()?.Split(" ").Last(); | ||||
|             var tokenHandler = new JwtSecurityTokenHandler(); | ||||
|             var token = (tokenHeader is not null) ? tokenHandler.ReadJwtToken(tokenHeader) : null; | ||||
|  | ||||
|             var log = new LogDetail<TMessage> | ||||
|             { | ||||
|                 Severity = severity, | ||||
|                 Target = new LogTarget | ||||
|                 { | ||||
|                     Method = context.Request.Method, | ||||
|                     Host = context.Request.Host.Host, | ||||
|                     Route = context.Request.Path, | ||||
|                 }, | ||||
|                 Email = token?.Claims.FirstOrDefault(c => c.Type == Claims.Email)?.Value, | ||||
|                 User = token?.Claims.FirstOrDefault(c => c.Type == Claims.Name)?.Value, | ||||
|                 UserId = token?.Claims.FirstOrDefault(c => c.Type == Claims.Id)?.Value, | ||||
|                 Environment = Environment.GetEnvironmentVariable(EnvironmentVariables.Stage), | ||||
|                 Operation = operation, | ||||
|                 RequestId = context.Request.Headers[DisplayNames.RequestId], | ||||
|                 ServiceId = serviceId, | ||||
|                 XForwardedFor = context.Request.Headers[DisplayNames.XForwardedForHeader], | ||||
|                 Timestamp = DateTime.Now, | ||||
|                 Message = message, | ||||
|             }; | ||||
|  | ||||
|             var serializedLog = JsonSerializer.Serialize(log, serializerOptions); | ||||
|  | ||||
|             return serializedLog | ||||
|                 .Replace("\\u0022", "\"") | ||||
|                 .Replace("\"{", "{") | ||||
|                 .Replace("}\"", "}") | ||||
|                 .Replace("\\u0027", "'") | ||||
|                 .Replace("\\\u0027", "'") | ||||
|                 .Replace("\n", ""); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Reads the stream. | ||||
|         /// </summary> | ||||
|         /// <param name="stream">The stream to be read.</param> | ||||
|         /// <returns>A <see cref="string?"/> representig the request body.</returns> | ||||
|         private static string? ReadStream(Stream stream) | ||||
|         { | ||||
|             const int readChunkBufferLength = 4096; | ||||
|             stream.Seek(0, SeekOrigin.Begin); | ||||
|             using var textWriter = new StringWriter(); | ||||
|             using var reader = new StreamReader(stream); | ||||
|             var readChunk = new char[readChunkBufferLength]; | ||||
|             int readChunkLength; | ||||
|             do | ||||
|             { | ||||
|                 readChunkLength = reader.ReadBlock(readChunk, 0, readChunkBufferLength); | ||||
|                 textWriter.Write(readChunk, 0, readChunkLength); | ||||
|             } while (readChunkLength > 0); | ||||
|  | ||||
|             var stringItem = textWriter.ToString(); | ||||
|  | ||||
|             return stringItem != string.Empty ? stringItem : null; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -0,0 +1,70 @@ | ||||
| // *********************************************************************** | ||||
| // <copyright file="HttpLoggingMiddleware.cs"> | ||||
| //     Heath | ||||
| // </copyright> | ||||
| // *********************************************************************** | ||||
|  | ||||
| using Lib.Common.LoggingAPI.Common.Settings; | ||||
| using Microsoft.AspNetCore.Http; | ||||
| using Microsoft.IO; | ||||
| using Serilog; | ||||
|  | ||||
| namespace Lib.Common.LoggingAPI.Service.Middleware.HttpLogger | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Handles HTTP logging. | ||||
|     /// </summary> | ||||
|     public class HttpLoggingMiddleware | ||||
|     { | ||||
|         private readonly ILogger logger; | ||||
|         private readonly RequestDelegate requestProcess; | ||||
|         private readonly ServiceSettings settings; | ||||
|         private readonly RecyclableMemoryStreamManager recyclableMemoryStreamManager; | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Creates a new instrance of <see cref="HttpLoggingMiddleware"/>. | ||||
|         /// </summary> | ||||
|         /// <param name="requestProcess">The request delegate process.</param> | ||||
|         /// <param name="logger">The logger representig an instance of <see cref="ILogger"/>.</param> | ||||
|         /// <param name="settings">The service settings.</param> | ||||
|         public HttpLoggingMiddleware(RequestDelegate requestProcess, ILogger logger, ServiceSettings settings) | ||||
|         { | ||||
|             this.logger = logger; | ||||
|             this.requestProcess = requestProcess; | ||||
|             this.settings = settings; | ||||
|             recyclableMemoryStreamManager = new RecyclableMemoryStreamManager(); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Invoke method. | ||||
|         /// </summary> | ||||
|         /// <param name="context">The HTTP context.</param> | ||||
|         /// <returns></returns> | ||||
|         public async Task Invoke(HttpContext context) | ||||
|         { | ||||
|             await LogRequest(context); | ||||
|             await LogResponse(context); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Logs an incoming HTTP request. | ||||
|         /// </summary> | ||||
|         /// <param name="context">The HTTP context.</param> | ||||
|         /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> | ||||
|         private async Task LogRequest(HttpContext context) | ||||
|         { | ||||
|             await this.logger.LogRequest(context, this.recyclableMemoryStreamManager, this.settings.ServiceId); | ||||
|         } | ||||
|  | ||||
|         /// <summary> | ||||
|         /// Logs an outcome HTTP response. | ||||
|         /// </summary> | ||||
|         /// <param name="context">The HTTP context.</param> | ||||
|         /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns> | ||||
|         private async Task LogResponse(HttpContext context) | ||||
|         { | ||||
|             await this.logger.LogResponse(context, recyclableMemoryStreamManager, requestProcess, this.settings.ServiceId); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -0,0 +1,20 @@ | ||||
| // *********************************************************************** | ||||
| // <copyright file="HttpLoggingMiddlewareExtension.cs"> | ||||
| //     Heath | ||||
| // </copyright> | ||||
| // *********************************************************************** | ||||
|  | ||||
| using Lib.Common.LoggingAPI.Common.Settings; | ||||
| using Microsoft.AspNetCore.Builder; | ||||
|  | ||||
| namespace Lib.Common.LoggingAPI.Service.Middleware.HttpLogger | ||||
| { | ||||
|     public static class HttpLoggingMiddlewareExtension | ||||
|     { | ||||
|         public static IApplicationBuilder UseCustomHttpLogging(this IApplicationBuilder builder, ServiceSettings settings) | ||||
|         { | ||||
|             return builder.UseMiddleware<HttpLoggingMiddleware>(settings); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 Sergio Matias Urquin
					Sergio Matias Urquin