From 7b0caeb98bf48a6bb9b4761bba11072cdf4d525c Mon Sep 17 00:00:00 2001 From: Mohammad Taherian Date: Tue, 26 Jul 2022 17:14:35 -0400 Subject: [PATCH 1/6] init ignore log --- .../uBeac.Core.Common/Attributes/LogIgnoreAttribute.cs | 6 ++++++ src/Identity/Tests/API/ViewModels/UserLogin.cs | 2 ++ src/Identity/Tests/API/ViewModels/UserResetPassword.cs | 1 + 3 files changed, 9 insertions(+) create mode 100644 src/Common/uBeac.Core.Common/Attributes/LogIgnoreAttribute.cs diff --git a/src/Common/uBeac.Core.Common/Attributes/LogIgnoreAttribute.cs b/src/Common/uBeac.Core.Common/Attributes/LogIgnoreAttribute.cs new file mode 100644 index 00000000..79cee283 --- /dev/null +++ b/src/Common/uBeac.Core.Common/Attributes/LogIgnoreAttribute.cs @@ -0,0 +1,6 @@ +namespace uBeac; + +[AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = false)] +public class LogIgnoreAttribute : Attribute +{ +} diff --git a/src/Identity/Tests/API/ViewModels/UserLogin.cs b/src/Identity/Tests/API/ViewModels/UserLogin.cs index a8a4d561..47cb72ed 100644 --- a/src/Identity/Tests/API/ViewModels/UserLogin.cs +++ b/src/Identity/Tests/API/ViewModels/UserLogin.cs @@ -6,10 +6,12 @@ namespace API; public class LoginRequest { [Required] + [LogIgnore] public string UserName { get; set; } [Required] [DataType(DataType.Password)] + [LogIgnore] public string Password { get; set; } } diff --git a/src/Identity/Tests/API/ViewModels/UserResetPassword.cs b/src/Identity/Tests/API/ViewModels/UserResetPassword.cs index 329903c4..315b6d06 100644 --- a/src/Identity/Tests/API/ViewModels/UserResetPassword.cs +++ b/src/Identity/Tests/API/ViewModels/UserResetPassword.cs @@ -12,5 +12,6 @@ public class ResetPasswordRequest [Required] [DataType(DataType.Password)] + [LogIgnore] public string NewPassword { get; set; } } \ No newline at end of file From ddf24391ecabde02be4ce77c078e8c7145c8efc7 Mon Sep 17 00:00:00 2001 From: Mohammad Taherian Date: Fri, 29 Jul 2022 13:50:35 -0400 Subject: [PATCH 2/6] added action filter to remove critical data from log --- src/Identity/Tests/API/Program.cs | 12 ++-- .../Tests/API/ViewModels/UserLogin.cs | 2 +- .../CriticalDataHandlerFilter.cs | 70 +++++++++++++++++++ .../HttpLoggingMiddleware.cs | 13 ++-- .../uBeac.Core.Web.Logging.csproj | 3 + 5 files changed, 90 insertions(+), 10 deletions(-) create mode 100644 src/Logging/uBeac.Core.Web.Logging/CriticalDataHandlerFilter.cs diff --git a/src/Identity/Tests/API/Program.cs b/src/Identity/Tests/API/Program.cs index 620826af..87d4cc49 100644 --- a/src/Identity/Tests/API/Program.cs +++ b/src/Identity/Tests/API/Program.cs @@ -18,7 +18,7 @@ builder.Services.AddMongoDbHttpLogging(builder.Configuration.GetInstance("HttpLogging")); builder.Services.AddHttpContextAccessor(); -builder.Services.AddControllers(); +builder.Services.AddControllers(options => options.Filters.Add()); builder.Services.AddAutoMapper(Assembly.GetExecutingAssembly()); // Disabling automatic model state validation @@ -111,15 +111,17 @@ app.UseCorsPolicy(corsPolicyOptions); app.UseHttpsRedirection(); + +app.UseHttpLoggingMiddleware(); + app.UseDefaultFiles(); app.UseStaticFiles(); -app.UseAuthentication(); -app.UseAuthorization(); - app.UseCoreSwagger(); -app.UseHttpLoggingMiddleware(); +app.UseAuthentication(); + +app.UseAuthorization(); app.MapControllers(); diff --git a/src/Identity/Tests/API/ViewModels/UserLogin.cs b/src/Identity/Tests/API/ViewModels/UserLogin.cs index 47cb72ed..f762a59f 100644 --- a/src/Identity/Tests/API/ViewModels/UserLogin.cs +++ b/src/Identity/Tests/API/ViewModels/UserLogin.cs @@ -1,4 +1,4 @@ -using System; +using Newtonsoft.Json; using System.ComponentModel.DataAnnotations; namespace API; diff --git a/src/Logging/uBeac.Core.Web.Logging/CriticalDataHandlerFilter.cs b/src/Logging/uBeac.Core.Web.Logging/CriticalDataHandlerFilter.cs new file mode 100644 index 00000000..e43f8039 --- /dev/null +++ b/src/Logging/uBeac.Core.Web.Logging/CriticalDataHandlerFilter.cs @@ -0,0 +1,70 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; + +namespace uBeac.Web.Logging; + +public class CriticalDataHandlerFilter : Attribute, IActionFilter + +{ + public void OnActionExecuted(ActionExecutedContext context) + { + try + { + var actionDescriptor = context.ActionDescriptor; + if (actionDescriptor != null) + { + var requestBody = context.HttpContext.Items["LogRequestBody"] != null ? context.HttpContext.Items["LogRequestBody"] : null; + var props = requestBody.GetType().GetProperties().Where(prop => IsDefined(prop, typeof(LogIgnoreAttribute))).ToList(); + + foreach (var prop in props) + { + prop.SetValue(requestBody, default); + } + context.HttpContext.Items["LogRequestBody"] = requestBody; + } + + var result = ((ObjectResult)context.Result).Value; + var responseProps = result.GetType().GetProperties().Where(prop => IsDefined(prop, typeof(LogIgnoreAttribute))).ToList(); + foreach (var prop in responseProps) + { + prop.SetValue(result, default); + } + context.HttpContext.Items["LogResponseBody"] = result; + } + catch (Exception ex) + { + + throw; + } + } + + public void OnActionExecuting(ActionExecutingContext context) + { + var arg = context.ActionArguments.First().Value; + context.HttpContext.Items["LogRequestBody"] = arg; + + //try + //{ + // var controllerActionDescriptor = context.ActionDescriptor as ControllerActionDescriptor; + + // if (controllerActionDescriptor != null) + // { + // var requestBody = FormatRequestBody(context.ActionArguments); + // context.HttpContext.Items["LogRequestBody"] = requestBody; + // } + //} + //catch (Exception ex) + //{ + // _logger.Error("Error in LogServiceCallFilter", ex); + //} + //var arg = context.ActionArguments.First().Value; + //var argType = arg.GetType(); + //var props = arg.GetType().GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(LogIgnoreAttribute))).ToList(); + + //foreach (var prop in props) + //{ + // prop.SetValue(arg, default); + //} + } +} + diff --git a/src/Logging/uBeac.Core.Web.Logging/HttpLoggingMiddleware.cs b/src/Logging/uBeac.Core.Web.Logging/HttpLoggingMiddleware.cs index 317098ce..bac05d74 100644 --- a/src/Logging/uBeac.Core.Web.Logging/HttpLoggingMiddleware.cs +++ b/src/Logging/uBeac.Core.Web.Logging/HttpLoggingMiddleware.cs @@ -4,6 +4,7 @@ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; +using Newtonsoft.Json; namespace uBeac.Web.Logging; @@ -21,8 +22,8 @@ public async Task Invoke(HttpContext context, IHttpLogRepository repository, IAp try { var stopwatch = Stopwatch.StartNew(); - - var requestBody = await ReadRequestBody(context.Request); + + //var requestBody = await ReadRequestBody(context.Request); var originalResponseStream = context.Response.Body; await using var responseMemoryStream = new MemoryStream(); @@ -41,7 +42,10 @@ public async Task Invoke(HttpContext context, IHttpLogRepository repository, IAp } finally { - var responseBody = await ReadResponseBody(context, originalResponseStream, responseMemoryStream); + //var responseBody = await ReadResponseBody(context, originalResponseStream, responseMemoryStream); + await ReadResponseBody(context, originalResponseStream, responseMemoryStream); + var requestBody = JsonConvert.SerializeObject(context.Items["LogRequestBody"]); + var responseBody = JsonConvert.SerializeObject(context.Items["LogResponseBody"]); stopwatch.Stop(); @@ -51,7 +55,7 @@ public async Task Invoke(HttpContext context, IHttpLogRepository repository, IAp } catch (Exception e) { - logger.LogError(e, "Occurred a unhandled exception when logging HTTP request."); + logger.LogError(e, "An unhandled exception has occured during logging HTTP request."); } } @@ -98,4 +102,5 @@ private static async Task Log(HttpLog log, IHttpLogRepository repository) { await repository.Create(log); } + } \ No newline at end of file diff --git a/src/Logging/uBeac.Core.Web.Logging/uBeac.Core.Web.Logging.csproj b/src/Logging/uBeac.Core.Web.Logging/uBeac.Core.Web.Logging.csproj index 67ffff06..883a35b9 100644 --- a/src/Logging/uBeac.Core.Web.Logging/uBeac.Core.Web.Logging.csproj +++ b/src/Logging/uBeac.Core.Web.Logging/uBeac.Core.Web.Logging.csproj @@ -22,7 +22,10 @@ + + + From 655ee23c13250af3472be86f506ff6af5efc0962 Mon Sep 17 00:00:00 2001 From: Mohammad Taherian Date: Wed, 3 Aug 2022 18:33:51 -0400 Subject: [PATCH 3/6] implemented custom json resolver to ignore properties in httplog --- .../Attributes/LogIgnoreAttribute.cs | 11 +++- .../Base/AccountsControllerBase.cs | 6 ++ .../Tests/API/ViewModels/UserLogin.cs | 27 +++++++- .../Tests/API/ViewModels/UserRegister.cs | 1 + .../Tests/API/ViewModels/UserResetPassword.cs | 1 - .../CriticalDataHandlerFilter.cs | 62 +++++-------------- .../LogIgnoreResolver.cs | 52 ++++++++++++++++ 7 files changed, 109 insertions(+), 51 deletions(-) create mode 100644 src/Logging/uBeac.Core.Web.Logging/LogIgnoreResolver.cs diff --git a/src/Common/uBeac.Core.Common/Attributes/LogIgnoreAttribute.cs b/src/Common/uBeac.Core.Common/Attributes/LogIgnoreAttribute.cs index 79cee283..8033ab0f 100644 --- a/src/Common/uBeac.Core.Common/Attributes/LogIgnoreAttribute.cs +++ b/src/Common/uBeac.Core.Common/Attributes/LogIgnoreAttribute.cs @@ -1,6 +1,15 @@ namespace uBeac; -[AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = false)] +[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Class, AllowMultiple = false, Inherited = true)] public class LogIgnoreAttribute : Attribute { + public object Value { get; set; } = (LogIgnoreAttribute)null; + + public LogIgnoreAttribute(object value) + { + Value = value; + } + public LogIgnoreAttribute() + { + } } diff --git a/src/Identity/Tests/API/Controllers/Base/AccountsControllerBase.cs b/src/Identity/Tests/API/Controllers/Base/AccountsControllerBase.cs index 00ee9342..3243fb65 100644 --- a/src/Identity/Tests/API/Controllers/Base/AccountsControllerBase.cs +++ b/src/Identity/Tests/API/Controllers/Base/AccountsControllerBase.cs @@ -21,6 +21,12 @@ public virtual async Task> Register([FromBody] RegisterRequest mod return true.ToResult(); } + [HttpGet] + public virtual async Task Test(int id, string name) + { + + } + [HttpPost] public virtual async Task>> Login([FromBody] LoginRequest model, CancellationToken cancellationToken = default) { diff --git a/src/Identity/Tests/API/ViewModels/UserLogin.cs b/src/Identity/Tests/API/ViewModels/UserLogin.cs index f762a59f..200f3fc6 100644 --- a/src/Identity/Tests/API/ViewModels/UserLogin.cs +++ b/src/Identity/Tests/API/ViewModels/UserLogin.cs @@ -6,13 +6,19 @@ namespace API; public class LoginRequest { [Required] - [LogIgnore] + [LogIgnore("-----")] public string UserName { get; set; } [Required] [DataType(DataType.Password)] - [LogIgnore] + [LogIgnore("*****")] public string Password { get; set; } + + public AltLoginRequest Nested { get; set; } + public LoginRequest() + { + Nested = new AltLoginRequest(); + } } public class LoginResponse @@ -23,4 +29,21 @@ public class LoginResponse public string Token { get; set; } public string RefreshToken { get; set; } public DateTime Expiry { get; set; } +} + +public class AltLoginRequest +{ + public int MyProperty { get; set; } + [LogIgnore("")] + public string MyProperty1 { get; set; } + [LogIgnore(null)] + public decimal MyProperty2 { get; set; } + public List MyProperty3 { get; set; } +} + +public class SecondLevel +{ + public int MyProperty { get; set; } + [LogIgnore] + public string MyProperty1 { get; set; } } \ No newline at end of file diff --git a/src/Identity/Tests/API/ViewModels/UserRegister.cs b/src/Identity/Tests/API/ViewModels/UserRegister.cs index 4f51d744..e931ff2f 100644 --- a/src/Identity/Tests/API/ViewModels/UserRegister.cs +++ b/src/Identity/Tests/API/ViewModels/UserRegister.cs @@ -3,6 +3,7 @@ namespace API; +[LogIgnore] public class RegisterRequest { [Required] diff --git a/src/Identity/Tests/API/ViewModels/UserResetPassword.cs b/src/Identity/Tests/API/ViewModels/UserResetPassword.cs index 315b6d06..329903c4 100644 --- a/src/Identity/Tests/API/ViewModels/UserResetPassword.cs +++ b/src/Identity/Tests/API/ViewModels/UserResetPassword.cs @@ -12,6 +12,5 @@ public class ResetPasswordRequest [Required] [DataType(DataType.Password)] - [LogIgnore] public string NewPassword { get; set; } } \ No newline at end of file diff --git a/src/Logging/uBeac.Core.Web.Logging/CriticalDataHandlerFilter.cs b/src/Logging/uBeac.Core.Web.Logging/CriticalDataHandlerFilter.cs index e43f8039..81946b9e 100644 --- a/src/Logging/uBeac.Core.Web.Logging/CriticalDataHandlerFilter.cs +++ b/src/Logging/uBeac.Core.Web.Logging/CriticalDataHandlerFilter.cs @@ -1,70 +1,38 @@ using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; +using Newtonsoft.Json; namespace uBeac.Web.Logging; -public class CriticalDataHandlerFilter : Attribute, IActionFilter - +public class CriticalDataHandlerFilter : IActionFilter { + private readonly JsonSerializerSettings settings = new JsonSerializerSettings + { + ContractResolver = new LogIgnoreResolver(), + Formatting = Formatting.Indented + }; public void OnActionExecuted(ActionExecutedContext context) { try { - var actionDescriptor = context.ActionDescriptor; - if (actionDescriptor != null) - { - var requestBody = context.HttpContext.Items["LogRequestBody"] != null ? context.HttpContext.Items["LogRequestBody"] : null; - var props = requestBody.GetType().GetProperties().Where(prop => IsDefined(prop, typeof(LogIgnoreAttribute))).ToList(); - - foreach (var prop in props) - { - prop.SetValue(requestBody, default); - } - context.HttpContext.Items["LogRequestBody"] = requestBody; - } - var result = ((ObjectResult)context.Result).Value; - var responseProps = result.GetType().GetProperties().Where(prop => IsDefined(prop, typeof(LogIgnoreAttribute))).ToList(); - foreach (var prop in responseProps) - { - prop.SetValue(result, default); - } - context.HttpContext.Items["LogResponseBody"] = result; + //var responseProps = result.GetType().GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(LogIgnoreAttribute))).ToList(); + //foreach (var prop in responseProps) + //{ + // prop.SetValue(result, default); + //} + context.HttpContext.Items["LogResponseBody"] = result != null ? JsonConvert.SerializeObject(result, settings) : null; } catch (Exception ex) { - throw; } } public void OnActionExecuting(ActionExecutingContext context) { - var arg = context.ActionArguments.First().Value; - context.HttpContext.Items["LogRequestBody"] = arg; - - //try - //{ - // var controllerActionDescriptor = context.ActionDescriptor as ControllerActionDescriptor; - - // if (controllerActionDescriptor != null) - // { - // var requestBody = FormatRequestBody(context.ActionArguments); - // context.HttpContext.Items["LogRequestBody"] = requestBody; - // } - //} - //catch (Exception ex) - //{ - // _logger.Error("Error in LogServiceCallFilter", ex); - //} - //var arg = context.ActionArguments.First().Value; - //var argType = arg.GetType(); - //var props = arg.GetType().GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(LogIgnoreAttribute))).ToList(); - - //foreach (var prop in props) - //{ - // prop.SetValue(arg, default); - //} + var args = context.ActionArguments?.Where(x => x.Value.GetType() != typeof(CancellationToken)).ToList(); + context.HttpContext.Items["LogRequestBody"] = args != null ? JsonConvert.SerializeObject(args, settings) : null; } } diff --git a/src/Logging/uBeac.Core.Web.Logging/LogIgnoreResolver.cs b/src/Logging/uBeac.Core.Web.Logging/LogIgnoreResolver.cs new file mode 100644 index 00000000..3fe50204 --- /dev/null +++ b/src/Logging/uBeac.Core.Web.Logging/LogIgnoreResolver.cs @@ -0,0 +1,52 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; +using System.Reflection; + +namespace uBeac.Web.Logging; + +public class LogIgnoreResolver : DefaultContractResolver +{ + protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) + { + JsonProperty result = base.CreateProperty(member, memberSerialization); + PropertyInfo property = member as PropertyInfo; + + LogIgnoreAttribute attribute = (LogIgnoreAttribute)result.AttributeProvider.GetAttributes(typeof(LogIgnoreAttribute), true).FirstOrDefault(); + + if (attribute != null) + { + var x = GetType(attribute.Value); + if (x == typeof(LogIgnoreAttribute)) + result.Ignored = true; + else + result.ValueProvider = new LogIgnoreValueProvider(property, attribute.Value); + } + + return result; + } + + private Type GetType(T obj) => typeof(T); + +} + +public class LogIgnoreValueProvider : IValueProvider +{ + private PropertyInfo _targetProperty; + private object _value; + + public LogIgnoreValueProvider(PropertyInfo targetProperty, object value) + { + _targetProperty = targetProperty; + _value = value; + } + + public void SetValue(object target, object value) + { + _targetProperty.SetValue(target, value); + } + + public object GetValue(object target) + { + return _value; + } +} From 3ae7a3a8a350a0271a11ea46a0ce34dd0140791c Mon Sep 17 00:00:00 2001 From: Mohammad Taherian Date: Tue, 30 Aug 2022 08:44:22 -0400 Subject: [PATCH 4/6] worked on handling null values in ignore attribute --- .../Attributes/LogIgnoreAttribute.cs | 9 ++-- ...oviders.FileManagement.Abstractions.csproj | 9 ---- ...oviders.FileManagement.LocalStorage.csproj | 9 ---- ...itories.FileManagement.Abstractions.csproj | 13 ----- ...Repositories.FileManagement.MongoDB.csproj | 14 ----- ...ervices.FileManagement.Abstractions.csproj | 9 ---- .../uBeac.Core.Services.FileManagement.csproj | 9 ---- .../Tests/API/Config/appsettings.json | 2 +- src/Identity/Tests/API/Config/hsts.json | 2 +- .../Tests/API/Config/http-logging.json | 7 +-- .../Base/AccountsControllerBase.cs | 4 +- src/Identity/Tests/API/Program.cs | 9 ++-- .../Tests/API/ViewModels/UserLogin.cs | 5 +- .../Extensions.cs | 8 +-- .../HttpLogCache.cs | 8 +++ .../HttpLogMongoDBContext.cs | 11 ++++ .../uBeac.Core.Web.Logging.MongoDB/Options.cs | 2 +- .../Repository.cs | 32 +++++++++-- .../uBeac.Core.Web.Logging.MongoDB.csproj | 5 ++ .../CriticalDataHandlerFilter.cs | 23 ++++---- src/Logging/uBeac.Core.Web.Logging/HttpLog.cs | 3 +- .../HttpLoggingMiddleware.cs | 27 +++++----- .../LogIgnoreResolver.cs | 22 ++++---- .../ServiceExtensions.cs | 2 +- src/uBeac.Core.sln | 54 ++----------------- 25 files changed, 120 insertions(+), 178 deletions(-) delete mode 100644 src/FileManagement/uBeac.Core.Providers.FileManagement.Abstractions/uBeac.Core.Providers.FileManagement.Abstractions.csproj delete mode 100644 src/FileManagement/uBeac.Core.Providers.FileManagement.LocalStorage/uBeac.Core.Providers.FileManagement.LocalStorage.csproj delete mode 100644 src/FileManagement/uBeac.Core.Repositories.FileManagement.Abstractions/uBeac.Core.Repositories.FileManagement.Abstractions.csproj delete mode 100644 src/FileManagement/uBeac.Core.Repositories.FileManagement.MongoDB/uBeac.Core.Repositories.FileManagement.MongoDB.csproj delete mode 100644 src/FileManagement/uBeac.Core.Services.FileManagement.Abstractions/uBeac.Core.Services.FileManagement.Abstractions.csproj delete mode 100644 src/FileManagement/uBeac.Core.Services.FileManagement/uBeac.Core.Services.FileManagement.csproj create mode 100644 src/Logging/uBeac.Core.Web.Logging.MongoDB/HttpLogCache.cs create mode 100644 src/Logging/uBeac.Core.Web.Logging.MongoDB/HttpLogMongoDBContext.cs diff --git a/src/Common/uBeac.Core.Common/Attributes/LogIgnoreAttribute.cs b/src/Common/uBeac.Core.Common/Attributes/LogIgnoreAttribute.cs index 8033ab0f..b1a694f6 100644 --- a/src/Common/uBeac.Core.Common/Attributes/LogIgnoreAttribute.cs +++ b/src/Common/uBeac.Core.Common/Attributes/LogIgnoreAttribute.cs @@ -3,13 +3,12 @@ [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Class, AllowMultiple = false, Inherited = true)] public class LogIgnoreAttribute : Attribute { - public object Value { get; set; } = (LogIgnoreAttribute)null; + public object Value { get; set; } = null; + + public LogIgnoreAttribute() {} public LogIgnoreAttribute(object value) { Value = value; - } - public LogIgnoreAttribute() - { - } + } } diff --git a/src/FileManagement/uBeac.Core.Providers.FileManagement.Abstractions/uBeac.Core.Providers.FileManagement.Abstractions.csproj b/src/FileManagement/uBeac.Core.Providers.FileManagement.Abstractions/uBeac.Core.Providers.FileManagement.Abstractions.csproj deleted file mode 100644 index 132c02c5..00000000 --- a/src/FileManagement/uBeac.Core.Providers.FileManagement.Abstractions/uBeac.Core.Providers.FileManagement.Abstractions.csproj +++ /dev/null @@ -1,9 +0,0 @@ - - - - net6.0 - enable - enable - - - diff --git a/src/FileManagement/uBeac.Core.Providers.FileManagement.LocalStorage/uBeac.Core.Providers.FileManagement.LocalStorage.csproj b/src/FileManagement/uBeac.Core.Providers.FileManagement.LocalStorage/uBeac.Core.Providers.FileManagement.LocalStorage.csproj deleted file mode 100644 index 132c02c5..00000000 --- a/src/FileManagement/uBeac.Core.Providers.FileManagement.LocalStorage/uBeac.Core.Providers.FileManagement.LocalStorage.csproj +++ /dev/null @@ -1,9 +0,0 @@ - - - - net6.0 - enable - enable - - - diff --git a/src/FileManagement/uBeac.Core.Repositories.FileManagement.Abstractions/uBeac.Core.Repositories.FileManagement.Abstractions.csproj b/src/FileManagement/uBeac.Core.Repositories.FileManagement.Abstractions/uBeac.Core.Repositories.FileManagement.Abstractions.csproj deleted file mode 100644 index 792d0a8b..00000000 --- a/src/FileManagement/uBeac.Core.Repositories.FileManagement.Abstractions/uBeac.Core.Repositories.FileManagement.Abstractions.csproj +++ /dev/null @@ -1,13 +0,0 @@ - - - - net6.0 - enable - enable - - - - - - - diff --git a/src/FileManagement/uBeac.Core.Repositories.FileManagement.MongoDB/uBeac.Core.Repositories.FileManagement.MongoDB.csproj b/src/FileManagement/uBeac.Core.Repositories.FileManagement.MongoDB/uBeac.Core.Repositories.FileManagement.MongoDB.csproj deleted file mode 100644 index 220c9528..00000000 --- a/src/FileManagement/uBeac.Core.Repositories.FileManagement.MongoDB/uBeac.Core.Repositories.FileManagement.MongoDB.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - - net6.0 - enable - enable - - - - - - - - diff --git a/src/FileManagement/uBeac.Core.Services.FileManagement.Abstractions/uBeac.Core.Services.FileManagement.Abstractions.csproj b/src/FileManagement/uBeac.Core.Services.FileManagement.Abstractions/uBeac.Core.Services.FileManagement.Abstractions.csproj deleted file mode 100644 index 132c02c5..00000000 --- a/src/FileManagement/uBeac.Core.Services.FileManagement.Abstractions/uBeac.Core.Services.FileManagement.Abstractions.csproj +++ /dev/null @@ -1,9 +0,0 @@ - - - - net6.0 - enable - enable - - - diff --git a/src/FileManagement/uBeac.Core.Services.FileManagement/uBeac.Core.Services.FileManagement.csproj b/src/FileManagement/uBeac.Core.Services.FileManagement/uBeac.Core.Services.FileManagement.csproj deleted file mode 100644 index 132c02c5..00000000 --- a/src/FileManagement/uBeac.Core.Services.FileManagement/uBeac.Core.Services.FileManagement.csproj +++ /dev/null @@ -1,9 +0,0 @@ - - - - net6.0 - enable - enable - - - diff --git a/src/Identity/Tests/API/Config/appsettings.json b/src/Identity/Tests/API/Config/appsettings.json index f8f67c63..6f9a4320 100644 --- a/src/Identity/Tests/API/Config/appsettings.json +++ b/src/Identity/Tests/API/Config/appsettings.json @@ -2,7 +2,7 @@ "ConnectionStrings": { "DefaultConnection": "mongodb://localhost:27017/Identity", "HistoryConnection": "mongodb://localhost:27017/IdentityHistory", - "HttpLoggingConnection": "mongodb://localhost:27017/Identity" + "HttpLoggingConnection": "mongodb://localhost:27017/IdentityLog" }, "Logging": { "LogLevel": { diff --git a/src/Identity/Tests/API/Config/hsts.json b/src/Identity/Tests/API/Config/hsts.json index 3f83a977..627d98de 100644 --- a/src/Identity/Tests/API/Config/hsts.json +++ b/src/Identity/Tests/API/Config/hsts.json @@ -3,6 +3,6 @@ "Preload": true, "IncludeSubDomains": true, "MaxAge": 30, - "ExcludedHosts": [ "example.com", "www.example.com" ] + "ExcludedHosts": [] } } \ No newline at end of file diff --git a/src/Identity/Tests/API/Config/http-logging.json b/src/Identity/Tests/API/Config/http-logging.json index 58d539df..3268db1a 100644 --- a/src/Identity/Tests/API/Config/http-logging.json +++ b/src/Identity/Tests/API/Config/http-logging.json @@ -1,7 +1,8 @@ { "HttpLogging": { - "Status200CollectionName": "Status200", - "Status400CollectionName": "Status400", - "Status500CollectionName": "Status500" + "Status200CollectionName": "Status200X", + "Status400CollectionName": "Status400X", + "Status500CollectionName": "Status500X", + "BypassLogTimeOut": 300 // Number of seconds to bypass insert log if there is an issue with Log DB } } diff --git a/src/Identity/Tests/API/Controllers/Base/AccountsControllerBase.cs b/src/Identity/Tests/API/Controllers/Base/AccountsControllerBase.cs index 3243fb65..ce66bc81 100644 --- a/src/Identity/Tests/API/Controllers/Base/AccountsControllerBase.cs +++ b/src/Identity/Tests/API/Controllers/Base/AccountsControllerBase.cs @@ -22,9 +22,9 @@ public virtual async Task> Register([FromBody] RegisterRequest mod } [HttpGet] - public virtual async Task Test(int id, string name) + public virtual async Task> Test(int id, string name) { - + return (await Task.FromResult(true)).ToResult(); } [HttpPost] diff --git a/src/Identity/Tests/API/Program.cs b/src/Identity/Tests/API/Program.cs index 87d4cc49..ecec77fd 100644 --- a/src/Identity/Tests/API/Program.cs +++ b/src/Identity/Tests/API/Program.cs @@ -15,7 +15,7 @@ //builder.Services.AddDefaultBsonSerializers(); // Adding http logging -builder.Services.AddMongoDbHttpLogging(builder.Configuration.GetInstance("HttpLogging")); +builder.Services.AddMongoDbHttpLogging("HttpLoggingConnection", builder.Configuration.GetInstance("HttpLogging")); builder.Services.AddHttpContextAccessor(); builder.Services.AddControllers(options => options.Filters.Add()); @@ -107,20 +107,17 @@ var app = builder.Build(); +app.UseHttpsRedirection(); app.UseHstsOnProduction(builder.Environment); app.UseCorsPolicy(corsPolicyOptions); -app.UseHttpsRedirection(); - -app.UseHttpLoggingMiddleware(); - app.UseDefaultFiles(); app.UseStaticFiles(); app.UseCoreSwagger(); app.UseAuthentication(); - +app.UseHttpLoggingMiddleware(); app.UseAuthorization(); app.MapControllers(); diff --git a/src/Identity/Tests/API/ViewModels/UserLogin.cs b/src/Identity/Tests/API/ViewModels/UserLogin.cs index 200f3fc6..f3368966 100644 --- a/src/Identity/Tests/API/ViewModels/UserLogin.cs +++ b/src/Identity/Tests/API/ViewModels/UserLogin.cs @@ -36,14 +36,15 @@ public class AltLoginRequest public int MyProperty { get; set; } [LogIgnore("")] public string MyProperty1 { get; set; } - [LogIgnore(null)] + [LogIgnore("")] public decimal MyProperty2 { get; set; } + [LogIgnore] public List MyProperty3 { get; set; } } + public class SecondLevel { public int MyProperty { get; set; } - [LogIgnore] public string MyProperty1 { get; set; } } \ No newline at end of file diff --git a/src/Logging/uBeac.Core.Web.Logging.MongoDB/Extensions.cs b/src/Logging/uBeac.Core.Web.Logging.MongoDB/Extensions.cs index 2cf925ce..5b0dc60c 100644 --- a/src/Logging/uBeac.Core.Web.Logging.MongoDB/Extensions.cs +++ b/src/Logging/uBeac.Core.Web.Logging.MongoDB/Extensions.cs @@ -6,11 +6,13 @@ namespace Microsoft.Extensions.DependencyInjection; public static class Extensions { - public static IServiceCollection AddMongoDbHttpLogging(this IServiceCollection services, MongoDbHttpLogOptions options) - where TContext : IMongoDBContext + public static IServiceCollection AddMongoDbHttpLogging(this IServiceCollection services, string connectionString, MongoDbHttpLogOptions options) + where TContext : class, IMongoDBContext { + services.AddSingleton(); + services.AddMongo(connectionString); services.AddSingleton(options); - services.AddScoped>(); + services.AddScoped>(); return services; } } \ No newline at end of file diff --git a/src/Logging/uBeac.Core.Web.Logging.MongoDB/HttpLogCache.cs b/src/Logging/uBeac.Core.Web.Logging.MongoDB/HttpLogCache.cs new file mode 100644 index 00000000..f9486613 --- /dev/null +++ b/src/Logging/uBeac.Core.Web.Logging.MongoDB/HttpLogCache.cs @@ -0,0 +1,8 @@ +using Microsoft.Extensions.Caching.Memory; + +namespace uBeac.Web.Logging.MongoDB; + +public class HttpLogCache +{ + public MemoryCache Cache { get; } = new MemoryCache(new MemoryCacheOptions()); +} diff --git a/src/Logging/uBeac.Core.Web.Logging.MongoDB/HttpLogMongoDBContext.cs b/src/Logging/uBeac.Core.Web.Logging.MongoDB/HttpLogMongoDBContext.cs new file mode 100644 index 00000000..561e95fc --- /dev/null +++ b/src/Logging/uBeac.Core.Web.Logging.MongoDB/HttpLogMongoDBContext.cs @@ -0,0 +1,11 @@ +using uBeac.Repositories.MongoDB; + +namespace uBeac.Web.Logging.MongoDB; + +public class HttpLogMongoDBContext : BaseMongoDBContext +{ + public HttpLogMongoDBContext(MongoDBOptions dbOptions, BsonSerializationOptions bsonSerializationOptions) : base(dbOptions, bsonSerializationOptions) + { + } +} + diff --git a/src/Logging/uBeac.Core.Web.Logging.MongoDB/Options.cs b/src/Logging/uBeac.Core.Web.Logging.MongoDB/Options.cs index c1c89c33..92187a76 100644 --- a/src/Logging/uBeac.Core.Web.Logging.MongoDB/Options.cs +++ b/src/Logging/uBeac.Core.Web.Logging.MongoDB/Options.cs @@ -2,10 +2,10 @@ public class MongoDbHttpLogOptions { - public string ConnectionString { get; set; } public string Status200CollectionName { get; set; } public string Status400CollectionName { get; set; } public string Status500CollectionName { get; set; } + public int BypassLogTimeOut { get; set; } = 300; // Number of seconds to bypass insert log if there is an issue with Log DB public string GetCollectionName(int statusCode) => statusCode switch { diff --git a/src/Logging/uBeac.Core.Web.Logging.MongoDB/Repository.cs b/src/Logging/uBeac.Core.Web.Logging.MongoDB/Repository.cs index 789b2114..6544a0d7 100644 --- a/src/Logging/uBeac.Core.Web.Logging.MongoDB/Repository.cs +++ b/src/Logging/uBeac.Core.Web.Logging.MongoDB/Repository.cs @@ -1,4 +1,5 @@ -using MongoDB.Driver; +using Microsoft.Extensions.Caching.Memory; +using MongoDB.Driver; using uBeac.Repositories.MongoDB; namespace uBeac.Web.Logging.MongoDB; @@ -8,18 +9,41 @@ public class MongoDbHttpLogRepository : IHttpLogRepository { protected readonly TContext Context; protected readonly MongoDbHttpLogOptions Options; + private readonly MemoryCache _memoryCache; + private const string CacheKey = "FailedLog"; - public MongoDbHttpLogRepository(TContext context, MongoDbHttpLogOptions options) + public MongoDbHttpLogRepository(TContext context, MongoDbHttpLogOptions options, HttpLogCache memoryCache) { Context = context; Options = options; + _memoryCache = memoryCache.Cache; } public async Task Create(HttpLog log, CancellationToken cancellationToken = default) { var collectionName = Options.GetCollectionName(log.StatusCode); - var collection = GetCollection(Context.Database, collectionName); - await collection.InsertOneAsync(log, new InsertOneOptions(), cancellationToken); + var collection = GetCollection(Context.Database, collectionName).WithWriteConcern(WriteConcern.Unacknowledged); + + if (!_memoryCache.TryGetValue(CacheKey, out bool result)) + { + try + { + await collection.InsertOneAsync(log, new InsertOneOptions(), cancellationToken); + return; + } + catch (Exception ex) + { + if (ex.GetType() == typeof(TimeoutException)) + { + _memoryCache.Set(CacheKey, true, TimeSpan.FromSeconds(Options.BypassLogTimeOut)); + new Exception("HttpLog: A timeout occurred during connection to the Database!"); + } + + throw new Exception("HttpLog: Error in insert log to Database!"); + } + } + else + throw new Exception("HttpLog: Log wasn't inserted in database during wait time!"); } protected virtual IMongoCollection GetCollection(IMongoDatabase database, string collectionName) diff --git a/src/Logging/uBeac.Core.Web.Logging.MongoDB/uBeac.Core.Web.Logging.MongoDB.csproj b/src/Logging/uBeac.Core.Web.Logging.MongoDB/uBeac.Core.Web.Logging.MongoDB.csproj index ca061cba..a4134299 100644 --- a/src/Logging/uBeac.Core.Web.Logging.MongoDB/uBeac.Core.Web.Logging.MongoDB.csproj +++ b/src/Logging/uBeac.Core.Web.Logging.MongoDB/uBeac.Core.Web.Logging.MongoDB.csproj @@ -18,6 +18,11 @@ + + + + + diff --git a/src/Logging/uBeac.Core.Web.Logging/CriticalDataHandlerFilter.cs b/src/Logging/uBeac.Core.Web.Logging/CriticalDataHandlerFilter.cs index 81946b9e..25411af6 100644 --- a/src/Logging/uBeac.Core.Web.Logging/CriticalDataHandlerFilter.cs +++ b/src/Logging/uBeac.Core.Web.Logging/CriticalDataHandlerFilter.cs @@ -6,27 +6,30 @@ namespace uBeac.Web.Logging; public class CriticalDataHandlerFilter : IActionFilter { + private readonly IDebugger _debugger; private readonly JsonSerializerSettings settings = new JsonSerializerSettings { ContractResolver = new LogIgnoreResolver(), - Formatting = Formatting.Indented + Formatting = Formatting.Indented, }; + public CriticalDataHandlerFilter(IDebugger debugger) + { + _debugger = debugger; + } public void OnActionExecuted(ActionExecutedContext context) { try { - var result = ((ObjectResult)context.Result).Value; - //var responseProps = result.GetType().GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(LogIgnoreAttribute))).ToList(); - //foreach (var prop in responseProps) - //{ - // prop.SetValue(result, default); - //} - context.HttpContext.Items["LogResponseBody"] = result != null ? JsonConvert.SerializeObject(result, settings) : null; + if (context.Result.GetType() != typeof(EmptyResult)) + { + var result = ((ObjectResult)context.Result).Value; + context.HttpContext.Items["LogResponseBody"] = result != null ? JsonConvert.SerializeObject(result, settings) : null; + } } catch (Exception ex) { - - } + _debugger.Add("HttpLogging: " + ex.Message); + } } public void OnActionExecuting(ActionExecutingContext context) diff --git a/src/Logging/uBeac.Core.Web.Logging/HttpLog.cs b/src/Logging/uBeac.Core.Web.Logging/HttpLog.cs index 0db3f6ad..f917b18f 100644 --- a/src/Logging/uBeac.Core.Web.Logging/HttpLog.cs +++ b/src/Logging/uBeac.Core.Web.Logging/HttpLog.cs @@ -11,7 +11,7 @@ public sealed class HttpLog : Entity public HttpLog() { var dateTime = DateTime.Now; - var assembly = Assembly.GetExecutingAssembly(); + var assembly = Assembly.GetEntryAssembly(); var assemblyName = assembly.GetName(); var process = Process.GetCurrentProcess(); var thread = Thread.CurrentThread; @@ -47,7 +47,6 @@ public HttpLog() public string MachineName { get; set; } public string EnvironmentName { get; set; } public string EnvironmentUserName { get; set; } - public string UserAgent { get; set; } public IApplicationContext Context { get; set; } diff --git a/src/Logging/uBeac.Core.Web.Logging/HttpLoggingMiddleware.cs b/src/Logging/uBeac.Core.Web.Logging/HttpLoggingMiddleware.cs index bac05d74..9c508142 100644 --- a/src/Logging/uBeac.Core.Web.Logging/HttpLoggingMiddleware.cs +++ b/src/Logging/uBeac.Core.Web.Logging/HttpLoggingMiddleware.cs @@ -2,8 +2,6 @@ using System.Text; using Microsoft.AspNetCore.Diagnostics; using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Logging; -using Microsoft.Net.Http.Headers; using Newtonsoft.Json; namespace uBeac.Web.Logging; @@ -17,7 +15,7 @@ public HttpLoggingMiddleware(RequestDelegate next) _next = next; } - public async Task Invoke(HttpContext context, IHttpLogRepository repository, IApplicationContext appContext, ILogger logger) + public async Task Invoke(HttpContext context, IHttpLogRepository repository, IApplicationContext appContext, IDebugger debugger) { try { @@ -53,22 +51,22 @@ public async Task Invoke(HttpContext context, IHttpLogRepository repository, IAp await Log(logModel, repository); } } - catch (Exception e) + catch (Exception ex) { - logger.LogError(e, "An unhandled exception has occured during logging HTTP request."); + debugger.Add(ex.Message); } } - private async Task ReadRequestBody(HttpRequest request) - { - request.EnableBuffering(); + //private async Task ReadRequestBody(HttpRequest request) + //{ + // request.EnableBuffering(); - using var reader = new StreamReader(request.Body, encoding: Encoding.UTF8, detectEncodingFromByteOrderMarks: false, leaveOpen: true); - var requestBody = await reader.ReadToEndAsync(); - request.Body.Position = 0; + // using var reader = new StreamReader(request.Body, encoding: Encoding.UTF8, detectEncodingFromByteOrderMarks: false, leaveOpen: true); + // var requestBody = await reader.ReadToEndAsync(); + // request.Body.Position = 0; - return requestBody; - } + // return requestBody; + //} private async Task ReadResponseBody(HttpContext context, Stream originalResponseStream, Stream memoryStream) { @@ -93,8 +91,7 @@ private static HttpLog CreateLogModel(HttpContext context, IApplicationContext a StatusCode = statusCode ?? context.Response.StatusCode, Duration = duration, Context = appContext, - Exception = exception == null ? null : new ExceptionModel(exception), - UserAgent = context.Request.Headers[HeaderNames.UserAgent].ToString() + Exception = exception == null ? null : new ExceptionModel(exception) }; } diff --git a/src/Logging/uBeac.Core.Web.Logging/LogIgnoreResolver.cs b/src/Logging/uBeac.Core.Web.Logging/LogIgnoreResolver.cs index 3fe50204..678ca5e3 100644 --- a/src/Logging/uBeac.Core.Web.Logging/LogIgnoreResolver.cs +++ b/src/Logging/uBeac.Core.Web.Logging/LogIgnoreResolver.cs @@ -10,23 +10,25 @@ protected override JsonProperty CreateProperty(MemberInfo member, MemberSerializ { JsonProperty result = base.CreateProperty(member, memberSerialization); PropertyInfo property = member as PropertyInfo; - + LogIgnoreAttribute attribute = (LogIgnoreAttribute)result.AttributeProvider.GetAttributes(typeof(LogIgnoreAttribute), true).FirstOrDefault(); if (attribute != null) { - var x = GetType(attribute.Value); - if (x == typeof(LogIgnoreAttribute)) + if (attribute.Value is null) + { result.Ignored = true; - else - result.ValueProvider = new LogIgnoreValueProvider(property, attribute.Value); - } + return result; + } - return result; - } + if (attribute.Value.ToString() == "") + attribute.Value = property.PropertyType == typeof(string) ? "" : null; - private Type GetType(T obj) => typeof(T); + result.ValueProvider = new LogIgnoreValueProvider(property, attribute.Value); + } + return result; + } } public class LogIgnoreValueProvider : IValueProvider @@ -47,6 +49,6 @@ public void SetValue(object target, object value) public object GetValue(object target) { - return _value; + return _value; } } diff --git a/src/Web/uBeac.Core.Web.Common/ServiceExtensions.cs b/src/Web/uBeac.Core.Web.Common/ServiceExtensions.cs index 6754adcc..e9fe6d7c 100644 --- a/src/Web/uBeac.Core.Web.Common/ServiceExtensions.cs +++ b/src/Web/uBeac.Core.Web.Common/ServiceExtensions.cs @@ -51,7 +51,7 @@ public static IServiceCollection AddHttpsPolicy(this IServiceCollection services options.Preload = hsts.Preload; options.IncludeSubDomains = hsts.IncludeSubDomains; options.MaxAge = TimeSpan.FromDays(hsts.MaxAge); - foreach (var host in hsts.ExcludedHosts) options.ExcludedHosts.Add(host); + if (hsts.ExcludedHosts != null) foreach (var host in hsts.ExcludedHosts) options.ExcludedHosts.Add(host); }); return services; diff --git a/src/uBeac.Core.sln b/src/uBeac.Core.sln index 14df339a..6b85beb4 100644 --- a/src/uBeac.Core.sln +++ b/src/uBeac.Core.sln @@ -53,21 +53,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "uBeac.Core.Repositories.His EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "uBeac.Core.Web.Logging.MongoDB", "Logging\uBeac.Core.Web.Logging.MongoDB\uBeac.Core.Web.Logging.MongoDB.csproj", "{2D538398-EAC9-4A18-8459-74CA9E499F4E}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "FileManagement", "FileManagement", "{0010CB43-FD6D-4C91-91D9-3065ACBAC2E1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "uBeac.Core.Services.FileManagement", "FileManagement\uBeac.Core.Services.FileManagement\uBeac.Core.Services.FileManagement.csproj", "{9020A29D-E4B8-42AE-9EE3-BC2B001A0CB9}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "uBeac.Core.Repositories.FileManagement.MongoDB", "FileManagement\uBeac.Core.Repositories.FileManagement.MongoDB\uBeac.Core.Repositories.FileManagement.MongoDB.csproj", "{6B9F2368-1F55-43C0-9DDD-1CDE744F3118}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "uBeac.Core.Repositories.FileManagement.Abstractions", "FileManagement\uBeac.Core.Repositories.FileManagement.Abstractions\uBeac.Core.Repositories.FileManagement.Abstractions.csproj", "{6BAB5C0D-2624-43BC-B126-D40601B1E7A9}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "uBeac.Core.Providers.FileManagement.Abstractions", "FileManagement\uBeac.Core.Providers.FileManagement.Abstractions\uBeac.Core.Providers.FileManagement.Abstractions.csproj", "{D3461DFD-840B-4AB9-93C2-7B329FCF8536}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "uBeac.Core.Providers.FileManagement.LocalStorage", "FileManagement\uBeac.Core.Providers.FileManagement.LocalStorage\uBeac.Core.Providers.FileManagement.LocalStorage.csproj", "{6780E437-7D94-4B08-9A25-CA6698C5D8DA}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "uBeac.Core.Services.FileManagement.Abstractions", "FileManagement\uBeac.Core.Services.FileManagement.Abstractions\uBeac.Core.Services.FileManagement.Abstractions.csproj", "{EA9BA74E-EE81-40E5-976A-EAB7C586F3D8}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "API", "Identity\Tests\API\API.csproj", "{1DB81852-0912-4139-BCC8-00EACF923088}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "API", "Identity\Tests\API\API.csproj", "{B8CF6F1F-5BA8-41E3-BDB9-951A64A3C95B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -151,34 +137,10 @@ Global {2D538398-EAC9-4A18-8459-74CA9E499F4E}.Debug|Any CPU.Build.0 = Debug|Any CPU {2D538398-EAC9-4A18-8459-74CA9E499F4E}.Release|Any CPU.ActiveCfg = Release|Any CPU {2D538398-EAC9-4A18-8459-74CA9E499F4E}.Release|Any CPU.Build.0 = Release|Any CPU - {9020A29D-E4B8-42AE-9EE3-BC2B001A0CB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9020A29D-E4B8-42AE-9EE3-BC2B001A0CB9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9020A29D-E4B8-42AE-9EE3-BC2B001A0CB9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9020A29D-E4B8-42AE-9EE3-BC2B001A0CB9}.Release|Any CPU.Build.0 = Release|Any CPU - {6B9F2368-1F55-43C0-9DDD-1CDE744F3118}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6B9F2368-1F55-43C0-9DDD-1CDE744F3118}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6B9F2368-1F55-43C0-9DDD-1CDE744F3118}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6B9F2368-1F55-43C0-9DDD-1CDE744F3118}.Release|Any CPU.Build.0 = Release|Any CPU - {6BAB5C0D-2624-43BC-B126-D40601B1E7A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6BAB5C0D-2624-43BC-B126-D40601B1E7A9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6BAB5C0D-2624-43BC-B126-D40601B1E7A9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6BAB5C0D-2624-43BC-B126-D40601B1E7A9}.Release|Any CPU.Build.0 = Release|Any CPU - {D3461DFD-840B-4AB9-93C2-7B329FCF8536}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D3461DFD-840B-4AB9-93C2-7B329FCF8536}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D3461DFD-840B-4AB9-93C2-7B329FCF8536}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D3461DFD-840B-4AB9-93C2-7B329FCF8536}.Release|Any CPU.Build.0 = Release|Any CPU - {6780E437-7D94-4B08-9A25-CA6698C5D8DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6780E437-7D94-4B08-9A25-CA6698C5D8DA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6780E437-7D94-4B08-9A25-CA6698C5D8DA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6780E437-7D94-4B08-9A25-CA6698C5D8DA}.Release|Any CPU.Build.0 = Release|Any CPU - {EA9BA74E-EE81-40E5-976A-EAB7C586F3D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EA9BA74E-EE81-40E5-976A-EAB7C586F3D8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EA9BA74E-EE81-40E5-976A-EAB7C586F3D8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EA9BA74E-EE81-40E5-976A-EAB7C586F3D8}.Release|Any CPU.Build.0 = Release|Any CPU - {1DB81852-0912-4139-BCC8-00EACF923088}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1DB81852-0912-4139-BCC8-00EACF923088}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1DB81852-0912-4139-BCC8-00EACF923088}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1DB81852-0912-4139-BCC8-00EACF923088}.Release|Any CPU.Build.0 = Release|Any CPU + {B8CF6F1F-5BA8-41E3-BDB9-951A64A3C95B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B8CF6F1F-5BA8-41E3-BDB9-951A64A3C95B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B8CF6F1F-5BA8-41E3-BDB9-951A64A3C95B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B8CF6F1F-5BA8-41E3-BDB9-951A64A3C95B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -203,12 +165,6 @@ Global {AAA5B1BE-4216-4ADB-9FB3-B03D35CF8D03} = {2939E5A7-F1C3-4853-9F35-31EB64C08036} {CA77313D-6D97-4F91-9818-B729FA0359D2} = {2939E5A7-F1C3-4853-9F35-31EB64C08036} {2D538398-EAC9-4A18-8459-74CA9E499F4E} = {117FC7BE-0153-4819-9A27-0FDA22132FA1} - {9020A29D-E4B8-42AE-9EE3-BC2B001A0CB9} = {0010CB43-FD6D-4C91-91D9-3065ACBAC2E1} - {6B9F2368-1F55-43C0-9DDD-1CDE744F3118} = {0010CB43-FD6D-4C91-91D9-3065ACBAC2E1} - {6BAB5C0D-2624-43BC-B126-D40601B1E7A9} = {0010CB43-FD6D-4C91-91D9-3065ACBAC2E1} - {D3461DFD-840B-4AB9-93C2-7B329FCF8536} = {0010CB43-FD6D-4C91-91D9-3065ACBAC2E1} - {6780E437-7D94-4B08-9A25-CA6698C5D8DA} = {0010CB43-FD6D-4C91-91D9-3065ACBAC2E1} - {EA9BA74E-EE81-40E5-976A-EAB7C586F3D8} = {0010CB43-FD6D-4C91-91D9-3065ACBAC2E1} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {247F9BF0-18BB-4A57-BB3E-2870DDAEF0D1} From 40890567695cd3b94f3a1b747c1c4783fdeaf1fe Mon Sep 17 00:00:00 2001 From: Hesam Asnaashari <46031836+ilhesam@users.noreply.github.com> Date: Thu, 1 Sep 2022 18:16:39 +0430 Subject: [PATCH 5/6] FEAT - HttpLogging: Implement ignoring and replacing values --- .../Attributes/LogIgnoreAttribute.cs | 14 ---- src/Identity/Tests/API/Program.cs | 2 +- .../Tests/API/ViewModels/UserLogin.cs | 18 ++-- .../Attributes/LogIgnoreAttribute.cs | 6 ++ .../Attributes/LogReplaceValueAttribute.cs | 12 +++ .../CriticalDataHandlerFilter.cs | 41 --------- .../Helpers/HttpLogDataHandlingFilter.cs | 84 +++++++++++++++++++ .../Helpers/HttpLoggingExtensions.cs | 41 +++++++++ .../HttpLoggingMiddleware.cs | 68 ++------------- .../LogIgnoreResolver.cs | 54 ------------ .../uBeac.Core.Web.Logging.csproj | 1 + 11 files changed, 160 insertions(+), 181 deletions(-) delete mode 100644 src/Common/uBeac.Core.Common/Attributes/LogIgnoreAttribute.cs create mode 100644 src/Logging/uBeac.Core.Web.Logging/Attributes/LogIgnoreAttribute.cs create mode 100644 src/Logging/uBeac.Core.Web.Logging/Attributes/LogReplaceValueAttribute.cs delete mode 100644 src/Logging/uBeac.Core.Web.Logging/CriticalDataHandlerFilter.cs create mode 100644 src/Logging/uBeac.Core.Web.Logging/Helpers/HttpLogDataHandlingFilter.cs create mode 100644 src/Logging/uBeac.Core.Web.Logging/Helpers/HttpLoggingExtensions.cs delete mode 100644 src/Logging/uBeac.Core.Web.Logging/LogIgnoreResolver.cs diff --git a/src/Common/uBeac.Core.Common/Attributes/LogIgnoreAttribute.cs b/src/Common/uBeac.Core.Common/Attributes/LogIgnoreAttribute.cs deleted file mode 100644 index b1a694f6..00000000 --- a/src/Common/uBeac.Core.Common/Attributes/LogIgnoreAttribute.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace uBeac; - -[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Class, AllowMultiple = false, Inherited = true)] -public class LogIgnoreAttribute : Attribute -{ - public object Value { get; set; } = null; - - public LogIgnoreAttribute() {} - - public LogIgnoreAttribute(object value) - { - Value = value; - } -} diff --git a/src/Identity/Tests/API/Program.cs b/src/Identity/Tests/API/Program.cs index ecec77fd..175990fe 100644 --- a/src/Identity/Tests/API/Program.cs +++ b/src/Identity/Tests/API/Program.cs @@ -18,7 +18,7 @@ builder.Services.AddMongoDbHttpLogging("HttpLoggingConnection", builder.Configuration.GetInstance("HttpLogging")); builder.Services.AddHttpContextAccessor(); -builder.Services.AddControllers(options => options.Filters.Add()); +builder.Services.AddControllers(options => options.Filters.Add()); builder.Services.AddAutoMapper(Assembly.GetExecutingAssembly()); // Disabling automatic model state validation diff --git a/src/Identity/Tests/API/ViewModels/UserLogin.cs b/src/Identity/Tests/API/ViewModels/UserLogin.cs index f3368966..eb18083f 100644 --- a/src/Identity/Tests/API/ViewModels/UserLogin.cs +++ b/src/Identity/Tests/API/ViewModels/UserLogin.cs @@ -1,20 +1,17 @@ -using Newtonsoft.Json; -using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations; namespace API; public class LoginRequest { [Required] - [LogIgnore("-----")] public string UserName { get; set; } - [Required] - [DataType(DataType.Password)] - [LogIgnore("*****")] + [Required, DataType(DataType.Password), LogReplaceValue("***")] public string Password { get; set; } public AltLoginRequest Nested { get; set; } + public LoginRequest() { Nested = new AltLoginRequest(); @@ -34,12 +31,9 @@ public class LoginResponse public class AltLoginRequest { public int MyProperty { get; set; } - [LogIgnore("")] - public string MyProperty1 { get; set; } - [LogIgnore("")] - public decimal MyProperty2 { get; set; } - [LogIgnore] - public List MyProperty3 { get; set; } + [LogIgnore] public string MyProperty1 { get; set; } + [LogIgnore] public decimal MyProperty2 { get; set; } + [LogIgnore] public List MyProperty3 { get; set; } } diff --git a/src/Logging/uBeac.Core.Web.Logging/Attributes/LogIgnoreAttribute.cs b/src/Logging/uBeac.Core.Web.Logging/Attributes/LogIgnoreAttribute.cs new file mode 100644 index 00000000..79cbcf16 --- /dev/null +++ b/src/Logging/uBeac.Core.Web.Logging/Attributes/LogIgnoreAttribute.cs @@ -0,0 +1,6 @@ +namespace uBeac; + +[AttributeUsage(AttributeTargets.Property | AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] +public class LogIgnoreAttribute : Attribute +{ +} \ No newline at end of file diff --git a/src/Logging/uBeac.Core.Web.Logging/Attributes/LogReplaceValueAttribute.cs b/src/Logging/uBeac.Core.Web.Logging/Attributes/LogReplaceValueAttribute.cs new file mode 100644 index 00000000..04cce6ad --- /dev/null +++ b/src/Logging/uBeac.Core.Web.Logging/Attributes/LogReplaceValueAttribute.cs @@ -0,0 +1,12 @@ +namespace uBeac; + +[AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)] +public class LogReplaceValueAttribute : Attribute +{ + public LogReplaceValueAttribute(object value) + { + Value = value; + } + + public object Value { get; set; } +} \ No newline at end of file diff --git a/src/Logging/uBeac.Core.Web.Logging/CriticalDataHandlerFilter.cs b/src/Logging/uBeac.Core.Web.Logging/CriticalDataHandlerFilter.cs deleted file mode 100644 index 25411af6..00000000 --- a/src/Logging/uBeac.Core.Web.Logging/CriticalDataHandlerFilter.cs +++ /dev/null @@ -1,41 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Filters; -using Newtonsoft.Json; - -namespace uBeac.Web.Logging; - -public class CriticalDataHandlerFilter : IActionFilter -{ - private readonly IDebugger _debugger; - private readonly JsonSerializerSettings settings = new JsonSerializerSettings - { - ContractResolver = new LogIgnoreResolver(), - Formatting = Formatting.Indented, - }; - public CriticalDataHandlerFilter(IDebugger debugger) - { - _debugger = debugger; - } - public void OnActionExecuted(ActionExecutedContext context) - { - try - { - if (context.Result.GetType() != typeof(EmptyResult)) - { - var result = ((ObjectResult)context.Result).Value; - context.HttpContext.Items["LogResponseBody"] = result != null ? JsonConvert.SerializeObject(result, settings) : null; - } - } - catch (Exception ex) - { - _debugger.Add("HttpLogging: " + ex.Message); - } - } - - public void OnActionExecuting(ActionExecutingContext context) - { - var args = context.ActionArguments?.Where(x => x.Value.GetType() != typeof(CancellationToken)).ToList(); - context.HttpContext.Items["LogRequestBody"] = args != null ? JsonConvert.SerializeObject(args, settings) : null; - } -} - diff --git a/src/Logging/uBeac.Core.Web.Logging/Helpers/HttpLogDataHandlingFilter.cs b/src/Logging/uBeac.Core.Web.Logging/Helpers/HttpLogDataHandlingFilter.cs new file mode 100644 index 00000000..6f2b98d1 --- /dev/null +++ b/src/Logging/uBeac.Core.Web.Logging/Helpers/HttpLogDataHandlingFilter.cs @@ -0,0 +1,84 @@ +using System.Reflection; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Controllers; +using Microsoft.AspNetCore.Mvc.Filters; +using Newtonsoft.Json; +using Newtonsoft.Json.Serialization; + +namespace uBeac.Web.Logging; + +public class HttpLogDataHandlingFilter : IActionFilter, IOrderedFilter +{ + private readonly JsonSerializerSettings _serializationSettings = new() + { + ContractResolver = new JsonLogResolver(), + Formatting = Formatting.Indented + }; + + private bool _logIgnored; + + public void OnActionExecuting(ActionExecutingContext context) + { + var ignoredController = context.Controller.GetType().IsIgnored(); + var ignoredAction = ((ControllerActionDescriptor)context.ActionDescriptor).MethodInfo.IsIgnored(); + _logIgnored = ignoredController || ignoredAction; + context.HttpContext.SetLogIgnored(_logIgnored); + if (_logIgnored) return; + + var requestArgs = context.ActionArguments.Where(arg => arg.Value == null || arg.Value.GetType() != typeof(CancellationToken)).ToList(); + var logRequestBody = JsonConvert.SerializeObject(requestArgs, _serializationSettings); + context.HttpContext.SetLogRequestBody(logRequestBody); + } + + public void OnActionExecuted(ActionExecutedContext context) + { + if (_logIgnored) return; + + if (context.Result == null || context.Result.GetType() == typeof(EmptyResult)) + { + var emptyResult = JsonConvert.SerializeObject(new { }, _serializationSettings); + context.HttpContext.SetLogResponseBody(emptyResult); + return; + } + + var resultValue = ((ObjectResult)context.Result).Value; + var logResponseBody = resultValue != null ? JsonConvert.SerializeObject(resultValue, _serializationSettings) : null; + context.HttpContext.SetLogResponseBody(logResponseBody); + } + + public int Order => int.MaxValue; +} + +internal class JsonLogResolver : CamelCasePropertyNamesContractResolver +{ + protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) + { + var result = base.CreateProperty(member, memberSerialization); + + // Ignore value + result.Ignored = member.IsIgnored(); + if (result.Ignored) return result; + + // Replace value + if (member.HasReplaceValue()) + { + var replaceValue = member.GetReplaceValue(); + result.ValueProvider = new ReplaceValueProvider(replaceValue); + } + + return result; + } +} + +internal class ReplaceValueProvider : IValueProvider +{ + private readonly object _replaceValue; + + public ReplaceValueProvider(object replaceValue) + { + _replaceValue = replaceValue; + } + + public void SetValue(object target, object value) { } + public object GetValue(object target) => _replaceValue; +} \ No newline at end of file diff --git a/src/Logging/uBeac.Core.Web.Logging/Helpers/HttpLoggingExtensions.cs b/src/Logging/uBeac.Core.Web.Logging/Helpers/HttpLoggingExtensions.cs new file mode 100644 index 00000000..69def501 --- /dev/null +++ b/src/Logging/uBeac.Core.Web.Logging/Helpers/HttpLoggingExtensions.cs @@ -0,0 +1,41 @@ +using System.Reflection; +using Microsoft.AspNetCore.Diagnostics; +using Microsoft.AspNetCore.Http; + +namespace uBeac.Web.Logging; + +internal static class HttpLoggingExtensions +{ + private const string LOG_REQUEST_BODY = "_LogRequestBody"; + private const string LOG_RESPONSE_BODY = "_LogResponseBody"; + private const string LOG_IGNORED = "_LogIgnored"; + + public static void SetLogRequestBody(this HttpContext context, object requestBody) => context.Items[LOG_REQUEST_BODY] = requestBody; + public static object GetLogRequestBody(this HttpContext context) => context.Items[LOG_REQUEST_BODY]; + + public static void SetLogResponseBody(this HttpContext context, object responseBody) => context.Items[LOG_RESPONSE_BODY] = responseBody; + public static object GetLogResponseBody(this HttpContext context) => context.Items[LOG_RESPONSE_BODY]; + + public static void SetLogIgnored(this HttpContext context, bool ignored = true) => context.Items[LOG_IGNORED] = ignored; + public static bool LogIgnored(this HttpContext context) => context.Items[LOG_IGNORED] != null && (bool)context.Items[LOG_IGNORED]; + + public static HttpLog CreateLogModel(this HttpContext context, IApplicationContext appContext, string requestBody, string responseBody, long duration, int? statusCode = null, Exception exception = null) + { + exception ??= context.Features.Get()?.Error; + + return new HttpLog + { + Request = new HttpRequestLog(context.Request, requestBody), + Response = new HttpResponseLog(context.Response, responseBody), + StatusCode = statusCode ?? context.Response.StatusCode, + Duration = duration, + Context = appContext, + Exception = exception == null ? null : new ExceptionModel(exception) + }; + } + + public static bool IsIgnored(this MemberInfo target) => target.GetCustomAttributes(typeof(LogIgnoreAttribute), true).Any(); + + public static bool HasReplaceValue(this MemberInfo target) => target.GetCustomAttributes(typeof(LogReplaceValueAttribute), true).Any(); + public static object GetReplaceValue(this MemberInfo target) => ((LogReplaceValueAttribute)target.GetCustomAttributes(typeof(LogReplaceValueAttribute), true).First()).Value; +} \ No newline at end of file diff --git a/src/Logging/uBeac.Core.Web.Logging/HttpLoggingMiddleware.cs b/src/Logging/uBeac.Core.Web.Logging/HttpLoggingMiddleware.cs index 9c508142..521c74ca 100644 --- a/src/Logging/uBeac.Core.Web.Logging/HttpLoggingMiddleware.cs +++ b/src/Logging/uBeac.Core.Web.Logging/HttpLoggingMiddleware.cs @@ -1,6 +1,4 @@ using System.Diagnostics; -using System.Text; -using Microsoft.AspNetCore.Diagnostics; using Microsoft.AspNetCore.Http; using Newtonsoft.Json; @@ -20,13 +18,6 @@ public async Task Invoke(HttpContext context, IHttpLogRepository repository, IAp try { var stopwatch = Stopwatch.StartNew(); - - //var requestBody = await ReadRequestBody(context.Request); - - var originalResponseStream = context.Response.Body; - await using var responseMemoryStream = new MemoryStream(); - context.Response.Body = responseMemoryStream; - Exception exception = null; try @@ -40,15 +31,16 @@ public async Task Invoke(HttpContext context, IHttpLogRepository repository, IAp } finally { - //var responseBody = await ReadResponseBody(context, originalResponseStream, responseMemoryStream); - await ReadResponseBody(context, originalResponseStream, responseMemoryStream); - var requestBody = JsonConvert.SerializeObject(context.Items["LogRequestBody"]); - var responseBody = JsonConvert.SerializeObject(context.Items["LogResponseBody"]); + if (context.LogIgnored() is false) + { + var requestBody = JsonConvert.SerializeObject(context.GetLogRequestBody() ?? new {}); + var responseBody = JsonConvert.SerializeObject(context.GetLogResponseBody() ?? new {}); - stopwatch.Stop(); + stopwatch.Stop(); - var logModel = CreateLogModel(context, appContext, requestBody, responseBody, stopwatch.ElapsedMilliseconds, exception != null ? 500 : null, exception); - await Log(logModel, repository); + var model = context.CreateLogModel(appContext, requestBody, responseBody, stopwatch.ElapsedMilliseconds, exception != null ? 500 : null, exception); + await Log(model, repository); + } } } catch (Exception ex) @@ -57,47 +49,5 @@ public async Task Invoke(HttpContext context, IHttpLogRepository repository, IAp } } - //private async Task ReadRequestBody(HttpRequest request) - //{ - // request.EnableBuffering(); - - // using var reader = new StreamReader(request.Body, encoding: Encoding.UTF8, detectEncodingFromByteOrderMarks: false, leaveOpen: true); - // var requestBody = await reader.ReadToEndAsync(); - // request.Body.Position = 0; - - // return requestBody; - //} - - private async Task ReadResponseBody(HttpContext context, Stream originalResponseStream, Stream memoryStream) - { - memoryStream.Position = 0; - using var reader = new StreamReader(memoryStream, encoding: Encoding.UTF8); - var responseBody = await reader.ReadToEndAsync(); - memoryStream.Position = 0; - await memoryStream.CopyToAsync(originalResponseStream); - context.Response.Body = originalResponseStream; - - return responseBody; - } - - private static HttpLog CreateLogModel(HttpContext context, IApplicationContext appContext, string requestBody, string responseBody, long duration, int? statusCode = null, Exception exception = null) - { - exception ??= context.Features.Get()?.Error; - - return new HttpLog - { - Request = new HttpRequestLog(context.Request, requestBody), - Response = new HttpResponseLog(context.Response, responseBody), - StatusCode = statusCode ?? context.Response.StatusCode, - Duration = duration, - Context = appContext, - Exception = exception == null ? null : new ExceptionModel(exception) - }; - } - - private static async Task Log(HttpLog log, IHttpLogRepository repository) - { - await repository.Create(log); - } - + private static async Task Log(HttpLog log, IHttpLogRepository repository) => await repository.Create(log); } \ No newline at end of file diff --git a/src/Logging/uBeac.Core.Web.Logging/LogIgnoreResolver.cs b/src/Logging/uBeac.Core.Web.Logging/LogIgnoreResolver.cs deleted file mode 100644 index 678ca5e3..00000000 --- a/src/Logging/uBeac.Core.Web.Logging/LogIgnoreResolver.cs +++ /dev/null @@ -1,54 +0,0 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Serialization; -using System.Reflection; - -namespace uBeac.Web.Logging; - -public class LogIgnoreResolver : DefaultContractResolver -{ - protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) - { - JsonProperty result = base.CreateProperty(member, memberSerialization); - PropertyInfo property = member as PropertyInfo; - - LogIgnoreAttribute attribute = (LogIgnoreAttribute)result.AttributeProvider.GetAttributes(typeof(LogIgnoreAttribute), true).FirstOrDefault(); - - if (attribute != null) - { - if (attribute.Value is null) - { - result.Ignored = true; - return result; - } - - if (attribute.Value.ToString() == "") - attribute.Value = property.PropertyType == typeof(string) ? "" : null; - - result.ValueProvider = new LogIgnoreValueProvider(property, attribute.Value); - } - - return result; - } -} - -public class LogIgnoreValueProvider : IValueProvider -{ - private PropertyInfo _targetProperty; - private object _value; - - public LogIgnoreValueProvider(PropertyInfo targetProperty, object value) - { - _targetProperty = targetProperty; - _value = value; - } - - public void SetValue(object target, object value) - { - _targetProperty.SetValue(target, value); - } - - public object GetValue(object target) - { - return _value; - } -} diff --git a/src/Logging/uBeac.Core.Web.Logging/uBeac.Core.Web.Logging.csproj b/src/Logging/uBeac.Core.Web.Logging/uBeac.Core.Web.Logging.csproj index 883a35b9..4f90eca9 100644 --- a/src/Logging/uBeac.Core.Web.Logging/uBeac.Core.Web.Logging.csproj +++ b/src/Logging/uBeac.Core.Web.Logging/uBeac.Core.Web.Logging.csproj @@ -19,6 +19,7 @@ + From c0d1bab3e002039441e6ec9dbc2135fb81807987 Mon Sep 17 00:00:00 2001 From: Mohammad Taherian Date: Thu, 20 Oct 2022 18:44:43 -0400 Subject: [PATCH 6/6] removed serialized object from httpcontext and put in scoped variable --- src/Examples/Identity.MongoDB.API/Program.cs | 1 + .../ViewModels/UserLogin.cs | 22 ------------------- .../Helpers/HttpLogDataHandlingFilter.cs | 20 ++++++++++++----- .../Helpers/HttpLoggingExtensions.cs | 13 ----------- .../uBeac.Core.Web.Logging/HttpLogChanges.cs | 10 +++++++++ .../HttpLoggingMiddleware.cs | 14 +++++------- .../uBeac.Core.Web.Logging/LogConstants.cs | 8 +++++++ .../ServiceExtensions.cs | 14 ++++++++++++ 8 files changed, 52 insertions(+), 50 deletions(-) create mode 100644 src/Logging/uBeac.Core.Web.Logging/HttpLogChanges.cs create mode 100644 src/Logging/uBeac.Core.Web.Logging/LogConstants.cs create mode 100644 src/Logging/uBeac.Core.Web.Logging/ServiceExtensions.cs diff --git a/src/Examples/Identity.MongoDB.API/Program.cs b/src/Examples/Identity.MongoDB.API/Program.cs index 4f6f190d..98c6eb9b 100644 --- a/src/Examples/Identity.MongoDB.API/Program.cs +++ b/src/Examples/Identity.MongoDB.API/Program.cs @@ -15,6 +15,7 @@ //builder.Services.AddDefaultBsonSerializers(); // Adding http logging +builder.Services.AddHttpLogServices(); builder.Services.AddMongoDbHttpLogging("HttpLoggingConnection", builder.Configuration.GetInstance("HttpLogging")); builder.Services.AddHttpContextAccessor(); diff --git a/src/Examples/Identity.MongoDB.API/ViewModels/UserLogin.cs b/src/Examples/Identity.MongoDB.API/ViewModels/UserLogin.cs index eb18083f..f76b1009 100644 --- a/src/Examples/Identity.MongoDB.API/ViewModels/UserLogin.cs +++ b/src/Examples/Identity.MongoDB.API/ViewModels/UserLogin.cs @@ -9,13 +9,6 @@ public class LoginRequest [Required, DataType(DataType.Password), LogReplaceValue("***")] public string Password { get; set; } - - public AltLoginRequest Nested { get; set; } - - public LoginRequest() - { - Nested = new AltLoginRequest(); - } } public class LoginResponse @@ -27,18 +20,3 @@ public class LoginResponse public string RefreshToken { get; set; } public DateTime Expiry { get; set; } } - -public class AltLoginRequest -{ - public int MyProperty { get; set; } - [LogIgnore] public string MyProperty1 { get; set; } - [LogIgnore] public decimal MyProperty2 { get; set; } - [LogIgnore] public List MyProperty3 { get; set; } -} - - -public class SecondLevel -{ - public int MyProperty { get; set; } - public string MyProperty1 { get; set; } -} \ No newline at end of file diff --git a/src/Logging/uBeac.Core.Web.Logging/Helpers/HttpLogDataHandlingFilter.cs b/src/Logging/uBeac.Core.Web.Logging/Helpers/HttpLogDataHandlingFilter.cs index 6f2b98d1..f295818d 100644 --- a/src/Logging/uBeac.Core.Web.Logging/Helpers/HttpLogDataHandlingFilter.cs +++ b/src/Logging/uBeac.Core.Web.Logging/Helpers/HttpLogDataHandlingFilter.cs @@ -9,6 +9,12 @@ namespace uBeac.Web.Logging; public class HttpLogDataHandlingFilter : IActionFilter, IOrderedFilter { + private readonly IHttpLogChanges _httpLogChanges; + + public HttpLogDataHandlingFilter(IHttpLogChanges httpLogChanges) + { + _httpLogChanges = httpLogChanges; + } private readonly JsonSerializerSettings _serializationSettings = new() { ContractResolver = new JsonLogResolver(), @@ -22,12 +28,13 @@ public void OnActionExecuting(ActionExecutingContext context) var ignoredController = context.Controller.GetType().IsIgnored(); var ignoredAction = ((ControllerActionDescriptor)context.ActionDescriptor).MethodInfo.IsIgnored(); _logIgnored = ignoredController || ignoredAction; - context.HttpContext.SetLogIgnored(_logIgnored); + _httpLogChanges.Add(LogConstants.LOG_IGNORED, _logIgnored); if (_logIgnored) return; - var requestArgs = context.ActionArguments.Where(arg => arg.Value == null || arg.Value.GetType() != typeof(CancellationToken)).ToList(); + var requestArgs = context.ActionArguments.Where(arg => arg.Value != null && arg.Value.GetType() != typeof(CancellationToken)).ToList(); var logRequestBody = JsonConvert.SerializeObject(requestArgs, _serializationSettings); - context.HttpContext.SetLogRequestBody(logRequestBody); + + _httpLogChanges.Add(LogConstants.REQUEST_BODY, logRequestBody); } public void OnActionExecuted(ActionExecutedContext context) @@ -37,13 +44,14 @@ public void OnActionExecuted(ActionExecutedContext context) if (context.Result == null || context.Result.GetType() == typeof(EmptyResult)) { var emptyResult = JsonConvert.SerializeObject(new { }, _serializationSettings); - context.HttpContext.SetLogResponseBody(emptyResult); + _httpLogChanges.Add(LogConstants.RESPONSE_BODY, emptyResult); return; } var resultValue = ((ObjectResult)context.Result).Value; var logResponseBody = resultValue != null ? JsonConvert.SerializeObject(resultValue, _serializationSettings) : null; - context.HttpContext.SetLogResponseBody(logResponseBody); + + _httpLogChanges.Add(LogConstants.RESPONSE_BODY, logResponseBody); } public int Order => int.MaxValue; @@ -65,7 +73,7 @@ protected override JsonProperty CreateProperty(MemberInfo member, MemberSerializ var replaceValue = member.GetReplaceValue(); result.ValueProvider = new ReplaceValueProvider(replaceValue); } - + return result; } } diff --git a/src/Logging/uBeac.Core.Web.Logging/Helpers/HttpLoggingExtensions.cs b/src/Logging/uBeac.Core.Web.Logging/Helpers/HttpLoggingExtensions.cs index 69def501..8abe9367 100644 --- a/src/Logging/uBeac.Core.Web.Logging/Helpers/HttpLoggingExtensions.cs +++ b/src/Logging/uBeac.Core.Web.Logging/Helpers/HttpLoggingExtensions.cs @@ -6,19 +6,6 @@ namespace uBeac.Web.Logging; internal static class HttpLoggingExtensions { - private const string LOG_REQUEST_BODY = "_LogRequestBody"; - private const string LOG_RESPONSE_BODY = "_LogResponseBody"; - private const string LOG_IGNORED = "_LogIgnored"; - - public static void SetLogRequestBody(this HttpContext context, object requestBody) => context.Items[LOG_REQUEST_BODY] = requestBody; - public static object GetLogRequestBody(this HttpContext context) => context.Items[LOG_REQUEST_BODY]; - - public static void SetLogResponseBody(this HttpContext context, object responseBody) => context.Items[LOG_RESPONSE_BODY] = responseBody; - public static object GetLogResponseBody(this HttpContext context) => context.Items[LOG_RESPONSE_BODY]; - - public static void SetLogIgnored(this HttpContext context, bool ignored = true) => context.Items[LOG_IGNORED] = ignored; - public static bool LogIgnored(this HttpContext context) => context.Items[LOG_IGNORED] != null && (bool)context.Items[LOG_IGNORED]; - public static HttpLog CreateLogModel(this HttpContext context, IApplicationContext appContext, string requestBody, string responseBody, long duration, int? statusCode = null, Exception exception = null) { exception ??= context.Features.Get()?.Error; diff --git a/src/Logging/uBeac.Core.Web.Logging/HttpLogChanges.cs b/src/Logging/uBeac.Core.Web.Logging/HttpLogChanges.cs new file mode 100644 index 00000000..7fedd3b8 --- /dev/null +++ b/src/Logging/uBeac.Core.Web.Logging/HttpLogChanges.cs @@ -0,0 +1,10 @@ +namespace uBeac.Web.Logging +{ + public interface IHttpLogChanges : IDictionary + { + + } + internal class HttpLogChanges : Dictionary, IHttpLogChanges + { + } +} diff --git a/src/Logging/uBeac.Core.Web.Logging/HttpLoggingMiddleware.cs b/src/Logging/uBeac.Core.Web.Logging/HttpLoggingMiddleware.cs index 521c74ca..af384194 100644 --- a/src/Logging/uBeac.Core.Web.Logging/HttpLoggingMiddleware.cs +++ b/src/Logging/uBeac.Core.Web.Logging/HttpLoggingMiddleware.cs @@ -1,6 +1,5 @@ using System.Diagnostics; using Microsoft.AspNetCore.Http; -using Newtonsoft.Json; namespace uBeac.Web.Logging; @@ -13,7 +12,7 @@ public HttpLoggingMiddleware(RequestDelegate next) _next = next; } - public async Task Invoke(HttpContext context, IHttpLogRepository repository, IApplicationContext appContext, IDebugger debugger) + public async Task Invoke(HttpContext context, IHttpLogRepository repository, IApplicationContext appContext, IDebugger debugger, IHttpLogChanges httpLogChanges) { try { @@ -31,14 +30,11 @@ public async Task Invoke(HttpContext context, IHttpLogRepository repository, IAp } finally { - if (context.LogIgnored() is false) - { - var requestBody = JsonConvert.SerializeObject(context.GetLogRequestBody() ?? new {}); - var responseBody = JsonConvert.SerializeObject(context.GetLogResponseBody() ?? new {}); - - stopwatch.Stop(); + stopwatch.Stop(); - var model = context.CreateLogModel(appContext, requestBody, responseBody, stopwatch.ElapsedMilliseconds, exception != null ? 500 : null, exception); + if (!httpLogChanges.ContainsKey(LogConstants.LOG_IGNORED) || httpLogChanges[LogConstants.LOG_IGNORED] is false) + { + var model = context.CreateLogModel(appContext, httpLogChanges[LogConstants.REQUEST_BODY].ToString(), httpLogChanges[LogConstants.RESPONSE_BODY].ToString(), stopwatch.ElapsedMilliseconds, exception != null ? 500 : null, exception); await Log(model, repository); } } diff --git a/src/Logging/uBeac.Core.Web.Logging/LogConstants.cs b/src/Logging/uBeac.Core.Web.Logging/LogConstants.cs new file mode 100644 index 00000000..cfc03f17 --- /dev/null +++ b/src/Logging/uBeac.Core.Web.Logging/LogConstants.cs @@ -0,0 +1,8 @@ +namespace uBeac.Web.Logging; + +public class LogConstants +{ + public const string LOG_IGNORED = "LogIgnored"; + public const string REQUEST_BODY = "LogRequestBody"; + public const string RESPONSE_BODY = "LogResponseBody"; +} diff --git a/src/Logging/uBeac.Core.Web.Logging/ServiceExtensions.cs b/src/Logging/uBeac.Core.Web.Logging/ServiceExtensions.cs new file mode 100644 index 00000000..63a65449 --- /dev/null +++ b/src/Logging/uBeac.Core.Web.Logging/ServiceExtensions.cs @@ -0,0 +1,14 @@ + +using uBeac.Web.Logging; + +namespace Microsoft.Extensions.DependencyInjection +{ + public static class ServiceExtensions + { + public static IServiceCollection AddHttpLogServices(this IServiceCollection services) + { + services.AddScoped(); + return services; + } + } +}