Skip to content

Commit 9c4d554

Browse files
authored
Merge pull request #42 from PandaTechAM/development
Logging upgrade
2 parents 0fb28d1 + 0e10b35 commit 9c4d554

File tree

9 files changed

+356
-191
lines changed

9 files changed

+356
-191
lines changed

Readme.md

Lines changed: 51 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ By leveraging this shared kernel, we aim to:
1818
This package currently supports:
1919

2020
- **OpenAPI Configuration** with SwaggerUI and Scalar.
21-
- **Logging** with Serilog.
21+
- **Logging** with Serilog (including ECS or Loki JSON file output, plus automatic log cleanup).
2222
- **MediatR and FluentValidation** configurations.
2323
- **Cors Configuration** with easy configuration options.
2424
- **Resilience Pipelines** for `HttpClient` operations.
@@ -129,7 +129,7 @@ AssemblyRegistry.Add(typeof(Program).Assembly);
129129

130130
builder
131131
.ConfigureWithPandaVault()
132-
.AddSerilog()
132+
.AddSerilog(LogBackend.Loki)
133133
.AddResponseCrafter(NamingConvention.ToUpperSnakeCase)
134134
.AddOpenApi()
135135
.AddOpenTelemetry()
@@ -267,33 +267,46 @@ Based on the above configuration, the UI will be accessible at the following URL
267267
### Key Features
268268

269269
- **Serilog Integration:** Simplified setup for structured logging using Serilog.
270-
- **Environment-Specific Configuration:** Logs are written to the console and/or files based on the environment (Local,
271-
Development, Production).
272-
- **Elastic Common Schema Formatting:** Logs are formatted using the Elastic Common Schema (ECS) for compatibility with
273-
Elasticsearch.
274-
- **Request Logging Middleware:** Middleware that logs incoming requests and outgoing responses while redacting
275-
sensitive information and 5kb exceeding properties.
276-
- **Log Filtering:** Excludes unwanted logs from Hangfire Dashboard, Swagger, and outbox database commands.
277-
- **Distributed:** Designed to work with distributed systems and microservices.
270+
- **Log Backend Option** Choose between:
271+
- `LogBackend.None` (disables file logging completely),
272+
- `LogBackend.ElasticSearch` (ECS formatter to file), or
273+
- `LogBackend.Loki` (Loki formatter to file).
274+
- **Environment-Specific Configuration:**
275+
- **Local:** Logs to console.
276+
- **Production:** Logs to file (in ECS or Loki format depending on the backend).
277+
- **Other Environments:** Logs to both console and file.
278+
- **Automatic Log Cleanup:** Log files are automatically cleaned up based on the configured retention period.
279+
- **Log File Location:** Logs are stored in a persistent path defined in your configuration, organized by repository
280+
name and environment, under the `logs` directory.
281+
- **Filtering:** Excludes unwanted logs from Hangfire Dashboard, Swagger, outbox DB commands, and MassTransit health
282+
checks.
283+
- **Request Logging:** Middleware that logs incoming requests and outgoing responses while redacting sensitive
284+
information and large payloads.
285+
- **Outbound Logging Handler:** For capturing outbound `HttpClient` requests (including headers and bodies) with the
286+
same redaction rules.
278287

279288
### Adding Logging to Your Project
280289

281-
To enable Serilog logging in your project, add the following code:
290+
Use the `AddSerilog` extension when building your `WebApplicationBuilder`. You can specify:
282291

283-
```csharp
284-
var builder = WebApplication.CreateBuilder(args);
285-
builder.AddSerilog();
286-
```
292+
- `logBackend`: One of `None`, `ElasticSearch` (ECS file format), or `Loki` (Loki JSON file format).
293+
- `daysToRetain`: Number of days to keep log files. Older files are automatically removed by the background hosted
294+
service.
287295

288296
In your middleware pipeline, add the request and response logging middleware:
289297

290298
```csharp
299+
var builder = WebApplication.CreateBuilder(args);
300+
301+
// Example: logs in Loki JSON format, keep logs for 14 days. (default = 7 days)
302+
builder.AddSerilog(logBackend: LogBackend.Loki, daysToRetain: 14);
303+
291304
var app = builder.Build();
292305
app.UseRequestLogging();
293306
```
294307

295-
In your `appsettings.{Environment}.json` configure `Serilog`.
296-
Example:
308+
Configure minimal Serilog settings in your environment JSON files as needed, for example in
309+
`appsettings.{Environment}.json`:
297310

298311
```json
299312
{
@@ -313,16 +326,28 @@ Example:
313326
}
314327
```
315328

316-
### Work Specifics
329+
### Log Cleanup
330+
331+
When you call `builder.AddSerilog(..., daysToRetain: X)`, a `LogCleanupHostedService` is automatically registered. This
332+
hosted service runs periodically to delete log files older than the specified retention period.
333+
334+
### Usage Notes
317335

318-
- Environment-Specific Logging:
319-
- Local Environment: Logs are written to the console.
320-
- Production: Logs are written to a file.
321-
- Other Environments: Logs are written to both the console and a file.
322-
- Log File Location: Logs are stored in a persistent path defined in your configuration, organized by repository name
323-
and environment.
324-
- Sensitive Data Redaction: The middleware automatically redacts sensitive information such as passwords, secrets,
325-
tokens, and authentication details from headers and bodies.
336+
- **No Direct Sinks to External Systems:** By default, logs are written to local files with ECS or Loki JSON format. You
337+
can
338+
later push these files to external systems (e.g., via Filebeat, Logstash, Promtail, or any specialized agent).
339+
- **Optional Enrichment:** You can pass a `Dictionary<string, string>` to `AddSerilog` to add extra log properties
340+
globally:
341+
```csharp
342+
builder.AddSerilog(
343+
logBackend: LogBackend.Loki,
344+
valueByNameRepeatedLog: new Dictionary<string, string>
345+
{
346+
{"ServiceName", "MyService"},
347+
{"ServiceVersion", "1.0.0"}
348+
}
349+
);
350+
```
326351

327352
### Startup Logging
328353

Shared.Kernel.Demo/Program.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
builder
2121
// .ConfigureWithPandaVault()
22-
.AddSerilog()
22+
.AddSerilog(LogBackend.Loki)
2323
.AddResponseCrafter(NamingConvention.ToSnakeCase)
2424
.AddOpenApi()
2525
.AddOpenTelemetry()
@@ -39,6 +39,7 @@
3939
.AddHealthChecks();
4040

4141

42+
4243
builder.Services
4344
.AddHttpClient("RandomApiClient",
4445
client =>

src/SharedKernel/Extensions/ConfigurationExtensions.cs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ internal static class ConfigurationExtensions
99
private const string RepositoryNameConfigurationPath = "RepositoryName";
1010
private const string TimeZoneConfigurationPath = "DefaultTimeZone";
1111
private const string RedisConfigurationPath = "Redis";
12+
private const string LokiConfigurationPath = "Loki";
1213

1314
internal static string GetAllowedCorsOrigins(this IConfiguration configuration)
1415
{
@@ -53,7 +54,7 @@ public static string GetDefaultTimeZone(this IConfiguration configuration)
5354

5455
return timeZone;
5556
}
56-
57+
5758
public static string GetRedisUrl(this IConfiguration configuration)
5859
{
5960
var redisConnectionString = configuration.GetConnectionString(RedisConfigurationPath);
@@ -64,4 +65,16 @@ public static string GetRedisUrl(this IConfiguration configuration)
6465

6566
return redisConnectionString;
6667
}
68+
69+
public static string GetLokiUrl(this IConfiguration configuration)
70+
{
71+
var lokiUrl = configuration.GetConnectionString(LokiConfigurationPath);
72+
73+
if (lokiUrl is null)
74+
{
75+
throw new InvalidOperationException("Loki URL is not configured.");
76+
}
77+
78+
return lokiUrl;
79+
}
6780
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace SharedKernel.Logging;
2+
3+
public enum LogBackend
4+
{
5+
None = 1,
6+
ElasticSearch = 2,
7+
Loki = 3
8+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using Microsoft.Extensions.Hosting;
2+
using Serilog;
3+
4+
namespace SharedKernel.Logging;
5+
6+
public class LogCleanupHostedService(string logsDirectory, TimeSpan retentionPeriod) : BackgroundService
7+
{
8+
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
9+
{
10+
while (!stoppingToken.IsCancellationRequested)
11+
{
12+
try
13+
{
14+
var files = Directory.EnumerateFiles(logsDirectory, "logs-*.json", SearchOption.TopDirectoryOnly);
15+
foreach (var file in files)
16+
{
17+
var creationTime = File.GetCreationTime(file);
18+
if (DateTime.UtcNow - creationTime > retentionPeriod)
19+
{
20+
File.Delete(file);
21+
}
22+
}
23+
}
24+
catch (IOException ex)
25+
{
26+
// If 2 instances do the same job at the same time, one of them will throw an exception
27+
Log.Logger.Information(ex, "Failed to delete log files");
28+
}
29+
catch (Exception ex)
30+
{
31+
Log.Logger.Error(ex, "Failed to delete logs");
32+
}
33+
34+
await Task.Delay(TimeSpan.FromHours(12), stoppingToken);
35+
}
36+
}
37+
}

src/SharedKernel/Logging/SerilogExtension.cs

Lines changed: 0 additions & 129 deletions
This file was deleted.

0 commit comments

Comments
 (0)