153 lines
5.7 KiB
C#
153 lines
5.7 KiB
C#
using Microsoft.AspNetCore.Builder;
|
|
using Microsoft.AspNetCore.Hosting;
|
|
using Microsoft.AspNetCore.Http;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace NATS.Server.Monitoring;
|
|
|
|
/// <summary>
|
|
/// HTTP monitoring server providing /healthz, /varz, and other monitoring endpoints.
|
|
/// Corresponds to Go server/monitor.go HTTP server setup.
|
|
/// </summary>
|
|
public sealed class MonitorServer : IAsyncDisposable
|
|
{
|
|
private readonly WebApplication _app;
|
|
private readonly ILogger<MonitorServer> _logger;
|
|
private readonly VarzHandler _varzHandler;
|
|
private readonly ConnzHandler _connzHandler;
|
|
private readonly SubszHandler _subszHandler;
|
|
private readonly JszHandler _jszHandler;
|
|
private readonly RoutezHandler _routezHandler;
|
|
private readonly GatewayzHandler _gatewayzHandler;
|
|
private readonly LeafzHandler _leafzHandler;
|
|
private readonly AccountzHandler _accountzHandler;
|
|
private readonly PprofHandler _pprofHandler;
|
|
|
|
public MonitorServer(NatsServer server, NatsOptions options, ServerStats stats, ILoggerFactory loggerFactory)
|
|
{
|
|
_logger = loggerFactory.CreateLogger<MonitorServer>();
|
|
|
|
var builder = WebApplication.CreateSlimBuilder();
|
|
builder.WebHost.UseUrls($"http://{options.MonitorHost}:{options.MonitorPort}");
|
|
builder.Logging.ClearProviders();
|
|
|
|
_app = builder.Build();
|
|
var basePath = options.MonitorBasePath ?? "";
|
|
|
|
_varzHandler = new VarzHandler(server, options);
|
|
_connzHandler = new ConnzHandler(server);
|
|
_subszHandler = new SubszHandler(server);
|
|
_jszHandler = new JszHandler(server, options);
|
|
_routezHandler = new RoutezHandler(server);
|
|
_gatewayzHandler = new GatewayzHandler(server);
|
|
_leafzHandler = new LeafzHandler(server);
|
|
_accountzHandler = new AccountzHandler(server);
|
|
_pprofHandler = new PprofHandler();
|
|
|
|
_app.MapGet(basePath + "/", () =>
|
|
{
|
|
stats.HttpReqStats.AddOrUpdate("/", 1, (_, v) => v + 1);
|
|
return Results.Ok(new
|
|
{
|
|
endpoints = new[]
|
|
{
|
|
"/varz", "/connz", "/healthz", "/routez",
|
|
"/gatewayz", "/leafz", "/subz", "/accountz", "/jsz",
|
|
},
|
|
});
|
|
});
|
|
_app.MapGet(basePath + "/healthz", () =>
|
|
{
|
|
stats.HttpReqStats.AddOrUpdate("/healthz", 1, (_, v) => v + 1);
|
|
return Results.Ok("ok");
|
|
});
|
|
_app.MapGet(basePath + "/varz", async (HttpContext ctx) =>
|
|
{
|
|
stats.HttpReqStats.AddOrUpdate("/varz", 1, (_, v) => v + 1);
|
|
return Results.Ok(await _varzHandler.HandleVarzAsync(ctx.RequestAborted));
|
|
});
|
|
|
|
_app.MapGet(basePath + "/connz", (HttpContext ctx) =>
|
|
{
|
|
stats.HttpReqStats.AddOrUpdate("/connz", 1, (_, v) => v + 1);
|
|
return Results.Ok(_connzHandler.HandleConnz(ctx));
|
|
});
|
|
|
|
_app.MapGet(basePath + "/routez", () =>
|
|
{
|
|
stats.HttpReqStats.AddOrUpdate("/routez", 1, (_, v) => v + 1);
|
|
return Results.Ok(_routezHandler.Build());
|
|
});
|
|
_app.MapGet(basePath + "/gatewayz", () =>
|
|
{
|
|
stats.HttpReqStats.AddOrUpdate("/gatewayz", 1, (_, v) => v + 1);
|
|
return Results.Ok(_gatewayzHandler.Build());
|
|
});
|
|
_app.MapGet(basePath + "/leafz", () =>
|
|
{
|
|
stats.HttpReqStats.AddOrUpdate("/leafz", 1, (_, v) => v + 1);
|
|
return Results.Ok(_leafzHandler.Build());
|
|
});
|
|
_app.MapGet(basePath + "/subz", (HttpContext ctx) =>
|
|
{
|
|
stats.HttpReqStats.AddOrUpdate("/subz", 1, (_, v) => v + 1);
|
|
return Results.Ok(_subszHandler.HandleSubsz(ctx));
|
|
});
|
|
_app.MapGet(basePath + "/subscriptionsz", (HttpContext ctx) =>
|
|
{
|
|
stats.HttpReqStats.AddOrUpdate("/subscriptionsz", 1, (_, v) => v + 1);
|
|
return Results.Ok(_subszHandler.HandleSubsz(ctx));
|
|
});
|
|
_app.MapGet(basePath + "/accountz", () =>
|
|
{
|
|
stats.HttpReqStats.AddOrUpdate("/accountz", 1, (_, v) => v + 1);
|
|
return Results.Ok(_accountzHandler.Build());
|
|
});
|
|
_app.MapGet(basePath + "/accstatz", () =>
|
|
{
|
|
stats.HttpReqStats.AddOrUpdate("/accstatz", 1, (_, v) => v + 1);
|
|
return Results.Ok(_accountzHandler.BuildStats());
|
|
});
|
|
_app.MapGet(basePath + "/jsz", () =>
|
|
{
|
|
stats.HttpReqStats.AddOrUpdate("/jsz", 1, (_, v) => v + 1);
|
|
return Results.Ok(_jszHandler.Build());
|
|
});
|
|
|
|
if (options.ProfPort > 0)
|
|
{
|
|
_app.MapGet(basePath + "/debug/pprof", () =>
|
|
{
|
|
stats.HttpReqStats.AddOrUpdate("/debug/pprof", 1, (_, v) => v + 1);
|
|
return Results.Text(_pprofHandler.Index(), "text/plain");
|
|
});
|
|
|
|
_app.MapGet(basePath + "/debug/pprof/profile", (HttpContext ctx) =>
|
|
{
|
|
stats.HttpReqStats.AddOrUpdate("/debug/pprof/profile", 1, (_, v) => v + 1);
|
|
var seconds = 30;
|
|
if (ctx.Request.Query.TryGetValue("seconds", out var values)
|
|
&& int.TryParse(values.ToString(), out var parsed))
|
|
{
|
|
seconds = parsed;
|
|
}
|
|
|
|
return Results.File(_pprofHandler.CaptureCpuProfile(seconds), "application/json");
|
|
});
|
|
}
|
|
}
|
|
|
|
public async Task StartAsync(CancellationToken ct)
|
|
{
|
|
await _app.StartAsync(ct);
|
|
_logger.LogInformation("Monitoring listening on {Urls}", string.Join(", ", _app.Urls));
|
|
}
|
|
|
|
public async ValueTask DisposeAsync()
|
|
{
|
|
await _app.StopAsync();
|
|
await _app.DisposeAsync();
|
|
_varzHandler.Dispose();
|
|
}
|
|
}
|