Files
2026-04-24 18:52:21 +02:00

230 lines
8.2 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<bool>("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<IConnectionMultiplexer>(redisConn);
var connectionString = builder.Configuration.GetConnectionString("Lux.All") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
// DataLayerContext (manca!)
builder.Services.AddDbContextFactory<DataLayerContext>(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<ReportContext>(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<ExternalMessageProcessor>();
// servizi hosted usano scope factory
builder.Services.AddHostedService<RedisSubscriberService>();
builder.Services.AddHostedService<StatsCollectService>();
var app = builder.Build();
// aggiunt base URL x routing corretto
string baseUrl = configuration.GetValue<string>("ServerConf:BaseUrl") ?? "";
app.UsePathBase(baseUrl);
logger.Info($"BaseUrl: {baseUrl}");
// log channels di ritorno UI
List<string> listParams = new List<string>() { "ChannelPng", "ChannelSub", "ChannelPub", "ChannelSvg" };
foreach (var param in listParams)
{
logger.Info($"{param}: {configuration.GetValue<string>($"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();