using NATS.Server; using Serilog; using Serilog.Sinks.SystemConsole.Themes; var options = new NatsOptions(); var windowsService = false; // Parse ALL CLI flags into NatsOptions first for (int i = 0; i < args.Length; i++) { switch (args[i]) { case "-p" or "--port" when i + 1 < args.Length: options.Port = int.Parse(args[++i]); break; case "-a" or "--addr" when i + 1 < args.Length: options.Host = args[++i]; break; case "-n" or "--name" when i + 1 < args.Length: options.ServerName = args[++i]; break; case "-m" or "--http_port" when i + 1 < args.Length: options.MonitorPort = int.Parse(args[++i]); break; case "--http_base_path" when i + 1 < args.Length: options.MonitorBasePath = args[++i]; break; case "--https_port" when i + 1 < args.Length: options.MonitorHttpsPort = int.Parse(args[++i]); break; case "-c" when i + 1 < args.Length: options.ConfigFile = args[++i]; break; case "--pid" when i + 1 < args.Length: options.PidFile = args[++i]; break; case "--ports_file_dir" when i + 1 < args.Length: options.PortsFileDir = args[++i]; break; case "--tls": break; case "--tlscert" when i + 1 < args.Length: options.TlsCert = args[++i]; break; case "--tlskey" when i + 1 < args.Length: options.TlsKey = args[++i]; break; case "--tlscacert" when i + 1 < args.Length: options.TlsCaCert = args[++i]; break; case "--tlsverify": options.TlsVerify = true; break; case "-D" or "--debug": options.Debug = true; break; case "-V" or "-T" or "--trace": options.Trace = true; break; case "-DV": options.Debug = true; options.Trace = true; break; case "-l" or "--log" or "--log_file" when i + 1 < args.Length: options.LogFile = args[++i]; break; case "--log_size_limit" when i + 1 < args.Length: options.LogSizeLimit = long.Parse(args[++i]); break; case "--log_max_files" when i + 1 < args.Length: options.LogMaxFiles = int.Parse(args[++i]); break; case "--logtime" when i + 1 < args.Length: options.Logtime = bool.Parse(args[++i]); break; case "--logtime_utc": options.LogtimeUTC = true; break; case "--syslog": options.Syslog = true; break; case "--remote_syslog" when i + 1 < args.Length: options.RemoteSyslog = args[++i]; break; case "--service": windowsService = true; break; case "--log_level_override" when i + 1 < args.Length: var parts = args[++i].Split('=', 2); if (parts.Length == 2) { options.LogOverrides ??= new(); options.LogOverrides[parts[0]] = parts[1]; } break; } } // Build Serilog configuration from options var logConfig = new LoggerConfiguration() .Enrich.FromLogContext(); // Set minimum level based on flags if (options.Trace) logConfig.MinimumLevel.Verbose(); else if (options.Debug) logConfig.MinimumLevel.Debug(); else logConfig.MinimumLevel.Information(); // Build output template var timestampFormat = options.LogtimeUTC ? "{Timestamp:yyyy/MM/dd HH:mm:ss.ffffff} " : "{Timestamp:HH:mm:ss} "; var template = options.Logtime ? $"[{timestampFormat}{{Level:u3}}] {{Message:lj}}{{NewLine}}{{Exception}}" : "[{Level:u3}] {Message:lj}{NewLine}{Exception}"; // Console sink with color auto-detection if (!Console.IsOutputRedirected) logConfig.WriteTo.Console(outputTemplate: template, theme: AnsiConsoleTheme.Code); else logConfig.WriteTo.Console(outputTemplate: template); // File sink with rotation if (!string.IsNullOrEmpty(options.LogFile)) { logConfig.WriteTo.File( options.LogFile, fileSizeLimitBytes: options.LogSizeLimit > 0 ? options.LogSizeLimit : null, retainedFileCountLimit: options.LogMaxFiles > 0 ? options.LogMaxFiles : null, rollOnFileSizeLimit: options.LogSizeLimit > 0, outputTemplate: template); } // Syslog sink if (!string.IsNullOrEmpty(options.RemoteSyslog)) { logConfig.WriteTo.UdpSyslog(options.RemoteSyslog); } else if (options.Syslog) { logConfig.WriteTo.LocalSyslog("nats-server"); } // Apply per-subsystem log level overrides if (options.LogOverrides is not null) { foreach (var (ns, level) in options.LogOverrides) { if (Enum.TryParse(level, true, out var serilogLevel)) logConfig.MinimumLevel.Override(ns, serilogLevel); } } Log.Logger = logConfig.CreateLogger(); if (windowsService) { Log.Information("Windows Service mode requested"); } using var loggerFactory = new Serilog.Extensions.Logging.SerilogLoggerFactory(Log.Logger); using var server = new NatsServer(options, loggerFactory); // Register signal handlers server.HandleSignals(); server.ReOpenLogFile = () => { Log.Information("Reopening log file"); Log.CloseAndFlush(); Log.Logger = logConfig.CreateLogger(); Log.Information("File log re-opened"); }; // Ctrl+C triggers graceful shutdown Console.CancelKeyPress += (_, e) => { e.Cancel = true; Log.Information("Trapped SIGINT signal"); _ = Task.Run(async () => await server.ShutdownAsync()); }; try { _ = server.StartAsync(CancellationToken.None); await server.WaitForReadyAsync(); server.WaitForShutdown(); } catch (OperationCanceledException) { Log.Information("Server shutdown requested"); } finally { Log.CloseAndFlush(); }