diff --git a/MyApp.ServiceInterface/BackgroundMqServices.cs b/MyApp.ServiceInterface/BackgroundMqServices.cs index 674dbf2..712a4aa 100644 --- a/MyApp.ServiceInterface/BackgroundMqServices.cs +++ b/MyApp.ServiceInterface/BackgroundMqServices.cs @@ -53,7 +53,7 @@ public async Task ExecuteAsync(IExecuteCommandAsync command, T request) wh await command.ExecuteAsync(request); log.LogDebug("{Command} took {ElapsedMilliseconds}ms to execute", commandName, sw.ElapsedMilliseconds); #if DEBUG - appConfig.CommandResults.Add(new() { + appConfig.AddCommandResult(new() { Name = commandName, Ms = sw.ElapsedMilliseconds, }); @@ -63,7 +63,7 @@ public async Task ExecuteAsync(IExecuteCommandAsync command, T request) wh { log.LogError(e, "{Command}({Request}) failed: {Message}", commandName, request.ToJsv(), e.Message); #if DEBUG - appConfig.CommandResults.Add(new() { + appConfig.AddCommandResult(new() { Name = commandName, Ms = sw.ElapsedMilliseconds, Request = request, @@ -148,11 +148,18 @@ public async Task Any(AnalyticsTasks request) public object Any(ViewCommands request) { - var results = appConfig.CommandResults; + var to = new ViewCommandsResponse + { + LatestResults = new(appConfig.CommandResults), + LatestFailed = new(appConfig.CommandFailures), + Totals = new(appConfig.CommandTotals.Values) + }; if (request.Clear == true) { - appConfig.CommandResults = []; + appConfig.CommandResults.Clear(); + appConfig.CommandFailures.Clear(); + appConfig.CommandTotals.Clear(); } - return results; + return to; } } diff --git a/MyApp.ServiceInterface/Data/AppConfig.cs b/MyApp.ServiceInterface/Data/AppConfig.cs index fe545bc..8833662 100644 --- a/MyApp.ServiceInterface/Data/AppConfig.cs +++ b/MyApp.ServiceInterface/Data/AppConfig.cs @@ -211,9 +211,53 @@ public bool HasUnreadAchievements(string? userName) return userName != null && UsersUnreadAchievements.TryGetValue(userName, out var count) && count > 0; } - public List CommandResults { get; set; } = []; - public bool IsHuman(string? userName) => userName != null && GetModelUser(userName) == null; + + public const int DefaultCapacity = 500; + public ConcurrentQueue CommandResults { get; set; } = []; + public ConcurrentQueue CommandFailures { get; set; } = new(); + + public ConcurrentDictionary CommandTotals { get; set; } = new(); + + public void AddCommandResult(CommandResult result) + { + var ms = result.Ms ?? 0; + if (result.Error == null) + { + CommandResults.Enqueue(result); + while (CommandResults.Count > DefaultCapacity) + CommandResults.TryDequeue(out _); + + CommandTotals.AddOrUpdate(result.Name, + _ => new CommandSummary { Name = result.Name, Count = 1, TotalMs = ms, MinMs = ms > 0 ? ms : int.MinValue }, + (_, summary) => + { + summary.Count++; + summary.TotalMs += ms; + summary.MaxMs = Math.Max(summary.MaxMs, ms); + if (ms > 0) + { + summary.MinMs = Math.Min(summary.MinMs, ms); + } + return summary; + }); + } + else + { + CommandFailures.Enqueue(result); + while (CommandFailures.Count > DefaultCapacity) + CommandFailures.TryDequeue(out _); + + CommandTotals.AddOrUpdate(result.Name, + _ => new CommandSummary { Name = result.Name, Failed = 1, Count = 0, TotalMs = 0, MinMs = int.MinValue, LastError = result.Error }, + (_, summary) => + { + summary.Failed++; + summary.LastError = result.Error; + return summary; + }); + } + } } public class CommandResult @@ -223,3 +267,15 @@ public class CommandResult public object Request { get; set; } public string? Error { get; set; } } + +public class CommandSummary +{ + public string Name { get; set; } + public long Count { get; set; } + public long Failed { get; set; } + public long TotalMs { get; set; } + public long MinMs { get; set; } + public long MaxMs { get; set; } + public int AverageMs => (int) Math.Floor(TotalMs / (double)Count); + public string? LastError { get; set; } +} \ No newline at end of file diff --git a/MyApp.ServiceInterface/Data/Tasks.cs b/MyApp.ServiceInterface/Data/Tasks.cs index 1c58684..69ea96f 100644 --- a/MyApp.ServiceInterface/Data/Tasks.cs +++ b/MyApp.ServiceInterface/Data/Tasks.cs @@ -111,8 +111,15 @@ public class SearchTasks [Tag(Tag.Tasks)] [ExcludeMetadata] -[Restrict(InternalOnly = true)] -public class ViewCommands : IGet, IReturn +[ValidateIsAdmin] +public class ViewCommands : IGet, IReturn { public bool? Clear { get; set; } } +public class ViewCommandsResponse +{ + public List LatestResults { get; set; } + public List LatestFailed { get; set; } + public List Totals { get; set; } + public ResponseStatus? ResponseStatus { get; set; } +} \ No newline at end of file