diff --git a/DigitalData.MessagingService.sln b/DigitalData.MessagingService.sln index 49264f7..cc5c98c 100644 --- a/DigitalData.MessagingService.sln +++ b/DigitalData.MessagingService.sln @@ -35,10 +35,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DigitalData.MessagingServic EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DigitalData.MessagingService.API", "src\presentation\DigitalData.MessagingService.API\DigitalData.MessagingService.API.csproj", "{8BF22107-3CB9-C326-B94B-C40C99DA9B68}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DigitalData.MessagingService.Client.DependencyInjection", "src\presentation\DigitalData.MessagingService.Client.DependencyInjection\DigitalData.MessagingService.Client.DependencyInjection.csproj", "{B67C6FA8-DA47-41EC-B15A-511C5B19C036}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DigitalData.MessagingService.Publisher", "src\infrastructure\DigitalData.MessagingService.Publisher\DigitalData.MessagingService.Publisher.csproj", "{8DBBAA7C-C4D3-4ADD-8372-B0D6260C8FFC}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DigitalData.MessagingService.Client", "src\presentation\DigitalData.MessagingService.Client\DigitalData.MessagingService.Client.csproj", "{770E96B0-C3C9-A9A3-4F98-F7A0295D1599}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -73,14 +73,14 @@ Global {8BF22107-3CB9-C326-B94B-C40C99DA9B68}.Debug|Any CPU.Build.0 = Debug|Any CPU {8BF22107-3CB9-C326-B94B-C40C99DA9B68}.Release|Any CPU.ActiveCfg = Release|Any CPU {8BF22107-3CB9-C326-B94B-C40C99DA9B68}.Release|Any CPU.Build.0 = Release|Any CPU - {B67C6FA8-DA47-41EC-B15A-511C5B19C036}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B67C6FA8-DA47-41EC-B15A-511C5B19C036}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B67C6FA8-DA47-41EC-B15A-511C5B19C036}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B67C6FA8-DA47-41EC-B15A-511C5B19C036}.Release|Any CPU.Build.0 = Release|Any CPU {8DBBAA7C-C4D3-4ADD-8372-B0D6260C8FFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8DBBAA7C-C4D3-4ADD-8372-B0D6260C8FFC}.Debug|Any CPU.Build.0 = Debug|Any CPU {8DBBAA7C-C4D3-4ADD-8372-B0D6260C8FFC}.Release|Any CPU.ActiveCfg = Release|Any CPU {8DBBAA7C-C4D3-4ADD-8372-B0D6260C8FFC}.Release|Any CPU.Build.0 = Release|Any CPU + {770E96B0-C3C9-A9A3-4F98-F7A0295D1599}.Debug|Any CPU.ActiveCfg = Release|Any CPU + {770E96B0-C3C9-A9A3-4F98-F7A0295D1599}.Debug|Any CPU.Build.0 = Release|Any CPU + {770E96B0-C3C9-A9A3-4F98-F7A0295D1599}.Release|Any CPU.ActiveCfg = Release|Any CPU + {770E96B0-C3C9-A9A3-4F98-F7A0295D1599}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -96,8 +96,8 @@ Global {56607AAB-3DEC-CB78-3062-56A8EEF5E9D2} = {71BEA4D0-7835-4A8C-B11E-1088E0801DCE} {4CF993A6-FA3E-CBF7-C4CB-FFAEBFCFF705} = {71BEA4D0-7835-4A8C-B11E-1088E0801DCE} {8BF22107-3CB9-C326-B94B-C40C99DA9B68} = {B52B4CEE-1C67-424B-8659-370FEA7EAF2A} - {B67C6FA8-DA47-41EC-B15A-511C5B19C036} = {B52B4CEE-1C67-424B-8659-370FEA7EAF2A} {8DBBAA7C-C4D3-4ADD-8372-B0D6260C8FFC} = {71BEA4D0-7835-4A8C-B11E-1088E0801DCE} + {770E96B0-C3C9-A9A3-4F98-F7A0295D1599} = {B52B4CEE-1C67-424B-8659-370FEA7EAF2A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {90E29FDC-F6C6-414F-94BF-25DF61D18060} diff --git a/src/presentation/DigitalData.MessagingService.API/DigitalData.MessagingService.API.csproj b/src/presentation/DigitalData.MessagingService.API/DigitalData.MessagingService.API.csproj index 14afe9a..b44a8c7 100644 --- a/src/presentation/DigitalData.MessagingService.API/DigitalData.MessagingService.API.csproj +++ b/src/presentation/DigitalData.MessagingService.API/DigitalData.MessagingService.API.csproj @@ -1,25 +1,35 @@ - + - net8.0 + net8.0 enable enable - dotnet-DigitalData.MessagingService.Service-e7ef6a9a-436f-48a1-b4f6-ec9381c8cb47 + true + DigitalData.MessagingService.API + + Digital Data GmbH + Digital Data GmbH + DigitalData.MessagingService.API + 1.0.0-beta + 1.0.0.0 + 1.0.0.0 + Copyright © 2026 Digital Data GmbH. All rights reserved. + bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml + + + + - - - - diff --git a/src/presentation/DigitalData.MessagingService.API/Program.cs b/src/presentation/DigitalData.MessagingService.API/Program.cs index bbd21a4..1fd24e1 100644 --- a/src/presentation/DigitalData.MessagingService.API/Program.cs +++ b/src/presentation/DigitalData.MessagingService.API/Program.cs @@ -3,15 +3,40 @@ using DigitalData.MessagingService.Application; using DigitalData.MessagingService.Application.Common.Dtos; using DigitalData.MessagingService.Infrastructure; using Serilog; +using Serilog.Ui.Core.Extensions; +using Serilog.Ui.SqliteDataProvider.Extensions; +using Serilog.Ui.Web.Extensions; -// Configure Serilog early +// Build temporary configuration to read log directory early +var tempConfig = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: false) + .AddJsonFile("appsettings.Secrets.json", optional: true, reloadOnChange: false) + .Build(); + +var logDirectoryRaw = tempConfig.GetValue("Application:LogDirectory") ?? "logs"; +// Always resolve to an absolute path so sink and UI provider point to the same file +var logDirectory = Path.IsPathRooted(logDirectoryRaw) + ? logDirectoryRaw + : Path.Combine(AppContext.BaseDirectory, logDirectoryRaw); +Directory.CreateDirectory(logDirectory); +var sqliteDbPath = Path.Combine(logDirectory, "logs.db"); + +// Configure Serilog early (bootstrap + full pipeline) Log.Logger = new LoggerConfiguration() - .WriteTo.Console() + .MinimumLevel.Information() + .MinimumLevel.Override("Microsoft", Serilog.Events.LogEventLevel.Warning) + .MinimumLevel.Override("Microsoft.AspNetCore", Serilog.Events.LogEventLevel.Warning) + .MinimumLevel.Override("Microsoft.EntityFrameworkCore", Serilog.Events.LogEventLevel.Warning) + .MinimumLevel.Override("System", Serilog.Events.LogEventLevel.Warning) + .Enrich.FromLogContext() + .WriteTo.Console(outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}") .WriteTo.File( - path: "logs/emailprofiler-.log", + path: Path.Combine(logDirectory, "emailprofiler-.log"), rollingInterval: RollingInterval.Day, retainedFileCountLimit: 30, outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}") + .WriteTo.SQLite(sqliteDbPath, storeTimestampInUtc: true) .CreateBootstrapLogger(); try @@ -25,12 +50,13 @@ try .ReadFrom.Configuration(context.Configuration) .ReadFrom.Services(services) .Enrich.FromLogContext() - .WriteTo.Console() + .WriteTo.Console(outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}") .WriteTo.File( - path: "logs/emailprofiler-.log", + path: Path.Combine(logDirectory, "emailprofiler-.log"), rollingInterval: RollingInterval.Day, retainedFileCountLimit: 30, - outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}")); + outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}") + .WriteTo.SQLite(sqliteDbPath, storeTimestampInUtc: true)); // Add appsettings.Secrets.json for sensitive configuration (not committed to git) builder.Configuration.AddJsonFile("appsettings.Secrets.json", optional: true, reloadOnChange: true); @@ -50,6 +76,16 @@ try builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); + // Register Serilog.UI with SQLite provider for web log viewer + builder.Services.AddSerilogUi(options => + { + options.UseSqliteServer(dbOpt => + { + dbOpt.WithConnectionString($"Data Source={sqliteDbPath}"); + dbOpt.WithTable("Logs"); + }); + }); + var app = builder.Build(); // Add global exception handling middleware @@ -58,8 +94,11 @@ try // Add Serilog request logging app.UseSerilogRequestLogging(); - // Configure the HTTP request pipeline. - if (app.Environment.IsDevelopment()) + // Configure Swagger — enabled in Development always, and in other environments based on appsettings + var swaggerEnabled = app.Environment.IsDevelopment() + || app.Configuration.GetValue("Swagger:Enabled"); + + if (swaggerEnabled) { app.UseSwagger(); app.UseSwaggerUI(); @@ -67,6 +106,9 @@ try app.UseHttpsRedirection(); + // Serve Serilog.UI log viewer at /serilog-ui + app.UseSerilogUi(); + app.UseAuthorization(); app.MapControllers(); diff --git a/src/presentation/DigitalData.MessagingService.API/appsettings.json b/src/presentation/DigitalData.MessagingService.API/appsettings.json index 8a90591..ee2602d 100644 --- a/src/presentation/DigitalData.MessagingService.API/appsettings.json +++ b/src/presentation/DigitalData.MessagingService.API/appsettings.json @@ -11,6 +11,12 @@ } }, "AllowedHosts": "*", + "Application": { + "LogDirectory": "logs" + }, + "Swagger": { + "Enabled": true + }, "Workers": { "EmailSender": { "Enabled": true