Skip to content

Commit 3710509

Browse files
authored
Merge pull request #64 from PandaTechAM/development
Logging enhancement
2 parents 3d5c302 + ea0f1fc commit 3710509

File tree

6 files changed

+105
-64
lines changed

6 files changed

+105
-64
lines changed

SharedKernel.Demo/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@
126126
System.Text.Encoding.UTF8,
127127
"application/json");
128128

129-
var response = await httpClient.PostAsync("body", content);
129+
var response = await httpClient.PostAsync("body?barev=5", content);
130130

131131
if (!response.IsSuccessStatusCode)
132132
{

src/SharedKernel/Logging/Middleware/OutboundLoggingHandler.cs

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -75,19 +75,37 @@ protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage
7575
resMedia);
7676
}
7777

78-
logger.LogInformation(
79-
"[Outbound Call] HTTP {Method} {Uri} responded with {StatusCode} in {ElapsedMs}ms. " +
80-
"Request Headers: {RequestHeaders}, Request Body: {RequestBody}, " +
81-
"Response Headers: {ResponseHeaders}, Response Body: {ResponseBody}",
82-
request.Method,
83-
request.RequestUri?.ToString(),
84-
(int)response.StatusCode,
85-
elapsed,
86-
reqHeaders,
87-
reqBody,
88-
resHeaders,
89-
resBody);
90-
91-
return response;
78+
var hostPath = request.RequestUri is null
79+
? ""
80+
: request.RequestUri.GetLeftPart(UriPartial.Path); // message: path only (no query)
81+
82+
var scope = new Dictionary<string, object?>
83+
{
84+
["RequestHeaders"] = reqHeaders,
85+
["RequestBody"] = reqBody,
86+
["ResponseHeaders"] = resHeaders,
87+
["ResponseBody"] = resBody,
88+
["ElapsedMs"] = elapsed,
89+
["Kind"] = "HttpOut"
90+
};
91+
92+
var query = request.RequestUri?.Query;
93+
if (!string.IsNullOrEmpty(query))
94+
{
95+
scope["Query"] = query;
96+
}
97+
98+
using (logger.BeginScope(scope))
99+
{
100+
logger.LogInformation(
101+
"[HTTP OUT] {Method} {HostPath} -> {StatusCode} in {ElapsedMilliseconds}ms",
102+
request.Method,
103+
hostPath,
104+
(int)response.StatusCode,
105+
elapsed
106+
);
107+
108+
return response;
109+
}
92110
}
93111
}

src/SharedKernel/Logging/Middleware/RequestLoggingMiddleware.cs

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ public async Task InvokeAsync(HttpContext context)
2929
context.Request.ContentType);
3030

3131
await using var responseBuffer =
32-
new Microsoft.AspNetCore.WebUtilities.FileBufferingWriteStream(bufferLimit: LoggingOptions.ResponseBufferLimitBytes);
32+
new Microsoft.AspNetCore.WebUtilities.FileBufferingWriteStream(
33+
bufferLimit: LoggingOptions.ResponseBufferLimitBytes);
3334

3435
var originalBody = context.Response.Body;
3536
context.Response.Body = responseBuffer;
@@ -84,18 +85,32 @@ public async Task InvokeAsync(HttpContext context)
8485

8586
context.Response.Body = originalBody;
8687

87-
logger.LogInformation(
88-
"[Incoming Request] HTTP {Method} {Url} responded with {StatusCode} in {ElapsedMilliseconds}ms. " +
89-
"Request Headers: {RequestHeaders}, Request Body: {RequestBody}, " +
90-
"Response Headers: {ResponseHeaders}, Response Body: {ResponseBody}",
91-
context.Request.Method,
92-
context.Request.GetDisplayUrl(),
93-
context.Response.StatusCode,
94-
elapsed,
95-
reqHeaders,
96-
reqBody,
97-
resHeaders,
98-
resBody);
88+
var scope = new Dictionary<string, object?>
89+
{
90+
["RequestHeaders"] = reqHeaders,
91+
["RequestBody"] = reqBody,
92+
["ResponseHeaders"] = resHeaders,
93+
["ResponseBody"] = resBody,
94+
["ElapsedMs"] = elapsed,
95+
["Kind"] = "HttpIn"
96+
};
97+
98+
if (context.Request.QueryString.HasValue)
99+
{
100+
scope["Query"] = context.Request.QueryString.Value;
101+
}
102+
103+
using (logger.BeginScope(scope))
104+
{
105+
// Distinct, human-readable prefix so it's unmistakably yours in Kibana
106+
logger.LogInformation(
107+
"[HTTP IN] {Method} {Path} -> {StatusCode} in {ElapsedMilliseconds}ms",
108+
context.Request.Method,
109+
context.Request.Path.Value,
110+
context.Response.StatusCode,
111+
elapsed
112+
);
113+
}
99114
}
100115
}
101116
}

src/SharedKernel/Logging/Middleware/SignalRLoggingHubFilter.cs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,23 +30,23 @@ internal sealed class SignalRLoggingHubFilter(ILogger<SignalRLoggingHubFilter> l
3030
var elapsedMs = Stopwatch.GetElapsedTime(start)
3131
.TotalMilliseconds;
3232

33-
using (logger.BeginScope(new
34-
{
35-
Hub = hubName,
36-
ConnId = connId,
37-
UserId = userId,
38-
Method = methodName
39-
}))
33+
var scope = new Dictionary<string, object?>
34+
{
35+
["Args"] = redactedArgs,
36+
["Hub"] = hubName,
37+
["ConnId"] = connId,
38+
["UserId"] = userId,
39+
["ElapsedMs"] = elapsedMs,
40+
["Kind"] = "SignalR"
41+
};
42+
43+
using (logger.BeginScope(scope))
4044
{
4145
logger.LogInformation(
42-
"[Incoming Message] SignalR {Hub}, ConnId={ConnId}, UserId={UserId}, Method={Method}, " +
43-
"completed in {ElapsedMs}ms, Args={Args}",
46+
"[SignalR] {Hub}.{Method} completed in {ElapsedMilliseconds}ms",
4447
hubName,
45-
connId,
46-
userId,
4748
methodName,
48-
elapsedMs,
49-
redactedArgs
49+
elapsedMs
5050
);
5151
}
5252

src/SharedKernel/Logging/SerilogExtensions.cs

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using Elastic.CommonSchema;
1+
using System.Globalization;
2+
using Elastic.CommonSchema;
23
using Elastic.CommonSchema.Serilog;
34
using Microsoft.AspNetCore.Builder;
45
using Microsoft.Extensions.DependencyInjection;
@@ -104,18 +105,37 @@ private static LoggerConfiguration WriteToFile(this LoggerConfiguration loggerCo
104105
{
105106
var ecsConfig = new EcsTextFormatterConfiguration
106107
{
108+
MessageFormatProvider = CultureInfo.InvariantCulture,
107109
IncludeHost = false,
108110
IncludeProcess = false,
109111
IncludeUser = false,
110112

111-
// Last-chance mutator: remove agent.*
112113
MapCustom = (doc, _) =>
113114
{
114115
doc.Agent = null;
116+
117+
if (doc.Labels is { Count: > 0 })
118+
{
119+
doc.Labels.Remove("MessageTemplate");
120+
}
121+
122+
if (doc.Event != null)
123+
{
124+
var dur = doc.Event.Duration;
125+
doc.Event = new Event
126+
{
127+
Duration = dur
128+
};
129+
}
130+
131+
if (doc.Service != null)
132+
{
133+
doc.Service.Type = null;
134+
}
135+
115136
return doc;
116137
}
117138
};
118-
// Choose the formatter based on the selected log backend
119139
ITextFormatter formatter = logBackend switch
120140
{
121141
LogBackend.ElasticSearch => new EcsTextFormatter(ecsConfig),
@@ -150,29 +170,17 @@ private static LoggerConfiguration FilterOutUnwantedLogs(this LoggerConfiguratio
150170

151171
private static bool ShouldDropByPath(LogEvent evt)
152172
{
153-
// Try Url first (absolute), then RequestPath, then Path
154-
var raw = GetScalar(evt, "Url") ?? GetScalar(evt, "RequestPath") ?? GetScalar(evt, "Path");
155-
156-
if (string.IsNullOrEmpty(raw))
173+
if (!evt.Properties.TryGetValue("RequestPath", out var p) || p is not ScalarValue sv)
157174
{
158175
return false;
159176
}
160177

161-
// If it's a full URL, extract the path
162-
var path = raw;
163-
if (Uri.TryCreate(raw, UriKind.Absolute, out var uri))
164-
{
165-
path = uri.AbsolutePath;
166-
}
167-
168-
// Case-insensitive match on path fragments you want to drop
169-
return path.Contains("/swagger", StringComparison.OrdinalIgnoreCase)
170-
|| path.Contains("/hangfire", StringComparison.OrdinalIgnoreCase)
171-
|| path.Contains("/above-board", StringComparison.OrdinalIgnoreCase)
172-
|| path.Contains("is-authenticated.json?api-version=v2", StringComparison.OrdinalIgnoreCase);
178+
var path = sv.Value as string ?? "";
173179

174-
static string? GetScalar(LogEvent e, string name) =>
175-
e.Properties.TryGetValue(name, out var v) && v is ScalarValue sv && sv.Value is string s ? s : null;
180+
return path.StartsWith("/swagger")
181+
|| path.StartsWith("/hangfire")
182+
|| path.Contains("/above-board")
183+
|| path.Contains("localhost/auth/is-authenticated.json?api-version=v2");
176184
}
177185

178186
private static bool IsEfOutboxQuery(LogEvent evt)

src/SharedKernel/SharedKernel.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@
88
<PackageReadmeFile>Readme.md</PackageReadmeFile>
99
<Authors>Pandatech</Authors>
1010
<Copyright>MIT</Copyright>
11-
<Version>1.6.0</Version>
11+
<Version>1.6.1</Version>
1212
<PackageId>Pandatech.SharedKernel</PackageId>
1313
<Title>Pandatech Shared Kernel Library</Title>
1414
<PackageTags>Pandatech, shared kernel, library, OpenAPI, Swagger, utilities, scalar</PackageTags>
1515
<Description>Pandatech.SharedKernel provides centralized configurations, utilities, and extensions for ASP.NET Core projects. For more information refere to readme.md document.</Description>
1616
<RepositoryUrl>https://github.com/PandaTechAM/be-lib-sharedkernel</RepositoryUrl>
17-
<PackageReleaseNotes>Logging got extremely tuned. Namespaces might be broken but no other breaking change</PackageReleaseNotes>
17+
<PackageReleaseNotes>Logging tuned and wording changed</PackageReleaseNotes>
1818
</PropertyGroup>
1919

2020
<ItemGroup>

0 commit comments

Comments
 (0)