using Microsoft.EntityFrameworkCore; using Microsoft.OpenApi.Models; using NLog.Targets; using NLog.Web; using OpenTelemetry.Resources; using OpenTelemetry.Trace; using System.Text.Json.Serialization; var builder = WebApplication.CreateBuilder(args); // recupero env corrente var env = builder.Environment; var logger = LogManager.Setup() .LoadConfigurationFromAppSettings() .GetCurrentClassLogger(); var assemblyVersion = Assembly.GetExecutingAssembly().GetName().Version?.ToString(); ConfigurationManager configuration = builder.Configuration; logger.Info($"Lux.API | Program.cs: startup | v.{assemblyVersion}"); logger.Info($"Current ASPNETCORE_ENVIRONMENT: {env.EnvironmentName}"); // costruzione connectionMultiplexer redis... string connStr = configuration.GetConnectionString("Redis") ?? "localhost"; ConnectionMultiplexer redisConn = ConnectionMultiplexer.Connect(connStr); // ==================================================================== // Setup Tracing e Telemetria... // ==================================================================== // 1. Leggiamo la configurazione var otelEnabled = builder.Configuration.GetValue("Otel:EnableTracing", false); var otelEndpoint = builder.Configuration["Otel:Endpoint"]; var otelDsn = builder.Configuration["Otel:Dsn"]; if (otelEnabled) { // ==================================================================== // SETUP OPENTELEMETRY BASE (Genera gli oggetti Activity) // Questo gira per i Livelli 1, 2 e 3. // ==================================================================== var appVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "1.0.0"; builder.Services.AddOpenTelemetry() .WithTracing(tracerProviderBuilder => { tracerProviderBuilder .SetResourceBuilder(OpenTelemetry.Resources.ResourceBuilder.CreateDefault() .AddService(serviceName: "LUX", serviceVersion: appVersion)) .AddSource("Lux.API") .AddSource("Lux.DATA") .AddSource("Lux.UI") .AddAspNetCoreInstrumentation(options => { options.Filter = ctx => !ctx.Request.Path.StartsWithSegments("/health"); }) .AddEntityFrameworkCoreInstrumentation() .AddRedisInstrumentation(redisConn); // ==================================================================== // ESPORTAZIONE DI RETE (Solo Livelli 1 e 2) // ==================================================================== if (!string.IsNullOrWhiteSpace(otelEndpoint)) { tracerProviderBuilder.AddOtlpExporter(options => { options.Endpoint = new Uri(otelEndpoint); if (!string.IsNullOrWhiteSpace(otelDsn)) { options.Headers = $"uptrace-dsn={otelDsn}"; } options.Protocol = OpenTelemetry.Exporter.OtlpExportProtocol.Grpc; }); } // Se otelEndpoint è vuoto (Livello 3), le tracce nascono e muoiono in RAM. }); // ==================================================================== // ESPORTAZIONE NLOG REALTIME (Solo Livelli 1 e 2) // ==================================================================== if (!string.IsNullOrWhiteSpace(otelEndpoint)) { var otlpTarget = new OtlpTarget { Name = "UptraceRealtime", Endpoint = otelEndpoint, ServiceName = "LUX.Data.Tracer" }; if (!string.IsNullOrWhiteSpace(otelDsn)) { otlpTarget.Headers = $"uptrace-dsn={otelDsn}"; } var config = LogManager.Configuration ?? new NLog.Config.LoggingConfiguration(); config.AddTarget(otlpTarget); config.AddRule(NLog.LogLevel.Info, NLog.LogLevel.Fatal, otlpTarget); LogManager.Configuration = config; LogManager.ReconfigExistingLoggers(); logger.Info($"🚀 NLog & OTel attivi e in invio verso: {otelEndpoint}"); } else { logger.Info("ℹ️ OTel attivo (Local mode). Esportazione di rete disabilitata."); } } else { // ==================================================================== // LIVELLO 4: TUTTO SPENTO // ==================================================================== logger.Info("⏸️ Telemetria e Tracing completamente disabilitati."); } // fix logging builder.Logging.AddFilter("Microsoft.EntityFrameworkCore", Microsoft.Extensions.Logging.LogLevel.None); // Add services to the container. builder.Services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); //builder.Services.AddSwaggerGen(); builder.Services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "EgaWare's Lux API", Version = $"v1 | {assemblyVersion}", Description = $"API documentation for v1 | AssemblyVersion {assemblyVersion}" }); }); // registro connMultiplexer REDIS builder.Services.AddSingleton(redisConn); var connectionString = builder.Configuration.GetConnectionString("Lux.All") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found."); // DataLayerContext (manca!) builder.Services.AddDbContextFactory(options => { var conn = builder.Configuration.GetConnectionString("Lux.All"); options.UseMySql(conn, ServerVersion.AutoDetect(conn), mySqlOptions => { mySqlOptions.EnableStringComparisonTranslations(); }) .EnableSensitiveDataLogging(false) .EnableDetailedErrors(false) .LogTo(_ => { }); // disabilita EF logging; }); builder.Services.AddDbContextFactory(options => { var conn = builder.Configuration.GetConnectionString("Lux.All"); options.UseMySql(conn, ServerVersion.AutoDetect(conn), mySqlOptions => { mySqlOptions.EnableStringComparisonTranslations(); }) .EnableSensitiveDataLogging(false) .EnableDetailedErrors(false) .LogTo(_ => { }); // disabilita EF logging; }); // fix serializazzione senza loop builder.Services.AddControllers().AddJsonOptions(options => { options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles; }); //// fix serializzazione con NewtonsoftJson e gestione loop... //builder.Services // .AddControllers() // .AddNewtonsoftJson(options => // { // options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; // }); // registrazione in blocco servizi con metodo extension custom builder.Services.AddLuxData(connectionString); // servizi con dipendenze" ma Scoped builder.Services.AddScoped(); // servizi hosted usano scope factory builder.Services.AddHostedService(); builder.Services.AddHostedService(); var app = builder.Build(); // aggiunt base URL x routing corretto string baseUrl = configuration.GetValue("ServerConf:BaseUrl") ?? ""; app.UsePathBase(baseUrl); logger.Info($"BaseUrl: {baseUrl}"); // log channels di ritorno UI List listParams = new List() { "ChannelPng", "ChannelSub", "ChannelPub", "ChannelSvg" }; foreach (var param in listParams) { logger.Info($"{param}: {configuration.GetValue($"ServerConf:{param}") ?? ""}"); } // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment() || app.Environment.IsStaging()) { app.UseSwagger(); //app.UseSwaggerUI(); app.UseSwaggerUI(c => { c.SwaggerEndpoint($"{baseUrl}swagger/v1/swagger.json", "EgalWare's Lux API v1"); }); } app.UseHttpsRedirection(); app.UseAuthorization(); app.MapControllers(); // inserisco evento in chiusura x fermare i thread di calcolo sottostanti: // https://shazwazza.com/post/aspnet-core-application-shutdown-events/ // https://dotnetblog.asphostportal.com/how-to-handle-application-shutdown-in-asp-net-core/ app.Lifetime.ApplicationStopping.Register(() => { #if false if (myExecProcessManager != null) { myExecProcessManager.StopExecutionThread(); } #endif }); // avvio app app.Run();