Added serilog

This commit is contained in:
henry 2025-05-16 21:08:23 -04:00
parent 3dc21bc54d
commit 7b34b55197
9 changed files with 132 additions and 21 deletions

View File

@ -8,6 +8,9 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Blazor.Bootstrap" Version="3.3.1" /> <PackageReference Include="Blazor.Bootstrap" Version="3.3.1" />
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="9.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -2,9 +2,20 @@ using AccessQueuePlayground.Components;
using AccessQueuePlayground.Services; using AccessQueuePlayground.Services;
using AccessQueueService.Data; using AccessQueueService.Data;
using AccessQueueService.Services; using AccessQueueService.Services;
using Serilog;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
// Add Serilog configuration for console logging only
builder.Host.UseSerilog((context, services, configuration) =>
{
configuration
.WriteTo.Console()
.ReadFrom.Configuration(context.Configuration)
.ReadFrom.Services(services)
.Enrich.FromLogContext();
});
// Add services to the container. // Add services to the container.
builder.Services.AddRazorComponents() builder.Services.AddRazorComponents()
.AddInteractiveServerComponents(); .AddInteractiveServerComponents();

View File

@ -7,6 +7,10 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="9.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
</ItemGroup> </ItemGroup>

View File

@ -1,8 +1,18 @@
using AccessQueueService.Data; using AccessQueueService.Data;
using AccessQueueService.Services; using AccessQueueService.Services;
using Serilog;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
// Add Serilog configuration from appsettings and serilog.json
builder.Host.UseSerilog((context, services, configuration) =>
{
configuration
.ReadFrom.Configuration(context.Configuration)
.ReadFrom.Services(services)
.Enrich.FromLogContext();
});
builder.Services.AddControllers(); builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer(); builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(); builder.Services.AddSwaggerGen();

View File

@ -1,15 +1,16 @@
 namespace AccessQueueService.Services
namespace AccessQueueService.Services
{ {
public class AccessCleanupBackgroundService : BackgroundService public class AccessCleanupBackgroundService : BackgroundService
{ {
private readonly IAccessService _accessService; private readonly IAccessService _accessService;
private readonly IConfiguration _config; private readonly IConfiguration _config;
private readonly ILogger<AccessCleanupBackgroundService> _logger;
public AccessCleanupBackgroundService(IAccessService accessService, IConfiguration config) public AccessCleanupBackgroundService(IAccessService accessService, IConfiguration config, ILogger<AccessCleanupBackgroundService> logger)
{ {
_accessService = accessService; _accessService = accessService;
_config = config; _config = config;
_logger = logger;
} }
protected override async Task ExecuteAsync(CancellationToken stoppingToken) protected override async Task ExecuteAsync(CancellationToken stoppingToken)
@ -17,7 +18,18 @@ namespace AccessQueueService.Services
var cleanupIntervalMillis = _config.GetValue<int>("AccessQueue:CleanupIntervalSeconds") * 1000; var cleanupIntervalMillis = _config.GetValue<int>("AccessQueue:CleanupIntervalSeconds") * 1000;
while (!stoppingToken.IsCancellationRequested) while (!stoppingToken.IsCancellationRequested)
{ {
await _accessService.DeleteExpiredTickets(); try
{
var removed = await _accessService.DeleteExpiredTickets();
if (removed > 0)
{
_logger.LogInformation("Background cleanup removed {Count} expired tickets.", removed);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "Exception occurred during background cleanup.");
}
await Task.Delay(cleanupIntervalMillis, stoppingToken); await Task.Delay(cleanupIntervalMillis, stoppingToken);
} }
} }

View File

@ -1,6 +1,7 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using AccessQueueService.Data; using AccessQueueService.Data;
using AccessQueueService.Models; using AccessQueueService.Models;
using Microsoft.Extensions.Logging;
namespace AccessQueueService.Services namespace AccessQueueService.Services
{ {
@ -8,16 +9,18 @@ namespace AccessQueueService.Services
{ {
private readonly IConfiguration _configuration; private readonly IConfiguration _configuration;
private readonly IAccessQueueRepo _accessQueueRepo; private readonly IAccessQueueRepo _accessQueueRepo;
private readonly ILogger<AccessService> _logger;
private readonly SemaphoreSlim _queueLock = new(1, 1); private readonly SemaphoreSlim _queueLock = new(1, 1);
private readonly int EXP_SECONDS; private readonly int EXP_SECONDS;
private readonly int ACT_SECONDS; private readonly int ACT_SECONDS;
private readonly int CAPACITY_LIMIT; private readonly int CAPACITY_LIMIT;
private readonly bool ROLLING_EXPIRATION; private readonly bool ROLLING_EXPIRATION;
public AccessService(IConfiguration configuration, IAccessQueueRepo accessQueueRepo) public AccessService(IConfiguration configuration, IAccessQueueRepo accessQueueRepo, ILogger<AccessService> logger)
{ {
_configuration = configuration; _configuration = configuration;
_accessQueueRepo = accessQueueRepo; _accessQueueRepo = accessQueueRepo;
_logger = logger;
EXP_SECONDS = _configuration.GetValue<int>("AccessQueue:ExpirationSeconds"); EXP_SECONDS = _configuration.GetValue<int>("AccessQueue:ExpirationSeconds");
ACT_SECONDS = _configuration.GetValue<int>("AccessQueue:ActivitySeconds"); ACT_SECONDS = _configuration.GetValue<int>("AccessQueue:ActivitySeconds");
CAPACITY_LIMIT = _configuration.GetValue<int>("AccessQueue:CapacityLimit"); CAPACITY_LIMIT = _configuration.GetValue<int>("AccessQueue:CapacityLimit");
@ -47,6 +50,7 @@ namespace AccessQueueService.Services
ExpiresOn = expiresOn, ExpiresOn = expiresOn,
LastActive = DateTime.UtcNow LastActive = DateTime.UtcNow
}); });
_logger.LogInformation("User {UserId} already has access. Expires on {ExpiresOn}.", userId, expiresOn);
return new AccessResponse return new AccessResponse
{ {
ExpiresOn = expiresOn ExpiresOn = expiresOn
@ -62,6 +66,7 @@ namespace AccessQueueService.Services
LastActive = DateTime.UtcNow LastActive = DateTime.UtcNow
}; };
_accessQueueRepo.UpsertTicket(accessTicket); _accessQueueRepo.UpsertTicket(accessTicket);
_logger.LogInformation("User {UserId} granted access. Expires on {ExpiresOn}.", userId, accessTicket.ExpiresOn);
return new AccessResponse return new AccessResponse
{ {
ExpiresOn = accessTicket.ExpiresOn, ExpiresOn = accessTicket.ExpiresOn,
@ -80,6 +85,7 @@ namespace AccessQueueService.Services
LastActive = DateTime.UtcNow, LastActive = DateTime.UtcNow,
ExpiresOn = DateTime.MaxValue, ExpiresOn = DateTime.MaxValue,
}); });
_logger.LogInformation("User {UserId} added to queue. Requests ahead: {RequestsAhead}.", userId, requestsAhead);
} }
return new AccessResponse return new AccessResponse
{ {
@ -88,6 +94,11 @@ namespace AccessQueueService.Services
}; };
} }
} }
catch (Exception ex)
{
_logger.LogError(ex, "Exception occurred while processing access request for user {UserId}.", userId);
throw;
}
finally finally
{ {
_queueLock.Release(); _queueLock.Release();
@ -99,7 +110,17 @@ namespace AccessQueueService.Services
await _queueLock.WaitAsync(); await _queueLock.WaitAsync();
try try
{ {
return _accessQueueRepo.RemoveUser(userId); var removed = _accessQueueRepo.RemoveUser(userId);
if (removed)
{
_logger.LogInformation("User {UserId} access revoked.", userId);
}
return removed;
}
catch (Exception ex)
{
_logger.LogError(ex, "Exception occurred while revoking access for user {UserId}.", userId);
throw;
} }
finally finally
{ {
@ -112,7 +133,17 @@ namespace AccessQueueService.Services
await _queueLock.WaitAsync(); await _queueLock.WaitAsync();
try try
{ {
return _accessQueueRepo.DeleteExpiredTickets(); var removed = _accessQueueRepo.DeleteExpiredTickets();
if (removed > 0)
{
_logger.LogInformation("Cleaned up {Count} expired tickets.", removed);
}
return removed;
}
catch (Exception ex)
{
_logger.LogError(ex, "Exception occurred during expired ticket cleanup.");
throw;
} }
finally finally
{ {

View File

@ -5,12 +5,30 @@
"Microsoft.AspNetCore": "Warning" "Microsoft.AspNetCore": "Warning"
} }
}, },
"Serilog": {
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
},
"WriteTo": [
{ "Name": "Console" },
{ "Name": "File", "Args": { "path": "Logs/log-.txt", "rollingInterval": "Day", "outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}" } }
],
"Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ],
"Properties": {
"Application": "AccessQueueService"
}
},
"AccessQueue": { "AccessQueue": {
"CapacityLimit": 100, // Maximum number of active users "CapacityLimit": 100,
"ActivitySeconds": 900, // Time since last access before a user is considered inactive "ActivitySeconds": 900,
"ExpirationSeconds": 43200, // 12 hours - Time before a user access is revoked "ExpirationSeconds": 43200,
"RollingExpiration": true, // Whether to extend expiration time on access "RollingExpiration": true,
"CleanupIntervalSeconds": 60 // Interval for cleaning up expired users "CleanupIntervalSeconds": 60
}, },
"AllowedHosts": "*" "AllowedHosts": "*"
} }

View File

@ -0,0 +1,20 @@
{
"Serilog": {
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
},
"WriteTo": [
{ "Name": "Console" },
{ "Name": "File", "Args": { "path": "Logs/log-.txt", "rollingInterval": "Day", "outputTemplate": "[{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}" } }
],
"Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId" ],
"Properties": {
"Application": "AccessQueueService"
}
}
}

View File

@ -3,6 +3,7 @@ namespace AccessQueueServiceTests
using global::AccessQueueService.Data; using global::AccessQueueService.Data;
using global::AccessQueueService.Services; using global::AccessQueueService.Services;
using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -31,8 +32,9 @@ namespace AccessQueueServiceTests
.AddInMemoryCollection(inMemorySettings) .AddInMemoryCollection(inMemorySettings)
.Build(); .Build();
var accessQueueRepo = new TakeANumberAccessQueueRepo(); var accessQueueRepo = new TakeANumberAccessQueueRepo();
var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole());
_accessService = new AccessService(configuration, accessQueueRepo); var logger = loggerFactory.CreateLogger<AccessService>();
_accessService = new AccessService(configuration, accessQueueRepo, logger);
} }
[Fact] [Fact]