Adds LogOverrides property to NatsOptions and a --log_level_override=namespace=level CLI flag that wires Serilog MinimumLevel.Override entries so operators can tune verbosity per .NET namespace without changing the global log level.
199 lines
5.9 KiB
C#
199 lines
5.9 KiB
C#
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<Serilog.Events.LogEventLevel>(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();
|
|
}
|