feat: add Varz and Connz monitoring JSON models with Go field name parity

This commit is contained in:
Joseph Doherty
2026-02-22 22:13:50 -05:00
parent b2f7b1b2a0
commit 045c12cce7
3 changed files with 673 additions and 0 deletions

View File

@@ -0,0 +1,207 @@
using System.Text.Json.Serialization;
namespace NATS.Server.Monitoring;
/// <summary>
/// Connection information response. Corresponds to Go server/monitor.go Connz struct.
/// </summary>
public sealed class Connz
{
[JsonPropertyName("server_id")]
public string Id { get; set; } = "";
[JsonPropertyName("now")]
public DateTime Now { get; set; }
[JsonPropertyName("num_connections")]
public int NumConns { get; set; }
[JsonPropertyName("total")]
public int Total { get; set; }
[JsonPropertyName("offset")]
public int Offset { get; set; }
[JsonPropertyName("limit")]
public int Limit { get; set; }
[JsonPropertyName("connections")]
public ConnInfo[] Conns { get; set; } = [];
}
/// <summary>
/// Detailed information on a per-connection basis.
/// Corresponds to Go server/monitor.go ConnInfo struct.
/// </summary>
public sealed class ConnInfo
{
[JsonPropertyName("cid")]
public ulong Cid { get; set; }
[JsonPropertyName("kind")]
public string Kind { get; set; } = "";
[JsonPropertyName("type")]
public string Type { get; set; } = "";
[JsonPropertyName("ip")]
public string Ip { get; set; } = "";
[JsonPropertyName("port")]
public int Port { get; set; }
[JsonPropertyName("start")]
public DateTime Start { get; set; }
[JsonPropertyName("last_activity")]
public DateTime LastActivity { get; set; }
[JsonPropertyName("stop")]
public DateTime? Stop { get; set; }
[JsonPropertyName("reason")]
public string Reason { get; set; } = "";
[JsonPropertyName("rtt")]
public string Rtt { get; set; } = "";
[JsonPropertyName("uptime")]
public string Uptime { get; set; } = "";
[JsonPropertyName("idle")]
public string Idle { get; set; } = "";
[JsonPropertyName("pending_bytes")]
public int Pending { get; set; }
[JsonPropertyName("in_msgs")]
public long InMsgs { get; set; }
[JsonPropertyName("out_msgs")]
public long OutMsgs { get; set; }
[JsonPropertyName("in_bytes")]
public long InBytes { get; set; }
[JsonPropertyName("out_bytes")]
public long OutBytes { get; set; }
[JsonPropertyName("subscriptions")]
public uint NumSubs { get; set; }
[JsonPropertyName("subscriptions_list")]
public string[] Subs { get; set; } = [];
[JsonPropertyName("subscriptions_list_detail")]
public SubDetail[] SubsDetail { get; set; } = [];
[JsonPropertyName("name")]
public string Name { get; set; } = "";
[JsonPropertyName("lang")]
public string Lang { get; set; } = "";
[JsonPropertyName("version")]
public string Version { get; set; } = "";
[JsonPropertyName("authorized_user")]
public string AuthorizedUser { get; set; } = "";
[JsonPropertyName("account")]
public string Account { get; set; } = "";
[JsonPropertyName("tls_version")]
public string TlsVersion { get; set; } = "";
[JsonPropertyName("tls_cipher_suite")]
public string TlsCipherSuite { get; set; } = "";
[JsonPropertyName("tls_first")]
public bool TlsFirst { get; set; }
[JsonPropertyName("mqtt_client")]
public string MqttClient { get; set; } = "";
}
/// <summary>
/// Subscription detail information.
/// Corresponds to Go server/monitor.go SubDetail struct.
/// </summary>
public sealed class SubDetail
{
[JsonPropertyName("account")]
public string Account { get; set; } = "";
[JsonPropertyName("subject")]
public string Subject { get; set; } = "";
[JsonPropertyName("qgroup")]
public string Queue { get; set; } = "";
[JsonPropertyName("sid")]
public string Sid { get; set; } = "";
[JsonPropertyName("msgs")]
public long Msgs { get; set; }
[JsonPropertyName("max")]
public long Max { get; set; }
[JsonPropertyName("cid")]
public ulong Cid { get; set; }
}
/// <summary>
/// Sort options for connection listing.
/// Corresponds to Go server/monitor_sort_opts.go SortOpt type.
/// </summary>
public enum SortOpt
{
ByCid,
ByStart,
BySubs,
ByPending,
ByMsgsTo,
ByMsgsFrom,
ByBytesTo,
ByBytesFrom,
ByLast,
ByIdle,
ByUptime,
}
/// <summary>
/// Connection state filter.
/// Corresponds to Go server/monitor.go ConnState type.
/// </summary>
public enum ConnState
{
Open,
Closed,
All,
}
/// <summary>
/// Options passed to Connz() for filtering and sorting.
/// Corresponds to Go server/monitor.go ConnzOptions struct.
/// </summary>
public sealed class ConnzOptions
{
public SortOpt Sort { get; set; } = SortOpt.ByCid;
public bool Subscriptions { get; set; }
public bool SubscriptionsDetail { get; set; }
public ConnState State { get; set; } = ConnState.Open;
public string User { get; set; } = "";
public string Account { get; set; } = "";
public string FilterSubject { get; set; } = "";
public int Offset { get; set; }
public int Limit { get; set; } = 1024;
}

View File

@@ -0,0 +1,415 @@
using System.Text.Json.Serialization;
namespace NATS.Server.Monitoring;
/// <summary>
/// Server general information. Corresponds to Go server/monitor.go Varz struct.
/// </summary>
public sealed class Varz
{
// Identity
[JsonPropertyName("server_id")]
public string Id { get; set; } = "";
[JsonPropertyName("server_name")]
public string Name { get; set; } = "";
[JsonPropertyName("version")]
public string Version { get; set; } = "";
[JsonPropertyName("proto")]
public int Proto { get; set; }
[JsonPropertyName("git_commit")]
public string GitCommit { get; set; } = "";
[JsonPropertyName("go")]
public string GoVersion { get; set; } = "";
[JsonPropertyName("host")]
public string Host { get; set; } = "";
[JsonPropertyName("port")]
public int Port { get; set; }
// Network
[JsonPropertyName("ip")]
public string Ip { get; set; } = "";
[JsonPropertyName("connect_urls")]
public string[] ConnectUrls { get; set; } = [];
[JsonPropertyName("ws_connect_urls")]
public string[] WsConnectUrls { get; set; } = [];
[JsonPropertyName("http_host")]
public string HttpHost { get; set; } = "";
[JsonPropertyName("http_port")]
public int HttpPort { get; set; }
[JsonPropertyName("http_base_path")]
public string HttpBasePath { get; set; } = "";
[JsonPropertyName("https_port")]
public int HttpsPort { get; set; }
// Security
[JsonPropertyName("auth_required")]
public bool AuthRequired { get; set; }
[JsonPropertyName("tls_required")]
public bool TlsRequired { get; set; }
[JsonPropertyName("tls_verify")]
public bool TlsVerify { get; set; }
[JsonPropertyName("tls_ocsp_peer_verify")]
public bool TlsOcspPeerVerify { get; set; }
[JsonPropertyName("auth_timeout")]
public double AuthTimeout { get; set; }
[JsonPropertyName("tls_timeout")]
public double TlsTimeout { get; set; }
// Limits
[JsonPropertyName("max_connections")]
public int MaxConnections { get; set; }
[JsonPropertyName("max_subscriptions")]
public int MaxSubscriptions { get; set; }
[JsonPropertyName("max_payload")]
public int MaxPayload { get; set; }
[JsonPropertyName("max_pending")]
public long MaxPending { get; set; }
[JsonPropertyName("max_control_line")]
public int MaxControlLine { get; set; }
[JsonPropertyName("ping_max")]
public int MaxPingsOut { get; set; }
// Timing
[JsonPropertyName("ping_interval")]
public long PingInterval { get; set; }
[JsonPropertyName("write_deadline")]
public long WriteDeadline { get; set; }
[JsonPropertyName("start")]
public DateTime Start { get; set; }
[JsonPropertyName("now")]
public DateTime Now { get; set; }
[JsonPropertyName("uptime")]
public string Uptime { get; set; } = "";
// Runtime
[JsonPropertyName("mem")]
public long Mem { get; set; }
[JsonPropertyName("cpu")]
public double Cpu { get; set; }
[JsonPropertyName("cores")]
public int Cores { get; set; }
[JsonPropertyName("gomaxprocs")]
public int MaxProcs { get; set; }
// Connections
[JsonPropertyName("connections")]
public int Connections { get; set; }
[JsonPropertyName("total_connections")]
public ulong TotalConnections { get; set; }
[JsonPropertyName("routes")]
public int Routes { get; set; }
[JsonPropertyName("remotes")]
public int Remotes { get; set; }
[JsonPropertyName("leafnodes")]
public int Leafnodes { get; set; }
// Messages
[JsonPropertyName("in_msgs")]
public long InMsgs { get; set; }
[JsonPropertyName("out_msgs")]
public long OutMsgs { get; set; }
[JsonPropertyName("in_bytes")]
public long InBytes { get; set; }
[JsonPropertyName("out_bytes")]
public long OutBytes { get; set; }
// Health
[JsonPropertyName("slow_consumers")]
public long SlowConsumers { get; set; }
[JsonPropertyName("slow_consumer_stats")]
public SlowConsumersStats SlowConsumerStats { get; set; } = new();
[JsonPropertyName("subscriptions")]
public uint Subscriptions { get; set; }
// Config
[JsonPropertyName("config_load_time")]
public DateTime ConfigLoadTime { get; set; }
[JsonPropertyName("tags")]
public string[] Tags { get; set; } = [];
[JsonPropertyName("system_account")]
public string SystemAccount { get; set; } = "";
[JsonPropertyName("pinned_account_fails")]
public ulong PinnedAccountFail { get; set; }
[JsonPropertyName("tls_cert_not_after")]
public DateTime TlsCertNotAfter { get; set; }
// HTTP
[JsonPropertyName("http_req_stats")]
public Dictionary<string, ulong> HttpReqStats { get; set; } = new();
// Subsystems
[JsonPropertyName("cluster")]
public ClusterOptsVarz Cluster { get; set; } = new();
[JsonPropertyName("gateway")]
public GatewayOptsVarz Gateway { get; set; } = new();
[JsonPropertyName("leaf")]
public LeafNodeOptsVarz Leaf { get; set; } = new();
[JsonPropertyName("mqtt")]
public MqttOptsVarz Mqtt { get; set; } = new();
[JsonPropertyName("websocket")]
public WebsocketOptsVarz Websocket { get; set; } = new();
[JsonPropertyName("jetstream")]
public JetStreamVarz JetStream { get; set; } = new();
}
/// <summary>
/// Statistics about slow consumers by connection type.
/// Corresponds to Go server/monitor.go SlowConsumersStats struct.
/// </summary>
public sealed class SlowConsumersStats
{
[JsonPropertyName("clients")]
public ulong Clients { get; set; }
[JsonPropertyName("routes")]
public ulong Routes { get; set; }
[JsonPropertyName("gateways")]
public ulong Gateways { get; set; }
[JsonPropertyName("leafs")]
public ulong Leafs { get; set; }
}
/// <summary>
/// Cluster configuration monitoring information.
/// Corresponds to Go server/monitor.go ClusterOptsVarz struct.
/// </summary>
public sealed class ClusterOptsVarz
{
[JsonPropertyName("name")]
public string Name { get; set; } = "";
[JsonPropertyName("addr")]
public string Host { get; set; } = "";
[JsonPropertyName("cluster_port")]
public int Port { get; set; }
[JsonPropertyName("auth_timeout")]
public double AuthTimeout { get; set; }
[JsonPropertyName("tls_timeout")]
public double TlsTimeout { get; set; }
[JsonPropertyName("tls_required")]
public bool TlsRequired { get; set; }
[JsonPropertyName("tls_verify")]
public bool TlsVerify { get; set; }
[JsonPropertyName("pool_size")]
public int PoolSize { get; set; }
[JsonPropertyName("urls")]
public string[] Urls { get; set; } = [];
}
/// <summary>
/// Gateway configuration monitoring information.
/// Corresponds to Go server/monitor.go GatewayOptsVarz struct.
/// </summary>
public sealed class GatewayOptsVarz
{
[JsonPropertyName("name")]
public string Name { get; set; } = "";
[JsonPropertyName("host")]
public string Host { get; set; } = "";
[JsonPropertyName("port")]
public int Port { get; set; }
[JsonPropertyName("auth_timeout")]
public double AuthTimeout { get; set; }
[JsonPropertyName("tls_timeout")]
public double TlsTimeout { get; set; }
[JsonPropertyName("tls_required")]
public bool TlsRequired { get; set; }
[JsonPropertyName("tls_verify")]
public bool TlsVerify { get; set; }
[JsonPropertyName("advertise")]
public string Advertise { get; set; } = "";
[JsonPropertyName("connect_retries")]
public int ConnectRetries { get; set; }
[JsonPropertyName("reject_unknown")]
public bool RejectUnknown { get; set; }
}
/// <summary>
/// Leaf node configuration monitoring information.
/// Corresponds to Go server/monitor.go LeafNodeOptsVarz struct.
/// </summary>
public sealed class LeafNodeOptsVarz
{
[JsonPropertyName("host")]
public string Host { get; set; } = "";
[JsonPropertyName("port")]
public int Port { get; set; }
[JsonPropertyName("auth_timeout")]
public double AuthTimeout { get; set; }
[JsonPropertyName("tls_timeout")]
public double TlsTimeout { get; set; }
[JsonPropertyName("tls_required")]
public bool TlsRequired { get; set; }
[JsonPropertyName("tls_verify")]
public bool TlsVerify { get; set; }
[JsonPropertyName("tls_ocsp_peer_verify")]
public bool TlsOcspPeerVerify { get; set; }
}
/// <summary>
/// MQTT configuration monitoring information.
/// Corresponds to Go server/monitor.go MQTTOptsVarz struct.
/// </summary>
public sealed class MqttOptsVarz
{
[JsonPropertyName("host")]
public string Host { get; set; } = "";
[JsonPropertyName("port")]
public int Port { get; set; }
[JsonPropertyName("tls_timeout")]
public double TlsTimeout { get; set; }
}
/// <summary>
/// Websocket configuration monitoring information.
/// Corresponds to Go server/monitor.go WebsocketOptsVarz struct.
/// </summary>
public sealed class WebsocketOptsVarz
{
[JsonPropertyName("host")]
public string Host { get; set; } = "";
[JsonPropertyName("port")]
public int Port { get; set; }
[JsonPropertyName("tls_timeout")]
public double TlsTimeout { get; set; }
}
/// <summary>
/// JetStream runtime information.
/// Corresponds to Go server/monitor.go JetStreamVarz struct.
/// </summary>
public sealed class JetStreamVarz
{
[JsonPropertyName("config")]
public JetStreamConfig Config { get; set; } = new();
[JsonPropertyName("stats")]
public JetStreamStats Stats { get; set; } = new();
}
/// <summary>
/// JetStream configuration.
/// Corresponds to Go server/jetstream.go JetStreamConfig struct.
/// </summary>
public sealed class JetStreamConfig
{
[JsonPropertyName("max_memory")]
public long MaxMemory { get; set; }
[JsonPropertyName("max_storage")]
public long MaxStorage { get; set; }
[JsonPropertyName("store_dir")]
public string StoreDir { get; set; } = "";
}
/// <summary>
/// JetStream statistics.
/// Corresponds to Go server/jetstream.go JetStreamStats struct.
/// </summary>
public sealed class JetStreamStats
{
[JsonPropertyName("memory")]
public ulong Memory { get; set; }
[JsonPropertyName("storage")]
public ulong Storage { get; set; }
[JsonPropertyName("accounts")]
public int Accounts { get; set; }
[JsonPropertyName("ha_assets")]
public int HaAssets { get; set; }
[JsonPropertyName("api")]
public JetStreamApiStats Api { get; set; } = new();
}
/// <summary>
/// JetStream API statistics.
/// Corresponds to Go server/jetstream.go JetStreamAPIStats struct.
/// </summary>
public sealed class JetStreamApiStats
{
[JsonPropertyName("total")]
public ulong Total { get; set; }
[JsonPropertyName("errors")]
public ulong Errors { get; set; }
}

View File

@@ -0,0 +1,51 @@
using System.Text.Json;
using NATS.Server.Monitoring;
namespace NATS.Server.Tests;
public class MonitorModelTests
{
[Fact]
public void Varz_serializes_with_go_field_names()
{
var varz = new Varz
{
Id = "TESTID", Name = "test-server", Version = "0.1.0",
Host = "0.0.0.0", Port = 4222, InMsgs = 100, OutMsgs = 200,
};
var json = JsonSerializer.Serialize(varz);
json.ShouldContain("\"server_id\":");
json.ShouldContain("\"server_name\":");
json.ShouldContain("\"in_msgs\":");
json.ShouldContain("\"out_msgs\":");
json.ShouldNotContain("\"InMsgs\"");
}
[Fact]
public void Connz_serializes_with_go_field_names()
{
var connz = new Connz
{
Id = "TESTID", Now = DateTime.UtcNow, NumConns = 1, Total = 1, Limit = 1024,
Conns = [new ConnInfo { Cid = 1, Ip = "127.0.0.1", Port = 5555,
InMsgs = 10, Uptime = "1s", Idle = "0s",
Start = DateTime.UtcNow, LastActivity = DateTime.UtcNow }],
};
var json = JsonSerializer.Serialize(connz);
json.ShouldContain("\"server_id\":");
json.ShouldContain("\"num_connections\":");
json.ShouldContain("\"in_msgs\":");
json.ShouldContain("\"pending_bytes\":");
}
[Fact]
public void Varz_includes_nested_config_stubs()
{
var varz = new Varz { Id = "X", Name = "X", Version = "X", Host = "X" };
var json = JsonSerializer.Serialize(varz);
json.ShouldContain("\"cluster\":");
json.ShouldContain("\"gateway\":");
json.ShouldContain("\"leaf\":");
json.ShouldContain("\"jetstream\":");
}
}