using AsbCloudApp.Data; using AsbCloudApp.Services; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; namespace AsbCloudInfrastructure.Services { public class RequestTrackerService : IRequerstTrackerService { const int fastRequestsCount = 1000; const int slowRequestsCount = 1000; const int errorRequestsCount = 1000; const int span = 100; const int fastLimitMs = 500; static readonly char[] stackTraceSeparators = "\r\n".ToCharArray(); private readonly ConcurrentQueue fastRequests = new ConcurrentQueue(); private readonly ConcurrentQueue slowRequests = new ConcurrentQueue(); private readonly ConcurrentQueue errorRequests = new ConcurrentQueue(); private readonly ConcurrentDictionary users = new ConcurrentDictionary(); private static IEnumerable Get(IEnumerable list, int take = -1) { IEnumerable orderedlist = list.OrderByDescending(r => r.Date); if (take > 0) orderedlist = orderedlist.Take(take); return orderedlist; } public IEnumerable GetUsersStat(int take = -1) { IEnumerable result = users.Values.OrderByDescending(u => u.LastDate); if (take > 0) result = result.Take(take); return result; } public IEnumerable GetAll(int take = -1) { var result = fastRequests .Union(slowRequests) .Union(errorRequests); return Get(result, take); } public IEnumerable GetFast(int take = -1) => Get(fastRequests, take); public IEnumerable GetSlow(int take = -1) => Get(slowRequests, take); public IEnumerable GetError(int take = -1) => Get(errorRequests, take); public void RegisterRequest(RequestLogDto requestLog) { if (requestLog.Status < 200) return; requestLog.Date = DateTime.Now; if (requestLog.ElapsedMilliseconds > fastLimitMs) RegisterSlowRequest(requestLog); else RegisterFastRequest(requestLog); UpdateUserStat(requestLog); } private void RegisterFastRequest(RequestLogDto requestLog) { fastRequests.Enqueue(requestLog); if (fastRequests.Count > fastRequestsCount + span) while (fastRequests.Count > fastRequestsCount) fastRequests.TryDequeue(out _); } private void RegisterSlowRequest(RequestLogDto requestLog) { slowRequests.Enqueue(requestLog); if (slowRequests.Count > slowRequestsCount + span) while (slowRequests.Count > slowRequestsCount) slowRequests.TryDequeue(out _); } public void RegisterRequestError(RequestLogDto requestLog, Exception ex) { requestLog.Date = DateTime.Now; requestLog.ExceptionMessage = ex.InnerException?.InnerException?.Message ?? ex.InnerException?.Message ?? ex.Message; requestLog.ExceptionStack = ex.StackTrace?.Split(stackTraceSeparators)[0]; errorRequests.Enqueue(requestLog); if (errorRequests.Count > errorRequestsCount + span) while (errorRequests.Count > errorRequestsCount) errorRequests.TryDequeue(out _); UpdateUserStat(requestLog); } private void UpdateUserStat(RequestLogDto requestLog) { if (!string.IsNullOrEmpty(requestLog.UserLogin)) { var key = $"{requestLog?.UserId}>{requestLog?.UserIp}"; if (!users.ContainsKey(key)) users[key] = new RequestLogUserDto { UserId = requestLog.UserId, Ip = requestLog.UserIp, Login = requestLog.UserLogin, //User = userService.Get(requestLog.UserId), }; users[key].ElapsedMs += requestLog.ElapsedMilliseconds; users[key].LastDate = requestLog.Date; users[key].Requests++; if (!string.IsNullOrEmpty(requestLog.ExceptionMessage)) users[key].Errors++; if (users.Count > 1000) { var count = 900 - users.Count; var toRemove = users.OrderBy(kv => kv.Value.LastDate).Take(count); foreach (var kv in toRemove) users.TryRemove(kv); } } } } }