diff --git a/AccessQueuePlayground/AccessQueuePlayground.csproj b/AccessQueuePlayground/AccessQueuePlayground.csproj
index 4a7df85..a8e0738 100644
--- a/AccessQueuePlayground/AccessQueuePlayground.csproj
+++ b/AccessQueuePlayground/AccessQueuePlayground.csproj
@@ -8,6 +8,9 @@
+
+
+
diff --git a/AccessQueuePlayground/Program.cs b/AccessQueuePlayground/Program.cs
index 9ad2124..f00ab40 100644
--- a/AccessQueuePlayground/Program.cs
+++ b/AccessQueuePlayground/Program.cs
@@ -2,9 +2,20 @@ using AccessQueuePlayground.Components;
using AccessQueuePlayground.Services;
using AccessQueueService.Data;
using AccessQueueService.Services;
+using Serilog;
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.
builder.Services.AddRazorComponents()
.AddInteractiveServerComponents();
diff --git a/AccessQueueService/AccessQueueService.csproj b/AccessQueueService/AccessQueueService.csproj
index 5419ef0..9c50d6d 100644
--- a/AccessQueueService/AccessQueueService.csproj
+++ b/AccessQueueService/AccessQueueService.csproj
@@ -7,6 +7,10 @@
+
+
+
+
diff --git a/AccessQueueService/Program.cs b/AccessQueueService/Program.cs
index b6ef6a4..3e6dbd5 100644
--- a/AccessQueueService/Program.cs
+++ b/AccessQueueService/Program.cs
@@ -1,8 +1,18 @@
using AccessQueueService.Data;
using AccessQueueService.Services;
+using Serilog;
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.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
diff --git a/AccessQueueService/Services/AccessCleanupBackgroundService.cs b/AccessQueueService/Services/AccessCleanupBackgroundService.cs
index 2cb7e93..5a9f470 100644
--- a/AccessQueueService/Services/AccessCleanupBackgroundService.cs
+++ b/AccessQueueService/Services/AccessCleanupBackgroundService.cs
@@ -1,15 +1,16 @@
-
-namespace AccessQueueService.Services
+namespace AccessQueueService.Services
{
public class AccessCleanupBackgroundService : BackgroundService
{
private readonly IAccessService _accessService;
private readonly IConfiguration _config;
+ private readonly ILogger _logger;
- public AccessCleanupBackgroundService(IAccessService accessService, IConfiguration config)
+ public AccessCleanupBackgroundService(IAccessService accessService, IConfiguration config, ILogger logger)
{
_accessService = accessService;
_config = config;
+ _logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
@@ -17,7 +18,18 @@ namespace AccessQueueService.Services
var cleanupIntervalMillis = _config.GetValue("AccessQueue:CleanupIntervalSeconds") * 1000;
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);
}
}
diff --git a/AccessQueueService/Services/AccessService.cs b/AccessQueueService/Services/AccessService.cs
index d0ca150..65d0f9a 100644
--- a/AccessQueueService/Services/AccessService.cs
+++ b/AccessQueueService/Services/AccessService.cs
@@ -1,6 +1,7 @@
using System.Threading.Tasks;
using AccessQueueService.Data;
using AccessQueueService.Models;
+using Microsoft.Extensions.Logging;
namespace AccessQueueService.Services
{
@@ -8,16 +9,18 @@ namespace AccessQueueService.Services
{
private readonly IConfiguration _configuration;
private readonly IAccessQueueRepo _accessQueueRepo;
+ private readonly ILogger _logger;
private readonly SemaphoreSlim _queueLock = new(1, 1);
private readonly int EXP_SECONDS;
private readonly int ACT_SECONDS;
private readonly int CAPACITY_LIMIT;
private readonly bool ROLLING_EXPIRATION;
- public AccessService(IConfiguration configuration, IAccessQueueRepo accessQueueRepo)
+ public AccessService(IConfiguration configuration, IAccessQueueRepo accessQueueRepo, ILogger logger)
{
_configuration = configuration;
_accessQueueRepo = accessQueueRepo;
+ _logger = logger;
EXP_SECONDS = _configuration.GetValue("AccessQueue:ExpirationSeconds");
ACT_SECONDS = _configuration.GetValue("AccessQueue:ActivitySeconds");
CAPACITY_LIMIT = _configuration.GetValue("AccessQueue:CapacityLimit");
@@ -47,6 +50,7 @@ namespace AccessQueueService.Services
ExpiresOn = expiresOn,
LastActive = DateTime.UtcNow
});
+ _logger.LogInformation("User {UserId} already has access. Expires on {ExpiresOn}.", userId, expiresOn);
return new AccessResponse
{
ExpiresOn = expiresOn
@@ -62,6 +66,7 @@ namespace AccessQueueService.Services
LastActive = DateTime.UtcNow
};
_accessQueueRepo.UpsertTicket(accessTicket);
+ _logger.LogInformation("User {UserId} granted access. Expires on {ExpiresOn}.", userId, accessTicket.ExpiresOn);
return new AccessResponse
{
ExpiresOn = accessTicket.ExpiresOn,
@@ -80,6 +85,7 @@ namespace AccessQueueService.Services
LastActive = DateTime.UtcNow,
ExpiresOn = DateTime.MaxValue,
});
+ _logger.LogInformation("User {UserId} added to queue. Requests ahead: {RequestsAhead}.", userId, requestsAhead);
}
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
{
_queueLock.Release();
@@ -99,7 +110,17 @@ namespace AccessQueueService.Services
await _queueLock.WaitAsync();
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
{
@@ -112,7 +133,17 @@ namespace AccessQueueService.Services
await _queueLock.WaitAsync();
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
{
diff --git a/AccessQueueService/appsettings.json b/AccessQueueService/appsettings.json
index 7a7cdb3..42f1ca9 100644
--- a/AccessQueueService/appsettings.json
+++ b/AccessQueueService/appsettings.json
@@ -1,16 +1,34 @@
{
- "Logging": {
- "LogLevel": {
- "Default": "Information",
- "Microsoft.AspNetCore": "Warning"
- }
- },
- "AccessQueue": {
- "CapacityLimit": 100, // Maximum number of active users
- "ActivitySeconds": 900, // Time since last access before a user is considered inactive
- "ExpirationSeconds": 43200, // 12 hours - Time before a user access is revoked
- "RollingExpiration": true, // Whether to extend expiration time on access
- "CleanupIntervalSeconds": 60 // Interval for cleaning up expired users
+ "Logging": {
+ "LogLevel": {
+ "Default": "Information",
+ "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": {
+ "CapacityLimit": 100,
+ "ActivitySeconds": 900,
+ "ExpirationSeconds": 43200,
+ "RollingExpiration": true,
+ "CleanupIntervalSeconds": 60
+ },
"AllowedHosts": "*"
}
diff --git a/AccessQueueService/serilog.json b/AccessQueueService/serilog.json
new file mode 100644
index 0000000..58f3655
--- /dev/null
+++ b/AccessQueueService/serilog.json
@@ -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"
+ }
+ }
+}
diff --git a/AccessQueueServiceTests/AccessServiceTests.cs b/AccessQueueServiceTests/AccessServiceTests.cs
index 97764b5..c44df63 100644
--- a/AccessQueueServiceTests/AccessServiceTests.cs
+++ b/AccessQueueServiceTests/AccessServiceTests.cs
@@ -3,6 +3,7 @@ namespace AccessQueueServiceTests
using global::AccessQueueService.Data;
using global::AccessQueueService.Services;
using Microsoft.Extensions.Configuration;
+ using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
@@ -31,8 +32,9 @@ namespace AccessQueueServiceTests
.AddInMemoryCollection(inMemorySettings)
.Build();
var accessQueueRepo = new TakeANumberAccessQueueRepo();
-
- _accessService = new AccessService(configuration, accessQueueRepo);
+ var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole());
+ var logger = loggerFactory.CreateLogger();
+ _accessService = new AccessService(configuration, accessQueueRepo, logger);
}
[Fact]