using Microsoft.Extensions.Hosting; using Serilog; using Serilog.Events; using ZB.MOM.WW.Telemetry; namespace ZB.MOM.WW.Telemetry.Serilog; /// /// Extension point for configuring the shared Serilog application logger on an /// . Wires config-driven sinks /// (ReadFrom.Configuration), an explicit minimum level (Serilog:MinimumLevel, /// default ), and the shared enricher/redaction/OTel-export /// set via . Does NOT configure OTel metrics/traces — call /// AddZbTelemetry in the core package for that. /// /// /// This method intentionally does not set the process-global /// (via CreateBootstrapLogger or otherwise). Mutating /// process-global state in a shared library causes "logger is already frozen" exceptions /// when multiple hosts are built in the same process (integration tests, multi-host apps). /// /// /// Apps that need a pre-Build() bootstrap logger to capture early startup exceptions /// should set themselves in Program.cs before calling /// AddZbSerilog: /// /// Log.Logger = new LoggerConfiguration().WriteTo.Console().CreateBootstrapLogger(); /// // ... then build the host ... /// builder.AddZbSerilog(o => { ... }); /// /// This keeps global-state mutation firmly in the application, not the library. /// /// public static class ZbSerilogExtensions { /// /// Registers the Serilog application logger in DI. Wires configuration-driven sinks /// (ReadFrom.Configuration), a code default of /// that config can override via Serilog:MinimumLevel or namespace overrides, plus /// the identity enrichers (SiteId/NodeRole from , /// NodeHostname = ). /// /// /// This method does not set the process-global . /// preserveStaticLogger: true is passed to AddSerilog so the static logger /// is left entirely untouched — safe to call multiple times in the same process (integration /// tests, multi-host scenarios) without hitting "logger is already frozen". /// /// /// If early-startup bootstrap logging is required (before Build()), set /// Log.Logger = new LoggerConfiguration().WriteTo.Console().CreateBootstrapLogger(); /// in Program.cs before calling this method. That decision belongs to the /// application, not the shared library. /// /// /// The host application builder. /// Populates the . public static IHostApplicationBuilder AddZbSerilog( this IHostApplicationBuilder builder, Action configure) { ArgumentNullException.ThrowIfNull(builder); ArgumentNullException.ThrowIfNull(configure); var options = new ZbTelemetryOptions(); configure(options); // Register the application logger in DI only. preserveStaticLogger: true ensures // AddSerilog does NOT freeze or replace Log.Logger — critical for multi-host // processes (integration tests etc.) where AddZbSerilog may be called more than once. builder.Services.AddSerilog( (serviceProvider, loggerConfiguration) => { loggerConfiguration .MinimumLevel.Is(LogEventLevel.Information) .ReadFrom.Configuration(builder.Configuration); ZbSerilogConfig.Apply(loggerConfiguration, options, serviceProvider); }, preserveStaticLogger: true); return builder; } }