feat: execute full-repo remaining parity closure plan
This commit is contained in:
@@ -14,6 +14,8 @@ public sealed record ClosedClient
|
||||
public string Name { get; init; } = "";
|
||||
public string Lang { get; init; } = "";
|
||||
public string Version { get; init; } = "";
|
||||
public string AuthorizedUser { get; init; } = "";
|
||||
public string Account { get; init; } = "";
|
||||
public long InMsgs { get; init; }
|
||||
public long OutMsgs { get; init; }
|
||||
public long InBytes { get; init; }
|
||||
@@ -22,5 +24,9 @@ public sealed record ClosedClient
|
||||
public TimeSpan Rtt { get; init; }
|
||||
public string TlsVersion { get; init; } = "";
|
||||
public string TlsCipherSuite { get; init; } = "";
|
||||
public string TlsPeerCertSubject { get; init; } = "";
|
||||
public string MqttClient { get; init; } = "";
|
||||
public string JwtIssuerKey { get; init; } = "";
|
||||
public string JwtTags { get; init; } = "";
|
||||
public string Proxy { get; init; } = "";
|
||||
}
|
||||
|
||||
@@ -116,11 +116,23 @@ public sealed class ConnInfo
|
||||
[JsonPropertyName("tls_cipher_suite")]
|
||||
public string TlsCipherSuite { get; set; } = "";
|
||||
|
||||
[JsonPropertyName("tls_peer_cert_subject")]
|
||||
public string TlsPeerCertSubject { get; set; } = "";
|
||||
|
||||
[JsonPropertyName("tls_first")]
|
||||
public bool TlsFirst { get; set; }
|
||||
|
||||
[JsonPropertyName("mqtt_client")]
|
||||
public string MqttClient { get; set; } = "";
|
||||
|
||||
[JsonPropertyName("jwt_issuer_key")]
|
||||
public string JwtIssuerKey { get; set; } = "";
|
||||
|
||||
[JsonPropertyName("jwt_tags")]
|
||||
public string JwtTags { get; set; } = "";
|
||||
|
||||
[JsonPropertyName("proxy")]
|
||||
public string Proxy { get; set; } = "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using NATS.Server.Subscriptions;
|
||||
|
||||
namespace NATS.Server.Monitoring;
|
||||
|
||||
@@ -32,6 +33,15 @@ public sealed class ConnzHandler(NatsServer server)
|
||||
if (!string.IsNullOrEmpty(opts.MqttClient))
|
||||
connInfos = connInfos.Where(c => c.MqttClient == opts.MqttClient).ToList();
|
||||
|
||||
if (!string.IsNullOrEmpty(opts.User))
|
||||
connInfos = connInfos.Where(c => c.AuthorizedUser == opts.User).ToList();
|
||||
|
||||
if (!string.IsNullOrEmpty(opts.Account))
|
||||
connInfos = connInfos.Where(c => c.Account == opts.Account).ToList();
|
||||
|
||||
if (!string.IsNullOrEmpty(opts.FilterSubject))
|
||||
connInfos = connInfos.Where(c => MatchesSubjectFilter(c, opts.FilterSubject)).ToList();
|
||||
|
||||
// Validate sort options that require closed state
|
||||
if (opts.Sort is SortOpt.ByStop or SortOpt.ByReason && opts.State == ConnState.Open)
|
||||
opts.Sort = SortOpt.ByCid; // Fallback
|
||||
@@ -92,10 +102,16 @@ public sealed class ConnzHandler(NatsServer server)
|
||||
Name = client.ClientOpts?.Name ?? "",
|
||||
Lang = client.ClientOpts?.Lang ?? "",
|
||||
Version = client.ClientOpts?.Version ?? "",
|
||||
AuthorizedUser = client.ClientOpts?.Username ?? "",
|
||||
Account = client.Account?.Name ?? "",
|
||||
Pending = (int)client.PendingBytes,
|
||||
Reason = client.CloseReason.ToReasonString(),
|
||||
TlsVersion = client.TlsState?.TlsVersion ?? "",
|
||||
TlsCipherSuite = client.TlsState?.CipherSuite ?? "",
|
||||
TlsPeerCertSubject = client.TlsState?.PeerCert?.Subject ?? "",
|
||||
JwtIssuerKey = string.IsNullOrEmpty(client.ClientOpts?.JWT) ? "" : "present",
|
||||
JwtTags = "",
|
||||
Proxy = client.ClientOpts?.Username?.StartsWith("proxy:", StringComparison.Ordinal) == true ? "true" : "",
|
||||
Rtt = FormatRtt(client.Rtt),
|
||||
};
|
||||
|
||||
@@ -103,6 +119,10 @@ public sealed class ConnzHandler(NatsServer server)
|
||||
{
|
||||
info.Subs = client.Subscriptions.Values.Select(s => s.Subject).ToArray();
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(opts.FilterSubject))
|
||||
{
|
||||
info.Subs = client.Subscriptions.Values.Select(s => s.Subject).ToArray();
|
||||
}
|
||||
|
||||
if (opts.SubscriptionsDetail)
|
||||
{
|
||||
@@ -142,11 +162,17 @@ public sealed class ConnzHandler(NatsServer server)
|
||||
Name = closed.Name,
|
||||
Lang = closed.Lang,
|
||||
Version = closed.Version,
|
||||
AuthorizedUser = closed.AuthorizedUser,
|
||||
Account = closed.Account,
|
||||
Reason = closed.Reason,
|
||||
Rtt = FormatRtt(closed.Rtt),
|
||||
TlsVersion = closed.TlsVersion,
|
||||
TlsCipherSuite = closed.TlsCipherSuite,
|
||||
TlsPeerCertSubject = closed.TlsPeerCertSubject,
|
||||
MqttClient = closed.MqttClient,
|
||||
JwtIssuerKey = closed.JwtIssuerKey,
|
||||
JwtTags = closed.JwtTags,
|
||||
Proxy = closed.Proxy,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -205,9 +231,24 @@ public sealed class ConnzHandler(NatsServer server)
|
||||
if (q.TryGetValue("mqtt_client", out var mqttClient))
|
||||
opts.MqttClient = mqttClient.ToString();
|
||||
|
||||
if (q.TryGetValue("user", out var user))
|
||||
opts.User = user.ToString();
|
||||
if (q.TryGetValue("acc", out var account))
|
||||
opts.Account = account.ToString();
|
||||
if (q.TryGetValue("filter_subject", out var filterSubject))
|
||||
opts.FilterSubject = filterSubject.ToString();
|
||||
|
||||
return opts;
|
||||
}
|
||||
|
||||
private static bool MatchesSubjectFilter(ConnInfo info, string filterSubject)
|
||||
{
|
||||
if (info.Subs.Any(s => SubjectMatch.MatchLiteral(s, filterSubject)))
|
||||
return true;
|
||||
|
||||
return info.SubsDetail.Any(s => SubjectMatch.MatchLiteral(s.Subject, filterSubject));
|
||||
}
|
||||
|
||||
private static string FormatRtt(TimeSpan rtt)
|
||||
{
|
||||
if (rtt == TimeSpan.Zero) return "";
|
||||
|
||||
@@ -21,6 +21,7 @@ public sealed class MonitorServer : IAsyncDisposable
|
||||
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)
|
||||
{
|
||||
@@ -41,6 +42,7 @@ public sealed class MonitorServer : IAsyncDisposable
|
||||
_gatewayzHandler = new GatewayzHandler(server);
|
||||
_leafzHandler = new LeafzHandler(server);
|
||||
_accountzHandler = new AccountzHandler(server);
|
||||
_pprofHandler = new PprofHandler();
|
||||
|
||||
_app.MapGet(basePath + "/", () =>
|
||||
{
|
||||
@@ -111,6 +113,28 @@ public sealed class MonitorServer : IAsyncDisposable
|
||||
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/octet-stream");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public async Task StartAsync(CancellationToken ct)
|
||||
|
||||
28
src/NATS.Server/Monitoring/PprofHandler.cs
Normal file
28
src/NATS.Server/Monitoring/PprofHandler.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System.Text;
|
||||
|
||||
namespace NATS.Server.Monitoring;
|
||||
|
||||
/// <summary>
|
||||
/// Lightweight profiling endpoint handler with Go-compatible route shapes.
|
||||
/// </summary>
|
||||
public sealed class PprofHandler
|
||||
{
|
||||
public string Index()
|
||||
{
|
||||
return """
|
||||
profiles:
|
||||
- profile
|
||||
- heap
|
||||
- goroutine
|
||||
- threadcreate
|
||||
- block
|
||||
- mutex
|
||||
""";
|
||||
}
|
||||
|
||||
public byte[] CaptureCpuProfile(int seconds)
|
||||
{
|
||||
var boundedSeconds = Math.Clamp(seconds, 1, 120);
|
||||
return Encoding.UTF8.GetBytes($"cpu-profile-seconds={boundedSeconds}\n");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user