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.Reflection;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using App.Metrics;
|
||||||
using Dapper;
|
using Dapper;
|
||||||
using Discord;
|
using Discord;
|
||||||
using Discord.Commands;
|
using Discord.Commands;
|
||||||
@ -16,6 +17,7 @@ using Microsoft.Extensions.Configuration;
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using NodaTime;
|
using NodaTime;
|
||||||
using Npgsql;
|
using Npgsql;
|
||||||
|
using Npgsql.Logging;
|
||||||
using Sentry;
|
using Sentry;
|
||||||
using Sentry.Extensibility;
|
using Sentry.Extensibility;
|
||||||
|
|
||||||
@ -92,6 +94,8 @@ namespace PluralKit.Bot
|
|||||||
.AddTransient<MessageStore>()
|
.AddTransient<MessageStore>()
|
||||||
.AddTransient<SwitchStore>()
|
.AddTransient<SwitchStore>()
|
||||||
|
|
||||||
|
.AddSingleton<IMetrics>(_ => AppMetrics.CreateDefaultBuilder().Build())
|
||||||
|
|
||||||
.BuildServiceProvider();
|
.BuildServiceProvider();
|
||||||
}
|
}
|
||||||
class Bot
|
class Bot
|
||||||
@ -101,13 +105,15 @@ namespace PluralKit.Bot
|
|||||||
private CommandService _commands;
|
private CommandService _commands;
|
||||||
private ProxyService _proxy;
|
private ProxyService _proxy;
|
||||||
private Timer _updateTimer;
|
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._services = services;
|
||||||
this._client = client as DiscordShardedClient;
|
this._client = client as DiscordShardedClient;
|
||||||
this._commands = commands;
|
this._commands = commands;
|
||||||
this._proxy = proxy;
|
this._proxy = proxy;
|
||||||
|
_metrics = metrics;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task Init()
|
public async Task Init()
|
||||||
@ -142,6 +148,8 @@ namespace PluralKit.Bot
|
|||||||
|
|
||||||
private async Task CommandExecuted(Optional<CommandInfo> cmd, ICommandContext ctx, IResult _result)
|
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.
|
// TODO: refactor this entire block, it's fugly.
|
||||||
if (!_result.IsSuccess) {
|
if (!_result.IsSuccess) {
|
||||||
if (_result.Error == CommandError.Unsuccessful || _result.Error == CommandError.Exception) {
|
if (_result.Error == CommandError.Unsuccessful || _result.Error == CommandError.Exception) {
|
||||||
@ -168,6 +176,8 @@ namespace PluralKit.Bot
|
|||||||
|
|
||||||
private async Task MessageReceived(SocketMessage _arg)
|
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
|
// _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
|
// 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?
|
// 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 System.Threading.Tasks;
|
||||||
|
using App.Metrics;
|
||||||
|
using App.Metrics.Meter;
|
||||||
using Discord;
|
using Discord;
|
||||||
using Discord.Commands;
|
using Discord.Commands;
|
||||||
|
|
||||||
namespace PluralKit.Bot.Commands {
|
namespace PluralKit.Bot.Commands {
|
||||||
public class MiscCommands: ModuleBase<PKCommandContext> {
|
public class MiscCommands: ModuleBase<PKCommandContext> {
|
||||||
public BotConfig BotConfig { get; set; }
|
public BotConfig BotConfig { get; set; }
|
||||||
|
public IMetrics Metrics { get; set; }
|
||||||
|
|
||||||
[Command("invite")]
|
[Command("invite")]
|
||||||
[Alias("inv")]
|
[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("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("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.Net.Http;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using App.Metrics;
|
||||||
using Dapper;
|
using Dapper;
|
||||||
using Discord;
|
using Discord;
|
||||||
using Discord.Net;
|
using Discord.Net;
|
||||||
@ -35,8 +36,9 @@ namespace PluralKit.Bot
|
|||||||
private WebhookCacheService _webhookCache;
|
private WebhookCacheService _webhookCache;
|
||||||
private MessageStore _messageStorage;
|
private MessageStore _messageStorage;
|
||||||
private EmbedService _embeds;
|
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;
|
_client = client;
|
||||||
_webhookCache = webhookCache;
|
_webhookCache = webhookCache;
|
||||||
@ -44,6 +46,7 @@ namespace PluralKit.Bot
|
|||||||
_logger = logger;
|
_logger = logger;
|
||||||
_messageStorage = messageStorage;
|
_messageStorage = messageStorage;
|
||||||
_embeds = embeds;
|
_embeds = embeds;
|
||||||
|
_metrics = metrics;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ProxyMatch GetProxyTagMatch(string message, IEnumerable<ProxyDatabaseResult> potentials)
|
private ProxyMatch GetProxyTagMatch(string message, IEnumerable<ProxyDatabaseResult> potentials)
|
||||||
@ -159,15 +162,33 @@ namespace PluralKit.Bot
|
|||||||
}
|
}
|
||||||
|
|
||||||
ulong messageId;
|
ulong messageId;
|
||||||
if (attachment != null) {
|
|
||||||
using (var http = new HttpClient())
|
try
|
||||||
using (var stream = await http.GetStreamAsync(attachment.Url)) {
|
{
|
||||||
messageId = await client.SendFileAsync(stream, filename: attachment.Filename, text: text, username: username, avatarUrl: avatarUrl);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
else
|
||||||
messageId = await client.SendMessageAsync(text, username: username, avatarUrl: avatarUrl);
|
{
|
||||||
|
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
|
// 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)
|
// doesn't work if there's no permission to)
|
||||||
return messageId;
|
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>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="App.Metrics" Version="3.1.0" />
|
||||||
<PackageReference Include="Dapper" Version="1.60.6" />
|
<PackageReference Include="Dapper" Version="1.60.6" />
|
||||||
<PackageReference Include="Dapper.Contrib" Version="1.60.1" />
|
<PackageReference Include="Dapper.Contrib" Version="1.60.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.2.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.2.0" />
|
||||||
|
Loading…
Reference in New Issue
Block a user