using System.Diagnostics; using System.Diagnostics.Metrics; using FinalFactory.Rendezvous.Server.State; namespace FinalFactory.Rendezvous.Server.Observability; internal sealed class RendezvousTelemetry : IDisposable { public const string MeterName = "FinalFactory.Rendezvous"; public const string ActivitySourceName = "FinalFactory.Rendezvous.Server"; private readonly InMemoryEphemeralRendezvousStore _store; private readonly Meter _meter = new(MeterName, "1.0.0"); private readonly ActivitySource _activities = new(ActivitySourceName, "1.0.0"); private readonly Counter _httpRequests; private readonly Histogram _httpDuration; private readonly Counter _udpResults; private readonly Histogram _udpDuration; private readonly Counter _limiterDrops; private readonly Counter _auditEvents; private readonly Counter _connectionOutcomes; private readonly Counter _operatorAuthentication; private readonly Histogram _pairingLatency; public RendezvousTelemetry(InMemoryEphemeralRendezvousStore store) { _store = store; _httpRequests = _meter.CreateCounter("rendezvous.http.requests"); _httpDuration = _meter.CreateHistogram( "rendezvous.http.duration", "ms"); _udpResults = _meter.CreateCounter("rendezvous.udp.results"); _udpDuration = _meter.CreateHistogram( "rendezvous.udp.duration", "ms"); _limiterDrops = _meter.CreateCounter("rendezvous.limiter.drops"); _auditEvents = _meter.CreateCounter("rendezvous.audit.events"); _connectionOutcomes = _meter.CreateCounter("rendezvous.connection.outcomes"); _operatorAuthentication = _meter.CreateCounter("rendezvous.operator.authentication"); _pairingLatency = _meter.CreateHistogram( "rendezvous.pairing.latency", "ms"); _meter.CreateObservableGauge( "rendezvous.store.active_listings", () => _store.GetMetricsSnapshot().ActiveListings); _meter.CreateObservableGauge( "rendezvous.store.active_leases", () => _store.GetMetricsSnapshot().ActiveListings); _meter.CreateObservableGauge( "rendezvous.store.active_attempts", () => _store.GetMetricsSnapshot().ActiveJoinAttempts); _meter.CreateObservableGauge( "rendezvous.queue.depth", () => _store.GetMetricsSnapshot().ActiveJoinAttempts); _meter.CreateObservableGauge( "rendezvous.store.replay_markers", () => _store.GetMetricsSnapshot().ReplayMarkers); _meter.CreateObservableGauge( "rendezvous.store.available", () => _store.GetMetricsSnapshot().IsAvailable ? 1 : 0); _meter.CreateObservableCounter( "rendezvous.store.expiry_churn", () => _store.GetMetricsSnapshot().ExpiryChurn); } public Activity? StartActivity(string name, ActivityKind kind = ActivityKind.Internal) => _activities.StartActivity(name, kind); public void RecordHttp(string operation, int statusCode, double elapsedMilliseconds) { TagList tags = new() { { "operation", operation }, { "status_code", statusCode }, }; _httpRequests.Add(1, tags); _httpDuration.Record(elapsedMilliseconds, tags); } public void RecordUdp(string operation, string result, double elapsedMilliseconds) { TagList tags = new() { { "operation", operation }, { "result", result }, }; _udpResults.Add(1, tags); _udpDuration.Record(elapsedMilliseconds, tags); } public void RecordLimiterDrop(string transport, string partition) => _limiterDrops.Add(1, new TagList { { "transport", transport }, { "partition", partition }, }); public void RecordAudit(string action, string result) => _auditEvents.Add(1, new TagList { { "action", action }, { "result", result }, }); public void RecordConnectionOutcome(string outcome, string elapsedBucket) => _connectionOutcomes.Add(1, new TagList { { "outcome", outcome }, { "elapsed_bucket", elapsedBucket }, }); public void RecordOperatorAuthentication(string result) => _operatorAuthentication.Add(1, new TagList { { "result", result }, }); public void RecordPairingLatency(double elapsedMilliseconds) => _pairingLatency.Record(elapsedMilliseconds); public void Dispose() { _activities.Dispose(); _meter.Dispose(); } }