using System.Collections.Concurrent; namespace MP.RIOC.Services { public class RouteStats { public long Count; public TimeSpan TotalDuration = TimeSpan.Zero; public TimeSpan MaxDuration = TimeSpan.Zero; public TimeSpan MinDuration = TimeSpan.MaxValue; public ConcurrentDictionary StatusCodes = new(); public ConcurrentDictionary ErrorMessages = new(); public TimeSpan AvgDuration => TotalDuration / (Count > 0 ? Count : 1); } public class RouteStatsManager { private readonly ConcurrentDictionary _map = new(); /// /// Registrazione del metodo + destinazione /// /// public void Record(string dest_method) { var stat = _map.GetOrAdd(dest_method, _ => new RouteStats()); Interlocked.Increment(ref stat.Count); } /// /// Registrazione destinazione+metodo /// /// chiave dest+metodo x salvataggio statistiche /// public void RecordDuration(string dest_method, TimeSpan duration) { if (_map.TryGetValue(dest_method, out var stat)) { lock (stat) { stat.TotalDuration += duration; if (stat.MaxDuration < duration) { stat.MaxDuration = duration; } if (stat.MinDuration > duration) { stat.MinDuration = duration; } } } } /// /// Registrazione errore /// /// /// public void RecordError(string dest_method, string errorMessage) { if (_map.TryGetValue(dest_method, out var stat)) { // Puliamo il messaggio per evitare chiavi infinite (es. togliamo timestamp o ID dinamici) var cleanMsg = errorMessage.Length > 100 ? errorMessage[..100] + "..." : errorMessage; stat.ErrorMessages.AddOrUpdate(cleanMsg, 1, (_, v) => v + 1); } } public void RecordStatusCode(string method, int statusCode) { if (_map.TryGetValue(method, out var stat)) { stat.StatusCodes.AddOrUpdate(statusCode, 1, (_, v) => v + 1); } } public IReadOnlyDictionary Snapshot() { return _map.ToDictionary(kv => kv.Key, kv => kv.Value); } public void Clear() { _map.Clear(); } } }