Files
ScadaBridge/lmxproxy/src/ZB.MOM.WW.LmxProxy.Client/LmxProxyClient.ClientMetrics.cs
T
2026-03-22 00:22:29 -04:00

83 lines
2.6 KiB
C#

using System.Collections.Concurrent;
namespace ZB.MOM.WW.LmxProxy.Client;
public partial class LmxProxyClient
{
/// <summary>
/// Tracks per-operation counts, errors, and latency with rolling buffer and percentile support.
/// </summary>
internal class ClientMetrics
{
private readonly ConcurrentDictionary<string, long> _operationCounts = new();
private readonly ConcurrentDictionary<string, long> _errorCounts = new();
private readonly ConcurrentDictionary<string, List<long>> _latencies = new();
private readonly Lock _latencyLock = new();
public void IncrementOperationCount(string operation)
{
_operationCounts.AddOrUpdate(operation, 1, (_, count) => count + 1);
}
public void IncrementErrorCount(string operation)
{
_errorCounts.AddOrUpdate(operation, 1, (_, count) => count + 1);
}
public void RecordLatency(string operation, long milliseconds)
{
lock (_latencyLock)
{
if (!_latencies.TryGetValue(operation, out var list))
{
list = [];
_latencies[operation] = list;
}
list.Add(milliseconds);
if (list.Count > 1000)
{
list.RemoveAt(0);
}
}
}
public Dictionary<string, object> GetSnapshot()
{
var snapshot = new Dictionary<string, object>();
foreach (var kvp in _operationCounts)
{
snapshot[$"{kvp.Key}_count"] = kvp.Value;
}
foreach (var kvp in _errorCounts)
{
snapshot[$"{kvp.Key}_errors"] = kvp.Value;
}
lock (_latencyLock)
{
foreach (var kvp in _latencies)
{
var values = kvp.Value;
if (values.Count == 0) continue;
double avg = values.Average();
snapshot[$"{kvp.Key}_avg_latency_ms"] = Math.Round(avg, 2);
snapshot[$"{kvp.Key}_p95_latency_ms"] = GetPercentile(values, 95);
snapshot[$"{kvp.Key}_p99_latency_ms"] = GetPercentile(values, 99);
}
}
return snapshot;
}
private static long GetPercentile(List<long> values, int percentile)
{
var sorted = values.OrderBy(v => v).ToList();
int index = Math.Max(0, (int)Math.Ceiling(percentile / 100.0 * sorted.Count) - 1);
return sorted[index];
}
}
}