From e24b5e352984a4d577213dd868c666b6f8eb3ec4 Mon Sep 17 00:00:00 2001 From: Ske Date: Mon, 16 Nov 2020 10:07:57 +0100 Subject: [PATCH] Add logging/metrics for error *messages* --- PluralKit.Bot/BotMetrics.cs | 1 + PluralKit.Bot/Services/ErrorMessageService.cs | 56 ++++++++++++++++--- 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/PluralKit.Bot/BotMetrics.cs b/PluralKit.Bot/BotMetrics.cs index 2686e570..1d8006ff 100644 --- a/PluralKit.Bot/BotMetrics.cs +++ b/PluralKit.Bot/BotMetrics.cs @@ -23,6 +23,7 @@ namespace PluralKit.Bot public static TimerOptions ProxyMembersQueryTime => new TimerOptions { Name = "Proxy member query duration", Context = "Bot", RateUnit = TimeUnit.Seconds, DurationUnit = TimeUnit.Seconds, MeasurementUnit = Unit.Calls }; public static TimerOptions DiscordApiRequests => new TimerOptions { Name = "Discord API requests", MeasurementUnit = Unit.Requests, Context = "Bot"}; public static MeterOptions BotErrors => new MeterOptions { Name = "Bot errors", MeasurementUnit = Unit.Errors, RateUnit = TimeUnit.Seconds, Context = "Bot"}; + public static MeterOptions ErrorMessagesSent => new MeterOptions { Name = "Error messages sent", MeasurementUnit = Unit.Errors, RateUnit = TimeUnit.Seconds, Context = "Bot"}; public static TimerOptions EventsHandled => new TimerOptions { Name = "Events handled", MeasurementUnit = Unit.Errors, RateUnit = TimeUnit.Seconds, DurationUnit = TimeUnit.Seconds, Context = "Bot"}; } } \ No newline at end of file diff --git a/PluralKit.Bot/Services/ErrorMessageService.cs b/PluralKit.Bot/Services/ErrorMessageService.cs index 48a4cd5c..1a22e04b 100644 --- a/PluralKit.Bot/Services/ErrorMessageService.cs +++ b/PluralKit.Bot/Services/ErrorMessageService.cs @@ -1,35 +1,73 @@ -using System.Collections.Concurrent; +using System; +using System.Collections.Concurrent; using System.Threading.Tasks; +using App.Metrics; + using DSharpPlus.Entities; using NodaTime; +using Serilog; + namespace PluralKit.Bot { public class ErrorMessageService { private static readonly Duration MinErrorInterval = Duration.FromSeconds(10); private readonly ConcurrentDictionary _lastErrorInChannel = new ConcurrentDictionary(); + + private readonly IMetrics _metrics; + private readonly ILogger _logger; + + public ErrorMessageService(IMetrics metrics, ILogger logger) + { + _metrics = metrics; + _logger = logger; + } public async Task SendErrorMessage(DiscordChannel channel, string errorId) { - var now = SystemClock.Instance.GetCurrentInstant(); - if (_lastErrorInChannel.TryGetValue(channel.Id, out var lastErrorTime)) + var now = SystemClock.Instance.GetCurrentInstant(); + if (!ShouldSendErrorMessage(channel, now)) { - var interval = now - lastErrorTime; - if (interval < MinErrorInterval) - return; + _logger.Warning("Rate limited sending error message to {ChannelId} with error code {ErrorId}", channel.Id, errorId); + _metrics.Measure.Meter.Mark(BotMetrics.ErrorMessagesSent, "throttled"); + return; } - _lastErrorInChannel[channel.Id] = now; - + var embed = new DiscordEmbedBuilder() .WithColor(new DiscordColor(0xE74C3C)) .WithTitle("Internal error occurred") .WithDescription("For support, please send the error code above in **#bug-reports-and-errors** on **[the support server *(click to join)*](https://discord.gg/PczBt78)** with a description of what you were doing at the time.") .WithFooter(errorId) .WithTimestamp(now.ToDateTimeOffset()); - await channel.SendMessageAsync($"> **Error code:** `{errorId}`", embed: embed.Build()); + + try + { + await channel.SendMessageAsync($"> **Error code:** `{errorId}`", embed: embed.Build()); + _logger.Information("Sent error message to {ChannelId} with error code {ErrorId}", channel.Id, errorId); + _metrics.Measure.Meter.Mark(BotMetrics.ErrorMessagesSent, "sent"); + } + catch (Exception e) + { + _logger.Error(e, "Error sending error message to {ChannelId}", channel.Id); + _metrics.Measure.Meter.Mark(BotMetrics.ErrorMessagesSent, "failed"); + throw; + } + } + + private bool ShouldSendErrorMessage(DiscordChannel channel, Instant now) + { + if (_lastErrorInChannel.TryGetValue(channel.Id, out var lastErrorTime)) + { + var interval = now - lastErrorTime; + if (interval < MinErrorInterval) + return false; + } + + _lastErrorInChannel[channel.Id] = now; + return true; } } } \ No newline at end of file