Skip to content

Commit ce3be30

Browse files
authored
Record metrics across the board (#347)
#346: Record metrics across the board #!components: grid-bot ~ The following record metrics now: - execute script command and interaction - render command and interaction - grid bot grpc server - admin utility - avatar utility - backtrace utility - discord webhook alert manager - script logger ~ Changed grid_ metrics to bot_ ~ Fixed error with committing in client settings factory ~ Fixed web server middleware not fetching path.
1 parent 6eb71b2 commit ce3be30

21 files changed

+501
-47
lines changed

services/grid-bot/lib/commands/Modules/Commands/ExecuteScript.cs

+56-6
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ namespace Grid.Bot.Commands.Public;
1313
using System.Text.RegularExpressions;
1414

1515
using Discord;
16-
using Discord.WebSocket;
1716

1817
using Discord.Commands;
1918

@@ -39,7 +38,6 @@ namespace Grid.Bot.Commands.Public;
3938
/// <param name="logger">The <see cref="ILogger"/>.</param>
4039
/// <param name="gridSettings">The <see cref="GridSettings"/>.</param>
4140
/// <param name="scriptsSettings">The <see cref="ScriptsSettings"/>.</param>
42-
/// <param name="discordClient">The <see cref="DiscordShardedClient"/>.</param>
4341
/// <param name="luaUtility">The <see cref="ILuaUtility"/>.</param>
4442
/// <param name="floodCheckerRegistry">The <see cref="IFloodCheckerRegistry"/>.</param>
4543
/// <param name="backtraceUtility">The <see cref="IBacktraceUtility"/>.</param>
@@ -52,7 +50,6 @@ namespace Grid.Bot.Commands.Public;
5250
/// - <paramref name="logger"/> cannot be null.
5351
/// - <paramref name="gridSettings"/> cannot be null.
5452
/// - <paramref name="scriptsSettings"/> cannot be null.
55-
/// - <paramref name="discordClient"/> cannot be null.
5653
/// - <paramref name="luaUtility"/> cannot be null.
5754
/// - <paramref name="floodCheckerRegistry"/> cannot be null.
5855
/// - <paramref name="backtraceUtility"/> cannot be null.
@@ -66,7 +63,6 @@ public partial class ExecuteScript(
6663
ILogger logger,
6764
GridSettings gridSettings,
6865
ScriptsSettings scriptsSettings,
69-
DiscordShardedClient discordClient,
7066
ILuaUtility luaUtility,
7167
IFloodCheckerRegistry floodCheckerRegistry,
7268
IBacktraceUtility backtraceUtility,
@@ -86,7 +82,6 @@ IGridServerFileHelper gridServerFileHelper
8682
private readonly GridSettings _gridSettings = gridSettings ?? throw new ArgumentNullException(nameof(gridSettings));
8783
private readonly ScriptsSettings _scriptsSettings = scriptsSettings ?? throw new ArgumentNullException(nameof(scriptsSettings));
8884

89-
private readonly DiscordShardedClient _discordClient = discordClient ?? throw new ArgumentNullException(nameof(discordClient));
9085
private readonly ILuaUtility _luaUtility = luaUtility ?? throw new ArgumentNullException(nameof(luaUtility));
9186
private readonly IFloodCheckerRegistry _floodCheckerRegistry = floodCheckerRegistry ?? throw new ArgumentNullException(nameof(floodCheckerRegistry));
9287
private readonly IBacktraceUtility _backtraceUtility = backtraceUtility ?? throw new ArgumentNullException(nameof(backtraceUtility));
@@ -96,7 +91,6 @@ IGridServerFileHelper gridServerFileHelper
9691
private readonly IScriptLogger _scriptLogger = scriptLogger ?? throw new ArgumentNullException(nameof(scriptLogger));
9792
private readonly IGridServerFileHelper _gridServerFileHelper = gridServerFileHelper ?? throw new ArgumentNullException(nameof(gridServerFileHelper));
9893

99-
10094
[GeneratedRegex(@"```(.*?)\s(.*?)```", RegexOptions.IgnoreCase | RegexOptions.Singleline)]
10195
private static partial Regex CodeBlockRegex();
10296
[GeneratedRegex("[\"“‘”]", RegexOptions.IgnoreCase | RegexOptions.Compiled)]
@@ -112,13 +106,21 @@ protected override async Task BeforeExecuteAsync(CommandInfo command)
112106
if (!_adminUtility.UserIsAdmin(Context.User))
113107
{
114108
if (_floodCheckerRegistry.ScriptExecutionFloodChecker.IsFlooded())
109+
{
110+
ScriptExecutionPerformanceCounters.TotalScriptExecutionsBlockedByGlobalFloodChecker.Inc();
111+
115112
throw new ApplicationException("Too many people are using this command at once, please wait a few moments and try again.");
113+
}
116114

117115
_floodCheckerRegistry.RenderFloodChecker.UpdateCount();
118116

119117
var perUserFloodChecker = _floodCheckerRegistry.GetPerUserScriptExecutionFloodChecker(Context.User.Id);
120118
if (perUserFloodChecker.IsFlooded())
119+
{
120+
ScriptExecutionPerformanceCounters.TotalScriptExecutionsBlockedByPerUserFloodChecker.WithLabels(Context.User.Id.ToString()).Inc();
121+
121122
throw new ApplicationException("You are sending execute script commands too quickly, please wait a few moments and try again.");
123+
}
122124

123125
perUserFloodChecker.UpdateCount();
124126
}
@@ -152,6 +154,8 @@ private static string GetCodeBlockContents(string s)
152154
// Check if the input matches grid syntax error
153155
if (GridSyntaxErrorRegex().IsMatch(input))
154156
{
157+
ScriptExecutionPerformanceCounters.TotalScriptExecutionsWithSyntaxErrors.WithLabels("grid-server-syntax-error:metadata").Inc();
158+
155159
var match = GridSyntaxErrorRegex().Match(input);
156160
var line = match.Groups[1].Value;
157161
var error = match.Groups[2].Value;
@@ -167,7 +171,13 @@ private static string GetCodeBlockContents(string s)
167171
var maxSize = _scriptsSettings.ScriptExecutionMaxFileSizeKb;
168172

169173
if (input.Length / 1000 > maxSize)
174+
{
175+
ScriptExecutionPerformanceCounters.TotalScriptExecutionsWithResultsExceedingMaxSize.WithLabels(input.Length.ToString()).Inc();
176+
170177
return ($"The output cannot be larger than {maxSize} KiB", null);
178+
}
179+
180+
ScriptExecutionPerformanceCounters.TotalScriptExecutionsWithResultsViaFiles.Inc();
171181

172182
return (fileName, new MemoryStream(Encoding.UTF8.GetBytes(input)));
173183
}
@@ -187,7 +197,13 @@ private static string GetCodeBlockContents(string s)
187197
var maxSize = _scriptsSettings.ScriptExecutionMaxResultSizeKb;
188198

189199
if (input.Length / 1000 > maxSize)
200+
{
201+
ScriptExecutionPerformanceCounters.TotalScriptExecutionsWithResultsExceedingMaxSize.WithLabels(input.Length.ToString()).Inc();
202+
190203
return ($"The result cannot be larger than {maxSize} KiB", null);
204+
}
205+
206+
ScriptExecutionPerformanceCounters.TotalScriptExecutionsWithResultsViaFiles.Inc();
191207

192208
return (fileName, new MemoryStream(Encoding.UTF8.GetBytes(input)));
193209
}
@@ -210,9 +226,17 @@ private async Task HandleResponseAsync(string result, ReturnMetadata metadata)
210226
.WithCurrentTimestamp();
211227

212228
if (metadata.Success)
229+
{
230+
ScriptExecutionPerformanceCounters.TotalSuccessfulScriptExecutions.Inc();
231+
213232
builder.WithColor(Color.Green);
233+
}
214234
else
235+
{
236+
ScriptExecutionPerformanceCounters.TotalFailedScriptExecutionsDueToLuaError.Inc();
237+
215238
builder.WithColor(Color.Red);
239+
}
216240

217241
var (fileNameOrOutput, outputFile) = DetermineDescription(
218242
metadata.Logs,
@@ -234,6 +258,8 @@ private async Task HandleResponseAsync(string result, ReturnMetadata metadata)
234258

235259
builder.AddField("Execution Time", $"{metadata.ExecutionTime:f5}s");
236260

261+
ScriptExecutionPerformanceCounters.ScriptExecutionAverageExecutionTime.Observe(metadata.ExecutionTime);
262+
237263
var attachments = new List<FileAttachment>();
238264
if (outputFile != null)
239265
attachments.Add(new(outputFile, fileNameOrOutput));
@@ -270,6 +296,8 @@ private async Task<bool> ParseLuaAsync(string input)
270296

271297
if (errors.Any())
272298
{
299+
ScriptExecutionPerformanceCounters.TotalScriptExecutionsWithSyntaxErrors.WithLabels("pre-parser-syntax-error").Inc();
300+
273301
var errorString = string.Join("\n", errors.Select(err => err.ToString()));
274302

275303
if (errorString.Length > _maxErrorLength)
@@ -303,6 +331,8 @@ private async Task<bool> ParseLuaAsync(string input)
303331
[Command("execute"), Summary("Execute a script via raw text."), Alias("ex", "exc", "x")]
304332
public async Task ExecuteScriptFromTextAsync([Remainder] string script = "")
305333
{
334+
ScriptExecutionPerformanceCounters.TotalScriptExecutionsByUser.WithLabels(Context.User.Id.ToString()).Inc();
335+
306336
using var _ = Context.Channel.EnterTypingState();
307337

308338
if (string.IsNullOrWhiteSpace(script))
@@ -331,13 +361,17 @@ public async Task ExecuteScriptFromTextAsync([Remainder] string script = "")
331361
return;
332362
}
333363

364+
ScriptExecutionPerformanceCounters.TotalScriptExecutionsFromFiles.WithLabels(file.Filename, file.Size.ToString()).Inc();
365+
334366
script = await file.GetAttachmentContentsAscii();
335367
}
336368

337369
script = GetCodeBlockContents(script);
338370

339371
if (string.IsNullOrEmpty(script))
340372
{
373+
ScriptExecutionPerformanceCounters.TotalScriptExecutionsWithNoContent.Inc();
374+
341375
await LuaErrorAsync("There must be content within a code block!");
342376

343377
return;
@@ -351,6 +385,8 @@ public async Task ExecuteScriptFromTextAsync([Remainder] string script = "")
351385

352386
if (ContainsUnicode(script))
353387
{
388+
ScriptExecutionPerformanceCounters.TotalScriptExecutionsWithUnicode.Inc();
389+
354390
await LuaErrorAsync("Scripts can only contain ASCII characters!");
355391

356392
return;
@@ -370,7 +406,11 @@ public async Task ExecuteScriptFromTextAsync([Remainder] string script = "")
370406
);
371407

372408
if (_scriptsSettings.LuaVMEnabled) // Disable if pre-luau, or wait for the file to be updated to support pre-luau
409+
{
410+
ScriptExecutionPerformanceCounters.TotalScriptExecutionsUsingLuaVM.Inc();
411+
373412
script = string.Format(_luaUtility.LuaVMTemplate, script);
413+
}
374414

375415
#if !PRE_JSON_EXECUTION
376416
// isAdmin allows a bypass of disabled methods and virtualized globals
@@ -430,6 +470,8 @@ public async Task ExecuteScriptFromTextAsync([Remainder] string script = "")
430470
var message = ex.Message;
431471
if (GridSyntaxErrorRegex().IsMatch(message))
432472
{
473+
ScriptExecutionPerformanceCounters.TotalScriptExecutionsWithSyntaxErrors.WithLabels("grid-server-syntax-error:fault").Inc();
474+
433475
var match = GridSyntaxErrorRegex().Match(message);
434476
var line = match.Groups[1].Value;
435477
var error = match.Groups[2].Value;
@@ -455,6 +497,8 @@ public async Task ExecuteScriptFromTextAsync([Remainder] string script = "")
455497

456498
if (message.Contains(_ErrorConvertingToJson))
457499
{
500+
ScriptExecutionPerformanceCounters.TotalScriptExecutionsWithNonJsonSerializableResults.Inc();
501+
458502
await LuaErrorAsync("The script returned a value that could not be converted to JSON.");
459503

460504
return;
@@ -465,18 +509,24 @@ public async Task ExecuteScriptFromTextAsync([Remainder] string script = "")
465509
// Catch this and alert the user (only in the case of ex is CommunicationException, ex.InnerException is InvalidOperationException and ex.InnerException.InnerException is XmlException)
466510
if (ex is CommunicationException && ex.InnerException is InvalidOperationException && ex.InnerException.InnerException is XmlException)
467511
{
512+
ScriptExecutionPerformanceCounters.TotalScriptExecutionsWithNonAsciiResults.Inc();
513+
468514
await LuaErrorAsync("The script returned invalid ASCII characters.");
469515

470516
return;
471517
}
472518

473519
if (ex is TimeoutException)
474520
{
521+
ScriptExecutionPerformanceCounters.TotalScriptExecutionsThatTimedOut.Inc();
522+
475523
await HandleResponseAsync(null, new() { ErrorMessage = "script exceeded timeout", ExecutionTime = sw.Elapsed.TotalSeconds, Success = false });
476524

477525
return;
478526
}
479527

528+
ScriptExecutionPerformanceCounters.TotalScriptExecutionsWithUnexpectedExceptions.WithLabels(ex.GetType().ToString()).Inc();
529+
480530
if (ex is not Discord.Net.HttpException)
481531
await AlertForSystem(script, originalScript, scriptId, scriptName, ex);
482532

services/grid-bot/lib/commands/Modules/Commands/Render.cs

+22-1
Original file line numberDiff line numberDiff line change
@@ -48,20 +48,27 @@ IAdminUtility adminUtility
4848
private readonly IFloodCheckerRegistry _floodCheckerRegistry = floodCheckerRegistry ?? throw new ArgumentNullException(nameof(floodCheckerRegistry));
4949
private readonly IAdminUtility _adminUtility = adminUtility ?? throw new ArgumentNullException(nameof(adminUtility));
5050

51-
5251
/// <inheritdoc cref="ModuleBase{TContext}.BeforeExecuteAsync(CommandInfo)"/>
5352
protected override async Task BeforeExecuteAsync(CommandInfo command)
5453
{
5554
if (!_adminUtility.UserIsAdmin(Context.User))
5655
{
5756
if (_floodCheckerRegistry.RenderFloodChecker.IsFlooded())
57+
{
58+
RenderPerformanceCounters.TotalRendersBlockedByGlobalFloodChecker.Inc();
59+
5860
throw new ApplicationException("Too many people are using this command at once, please wait a few moments and try again.");
61+
}
5962

6063
_floodCheckerRegistry.RenderFloodChecker.UpdateCount();
6164

6265
var perUserFloodChecker = _floodCheckerRegistry.GetPerUserRenderFloodChecker(Context.User.Id);
6366
if (perUserFloodChecker.IsFlooded())
67+
{
68+
RenderPerformanceCounters.TotalRendersBlockedByPerUserFloodChecker.WithLabels(Context.User.Id.ToString()).Inc();
69+
6470
throw new ApplicationException("You are sending render commands too quickly, please wait a few moments and try again.");
71+
}
6572

6673
perUserFloodChecker.UpdateCount();
6774
}
@@ -76,10 +83,14 @@ protected override async Task BeforeExecuteAsync(CommandInfo command)
7683
[Command("render"), Summary("Renders a Roblox character by Roblox user ID."), Alias("r")]
7784
public async Task DoRenderAsync(string userNameOrId)
7885
{
86+
RenderPerformanceCounters.TotalRenders.WithLabels(userNameOrId).Inc();
87+
7988
using var _ = Context.Channel.EnterTypingState();
8089

8190
if (!long.TryParse(userNameOrId, out var userId))
8291
{
92+
RenderPerformanceCounters.TotalRendersViaUsername.Inc();
93+
8394
var id = await _rbxUsersUtility.GetUserIdByUsernameAsync(userNameOrId).ConfigureAwait(false);
8495
if (id == null)
8596
{
@@ -93,13 +104,17 @@ public async Task DoRenderAsync(string userNameOrId)
93104

94105
if (userId < 1)
95106
{
107+
RenderPerformanceCounters.TotalRendersWithInvalidIds.Inc();
108+
96109
await this.ReplyWithReferenceAsync("The ID must be greater than 0.");
97110

98111
return;
99112
}
100113

101114
if (await _rbxUsersUtility.GetIsUserBannedAsync(userId).ConfigureAwait(false))
102115
{
116+
RenderPerformanceCounters.TotalRendersAgainstBannedUsers.WithLabels(userNameOrId).Inc();
117+
103118
_logger.Warning("The input user ID of {0} was linked to a banned user account.", userId);
104119
await this.ReplyWithReferenceAsync($"The user '{userNameOrId}' is banned or does not exist.");
105120

@@ -127,6 +142,8 @@ public async Task DoRenderAsync(string userNameOrId)
127142

128143
if (stream == null)
129144
{
145+
RenderPerformanceCounters.TotalRendersWithErrors.Inc();
146+
130147
await this.ReplyWithReferenceAsync("An error occurred while rendering the character.");
131148

132149
return;
@@ -141,6 +158,8 @@ await this.ReplyWithFileAsync(
141158
}
142159
catch (ThumbnailResponseException e)
143160
{
161+
RenderPerformanceCounters.TotalRendersWithRbxThumbnailsErrors.WithLabels(e.State.ToString()).Inc();
162+
144163
_logger.Warning("The thumbnail service responded with the following state: {0}, message: {1}", e.State, e.Message);
145164

146165
if (e.State == ThumbnailResponseState.InReview)
@@ -156,6 +175,8 @@ await this.ReplyWithFileAsync(
156175
}
157176
catch (Exception e)
158177
{
178+
RenderPerformanceCounters.TotalRendersWithErrors.Inc();
179+
159180
_logger.Error("An error occurred while rendering the character for the user '{0}': {1}", userNameOrId, e);
160181

161182
await this.ReplyWithReferenceAsync("An error occurred while rendering the character.");

0 commit comments

Comments
 (0)