using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; namespace ZB.MOM.WW.LmxProxy.Client { /// /// Metrics collection for client operations /// internal class ClientMetrics { private readonly ConcurrentDictionary _operationCounts = new(); private readonly ConcurrentDictionary _errorCounts = new(); private readonly ConcurrentDictionary> _latencies = new(); private readonly object _latencyLock = new(); /// /// Increments the operation count for a specific operation. /// /// The operation name. public void IncrementOperationCount(string operation) { _operationCounts.AddOrUpdate(operation, 1, (_, oldValue) => oldValue + 1); } /// /// Increments the error count for a specific operation. /// /// The operation name. public void IncrementErrorCount(string operation) { _errorCounts.AddOrUpdate(operation, 1, (_, oldValue) => oldValue + 1); } /// /// Records latency for a specific operation. /// /// The operation name. /// The latency in milliseconds. public void RecordLatency(string operation, long milliseconds) { lock (_latencyLock) { if (!_latencies.ContainsKey(operation)) { _latencies[operation] = []; } _latencies[operation].Add(milliseconds); // Keep only last 1000 entries to prevent memory growth if (_latencies[operation].Count > 1000) { _latencies[operation].RemoveAt(0); } } } /// /// Gets a snapshot of current metrics. /// /// A dictionary containing metric data. public Dictionary GetSnapshot() { var snapshot = new Dictionary(); foreach (KeyValuePair kvp in _operationCounts) { snapshot[$"{kvp.Key}_count"] = kvp.Value; } foreach (KeyValuePair kvp in _errorCounts) { snapshot[$"{kvp.Key}_errors"] = kvp.Value; } lock (_latencyLock) { foreach (KeyValuePair> kvp in _latencies) { if (kvp.Value.Any()) { snapshot[$"{kvp.Key}_avg_latency_ms"] = kvp.Value.Average(); snapshot[$"{kvp.Key}_p95_latency_ms"] = GetPercentile(kvp.Value, 95); snapshot[$"{kvp.Key}_p99_latency_ms"] = GetPercentile(kvp.Value, 99); } } } return snapshot; } private double GetPercentile(List values, int percentile) { var sorted = values.OrderBy(x => x).ToList(); int index = (int)Math.Ceiling(percentile / 100.0 * sorted.Count) - 1; return sorted[Math.Max(0, index)]; } } }