Removing serilog from server

This commit is contained in:
Aaron Powell 2025-12-11 15:26:27 +11:00
parent e236927f54
commit 52642ccb25
15 changed files with 176 additions and 130 deletions

View file

@ -4,7 +4,6 @@ using LANCommander.Server.Data.Models;
using LANCommander.Server.Extensions;
using LANCommander.Server.Services;
using Microsoft.AspNetCore.Mvc;
using Serilog;
using System.DirectoryServices.AccountManagement;
using System.Security.Claims;
using File = System.IO.File;
@ -30,6 +29,7 @@ public static class SaveEndpoints
public static async Task<IResult> GetAsync(
ClaimsPrincipal userPrincipal,
[FromServices] ILogger<Program> logger,
[FromServices] UserService userService,
[FromServices] GameSaveService saveService)
{
@ -37,7 +37,7 @@ public static class SaveEndpoints
if (user == null)
{
Log.Error("Could not find user from claim principal: {UserName}", userPrincipal?.Identity?.Name);
logger.LogError("Could not find user from claim principal: {UserName}", userPrincipal?.Identity?.Name);
return TypedResults.Unauthorized();
}
@ -50,6 +50,7 @@ public static class SaveEndpoints
public static async Task<IResult> GetByIdAsync(
Guid id,
ClaimsPrincipal userPrincipal,
[FromServices] ILogger<Program> logger,
[FromServices] UserService userService,
[FromServices] GameSaveService saveService)
{
@ -57,7 +58,7 @@ public static class SaveEndpoints
if (user == null)
{
Log.Error("Could not find user from claim principal: {UserName}", userPrincipal?.Identity?.Name);
logger.LogError("Could not find user from claim principal: {UserName}", userPrincipal?.Identity?.Name);
return TypedResults.Unauthorized();
}
@ -73,6 +74,7 @@ public static class SaveEndpoints
public static async Task<IResult> DeleteByIdAsync(
Guid id,
ClaimsPrincipal userPrincipal,
[FromServices] ILogger<Program> logger,
[FromServices] UserService userService,
[FromServices] GameSaveService saveService)
{
@ -80,7 +82,7 @@ public static class SaveEndpoints
if (user == null)
{
Log.Error("Could not find user from claim principal: {UserName}", userPrincipal?.Identity?.Name);
logger.LogError("Could not find user from claim principal: {UserName}", userPrincipal?.Identity?.Name);
return TypedResults.Unauthorized();
}
@ -98,6 +100,7 @@ public static class SaveEndpoints
public static async Task<IResult> GetSavesByGameAsync(
Guid gameId,
ClaimsPrincipal userPrincipal,
[FromServices] ILogger<Program> logger,
[FromServices] UserService userService,
[FromServices] GameSaveService saveService)
{
@ -105,7 +108,7 @@ public static class SaveEndpoints
if (user == null)
{
Log.Error("Could not find user from claim principal: {UserName}", userPrincipal?.Identity?.Name);
logger.LogError("Could not find user from claim principal: {UserName}", userPrincipal?.Identity?.Name);
return TypedResults.Unauthorized();
}
@ -118,6 +121,7 @@ public static class SaveEndpoints
public static async Task<IResult> GetLatestSaveByGameAsync(
Guid gameId,
ClaimsPrincipal userPrincipal,
[FromServices] ILogger<Program> logger,
[FromServices] IMapper mapper,
[FromServices] UserService userService,
[FromServices] GameSaveService saveService)
@ -126,7 +130,7 @@ public static class SaveEndpoints
if (user == null)
{
Log.Error("Could not find user from claim principal: {UserName}", userPrincipal?.Identity?.Name);
logger.LogError("Could not find user from claim principal: {UserName}", userPrincipal?.Identity?.Name);
return TypedResults.Unauthorized();
}
@ -144,6 +148,7 @@ public static class SaveEndpoints
public static async Task<IResult> DownloadLatestSaveByGameAsync(
Guid gameId,
ClaimsPrincipal userPrincipal,
[FromServices] ILogger<Program> logger,
[FromServices] UserService userService,
[FromServices] GameSaveService saveService)
{
@ -151,7 +156,7 @@ public static class SaveEndpoints
if (user == null)
{
Log.Error("Could not find user from claim principal: {UserName}", userPrincipal?.Identity?.Name);
logger.LogError("Could not find user from claim principal: {UserName}", userPrincipal?.Identity?.Name);
return TypedResults.Unauthorized();
}
@ -178,6 +183,7 @@ public static class SaveEndpoints
public static async Task<IResult> DownloadSaveByIdAsync(
Guid id,
ClaimsPrincipal userPrincipal,
[FromServices] ILogger<Program> logger,
[FromServices] UserService userService,
[FromServices] GameSaveService saveService)
{
@ -185,7 +191,7 @@ public static class SaveEndpoints
if (user == null)
{
Log.Error("Could not find user from claim principal: {UserName}", userPrincipal?.Identity?.Name);
logger.LogError("Could not find user from claim principal: {UserName}", userPrincipal?.Identity?.Name);
return TypedResults.Unauthorized();
}
@ -209,6 +215,7 @@ public static class SaveEndpoints
Guid gameId,
ClaimsPrincipal userPrincipal,
HttpContext httpContext,
[FromServices] ILogger<Program> logger,
[FromServices] SettingsProvider<Settings.Settings> settingsProvider,
[FromServices] IMapper mapper,
[FromServices] UserService userService,
@ -220,7 +227,7 @@ public static class SaveEndpoints
if (user == null)
{
Log.Error("Could not find user from claim principal: {UserName}", userPrincipal?.Identity?.Name);
logger.LogError("Could not find user from claim principal: {UserName}", userPrincipal?.Identity?.Name);
return TypedResults.Unauthorized();
}

View file

@ -1,7 +1,4 @@
using LANCommander.SDK;
using LANCommander.Server.Services;
using Microsoft.AspNetCore.SignalR;
using Serilog.Events;
using Microsoft.AspNetCore.SignalR;
namespace LANCommander.Server.Hubs
{
@ -9,14 +6,13 @@ namespace LANCommander.Server.Hubs
{
public override async Task OnConnectedAsync()
{
Clients.Caller.SendAsync("Log", "Connected to server logging provider!", LogEventLevel.Information, DateTime.Now);
await Clients.Caller.SendAsync("Log", "Connected to server logging provider!", LogLevel.Information, DateTime.Now);
await base.OnConnectedAsync();
}
public static async Task Log(IHubContext<LoggingHub> context, string message, LogEvent logEvent)
public static async Task Log(IHubContext<LoggingHub> context, string message, LogLevel logLevel, DateTime timestamp)
{
await context.Clients.All.SendAsync("Log", message, logEvent.Level, logEvent.Timestamp.DateTime);
await context.Clients.All.SendAsync("Log", message, logLevel, timestamp);
}
}
}

View file

@ -85,7 +85,6 @@
<PackageReference Include="AntDesign.Charts" />
<PackageReference Include="CaseConverter" />
<PackageReference Include="CoreRCON" />
<PackageReference Include="Elastic.Serilog.Sinks" />
<PackageReference Include="Hangfire.AspNetCore" />
<PackageReference Include="Hangfire.Core" />
<PackageReference Include="Hangfire.InMemory" />
@ -113,12 +112,6 @@
<PackageReference Include="Razor.Templating.Core" />
<PackageReference Include="RegParserDotNet" />
<PackageReference Include="Scalar.AspNetCore" />
<PackageReference Include="Serilog" />
<PackageReference Include="Serilog.AspNetCore" />
<PackageReference Include="Serilog.Extensions.Logging" />
<PackageReference Include="Serilog.Sinks.AspNetCore.App.SignalR" />
<PackageReference Include="Serilog.Sinks.File" />
<PackageReference Include="Serilog.Sinks.Seq" />
<PackageReference Include="System.Diagnostics.PerformanceCounter" />
</ItemGroup>

View file

@ -0,0 +1,55 @@
using LANCommander.Server.Hubs;
using Microsoft.AspNetCore.SignalR;
namespace LANCommander.Server.Logging;
public class SignalRLoggerProvider(IServiceProvider serviceProvider, LogLevel minimumLevel = LogLevel.Information) : ILoggerProvider
{
public ILogger CreateLogger(string categoryName) => new SignalRLogger(serviceProvider, minimumLevel);
public void Dispose()
{
}
private class SignalRLogger(IServiceProvider serviceProvider, LogLevel minimumLevel) : ILogger
{
private readonly IServiceProvider _serviceProvider = serviceProvider;
private readonly LogLevel _minimumLevel = minimumLevel;
public IDisposable? BeginScope<TState>(TState state) where TState : notnull => null;
public bool IsEnabled(LogLevel logLevel) => logLevel >= _minimumLevel;
public void Log<TState>(
LogLevel logLevel,
EventId eventId,
TState state,
Exception? exception,
Func<TState, Exception?, string> formatter)
{
if (!IsEnabled(logLevel))
return;
var message = formatter(state, exception);
if (exception != null)
message += Environment.NewLine + exception;
// Use a background task to avoid blocking
_ = Task.Run(async () =>
{
try
{
var hubContext = _serviceProvider.GetService<IHubContext<LoggingHub>>();
if (hubContext != null)
{
await hubContext.Clients.All.SendAsync("Log", message, logLevel, DateTime.Now);
}
}
catch
{
// Silently fail if SignalR is unavailable
}
});
}
}
}

View file

@ -1,7 +1,4 @@
using Serilog.Events;
using System.Drawing.Text;
namespace LANCommander.Server.Logging
namespace LANCommander.Server.Logging
{
public static class TerminalColor
{

View file

@ -1,4 +1,3 @@
using Serilog;
using LANCommander.Server.UI;
using LANCommander.Server.Startup;
@ -26,9 +25,11 @@ builder.AddDatabase(args);
builder.Services.AddHealthChecks();
Log.Debug("Building Application");
var app = builder.Build();
var logger = app.Services.GetRequiredService<ILogger<Program>>();
logger.LogDebug("Building Application");
app.UseDatabase(args);
app.ValidateSettings();
@ -42,9 +43,8 @@ app.UseMiddlewares();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
Log.Debug("App has been run in a development environment");
logger.LogDebug("App has been run in a development environment");
app.UseMigrationsEndPoint();
}
else
{

View file

@ -1,9 +1,5 @@
using LANCommander.SDK;
using LANCommander.SDK.Extensions;
using Microsoft.Extensions.Options;
using Serilog;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
namespace LANCommander.Server.Startup;
@ -25,10 +21,11 @@ public static class ApplicationSettings
{
var settingsProvider = app.Services.GetRequiredService<SettingsProvider<Settings.Settings>>();
var settings = app.Services.GetRequiredService<IOptions<Settings.Settings>>();
var logger = app.Services.GetRequiredService<ILogger<Program>>();
if (settings.Value.Server.Authentication.TokenSecret.Length < 16)
{
Log.Debug("JWT token secret is too short. Regenerating...");
logger.LogDebug("JWT token secret is too short. Regenerating...");
settingsProvider.Update(s =>
{
s.Server.Authentication.TokenSecret = Guid.NewGuid().ToString();

View file

@ -11,7 +11,6 @@ using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;
using Serilog;
namespace LANCommander.Server.Startup;
@ -47,8 +46,7 @@ public static class Authentication
}
catch (Exception ex)
{
Log.Error(ex, "Authentication Provider {Name} could not be registered",
authenticationProvider.Name);
Console.WriteLine($"Error: Authentication Provider {authenticationProvider.Name} could not be registered: {ex.Message}");
}
}
@ -114,7 +112,7 @@ public static class Authentication
{
context.Response.Redirect("/Login");
Log.Error(context.Failure, "OIDC authentication failed");
Console.WriteLine($"Error: OIDC authentication failed: {context.Failure?.Message}");
await context.Response.CompleteAsync();
};
@ -163,7 +161,7 @@ public static class Authentication
{
context.Response.Redirect("/Login");
Log.Error(context.Failure, "OAuth authentication failed");
Console.WriteLine($"Error: OAuth authentication failed: {context.Failure?.Message}");
await context.Response.CompleteAsync();
};

View file

@ -10,7 +10,6 @@ using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using Octokit;
using Serilog;
namespace LANCommander.Server.Startup;
@ -65,7 +64,7 @@ public static class Database
if (File.Exists(dataSource))
{
Log.Information("Migrations pending, database will be backed up to {BackupName}", backupName);
logger.LogInformation("Migrations pending, database will be backed up to {BackupName}", backupName);
File.Copy(dataSource, backupName);
}
}

View file

@ -1,5 +1,4 @@
using LANCommander.Server.Endpoints;
using Serilog;
namespace LANCommander.Server.Startup;
@ -7,8 +6,6 @@ public static class Endpoints
{
public static void AddControllers(this WebApplicationBuilder builder)
{
Log.Debug("Initializing Controllers");
builder.Services.AddControllers().AddJsonOptions(static x =>
{
x.JsonSerializerOptions.ReferenceHandler = System.Text.Json.Serialization.ReferenceHandler.IgnoreCycles;
@ -17,8 +14,6 @@ public static class Endpoints
public static WebApplication MapEndpoints(this WebApplication app)
{
Log.Debug("Registering Endpoints");
app.UseEndpoints(endpoints =>
{
endpoints.MapAuthenticationEndpoints();

View file

@ -4,7 +4,6 @@ using LANCommander.Server.Services;
using LANCommander.Server.Services.Models;
using Microsoft.AspNetCore.Identity;
using Microsoft.IdentityModel.Tokens;
using Serilog;
namespace LANCommander.Server.Startup;
@ -15,7 +14,6 @@ public static class Identity
var settings = new Settings.Settings();
builder.Configuration.Bind(settings);
Log.Debug("Initializing Identity");
builder.Services.AddIdentityCore<User>((options) =>
{
options.SignIn.RequireConfirmedAccount = false;

View file

@ -1,14 +1,6 @@
using Elastic.Serilog.Sinks;
using LANCommander.Server.Configuration;
using LANCommander.Server.Hubs;
using LANCommander.Server.Parsers;
using LANCommander.Server.Services.Models;
using LANCommander.Server.Logging;
using LANCommander.Server.Settings.Enums;
using Microsoft.Extensions.Options;
using Serilog;
using Serilog.Events;
using Serilog.Filters;
using Serilog.Sinks.AspNetCore.App.SignalR.Extensions;
namespace LANCommander.Server.Startup;
@ -16,73 +8,49 @@ public static class Logger
{
public static WebApplicationBuilder AddLogger(this WebApplicationBuilder builder)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Verbose()
.WriteTo.Console()
.CreateBootstrapLogger();
builder.Services.AddSerilogHub<LoggingHub>();
builder.Services.AddSerilog((serviceProvider, config) =>
builder.Services.AddLogging((loggingBuilder) =>
{
config.MinimumLevel.Verbose();
config.Enrich.FromLogContext();
var serviceProvider = builder.Services.BuildServiceProvider();
var settings = serviceProvider.GetRequiredService<IOptions<Settings.Settings>>();
// Configure filters
if (settings.Value.Server.Logs.IgnorePings)
config.Filter.ByExcluding(Matching.WithProperty<string>("RequestPath", v => v.StartsWith("/api/Ping", StringComparison.OrdinalIgnoreCase)));
{
loggingBuilder.AddFilter((category, level) =>
{
// Filter out ping-related logs if IgnorePings is enabled
if (category != null && category.Contains("Ping", StringComparison.OrdinalIgnoreCase))
return false;
return true;
});
}
foreach (var provider in settings.Value.Server.Logs.Providers)
{
LogEventLevel minimumLevel = provider.MinimumLevel switch
{
LogLevel.Trace => LogEventLevel.Verbose,
LogLevel.Debug => LogEventLevel.Debug,
LogLevel.Warning => LogEventLevel.Warning,
LogLevel.Information => LogEventLevel.Information,
LogLevel.Error => LogEventLevel.Error,
LogLevel.Critical => LogEventLevel.Fatal,
_ => LogEventLevel.Information
};
var minimumLevel = provider.MinimumLevel;
switch (provider.Type)
{
case LoggingProviderType.Console:
config.WriteTo.Console(restrictedToMinimumLevel: minimumLevel);
loggingBuilder.AddConsole();
loggingBuilder.SetMinimumLevel(minimumLevel);
break;
case LoggingProviderType.SignalR:
config.WriteTo.SignalR<LoggingHub>(
serviceProvider,
(context, message, logEvent) => LoggingHub.Log(context, message, logEvent));
break;
case LoggingProviderType.File:
config.WriteTo.File(
Path.Combine(provider.ConnectionString,"log-.txt"),
rollingInterval: (RollingInterval)(int)(provider.ArchiveEvery ?? LogInterval.Day),
restrictedToMinimumLevel: minimumLevel);
break;
case LoggingProviderType.Seq:
try
{
var options = ConnectionStringBinder.Bind<SeqOptions>(provider.ConnectionString);
config.WriteTo.Seq(
restrictedToMinimumLevel: minimumLevel,
serverUrl: options.ServerUrl,
apiKey: options.ApiKey);
}
catch (Exception ex)
{
Log.Error(ex, "Could not bind Seq connection string");
}
case LoggingProviderType.SignalR:
loggingBuilder.Services.AddSingleton<ILoggerProvider>(sp =>
new SignalRLoggerProvider(sp, minimumLevel));
break;
case LoggingProviderType.File:
loggingBuilder.AddFile(provider.ConnectionString, minimumLevel);
break;
case LoggingProviderType.Seq:
case LoggingProviderType.ElasticSearch:
config.WriteTo.Elasticsearch([new Uri(provider.ConnectionString)], restrictedToMinimumLevel: minimumLevel);
// Note: Standard .NET logging doesn't have built-in support for Seq or Elasticsearch
// Users will need to add these providers manually if needed
builder.Logging.AddDebug();
Console.WriteLine($"Warning: {provider.Type} logging provider is not supported in standard .NET logging. Consider using a third-party logging library.");
break;
}
}
@ -90,4 +58,55 @@ public static class Logger
return builder;
}
private static ILoggingBuilder AddFile(this ILoggingBuilder builder, string logDirectory, LogLevel minimumLevel)
=> builder.AddProvider(new FileLoggerProvider(logDirectory, minimumLevel));
private class FileLogger(string logDirectory, string categoryName, LogLevel minimumLevel) : ILogger
{
public IDisposable? BeginScope<TState>(TState state) where TState : notnull => null;
public bool IsEnabled(LogLevel logLevel) => logLevel >= minimumLevel;
public void Log<TState>(
LogLevel logLevel,
EventId eventId,
TState state,
Exception? exception,
Func<TState, Exception?, string> formatter)
{
if (!IsEnabled(logLevel))
return;
var logFilePath = Path.Combine(
logDirectory,
$"log-{DateTime.Now:yyyy-MM-dd}.txt");
var message = formatter(state, exception);
var logMessage = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} [{logLevel}] [{categoryName}] {message}";
if (exception != null)
logMessage += Environment.NewLine + exception;
try
{
lock (logDirectory)
{
Directory.CreateDirectory(logDirectory);
File.AppendAllText(logFilePath, logMessage + Environment.NewLine);
}
}
catch
{
// Silently fail if logging to file fails
}
}
}
private class FileLoggerProvider(string logDirectory, LogLevel minimumLevel) : ILoggerProvider
{
public ILogger CreateLogger(string categoryName) => new FileLogger(logDirectory, categoryName, minimumLevel);
public void Dispose() { }
}
}

View file

@ -1,13 +1,9 @@
using Serilog;
namespace LANCommander.Server.Startup;
public static class Razor
{
public static WebApplicationBuilder AddRazor(this WebApplicationBuilder builder)
{
Log.Debug("Configuring MVC and Blazor");
builder.Services
.AddMvc(static options => options.EnableEndpointRouting = false)
.AddRazorOptions(static options =>

View file

@ -4,7 +4,6 @@ using LANCommander.Server.Providers;
using LANCommander.Server.Services.Abstractions;
using LANCommander.Server.Services.Extensions;
using LANCommander.UI.Extensions;
using Serilog;
namespace LANCommander.Server.Startup;
@ -12,8 +11,6 @@ public static class Services
{
public static WebApplicationBuilder AddLANCommanderServices(this WebApplicationBuilder builder)
{
Log.Debug("Registering services");
builder.Services.AddLANCommanderClient<Settings.Settings>();
builder.Services.AddLANCommanderServer();
builder.Services.AddLANCommanderImportExport();

View file

@ -1,6 +1,5 @@
@using LANCommander.Server.Logging
@using Microsoft.AspNetCore.SignalR.Client
@using Serilog.Events
@using XtermBlazor
@inject ILogger<LogViewer> Logger
@inject MessageService MessageService
@ -52,7 +51,7 @@
.WithUrl(NavigationManager.ToAbsoluteUri("/logging"))
.Build();
HubConnection.On<string, LogEventLevel, DateTime>("Log", (message, level, timestamp) =>
HubConnection.On<string, Microsoft.Extensions.Logging.LogLevel, DateTime>("Log", (message, level, timestamp) =>
{
var parts = new string[]
{
@ -74,16 +73,16 @@
await HubConnection.StartAsync();
}
string GetColorCode(LogEventLevel level)
string GetColorCode(Microsoft.Extensions.Logging.LogLevel level)
{
return level switch
{
LogEventLevel.Verbose => TerminalColor.BrightBlack,
LogEventLevel.Debug => TerminalColor.BrightCyan,
LogEventLevel.Information => TerminalColor.BrightGreen,
LogEventLevel.Warning => TerminalColor.BrightYellow,
LogEventLevel.Error => TerminalColor.BrightRed,
LogEventLevel.Fatal => TerminalColor.BrightMagenta,
Microsoft.Extensions.Logging.LogLevel.Trace => TerminalColor.BrightBlack,
Microsoft.Extensions.Logging.LogLevel.Debug => TerminalColor.BrightCyan,
Microsoft.Extensions.Logging.LogLevel.Information => TerminalColor.BrightGreen,
Microsoft.Extensions.Logging.LogLevel.Warning => TerminalColor.BrightYellow,
Microsoft.Extensions.Logging.LogLevel.Error => TerminalColor.BrightRed,
Microsoft.Extensions.Logging.LogLevel.Critical => TerminalColor.BrightMagenta,
_ => TerminalColor.Default,
};
}