Add project files.

This commit is contained in:
Sergio Matias Urquin
2025-04-29 18:42:29 -06:00
parent 9c1958d351
commit 83fc1878c4
67 changed files with 4586 additions and 0 deletions

View File

@@ -0,0 +1,43 @@
// ***********************************************************************
// <copyright file="ErrorDetailsDto.cs">
// Heath
// </copyright>
// ***********************************************************************
using System.ComponentModel;
using System.Text.Json.Serialization;
namespace Core.Blueprint.Logging
{
/// <summary>
/// The service error details transfer object.
/// </summary>
public class ErrorDetails
{
/// <summary>
/// Gets or sets the service error code.
/// </summary>
/// <example>healthy</example>
[DisplayName(DisplayNames.ErrorCode)]
[JsonPropertyName(DisplayNames.ErrorCode)]
public string? ErrorCode { get; set; }
/// <summary>
/// Gets or sets the service error message.
/// </summary>
/// <example>This is an example message.</example>
[DisplayName(DisplayNames.Message)]
[JsonPropertyName(DisplayNames.Message)]
public string? Message { get; set; }
/// <summary>
/// Gets or sets the service target.
/// </summary>
/// <example>healthy</example>
[DisplayName(DisplayNames.Target)]
[JsonPropertyName(DisplayNames.Target)]
public string? Target { get; set; }
}
}

View File

@@ -0,0 +1,45 @@
// ***********************************************************************
// <copyright file="HttpErrorDto.cs">
// Heath
// </copyright>
// ***********************************************************************
using System.ComponentModel;
using System.Text.Json.Serialization;
namespace Core.Blueprint.Logging
{
/// <summary>
/// The service HTTP error data transfer object.
/// </summary>
public class HttpError
{
/// <summary>
/// Gets or sets the error.
/// </summary>
[DisplayName(DisplayNames.Error)]
[JsonPropertyName(DisplayNames.Error)]
public ErrorDetails Error { get; set; }
/// <summary>
/// Creates a new instance of <see cref="HttpError{TMessage}"/>
/// with custom parameters.
/// </summary>
/// <param name="message">The HTTP error message.</param>
/// <param name="errorCode">The HTTP error code.</param>
/// <param name="target">The HTTP error target.</param>
public HttpError(
string? message,
string? errorCode,
string? target)
{
Error = new ErrorDetails
{
ErrorCode = errorCode,
Message = message,
Target = target,
};
}
}
}

View File

@@ -0,0 +1,41 @@
// ***********************************************************************
// <copyright file="HttpException.cs">
// Heath
// </copyright>
// ***********************************************************************
namespace Core.Blueprint.Logging
{
/// <summary>
/// The service HTTP exception.
/// Extends the <see cref="Exception"/> class.
/// </summary>
public class HttpException : Exception
{
/// <summary>
/// Gets or sets the exception error code.
/// </summary>
public string? ErrorCode { get; set; }
/// <summary>
/// Gets or sets the exception status code.
/// </summary>
public int StatusCode { get; set; }
/// <summary>
/// Creates a new instance of <see cref="HttpException"/>.
/// </summary>
/// <param name="statusCode">The exception status code.</param>
/// <param name="errorCode">The exception error code.</param>
/// <param name="message">The exception message.</param>
public HttpException(
int statusCode,
string errorCode,
string message)
: base(message)
{
ErrorCode = errorCode;
StatusCode = statusCode;
}
}
}

View File

@@ -0,0 +1,120 @@
// ***********************************************************************
// <copyright file="LogDetail.cs">
// Heath
// </copyright>
// ***********************************************************************
using System.ComponentModel;
using System.Text.Json.Serialization;
namespace Core.Blueprint.Logging
{
/// <summary>
/// The service logger detail object.
/// </summary>
/// <typeparam name="TMessage">The generic message type.</typeparam>
public class LogDetail<TMessage>
{
/// <summary>
/// Gets or sets the log severity.
/// </summary>
/// <example>info</example>
[DisplayName(DisplayNames.Severity)]
[JsonPropertyName(DisplayNames.Severity)]
public LogSeverity Severity { get; set; }
/// <summary>
/// Gets or sets the timestamp.
/// </summary>
[DisplayName(DisplayNames.Timestamp)]
[JsonPropertyName(DisplayNames.Timestamp)]
public DateTime Timestamp { get; set; }
/// <summary>
/// Gets or sets the environment.
/// </summary>
/// <example>Development</example>
[DisplayName(DisplayNames.Environment)]
[JsonPropertyName(DisplayNames.Environment)]
public string? Environment { get; set; }
/// <summary>
/// Gets or sets the target.
/// </summary>
[DisplayName(DisplayNames.Target)]
[JsonPropertyName(DisplayNames.Target)]
public LogTarget? Target { get; set; }
/// <summary>
/// Gets or sets the x-forwarded-for header.
/// </summary>
/// <example>localhost</example>
[DisplayName(DisplayNames.XForwardedFor)]
[JsonPropertyName(DisplayNames.XForwardedFor)]
public string? XForwardedFor { get; set; }
/// <summary>
/// Gets or sets the service identifier.
/// </summary>
/// <example><see cref="Guid.NewGuid()"/></example>
[DisplayName(DisplayNames.ServiceId)]
[JsonPropertyName(DisplayNames.ServiceId)]
public string? ServiceId { get; set; }
/// <summary>
/// Gets or sets the request identifier.
/// </summary>
/// <example><see cref="Guid.NewGuid()"/></example>
[DisplayName(DisplayNames.RequestId)]
[JsonPropertyName(DisplayNames.RequestId)]
public string? RequestId { get; set; }
/// <summary>
/// Gets or sets the keyVaultProvider identifier.
/// </summary>
/// <example><see cref="Guid.NewGuid()"/></example>
[DisplayName(DisplayNames.ClientId)]
[JsonPropertyName(DisplayNames.ClientId)]
public string? ClientId { get; set; }
/// <summary>
/// Gets or sets the keyVaultProvider identifier.
/// </summary>
/// <example>keyVaultProviderRequest</example>
[DisplayName(DisplayNames.Operation)]
[JsonPropertyName(DisplayNames.Operation)]
public LogOperation Operation { get; set; }
/// <summary>
/// Gets or sets the user name.
/// </summary>
/// <example>keyVaultProviderRequest</example>
[DisplayName(DisplayNames.User)]
[JsonPropertyName(DisplayNames.User)]
public string? User { get; set; }
/// <summary>
/// Gets or sets user's email.
/// </summary>
/// <example>keyVaultProviderRequest</example>
[DisplayName(DisplayNames.Email)]
[JsonPropertyName(DisplayNames.Email)]
public string? Email { get; set; }
/// <summary>
/// Gets or sets the user identifier.
/// </summary>
/// <example>keyVaultProviderRequest</example>
[DisplayName(DisplayNames.UserId)]
[JsonPropertyName(DisplayNames.UserId)]
public string? UserId { get; set; }
/// <summary>
/// Gets or sets the message.
/// </summary>
/// <example>A custom log message.</example>
[DisplayName(DisplayNames.Message)]
[JsonPropertyName(DisplayNames.Message)]
public TMessage? Message { get; set; }
}
}

View File

@@ -0,0 +1,55 @@
// ***********************************************************************
// <copyright file="LogOperation.cs">
// Heath
// </copyright>
// ***********************************************************************
using System.Runtime.Serialization;
using System.Text.Json.Serialization;
namespace Core.Blueprint.Logging
{
/// <summary>
/// Represents all possible values for log operation.
/// </summary>
[DataContract]
public enum LogOperation
{
/// <summary>
/// The keyVaultProvider request log operation type.
/// </summary>
[EnumMember(Value = DisplayNames.ClientRequest)]
[JsonPropertyName(DisplayNames.ClientRequest)]
ClientRequest = 0,
/// <summary>
/// The keyVaultProvider response log operation type.
/// </summary>
[EnumMember(Value = DisplayNames.ClientResponse)]
ClientResponse = 1,
/// <summary>
/// The external request log operation type.
/// </summary>
[EnumMember(Value = DisplayNames.ExternalRequest)]
ExternalRequest = 2,
/// <summary>
/// The external response log operation type.
/// </summary>
[EnumMember(Value = DisplayNames.ExternalResponse)]
ExternalResponse = 3,
/// <summary>
/// The error log operation type.
/// </summary>
[EnumMember(Value = DisplayNames.Error)]
Error = 4,
/// <summary>
/// The info log operation type.
/// </summary>
[EnumMember(Value = DisplayNames.Info)]
Info = 5,
}
}

View File

@@ -0,0 +1,41 @@
// ***********************************************************************
// <copyright file="LogSeverity.cs">
// Heath
// </copyright>
// ***********************************************************************
using System.Runtime.Serialization;
namespace Core.Blueprint.Logging
{
/// <summary>
/// Represents all possible values for log severity.
/// </summary>
[DataContract]
public enum LogSeverity
{
/// <summary>
/// The information severity level.
/// </summary>
[EnumMember(Value = DisplayNames.Information)]
Info = 0,
/// <summary>
/// The warning severity level.
/// </summary>
[EnumMember(Value = DisplayNames.Warning)]
Warn = 1,
/// <summary>
/// The error severity level.
/// </summary>
[EnumMember(Value = DisplayNames.Error)]
Error = 2,
/// <summary>
/// The fatal severity level.
/// </summary>
[EnumMember(Value = DisplayNames.Fatal)]
Fatal = 3,
}
}

View File

@@ -0,0 +1,41 @@
// ***********************************************************************
// <copyright file="LogTarget.cs">
// Heath
// </copyright>
// ***********************************************************************
using System.ComponentModel;
using System.Text.Json.Serialization;
namespace Core.Blueprint.Logging
{
/// <summary>
/// The service logger target object.
/// </summary>
public class LogTarget
{
/// <summary>
/// Gets or sets the log target method.
/// </summary>
/// <example>GET</example>
[DisplayName(DisplayNames.Method)]
[JsonPropertyName(DisplayNames.Method)]
public string? Method { get; set; }
/// <summary>
/// Gets or sets the log target host.
/// </summary>
/// <example>GET</example>
[DisplayName(DisplayNames.Host)]
[JsonPropertyName(DisplayNames.Host)]
public string? Host { get; set; }
/// <summary>
/// Gets or sets the log target route.
/// </summary>
/// <example>GET</example>
[DisplayName(DisplayNames.Route)]
[JsonPropertyName(DisplayNames.Route)]
public string? Route { get; set; }
}
}

View File

@@ -0,0 +1,20 @@
// ***********************************************************************
// <copyright file="ServiceSettings.cs">
// Heath
// </copyright>
// ***********************************************************************
namespace Core.Blueprint.Logging
{
/// <summary>
/// The service settings.
/// </summary>
public class ServiceSettings
{
/// <summary>
/// Gets or sets the service identifier.
/// </summary>
public string? ApplicationName { get; set; }
public string? LayerName { get; set; }
}
}

View File

@@ -0,0 +1,70 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Serilog;
using Serilog.Events;
namespace Core.Blueprint.Logging.Configuration
{
/// <summary>
/// Provides extension methods for configuring logging in the application.
/// </summary>
public static class Registerblueprint
{
/// <summary>
/// Registers logging services in the application.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> to add the services to.</param>
/// <param name="builder">The <see cref="WebApplicationBuilder"/> for accessing configuration and application setup.</param>
/// <returns>The updated <see cref="IServiceCollection"/>.</returns>
public static IServiceCollection AddLogs(this IServiceCollection services, WebApplicationBuilder builder)
{
Log.Logger = new LoggerConfiguration()
.WriteTo.Console(restrictedToMinimumLevel: LogEventLevel.Information)
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.CreateLogger();
builder.Host.UseSerilog(Log.Logger);
services.AddScoped<ILoggerProvider, LoggerProvider>();
return services;
}
/// <summary>
/// Configures middleware for logging and error handling in the application.
/// </summary>
/// <param name="app">The <see cref="IApplicationBuilder"/> used to configure the middleware pipeline.</param>
/// <param name="serviceSettings">The service settings required by the middleware.</param>
public static void UseLogging(this IApplicationBuilder app, IConfiguration configuration)
{
var serviceSettings = new ServiceSettings();
configuration.GetSection(nameof(ServiceSettings)).Bind(serviceSettings);
app.UseCustomHttpLogging(serviceSettings);
app.UseHttpExceptionHandler(serviceSettings);
}
/// <summary>
/// Adds middleware to handle HTTP exceptions globally.
/// </summary>
/// <param name="builder">The <see cref="IApplicationBuilder"/> to add the middleware to.</param>
/// <param name="settings">The settings used by the exception handler middleware.</param>
/// <returns>The updated <see cref="IApplicationBuilder"/>.</returns>
public static IApplicationBuilder UseHttpExceptionHandler(this IApplicationBuilder builder, ServiceSettings settings)
{
return builder.UseMiddleware<HttpErrorMiddleware>(settings);
}
/// <summary>
/// Adds custom HTTP logging middleware to the application pipeline.
/// </summary>
/// <param name="builder">The <see cref="IApplicationBuilder"/> to add the middleware to.</param>
/// <param name="settings">The settings used by the logging middleware.</param>
/// <returns>The updated <see cref="IApplicationBuilder"/>.</returns>
public static IApplicationBuilder UseCustomHttpLogging(this IApplicationBuilder builder, ServiceSettings settings)
{
return builder.UseMiddleware<HttpLoggingMiddleware>(settings);
}
}
}

View File

@@ -0,0 +1,48 @@
// ***********************************************************************
// <copyright file="Claims.cs">
// Heath
// </copyright>
// ***********************************************************************
namespace Core.Blueprint.Logging
{
/// <summary>
/// Constants for claims used in JWT tokens.
/// </summary>
public class Claims
{
/// <summary>
/// Claim name for user's name.
/// </summary>
public const string Name = "name";
/// <summary>
/// Claim name for user's name.
/// </summary>
public const string Email = "email";
/// <summary>
/// Claim name for user's ID.
/// </summary>
public const string Id = "id";
/// <summary>
/// Claim name for user's role ID.
/// </summary>
public const string Rol = "rol";
/// <summary>
/// Claim name for user's companies.
/// </summary>
public const string Companies = "companies";
/// <summary>
/// Claim name for user's projects.
/// </summary>
public const string Projects = "projects";
/// <summary>
/// Claim name for user's surveys.
/// </summary>
public const string Surveys = "surveys";
}
}

View File

@@ -0,0 +1,397 @@
// ***********************************************************************
// <copyright file="DisplayNames.cs">
// Heath
// </copyright>
// ***********************************************************************
namespace Core.Blueprint.Logging
{
/// <summary>
/// Constants of the display names for this service.
/// </summary>
public static class DisplayNames
{
/// <summary>
/// The active patameter.
/// </summary>
public const string Active = "active";
/// <summary>
/// The keyVaultProvider identifier parameter.
/// </summary>
public const string ClientId = "keyVaultProviderId";
/// <summary>
/// The keyVaultProvider request parameter.
/// </summary>
public const string ClientRequest = "keyVaultProviderRequest";
/// <summary>
/// The keyVaultProvider response parameter.
/// </summary>
public const string ClientResponse = "keyVaultProviderResponse";
/// <summary>
/// The creation date.
/// </summary>
public const string CreationDate = "creationDate";
/// <summary>
/// The content parameter.
/// </summary>
public const string Content = "content";
/// <summary>
/// The delete parameter.
/// </summary>
public const string Delete = "delete";
/// <summary>
/// The description parameter.
/// </summary>
public const string Description = "description";
/// <summary>
/// The detail parameter.
/// </summary>
public const string Detail = "detail";
/// <summary>
/// The environment parameter.
/// </summary>
public const string Environment = "environment";
/// <summary>
/// The error log severity level parameter.
/// </summary>
public const string Error = "error";
/// <summary>
/// The error code parameter.
/// </summary>
public const string ErrorCode = "errorCode";
/// <summary>
/// The external request parameter.
/// </summary>
public const string ExternalRequest = "externalRequest";
/// <summary>
/// The external response parameter.
/// </summary>
public const string ExternalResponse = "externalResponse";
/// <summary>
/// The fatal log severity level parameter.
/// </summary>
public const string Fatal = "fatal";
/// <summary>
/// The host parameter.
/// </summary>
public const string Host = "host";
/// <summary>
/// The identifier parameter.
/// </summary>
public const string Id = "id";
/// <summary>
/// The inactive parameter.
/// </summary>
public const string Inactive = "inactive";
/// <summary>
/// The info log severity level parameter.
/// </summary>
public const string Info = "info";
/// <summary>
/// The information log severity level parameter.
/// </summary>
public const string Information = "information";
/// <summary>
/// The media parameter.
/// </summary>
public const string Media = "media";
/// <summary>
/// The media type parameter.
/// </summary>
public const string MediaType = "mediaType";
/// <summary>
/// The media use type parameter.
/// </summary>
public const string MediaUseType = "mediaUseType";
/// <summary>
/// Th message parameter.
/// </summary>
public const string Message = "message";
/// <summary>
/// The method parameter.
/// </summary>
public const string Method = "method";
/// <summary>
/// The monday parameter.
/// </summary>
public const string Monday = "monday";
/// <summary>
/// The MXN parameter.
/// </summary>
public const string MXN = "MXN";
/// <summary>
/// The name parameter.
/// </summary>
public const string Name = "name";
/// <summary>
/// The next page parameter.
/// </summary>
public const string NextPage = "nextPage";
/// <summary>
/// The nick name parameter.
/// </summary>
public const string NickName = "nickName";
/// <summary>
/// The note parameter.
/// </summary>
public const string Note = "note";
/// <summary>
/// The not so affordable parameter.
/// </summary>
public const string NotSoAffordable = "notSoAffordable";
/// <summary>
/// The object status parameter.
/// </summary>
public const string ObjectStatus = "objectStatus";
/// <summary>
/// The opening time parameter.
/// </summary>
public const string OpeningTime = "openingTime";
/// <summary>
/// The operation days parameter.
/// </summary>
public const string OperationDays = "operationDays";
/// <summary>
/// The page parameter.
/// </summary>
public const string Page = "page";
/// <summary>
/// The page count parameter.
/// </summary>
public const string PageCount = "pageCount";
/// <summary>
/// The page metadata parameter.
/// </summary>
public const string PageMetadata = "pageMetadata";
/// <summary>
/// The page size parameter.
/// </summary>
public const string PageSize = "pageSize";
/// <summary>
/// The parent identifier parameter.
/// </summary>
public const string ParentId = "ParentId";
/// <summary>
/// The pet ticket price parameter.
/// </summary>
public const string PetTicketPrice = "petTicketPrice";
/// <summary>
/// The place parameter.
/// </summary>
public const string Place = "place";
/// <summary>
/// The place type parameter.
/// </summary>
public const string PlaceType = "placeType";
/// <summary>
/// The previous page parameter.
/// </summary>
public const string PreviousPage = "previousPage";
/// <summary>
/// The provider identifier parameter.
/// </summary>
public const string ProviderId = "providerId";
/// <summary>
/// The provider type identifier parameter.
/// </summary>
public const string ProviderTypeId = "providerTypeId";
/// <summary>
/// The request identifier parameter.
/// </summary>
public const string RequestId = "requestId";
/// <summary>
/// The RNT identifier parameter.
/// </summary>
public const string RntId = "rntId";
/// <summary>
/// The route parameter.
/// </summary>
public const string Route = "route";
/// <summary>
/// The operation parameter.
/// </summary>
public const string Operation = "operation";
/// <summary>
/// The other ticket price parameter.
/// </summary>
public const string OtherTicketPrice = "otherTicketPrice";
/// <summary>
/// The path parameter.
/// </summary>
public const string Path = "path";
/// <summary>
/// The saturday parameter.
/// </summary>
public const string Saturday = "saturday";
/// <summary>
/// The secondary parameter.
/// </summary>
public const string Secondary = "secondary";
/// <summary>
/// The service health parameter.
/// </summary>
public const string ServiceHealth = "serviceHealth";
/// <summary>
/// The service identifier parameter.
/// </summary>
public const string ServiceId = "serviceId";
/// <summary>
/// The severity parameter.
/// </summary>
public const string Severity = "severity";
/// <summary>
/// The state identifier parameter.
/// </summary>
public const string StateId = "stateId";
/// <summary>
/// The region identifier parameter.
/// </summary>
public const string StateProvinceRegionId = "regionId";
/// <summary>
/// The sunday parameter.
/// </summary>
public const string Sunday = "sunday";
/// <summary>
/// The tax identifier parameter.
/// </summary>
public const string TaxId = "taxId";
/// <summary>
/// The target parameter.
/// </summary>
public const string Target = "target";
/// <summary>
/// The thursday parameter.
/// </summary>
public const string Thursday = "thursday";
/// <summary>
/// The timestamp parameter.
/// </summary>
public const string Timestamp = "timestamp";
/// <summary>
/// The total items parameter.
/// </summary>
public const string TotalItems = "totalItems";
/// <summary>
/// Gets or sets the transaction identifier parameter.
/// </summary>
public const string TransactionId = "transactionId";
/// <summary>
/// The tuesday parameter.
/// </summary>
public const string Tuesday = "tuesday";
/// <summary>
/// The URI parameter.
/// </summary>
public const string Uri = "uri";
/// <summary>
/// The update parameter.
/// </summary>
public const string Update = "update";
/// <summary>
/// The x-forwarded-for header parameter.
/// </summary>
public const string XForwardedFor = "xForwardedFor";
/// <summary>
/// The x-forwarded-for header parameter.
/// </summary>
public const string XForwardedForHeader = "X-Forwarded-For";
/// <summary>
/// The final currency identifier parameter.
/// </summary>
public const string CurrencyId = "currencyId";
/// <summary>
/// The user identifier parameter.
/// </summary>
public const string UserId = "userId";
/// <summary>
/// The user parameter.
/// </summary>
public const string User = "user";
/// <summary>
/// The warning log severity level.
/// </summary>
public const string Warning = "warning";
/// <summary>
/// The email parameter.
/// </summary>
public const string Email = "email";
}
}

View File

@@ -0,0 +1,21 @@
// ***********************************************************************
// <copyright file="EnvironmentVariables.cs">
// Heath
// </copyright>
// ***********************************************************************
namespace Core.Blueprint.Logging
{
/// <summary>
/// Constants of the environment variables for this service.
/// </summary>
public static class EnvironmentVariables
{
/// <summary>
/// The stage environment vriable.
/// </summary>
public const string Stage = "ASPNETCORE_ENVIRONMENT";
}
}

View File

@@ -0,0 +1,79 @@
// ***********************************************************************
// <copyright file="ErrorCodes.cs">
// Heath
// </copyright>
// ***********************************************************************
namespace Core.Blueprint.Logging
{
/// <summary>
/// Constants for the error codes.
/// </summary>
public static class ErrorCodes
{
/// <summary>
/// The generic entities not found error code.
/// </summary>
public const string EntitiesNotFound = "{0}EntitiesNotFound";
/// <summary>
/// The entity already exsits error message.
/// </summary>
public const string EntityAlreadyExists = "{0}EntityAlreadyExists";
/// <summary>
/// The generic entity not found error code.
/// </summary>
public const string EntityNotFound = "{0}EntityNotFound";
/// <summary>
/// The generic not supported error code.
/// </summary>
public const string EntityNotSupported = "{0}NotSupported";
/// <summary>
/// The internal server error code.
/// </summary>
public const string InternalServerError = "InternalServerError";
/// <summary>
/// The invalid parameters in mapper error code.
/// </summary>
public const string InvalidParametersMapper = "InvalidParametersMapper";
/// <summary>
/// The page size invalid value error code.
/// </summary>
public const string PageSizeInvalidValue = "PageSizeInvalidValue";
/// <summary>
/// The page ot of range error code.
/// </summary>
public const string PageOutOfRange = "PageOutOfRange";
/// <summary>
/// The property does not match the regular expresion error code.
/// </summary>
public const string PropertyDoesNotMatchRegex = "{0}PropertyDoesNotMatchRegex";
/// <summary>
/// The property is required error code.
/// </summary>
public const string PropertyIsRequired = "{0}PropertyIsRequired";
/// <summary>
/// The property length invalid error code.
/// </summary>
public const string PropertyLengthInvalid = "{0}PropertyLengthInvalid";
/// <summary>
/// The property must be in range error code.
/// </summary>
public const string PropertyMustBeInRange = "{0}PropertyMustBeInRange";
/// <summary>
/// The route not found error code.
/// </summary>
public const string RouteNotFound = "RouteNotFound";
}
}

View File

@@ -0,0 +1,10 @@
namespace Core.Blueprint.Logging
{
public static class Headers
{
/// <summary>
/// The authorization header.
/// </summary>
public const string Authorization = "Authorization";
}
}

View File

@@ -0,0 +1,148 @@
// ***********************************************************************
// <copyright file="MimeTypes.cs">
// Heath
// </copyright>
// ***********************************************************************
using System.Globalization;
namespace Core.Blueprint.Logging
{
/// <summary>
/// Constants for the mime types.
/// </summary>
public static class MimeTypes
{
/// <summary>
/// The service application/json mime type.
/// </summary>
public const string ApplicationJson = "application/json";
/// <summary>
/// The application/pdf mime type.
/// </summary>
public const string ApplicationPdf = "application/pdf";
/// <summary>
/// The end index.
/// </summary>
public const int EndIndex = 5;
/// <summary>
/// The JPEG extension.
/// </summary>
public const string ExtensionGif = "gif";
/// <summary>
/// The JPEG extension.
/// </summary>
public const string ExtensionJpeg = "jpeg";
/// <summary>
/// The PNG extension.
/// </summary>
public const string ExtensionPng = "png";
/// <summary>
/// The SVG extension.
/// </summary>
public const string ExtensionSvg = "svg";
/// <summary>
/// The image/gif mime type.
/// </summary>
public const string ImageGif = "image/gif";
/// <summary>
/// The image/jpeg mime type.
/// </summary>
public const string ImageJpeg = "image/jpeg";
/// <summary>
/// The image/png mime type.
/// </summary>
public const string ImagePng = "image/png";
/// <summary>
/// The image/svg+xml mime type.
/// </summary>
public const string ImageSvg = "image/svg+xml";
/// <summary>
/// The identifier GIF.
/// </summary>
public const string IdentifierGif = "R0LGO";
/// <summary>
/// The identifier PNG.
/// </summary>
public const string IdentifierJpeg = "/9J/4";
/// <summary>
/// The identifier PDF.
/// </summary>
public const string IdentifierPdf = "JVBER";
/// <summary>
/// The identifier PNG.
/// </summary>
public const string IdentifierPng = "IVBOR";
/// <summary>
/// The identifier SVG.
/// </summary>
public const string IdentifierSvg = "PHN2Z";
/// <summary>
/// The parameter name.
/// </summary>
public const string ParameterName = "MimeType";
/// <summary>
/// The start index.
/// </summary>
public const int StartIndex = 0;
/// <summary>
/// The mime type dictionary.
/// </summary>
public static readonly Dictionary<string, string> Dictionary = new Dictionary<string, string>
{
{ IdentifierJpeg, ImageJpeg },
{ IdentifierPng, ImagePng },
{ IdentifierGif, ImageGif },
{ IdentifierSvg, ImageSvg },
};
/// <summary>
/// The mime type dictionary.
/// </summary>
public static readonly Dictionary<string, string> DictionaryExtension = new Dictionary<string, string>
{
{ IdentifierJpeg, ExtensionJpeg },
{ IdentifierPng, ExtensionPng },
{ IdentifierGif, ExtensionGif },
{ IdentifierSvg, ExtensionSvg },
};
/// <summary>
/// Gets the mime type.
/// </summary>
/// <param name="content">The cpntent with mime type identifier, substring 0, 5 from content.</param>
/// <returns>A <see cref="string"/> representing the value.</returns>
public static string GetMimeType(this string content)
{
return Dictionary.FirstOrDefault(_ => _.Key == content[..EndIndex].ToUpper(CultureInfo.InvariantCulture)).Value;
}
/// <summary>
/// Gets the extension.
/// </summary>
/// <param name="content">The mime type identifier, substring 0, 5 from content.</param>
/// <returns>A <see cref="string"/> representing the value.</returns>
public static string GetExtension(this string content)
{
return DictionaryExtension.FirstOrDefault(_ => _.Key == content[..EndIndex].ToUpper(CultureInfo.InvariantCulture)).Value;
}
}
}

View File

@@ -0,0 +1,29 @@
// ***********************************************************************
// <copyright file="Responses.cs">
// Heath
// </copyright>
// ***********************************************************************
namespace Core.Blueprint.Logging
{
/// <summary>
/// Constants of the responses for this service.
/// </summary>
public static class Responses
{
/// <summary>
/// The health response.
/// </summary>
public const string HealthyService = "healthy";
/// <summary>
/// The route does not exist response.
/// </summary>
public const string RouteDoesNotExist = "The specified route '{0}' does not exist for method '{1}' in this service.";
/// <summary>
/// The target response.
/// </summary>
public const string Target = "{0}|{1}://{2}{3}";
}
}

View File

@@ -0,0 +1,12 @@
namespace Core.Blueprint.Logging
{
public interface ILoggerProvider
{
public void LogInformation(string service, params object[] args);
public void LogOperationStarted(string service, params object[] args);
public void LogOperationFinished(string service, params object[] args);
public void LogWarning(string message, params object[] args);
public void LogError(string servicee, params object[] args);
public void LogCritical(Exception exception, string message, params object[] args);
}
}

View File

@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.0" />
<PackageReference Include="Microsoft.IO.RecyclableMemoryStream" Version="3.0.1" />
<PackageReference Include="Serilog.Extensions.Hosting" Version="9.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.3.0" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,92 @@
// ***********************************************************************
// <copyright file="HttpErrorMiddleware.cs">
// Heath
// </copyright>
// ***********************************************************************
using Microsoft.AspNetCore.Http;
using Serilog;
using System.Text.Json;
namespace Core.Blueprint.Logging
{
/// <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 requestProcess(context).ConfigureAwait(false);
}
catch (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 HttpError(
message,
errorCode,
string.Format(
Responses.Target,
context.Request.Method,
context.Request.Scheme,
context.Request.Host.Host,
context.Request.Path));
logger.LogError<HttpError>(context, errorMessage, $"{settings.ApplicationName}-{settings.LayerName}");
context.Response.ContentType = MimeTypes.ApplicationJson;
context.Response.StatusCode = statusCode;
await context.Response.WriteAsync(JsonSerializer.Serialize(errorMessage)).ConfigureAwait(false);
}
}
}

View File

@@ -0,0 +1,237 @@
// ***********************************************************************
// <copyright file="HttpLogger.cs">
// Heath
// </copyright>
// ***********************************************************************
using Microsoft.AspNetCore.Http;
using Microsoft.IO;
using Serilog;
using System.IdentityModel.Tokens.Jwt;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace Core.Blueprint.Logging
{
/// <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;
}
}
}

View File

@@ -0,0 +1,69 @@
// ***********************************************************************
// <copyright file="HttpLoggingMiddleware.cs">
// Heath
// </copyright>
// ***********************************************************************
using Microsoft.AspNetCore.Http;
using Microsoft.IO;
using Serilog;
namespace Core.Blueprint.Logging
{
/// <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 logger.LogRequest(context, recyclableMemoryStreamManager, $"{settings.ApplicationName}-{settings.LayerName}");
}
/// <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 logger.LogResponse(context, recyclableMemoryStreamManager, requestProcess, $"{settings.ApplicationName}-{settings.LayerName}");
}
}
}

View File

@@ -0,0 +1,89 @@
namespace Core.Blueprint.Logging
{
/// <summary>
/// Provides logging functionalities using Serilog.
/// </summary>
public class LoggerProvider : ILoggerProvider
{
private readonly Serilog.ILogger logger;
/// <summary>
/// Initializes a new instance of the <see cref="LoggerProvider"/> class.
/// </summary>
/// <param name="logger">The Serilog logger instance.</param>
public LoggerProvider(Serilog.ILogger logger)
{
this.logger = logger;
}
/// <summary>
/// Logs an informational message for a specific service.
/// </summary>
/// <param name="service">The name of the service.</param>
/// <param name="args">Additional arguments to include in the log.</param>
public void LogInformation(string service, params object[] args)
{
logger.Information("Starting operation in {service} service", service, args);
}
/// <summary>
/// Logs a message indicating the start of an operation in a specific service.
/// </summary>
/// <param name="service">The name of the service.</param>
/// <param name="args">Additional parameters associated with the operation.</param>
public void LogOperationStarted(string service, params object[] args)
{
logger.Information("Starting operation in {Service} service with parameters: {@Args}", service, args);
}
/// <summary>
/// Logs a message indicating the completion of an operation in a specific service.
/// </summary>
/// <param name="service">The name of the service.</param>
/// <param name="args">Additional parameters associated with the operation.</param>
public void LogOperationFinished(string service, params object[] args)
{
logger.Information("Finishing operation in {Service} service with parameters: {@Args}", service, args);
}
/// <summary>
/// Logs a general informational message.
/// </summary>
/// <param name="message">The message to log.</param>
public void LogInformation(string message)
{
logger.Information(message);
}
/// <summary>
/// Logs a warning message with additional context.
/// </summary>
/// <param name="message">The warning message to log.</param>
/// <param name="args">Additional arguments to include in the log.</param>
public void LogWarning(string message, params object[] args)
{
logger.Warning(message, args);
}
/// <summary>
/// Logs an error that occurred in a specific service.
/// </summary>
/// <param name="service">The name of the service.</param>
/// <param name="args">Additional details about the error.</param>
public void LogError(string service, params object[] args)
{
logger.Error("An error occurred in `{service}` Exception: {@Args}", service, args);
}
/// <summary>
/// Logs a critical error with an exception, message, and additional context.
/// </summary>
/// <param name="exception">The exception associated with the critical error.</param>
/// <param name="message">The critical error message.</param>
/// <param name="args">Additional arguments to include in the log.</param>
public void LogCritical(Exception exception, string message, params object[] args)
{
logger.Fatal(exception, message, args);
}
}
}