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)];
}
}
}