using DigitalData.MessagingService.API.Middleware; using DigitalData.MessagingService.Application; using DigitalData.MessagingService.Infrastructure; using Serilog; using Serilog.Ui.Core.Extensions; using Serilog.Ui.SqliteDataProvider.Extensions; using Serilog.Ui.Web.Extensions; // 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() .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: 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 { Log.Information("Starting MessagingService API"); var builder = WebApplication.CreateBuilder(args); // Use Serilog for logging builder.Host.UseSerilog((context, services, configuration) => configuration .ReadFrom.Configuration(context.Configuration) .ReadFrom.Services(services) .Enrich.FromLogContext() .WriteTo.Console(outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}") .WriteTo.File( 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)); // Add appsettings.Secrets.json for sensitive configuration (not committed to git) builder.Configuration.AddJsonFile("appsettings.Secrets.json", optional: true, reloadOnChange: true); // Register Application layer (MediatR, AutoMapper, FluentValidation) builder.Services.AddApplicationServices(builder.Configuration); // Register Infrastructure layer (RabbitMQ, Repositories, etc.) builder.Services.AddInfrastructure(builder.Configuration); builder.Services.AddControllers(); 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 app.UseMiddleware(); // Add Serilog request logging app.UseSerilogRequestLogging(); // 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(); } app.UseHttpsRedirection(); // Serve Serilog.UI log viewer at /serilog-ui app.UseSerilogUi(); app.UseAuthorization(); app.MapControllers(); Log.Information("MessagingService API started successfully"); app.Run(); } catch (Exception ex) { Log.Fatal(ex, "MessagingService API failed to start"); throw; } finally { Log.CloseAndFlush(); }