8ba75b50e8
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
83 lines
2.6 KiB
C#
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];
|
|
}
|
|
}
|
|
}
|