diff --git a/SVSim.EmulatedEntrypoint/Program.cs b/SVSim.EmulatedEntrypoint/Program.cs
index e5ec51ea..e3e2355d 100644
--- a/SVSim.EmulatedEntrypoint/Program.cs
+++ b/SVSim.EmulatedEntrypoint/Program.cs
@@ -20,6 +20,8 @@ using SVSim.EmulatedEntrypoint.Middlewares;
using SVSim.EmulatedEntrypoint.Security.SteamSessionAuthentication;
using SVSim.EmulatedEntrypoint.Services;
using SVSim.BattleNode.Hosting;
+using Serilog;
+using SVSim.Hosting;
namespace SVSim.EmulatedEntrypoint;
@@ -29,6 +31,13 @@ public class Program
{
var builder = WebApplication.CreateBuilder(args);
+ // Always register Serilog. The file sink self-gates on the Testing environment inside
+ // the UseSvSimSerilog callback — a caller-side check here would be too early because
+ // WebApplicationFactory applies UseEnvironment("Testing") after CreateBuilder returns.
+ builder.Host.UseSvSimSerilog("svsim-api");
+
+ try
+ {
// Add services to the container.
builder.Services.AddControllers().AddJsonOptions(opt =>
@@ -253,5 +262,15 @@ public class Program
app.MapControllers();
app.Run();
+ }
+ catch (Exception ex)
+ {
+ Log.Fatal(ex, "Host terminated unexpectedly");
+ throw;
+ }
+ finally
+ {
+ Log.CloseAndFlush();
+ }
}
}
diff --git a/SVSim.EmulatedEntrypoint/SVSim.EmulatedEntrypoint.csproj b/SVSim.EmulatedEntrypoint/SVSim.EmulatedEntrypoint.csproj
index 3ba3dd94..65d4f80c 100644
--- a/SVSim.EmulatedEntrypoint/SVSim.EmulatedEntrypoint.csproj
+++ b/SVSim.EmulatedEntrypoint/SVSim.EmulatedEntrypoint.csproj
@@ -39,6 +39,7 @@
+
diff --git a/SVSim.EmulatedEntrypoint/appsettings.json b/SVSim.EmulatedEntrypoint/appsettings.json
index 01772859..dee87515 100644
--- a/SVSim.EmulatedEntrypoint/appsettings.json
+++ b/SVSim.EmulatedEntrypoint/appsettings.json
@@ -9,6 +9,18 @@
"Microsoft.EntityFrameworkCore.Infrastructure": "Warning"
}
},
+ "Serilog": {
+ "MinimumLevel": {
+ "Default": "Information",
+ "Override": {
+ "Microsoft.AspNetCore": "Warning",
+ "Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware": "Information",
+ "Microsoft.EntityFrameworkCore": "Warning",
+ "Microsoft.EntityFrameworkCore.Database.Command": "Warning",
+ "Microsoft.EntityFrameworkCore.Infrastructure": "Warning"
+ }
+ }
+ },
"ConnectionStrings": {
"ApplicationDb": "Host=localhost;Database=svsim;Username=postgres;password=postgres"
},
diff --git a/SVSim.Hosting/SerilogHostingExtensions.cs b/SVSim.Hosting/SerilogHostingExtensions.cs
index b6f91eb9..c4413ea7 100644
--- a/SVSim.Hosting/SerilogHostingExtensions.cs
+++ b/SVSim.Hosting/SerilogHostingExtensions.cs
@@ -1,3 +1,4 @@
+using System.Reflection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Serilog;
@@ -8,6 +9,14 @@ namespace SVSim.Hosting;
public static class SerilogHostingExtensions
{
+ // Detects `dotnet test` runs regardless of ASPNETCORE_ENVIRONMENT wiring. The env check
+ // via HostingEnvironment.IsEnvironment("Testing") is unreliable here because
+ // WebApplicationFactory sets the environment via a mechanism that fires after
+ // both CreateBuilder and the UseSerilog callback resolve their environment view. The entry
+ // assembly under NUnit + dotnet test is always "testhost" or "testhost.x86".
+ private static readonly bool IsTestHost =
+ Assembly.GetEntryAssembly()?.GetName().Name?.StartsWith("testhost", StringComparison.OrdinalIgnoreCase) == true;
+
// Wires Serilog as the host's logging provider. Sinks, rotation, retention,
// and enrichment defaults are baked in here; per-category level overrides
// ride via appsettings under "Serilog:MinimumLevel:Override". The file sink
@@ -28,15 +37,23 @@ public static class SerilogHostingExtensions
.Enrich.WithThreadId()
.WriteTo.Console(
theme: AnsiConsoleTheme.Code,
- outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {SourceContext} {Message:lj}{NewLine}{Exception}")
- .WriteTo.File(
+ outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {SourceContext} {Message:lj}{NewLine}{Exception}");
+
+ // Skip the file sink under `dotnet test` so unit-test runs don't scatter log files
+ // under SVSim.UnitTests/bin/. See IsTestHost above for why we detect via entry
+ // assembly instead of the ASP.NET environment name.
+ if (!IsTestHost)
+ {
+ loggerConfig.WriteTo.File(
path: filePathBase,
rollingInterval: RollingInterval.Day,
rollOnFileSizeLimit: true,
fileSizeLimitBytes: 100_000_000,
retainedFileCountLimit: 7,
- outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {SourceContext} {Message:lj}{NewLine}{Exception}")
- .ReadFrom.Configuration(context.Configuration);
+ outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {SourceContext} {Message:lj}{NewLine}{Exception}");
+ }
+
+ loggerConfig.ReadFrom.Configuration(context.Configuration);
});
}
}