Add basic WIP metrics system
This commit is contained in:
parent
a2040f959d
commit
2d58705e85
@ -8,6 +8,7 @@ using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using App.Metrics;
|
||||
using Dapper;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
@ -16,6 +17,7 @@ using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using NodaTime;
|
||||
using Npgsql;
|
||||
using Npgsql.Logging;
|
||||
using Sentry;
|
||||
using Sentry.Extensibility;
|
||||
|
||||
@ -92,6 +94,8 @@ namespace PluralKit.Bot
|
||||
.AddTransient<MessageStore>()
|
||||
.AddTransient<SwitchStore>()
|
||||
|
||||
.AddSingleton<IMetrics>(_ => AppMetrics.CreateDefaultBuilder().Build())
|
||||
|
||||
.BuildServiceProvider();
|
||||
}
|
||||
class Bot
|
||||
@ -101,13 +105,15 @@ namespace PluralKit.Bot
|
||||
private CommandService _commands;
|
||||
private ProxyService _proxy;
|
||||
private Timer _updateTimer;
|
||||
private IMetrics _metrics;
|
||||
|
||||
public Bot(IServiceProvider services, IDiscordClient client, CommandService commands, ProxyService proxy)
|
||||
public Bot(IServiceProvider services, IDiscordClient client, CommandService commands, ProxyService proxy, IMetrics metrics)
|
||||
{
|
||||
this._services = services;
|
||||
this._client = client as DiscordShardedClient;
|
||||
this._commands = commands;
|
||||
this._proxy = proxy;
|
||||
_metrics = metrics;
|
||||
}
|
||||
|
||||
public async Task Init()
|
||||
@ -142,6 +148,8 @@ namespace PluralKit.Bot
|
||||
|
||||
private async Task CommandExecuted(Optional<CommandInfo> cmd, ICommandContext ctx, IResult _result)
|
||||
{
|
||||
_metrics.Measure.Meter.Mark(BotMetrics.CommandsRun);
|
||||
|
||||
// TODO: refactor this entire block, it's fugly.
|
||||
if (!_result.IsSuccess) {
|
||||
if (_result.Error == CommandError.Unsuccessful || _result.Error == CommandError.Exception) {
|
||||
@ -168,6 +176,8 @@ namespace PluralKit.Bot
|
||||
|
||||
private async Task MessageReceived(SocketMessage _arg)
|
||||
{
|
||||
_metrics.Measure.Meter.Mark(BotMetrics.MessagesReceived);
|
||||
|
||||
// _client.CurrentUser will be null if we've connected *some* shards but not shard #0 yet
|
||||
// This will cause an error in WebhookCacheServices so we just workaround and don't process any messages
|
||||
// until we properly connect. TODO: can we do this without chucking away a bunch of messages?
|
||||
|
16
PluralKit.Bot/BotMetrics.cs
Normal file
16
PluralKit.Bot/BotMetrics.cs
Normal file
@ -0,0 +1,16 @@
|
||||
using App.Metrics;
|
||||
using App.Metrics.Gauge;
|
||||
using App.Metrics.Meter;
|
||||
|
||||
namespace PluralKit.Bot
|
||||
{
|
||||
public static class BotMetrics
|
||||
{
|
||||
public static MeterOptions MessagesReceived => new MeterOptions {Name = "Messages processed", MeasurementUnit = Unit.Events, RateUnit = TimeUnit.Seconds, Context = "Bot"};
|
||||
public static MeterOptions MessagesProxied => new MeterOptions {Name = "Messages proxied", MeasurementUnit = Unit.Events, RateUnit = TimeUnit.Seconds, Context = "Bot"};
|
||||
public static MeterOptions CommandsRun => new MeterOptions {Name = "Commands run", MeasurementUnit = Unit.Commands, RateUnit = TimeUnit.Seconds, Context = "Bot"};
|
||||
public static GaugeOptions MembersOnline => new GaugeOptions {Name = "Members online", MeasurementUnit = Unit.None, Context = "Bot"};
|
||||
|
||||
public static GaugeOptions DatabasePoolSize => new GaugeOptions { Name = "Database pool size", Context = "Database" };
|
||||
}
|
||||
}
|
@ -1,10 +1,14 @@
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using App.Metrics;
|
||||
using App.Metrics.Meter;
|
||||
using Discord;
|
||||
using Discord.Commands;
|
||||
|
||||
namespace PluralKit.Bot.Commands {
|
||||
public class MiscCommands: ModuleBase<PKCommandContext> {
|
||||
public BotConfig BotConfig { get; set; }
|
||||
public IMetrics Metrics { get; set; }
|
||||
|
||||
[Command("invite")]
|
||||
[Alias("inv")]
|
||||
@ -32,5 +36,21 @@ namespace PluralKit.Bot.Commands {
|
||||
[Command("freeze")] public Task Freeze() => Context.Channel.SendMessageAsync("*A giant crystal ball of ice is charged and hurled toward your opponent, bursting open and freezing them solid on contact.*");
|
||||
[Command("starstorm")] public Task Starstorm() => Context.Channel.SendMessageAsync("*Vibrant colours burst forth from the sky as meteors rain down upon your opponent.*");
|
||||
|
||||
[Command("stats")]
|
||||
public async Task Stats()
|
||||
{
|
||||
var messagesReceived = Metrics.Snapshot.GetForContext("Bot").Meters.First(m => m.MultidimensionalName == BotMetrics.MessagesReceived.Name).Value;
|
||||
var messagesProxied = Metrics.Snapshot.GetForContext("Bot").Meters.First(m => m.MultidimensionalName == BotMetrics.MessagesProxied.Name).Value;
|
||||
var proxySuccessRate = messagesProxied.Items.First(i => i.Item == "success");
|
||||
|
||||
var commandsRun = Metrics.Snapshot.GetForContext("Bot").Meters.First(m => m.MultidimensionalName == BotMetrics.CommandsRun.Name).Value;
|
||||
|
||||
await Context.Channel.SendMessageAsync(embed: new EmbedBuilder()
|
||||
.AddField("Messages processed", $"{messagesReceived.OneMinuteRate:F1}/s ({messagesReceived.FifteenMinuteRate:F1}/s over 15m)")
|
||||
.AddField("Messages proxied", $"{messagesProxied.OneMinuteRate:F1}/s ({messagesProxied.FifteenMinuteRate:F1}/s over 15m)")
|
||||
.AddField("Commands executed", $"{commandsRun.OneMinuteRate:F1}/s ({commandsRun.FifteenMinuteRate:F1}/s over 15m)")
|
||||
.AddField("Proxy success rate", $"{proxySuccessRate.Percent/100:P1}")
|
||||
.Build());
|
||||
}
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@ using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using App.Metrics;
|
||||
using Dapper;
|
||||
using Discord;
|
||||
using Discord.Net;
|
||||
@ -35,8 +36,9 @@ namespace PluralKit.Bot
|
||||
private WebhookCacheService _webhookCache;
|
||||
private MessageStore _messageStorage;
|
||||
private EmbedService _embeds;
|
||||
private IMetrics _metrics;
|
||||
|
||||
public ProxyService(IDiscordClient client, WebhookCacheService webhookCache, DbConnectionFactory conn, LogChannelService logger, MessageStore messageStorage, EmbedService embeds)
|
||||
public ProxyService(IDiscordClient client, WebhookCacheService webhookCache, DbConnectionFactory conn, LogChannelService logger, MessageStore messageStorage, EmbedService embeds, IMetrics metrics)
|
||||
{
|
||||
_client = client;
|
||||
_webhookCache = webhookCache;
|
||||
@ -44,6 +46,7 @@ namespace PluralKit.Bot
|
||||
_logger = logger;
|
||||
_messageStorage = messageStorage;
|
||||
_embeds = embeds;
|
||||
_metrics = metrics;
|
||||
}
|
||||
|
||||
private ProxyMatch GetProxyTagMatch(string message, IEnumerable<ProxyDatabaseResult> potentials)
|
||||
@ -159,15 +162,33 @@ namespace PluralKit.Bot
|
||||
}
|
||||
|
||||
ulong messageId;
|
||||
if (attachment != null) {
|
||||
|
||||
try
|
||||
{
|
||||
if (attachment != null)
|
||||
{
|
||||
using (var http = new HttpClient())
|
||||
using (var stream = await http.GetStreamAsync(attachment.Url)) {
|
||||
messageId = await client.SendFileAsync(stream, filename: attachment.Filename, text: text, username: username, avatarUrl: avatarUrl);
|
||||
using (var stream = await http.GetStreamAsync(attachment.Url))
|
||||
{
|
||||
messageId = await client.SendFileAsync(stream, filename: attachment.Filename, text: text,
|
||||
username: username, avatarUrl: avatarUrl);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
messageId = await client.SendMessageAsync(text, username: username, avatarUrl: avatarUrl);
|
||||
}
|
||||
|
||||
// Log it in the metrics
|
||||
_metrics.Measure.Meter.Mark(BotMetrics.MessagesProxied, "success");
|
||||
}
|
||||
catch (HttpException)
|
||||
{
|
||||
// Log failure in metrics and rethrow (we still need to cancel everything else)
|
||||
_metrics.Measure.Meter.Mark(BotMetrics.MessagesProxied, "failure");
|
||||
throw;
|
||||
}
|
||||
|
||||
// TODO: figure out a way to return the full message object (without doing a GetMessageAsync call, which
|
||||
// doesn't work if there's no permission to)
|
||||
return messageId;
|
||||
|
13
PluralKit.Core/CoreMetrics.cs
Normal file
13
PluralKit.Core/CoreMetrics.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using App.Metrics;
|
||||
using App.Metrics.Gauge;
|
||||
|
||||
namespace PluralKit.Core
|
||||
{
|
||||
public static class CoreMetrics
|
||||
{
|
||||
public static GaugeOptions SystemCount => new GaugeOptions { Name = "Systems", MeasurementUnit = Unit.Items};
|
||||
public static GaugeOptions MemberCount => new GaugeOptions { Name = "Members", MeasurementUnit = Unit.Items };
|
||||
public static GaugeOptions MessageCount => new GaugeOptions { Name = "Messages", MeasurementUnit = Unit.Items };
|
||||
public static GaugeOptions SwitchCount => new GaugeOptions { Name = "Switches", MeasurementUnit = Unit.Items };
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="App.Metrics" Version="3.1.0" />
|
||||
<PackageReference Include="Dapper" Version="1.60.6" />
|
||||
<PackageReference Include="Dapper.Contrib" Version="1.60.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.2.0" />
|
||||
|
Loading…
Reference in New Issue
Block a user