diff --git a/SharedKernel.Demo/Program.cs b/SharedKernel.Demo/Program.cs index 7bb2a90..4c8b38e 100644 --- a/SharedKernel.Demo/Program.cs +++ b/SharedKernel.Demo/Program.cs @@ -126,7 +126,7 @@ System.Text.Encoding.UTF8, "application/json"); - var response = await httpClient.PostAsync("body", content); + var response = await httpClient.PostAsync("body?barev=5", content); if (!response.IsSuccessStatusCode) { diff --git a/src/SharedKernel/Logging/Middleware/OutboundLoggingHandler.cs b/src/SharedKernel/Logging/Middleware/OutboundLoggingHandler.cs index 4813e68..5278757 100644 --- a/src/SharedKernel/Logging/Middleware/OutboundLoggingHandler.cs +++ b/src/SharedKernel/Logging/Middleware/OutboundLoggingHandler.cs @@ -75,19 +75,37 @@ protected override async Task SendAsync(HttpRequestMessage resMedia); } - logger.LogInformation( - "[Outbound Call] HTTP {Method} {Uri} responded with {StatusCode} in {ElapsedMs}ms. " + - "Request Headers: {RequestHeaders}, Request Body: {RequestBody}, " + - "Response Headers: {ResponseHeaders}, Response Body: {ResponseBody}", - request.Method, - request.RequestUri?.ToString(), - (int)response.StatusCode, - elapsed, - reqHeaders, - reqBody, - resHeaders, - resBody); - - return response; + var hostPath = request.RequestUri is null + ? "" + : request.RequestUri.GetLeftPart(UriPartial.Path); // message: path only (no query) + + var scope = new Dictionary + { + ["RequestHeaders"] = reqHeaders, + ["RequestBody"] = reqBody, + ["ResponseHeaders"] = resHeaders, + ["ResponseBody"] = resBody, + ["ElapsedMs"] = elapsed, + ["Kind"] = "HttpOut" + }; + + var query = request.RequestUri?.Query; + if (!string.IsNullOrEmpty(query)) + { + scope["Query"] = query; + } + + using (logger.BeginScope(scope)) + { + logger.LogInformation( + "[HTTP OUT] {Method} {HostPath} -> {StatusCode} in {ElapsedMilliseconds}ms", + request.Method, + hostPath, + (int)response.StatusCode, + elapsed + ); + + return response; + } } } \ No newline at end of file diff --git a/src/SharedKernel/Logging/Middleware/RequestLoggingMiddleware.cs b/src/SharedKernel/Logging/Middleware/RequestLoggingMiddleware.cs index b3ede70..d4c7c38 100644 --- a/src/SharedKernel/Logging/Middleware/RequestLoggingMiddleware.cs +++ b/src/SharedKernel/Logging/Middleware/RequestLoggingMiddleware.cs @@ -29,7 +29,8 @@ public async Task InvokeAsync(HttpContext context) context.Request.ContentType); await using var responseBuffer = - new Microsoft.AspNetCore.WebUtilities.FileBufferingWriteStream(bufferLimit: LoggingOptions.ResponseBufferLimitBytes); + new Microsoft.AspNetCore.WebUtilities.FileBufferingWriteStream( + bufferLimit: LoggingOptions.ResponseBufferLimitBytes); var originalBody = context.Response.Body; context.Response.Body = responseBuffer; @@ -84,18 +85,32 @@ public async Task InvokeAsync(HttpContext context) context.Response.Body = originalBody; - logger.LogInformation( - "[Incoming Request] HTTP {Method} {Url} responded with {StatusCode} in {ElapsedMilliseconds}ms. " + - "Request Headers: {RequestHeaders}, Request Body: {RequestBody}, " + - "Response Headers: {ResponseHeaders}, Response Body: {ResponseBody}", - context.Request.Method, - context.Request.GetDisplayUrl(), - context.Response.StatusCode, - elapsed, - reqHeaders, - reqBody, - resHeaders, - resBody); + var scope = new Dictionary + { + ["RequestHeaders"] = reqHeaders, + ["RequestBody"] = reqBody, + ["ResponseHeaders"] = resHeaders, + ["ResponseBody"] = resBody, + ["ElapsedMs"] = elapsed, + ["Kind"] = "HttpIn" + }; + + if (context.Request.QueryString.HasValue) + { + scope["Query"] = context.Request.QueryString.Value; + } + + using (logger.BeginScope(scope)) + { + // Distinct, human-readable prefix so it's unmistakably yours in Kibana + logger.LogInformation( + "[HTTP IN] {Method} {Path} -> {StatusCode} in {ElapsedMilliseconds}ms", + context.Request.Method, + context.Request.Path.Value, + context.Response.StatusCode, + elapsed + ); + } } } } \ No newline at end of file diff --git a/src/SharedKernel/Logging/Middleware/SignalRLoggingHubFilter.cs b/src/SharedKernel/Logging/Middleware/SignalRLoggingHubFilter.cs index ce2f2c6..be98343 100644 --- a/src/SharedKernel/Logging/Middleware/SignalRLoggingHubFilter.cs +++ b/src/SharedKernel/Logging/Middleware/SignalRLoggingHubFilter.cs @@ -30,23 +30,23 @@ internal sealed class SignalRLoggingHubFilter(ILogger l var elapsedMs = Stopwatch.GetElapsedTime(start) .TotalMilliseconds; - using (logger.BeginScope(new - { - Hub = hubName, - ConnId = connId, - UserId = userId, - Method = methodName - })) + var scope = new Dictionary + { + ["Args"] = redactedArgs, + ["Hub"] = hubName, + ["ConnId"] = connId, + ["UserId"] = userId, + ["ElapsedMs"] = elapsedMs, + ["Kind"] = "SignalR" + }; + + using (logger.BeginScope(scope)) { logger.LogInformation( - "[Incoming Message] SignalR {Hub}, ConnId={ConnId}, UserId={UserId}, Method={Method}, " + - "completed in {ElapsedMs}ms, Args={Args}", + "[SignalR] {Hub}.{Method} completed in {ElapsedMilliseconds}ms", hubName, - connId, - userId, methodName, - elapsedMs, - redactedArgs + elapsedMs ); } diff --git a/src/SharedKernel/Logging/SerilogExtensions.cs b/src/SharedKernel/Logging/SerilogExtensions.cs index 4ac60f1..a53a86b 100644 --- a/src/SharedKernel/Logging/SerilogExtensions.cs +++ b/src/SharedKernel/Logging/SerilogExtensions.cs @@ -1,4 +1,5 @@ -using Elastic.CommonSchema; +using System.Globalization; +using Elastic.CommonSchema; using Elastic.CommonSchema.Serilog; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; @@ -104,18 +105,37 @@ private static LoggerConfiguration WriteToFile(this LoggerConfiguration loggerCo { var ecsConfig = new EcsTextFormatterConfiguration { + MessageFormatProvider = CultureInfo.InvariantCulture, IncludeHost = false, IncludeProcess = false, IncludeUser = false, - // Last-chance mutator: remove agent.* MapCustom = (doc, _) => { doc.Agent = null; + + if (doc.Labels is { Count: > 0 }) + { + doc.Labels.Remove("MessageTemplate"); + } + + if (doc.Event != null) + { + var dur = doc.Event.Duration; + doc.Event = new Event + { + Duration = dur + }; + } + + if (doc.Service != null) + { + doc.Service.Type = null; + } + return doc; } }; - // Choose the formatter based on the selected log backend ITextFormatter formatter = logBackend switch { LogBackend.ElasticSearch => new EcsTextFormatter(ecsConfig), @@ -150,29 +170,17 @@ private static LoggerConfiguration FilterOutUnwantedLogs(this LoggerConfiguratio private static bool ShouldDropByPath(LogEvent evt) { - // Try Url first (absolute), then RequestPath, then Path - var raw = GetScalar(evt, "Url") ?? GetScalar(evt, "RequestPath") ?? GetScalar(evt, "Path"); - - if (string.IsNullOrEmpty(raw)) + if (!evt.Properties.TryGetValue("RequestPath", out var p) || p is not ScalarValue sv) { return false; } - // If it's a full URL, extract the path - var path = raw; - if (Uri.TryCreate(raw, UriKind.Absolute, out var uri)) - { - path = uri.AbsolutePath; - } - - // Case-insensitive match on path fragments you want to drop - return path.Contains("/swagger", StringComparison.OrdinalIgnoreCase) - || path.Contains("/hangfire", StringComparison.OrdinalIgnoreCase) - || path.Contains("/above-board", StringComparison.OrdinalIgnoreCase) - || path.Contains("is-authenticated.json?api-version=v2", StringComparison.OrdinalIgnoreCase); + var path = sv.Value as string ?? ""; - static string? GetScalar(LogEvent e, string name) => - e.Properties.TryGetValue(name, out var v) && v is ScalarValue sv && sv.Value is string s ? s : null; + return path.StartsWith("/swagger") + || path.StartsWith("/hangfire") + || path.Contains("/above-board") + || path.Contains("localhost/auth/is-authenticated.json?api-version=v2"); } private static bool IsEfOutboxQuery(LogEvent evt) diff --git a/src/SharedKernel/SharedKernel.csproj b/src/SharedKernel/SharedKernel.csproj index a26b4e6..74fb10c 100644 --- a/src/SharedKernel/SharedKernel.csproj +++ b/src/SharedKernel/SharedKernel.csproj @@ -8,13 +8,13 @@ Readme.md Pandatech MIT - 1.6.0 + 1.6.1 Pandatech.SharedKernel Pandatech Shared Kernel Library Pandatech, shared kernel, library, OpenAPI, Swagger, utilities, scalar Pandatech.SharedKernel provides centralized configurations, utilities, and extensions for ASP.NET Core projects. For more information refere to readme.md document. https://github.com/PandaTechAM/be-lib-sharedkernel - Logging got extremely tuned. Namespaces might be broken but no other breaking change + Logging tuned and wording changed