Skip to content

Commit

Permalink
Added metrics to commands
Browse files Browse the repository at this point in the history
  • Loading branch information
sphexator committed Dec 29, 2023
1 parent de34ad3 commit e4add3c
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 14 deletions.
40 changes: 40 additions & 0 deletions Hanekawa.Application/Metrics.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using System.Diagnostics.Metrics;

namespace Hanekawa.Application;

public class Metrics<T> where T : class
{
private readonly Counter<long> _counter;
private readonly Histogram<double> _histogram;

public Metrics(IMeterFactory meterFactory)
{
var meter = meterFactory.Create(nameof(T));

_counter = meter.CreateCounter<long>($"hanekawa.{nameof(T)}.request.counter");
_histogram = meter.CreateHistogram<double>($"hanekawa.{nameof(T)}.request.duration");
}

public void IncrementCounter() => _counter.Add(1);
public TrackedDuration MeasureDuration() => new(TimeProvider.System, _histogram);
}

public class TrackedDuration : IDisposable
{
private readonly long _requestStartTime;
private readonly Histogram<double> _histogram;
private readonly TimeProvider _timeProvider;

public TrackedDuration(TimeProvider timeProvider, Histogram<double> histogram)
{
_requestStartTime = timeProvider.GetTimestamp();
_timeProvider = timeProvider;
_histogram = histogram;
}

public void Dispose()
{
var elapsed = _timeProvider.GetElapsedTime(_requestStartTime);
_histogram.Record(elapsed.TotalMilliseconds);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Disqord.Bot.Commands.Interaction;
using Disqord.Gateway;
using Disqord.Rest;
using Hanekawa.Application;
using Hanekawa.Application.Commands.Administration;
using Hanekawa.Application.Handlers.Warnings;
using Hanekawa.Bot.Mapper;
Expand All @@ -16,17 +17,23 @@ namespace Hanekawa.Bot.Commands.Slash.Administration;
[Description("Administration commands")]
public class AdministrationCommands : DiscordApplicationGuildModuleBase
{
private readonly IServiceProvider _serviceProvider;
public AdministrationCommands(IServiceProvider serviceProvider) => _serviceProvider = serviceProvider;
private readonly Metrics<AdministrationCommands> _metrics;

public AdministrationCommands(Metrics<AdministrationCommands> metrics)
{
_metrics = metrics;
}

[SlashCommand("ban")]
[RequireBotPermissions(Permissions.BanMembers)]
[RequireAuthorPermissions(Permissions.BanMembers)]
[Description("Bans a user from the server")]
public async Task<DiscordInteractionResponseCommandResult> BanAsync(AutoComplete<IMember> member, string reason)
{
_metrics.IncrementCounter();
using var _ = _metrics.MeasureDuration();
var user = member.Argument.Value;
var result = await _serviceProvider.GetRequiredService<AdministrationCommandService>()
var result = await Bot.Services.GetRequiredService<AdministrationCommandService>()
.BanUserAsync(user.ToDiscordMember(), Context.AuthorId, reason);
return Response(result.ToLocalInteractionMessageResponse());
}
Expand All @@ -37,8 +44,10 @@ public async Task<DiscordInteractionResponseCommandResult> BanAsync(AutoComplete
[Description("Unbans a user from the server")]
public async Task<DiscordInteractionResponseCommandResult> UnbanAsync(AutoComplete<IMember> member, string reason)
{
_metrics.IncrementCounter();
using var _ = _metrics.MeasureDuration();
var user = member.Argument.Value;
var result = await _serviceProvider.GetRequiredService<AdministrationCommandService>()
var result = await Bot.Services.GetRequiredService<AdministrationCommandService>()
.UnbanUserAsync(user.ToGuild(),user.Id, Context.AuthorId.RawValue, reason);
return Response(result.ToLocalInteractionMessageResponse());
}
Expand All @@ -49,8 +58,10 @@ public async Task<DiscordInteractionResponseCommandResult> UnbanAsync(AutoComple
[Description("Kick a user from the server")]
public async Task<DiscordInteractionResponseCommandResult> KickAsync(AutoComplete<IMember> member, string reason)
{
_metrics.IncrementCounter();
using var _ = _metrics.MeasureDuration();
var user = member.Argument.Value;
var result = await _serviceProvider.GetRequiredService<AdministrationCommandService>()
var result = await Bot.Services.GetRequiredService<AdministrationCommandService>()
.KickUserAsync(user.ToDiscordMember(), Context.AuthorId, reason);
return Response(result.ToLocalInteractionMessageResponse());
}
Expand All @@ -62,8 +73,10 @@ public async Task<DiscordInteractionResponseCommandResult> KickAsync(AutoComplet
public async Task<DiscordInteractionResponseCommandResult> MuteAsync(AutoComplete<IMember> member,
TimeSpan duration, string reason)
{
_metrics.IncrementCounter();
using var _ = _metrics.MeasureDuration();
var user = member.Argument.Value;
var result = await _serviceProvider.GetRequiredService<AdministrationCommandService>()
var result = await Bot.Services.GetRequiredService<AdministrationCommandService>()
.MuteUserAsync(user.ToDiscordMember(), Context.AuthorId, reason, duration);
return Response(result.ToLocalInteractionMessageResponse());
}
Expand All @@ -74,8 +87,10 @@ public async Task<DiscordInteractionResponseCommandResult> MuteAsync(AutoComplet
[Description("Un-mutes a user in the server")]
public async Task<DiscordInteractionResponseCommandResult> UnmuteAsync(AutoComplete<IMember> member, string reason)
{
_metrics.IncrementCounter();
using var _ = _metrics.MeasureDuration();
var user = member.Argument.Value;
var result = await _serviceProvider.GetRequiredService<AdministrationCommandService>()
var result = await Bot.Services.GetRequiredService<AdministrationCommandService>()
.UnmuteUserAsync(user.ToDiscordMember(), Context.AuthorId, reason);
return Response(result.ToLocalInteractionMessageResponse());
}
Expand All @@ -85,8 +100,10 @@ public async Task<DiscordInteractionResponseCommandResult> UnmuteAsync(AutoCompl
[Description("Warns a user in the server")]
public async Task<DiscordInteractionResponseCommandResult> WarnAsync(AutoComplete<IMember> member, string reason)
{
_metrics.IncrementCounter();
using var _ = _metrics.MeasureDuration();
var user = member.Argument.Value;
var response = await _serviceProvider.GetRequiredService<IMediator>()
var response = await Bot.Services.GetRequiredService<IMediator>()
.Send(new WarningReceived(user.ToDiscordMember(), reason, Context.AuthorId));
return Response(response.ToLocalInteractionMessageResponse());
}
Expand All @@ -96,8 +113,10 @@ public async Task<DiscordInteractionResponseCommandResult> WarnAsync(AutoComplet
[Description("Voids a warn from a user in the server")]
public async Task<DiscordInteractionResponseCommandResult> VoidWarnAsync(AutoComplete<IMember> member)
{
_metrics.IncrementCounter();
using var _ = _metrics.MeasureDuration();
var user = member.Argument.Value;
var response = await _serviceProvider.GetRequiredService<IMediator>()
var response = await Bot.Services.GetRequiredService<IMediator>()
.Send(new WarningClear(user.ToDiscordMember(), Context.AuthorId, "Voided by moderator"));
return Response(response.ToLocalInteractionMessageResponse());
}
Expand All @@ -107,8 +126,10 @@ public async Task<DiscordInteractionResponseCommandResult> VoidWarnAsync(AutoCom
[Description("List all warnings from a user in the server")]
public async Task<DiscordInteractionResponseCommandResult> WarnsAsync(AutoComplete<IMember> member)
{
_metrics.IncrementCounter();
using var _ = _metrics.MeasureDuration();
var user = member.Argument.Value;
var response = await _serviceProvider.GetRequiredService<IMediator>()
var response = await Bot.Services.GetRequiredService<IMediator>()
.Send(new WarningList(user.GuildId, user.Id));
return Response(response.ToLocalInteractionMessageResponse());
}
Expand All @@ -118,8 +139,10 @@ public async Task<DiscordInteractionResponseCommandResult> WarnsAsync(AutoComple
[Description("Clears all warnings from a user in the server")]
public async Task<DiscordInteractionResponseCommandResult> ClearWarnsAsync(AutoComplete<IMember> member)
{
_metrics.IncrementCounter();
using var _ = _metrics.MeasureDuration();
var user = member.Argument.Value;
var response = await _serviceProvider.GetRequiredService<IMediator>()
var response = await Bot.Services.GetRequiredService<IMediator>()
.Send(new WarningClear(user.ToDiscordMember(), Context.AuthorId,
"Cleared by moderator", true));
return Response(response.ToLocalInteractionMessageResponse());
Expand All @@ -131,6 +154,8 @@ public async Task<DiscordInteractionResponseCommandResult> ClearWarnsAsync(AutoC
[Description("Prunes a number of messages from a channel")]
public async Task<DiscordInteractionResponseCommandResult> Prune(int messageCount = 100)
{
_metrics.IncrementCounter();
using var _ = _metrics.MeasureDuration();
var channel = Bot.GetChannel(Context.GuildId, Context.ChannelId) as ITextChannel;
var messagesAsync = await channel.FetchMessagesAsync(messageCount);
var messageIds = new ulong[messagesAsync.Count];
Expand All @@ -142,7 +167,7 @@ public async Task<DiscordInteractionResponseCommandResult> Prune(int messageCoun
messageIds[i] = msg.Id.RawValue;
}

var result= await _serviceProvider.GetRequiredService<AdministrationCommandService>()
var result= await Bot.Services.GetRequiredService<AdministrationCommandService>()
.PruneAsync(Context.GuildId, Context.ChannelId,
messageIds, Context.AuthorId,
"");
Expand Down
16 changes: 16 additions & 0 deletions Hanekawa.Bot/Commands/Slash/Club/ClubCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Disqord.Bot.Commands;
using Disqord.Bot.Commands.Application;
using Disqord.Bot.Commands.Interaction;
using Hanekawa.Application;
using Hanekawa.Application.Interfaces.Commands;
using Hanekawa.Bot.Mapper;
using Qmmands;
Expand All @@ -11,10 +12,17 @@ namespace Hanekawa.Bot.Commands.Slash.Club;
[SlashGroup("club")]
public class ClubCommands : DiscordApplicationGuildModuleBase
{
private readonly Metrics<ClubCommands> _metrics;

public ClubCommands(Metrics<ClubCommands> metrics)
=> _metrics = metrics;

[SlashCommand("create")]
[Description("Create a club")]
public async Task<DiscordInteractionResponseCommandResult> Create(string name, string description)
{
using var _ = _metrics.MeasureDuration();
_metrics.IncrementCounter();
await using var scope = Bot.Services.CreateAsyncScope();
var service = scope.ServiceProvider.GetRequiredService<IClubCommandService>();
var response = await service.Create(Context.GuildId, name, description, Context.AuthorId);
Expand All @@ -36,6 +44,8 @@ public async Task<DiscordInteractionResponseCommandResult> Delete(string name)
[Description("List all clubs")]
public async Task<DiscordInteractionResponseCommandResult> List()
{
using var _ = _metrics.MeasureDuration();
_metrics.IncrementCounter();
await using var scope = Bot.Services.CreateAsyncScope();
var service = scope.ServiceProvider.GetRequiredService<IClubCommandService>();
var response = await service.List(Context.GuildId);
Expand All @@ -46,6 +56,8 @@ public async Task<DiscordInteractionResponseCommandResult> List()
[Description("Join a club")]
public async Task<DiscordInteractionResponseCommandResult> Join(string name)
{
using var _ = _metrics.MeasureDuration();
_metrics.IncrementCounter();
await using var scope = Bot.Services.CreateAsyncScope();
var service = scope.ServiceProvider.GetRequiredService<IClubCommandService>();
var response = await service.Join(Context.GuildId, name, Context.AuthorId);
Expand All @@ -56,6 +68,8 @@ public async Task<DiscordInteractionResponseCommandResult> Join(string name)
[Description("Leave a club")]
public async Task<DiscordInteractionResponseCommandResult> Leave(string name)
{
using var _ = _metrics.MeasureDuration();
_metrics.IncrementCounter();
await using var scope = Bot.Services.CreateAsyncScope();
var service = scope.ServiceProvider.GetRequiredService<IClubCommandService>();
var response = await service.Leave(Context.GuildId, name, Context.AuthorId);
Expand All @@ -66,6 +80,8 @@ public async Task<DiscordInteractionResponseCommandResult> Leave(string name)
[Description("Get club info")]
public async Task<DiscordInteractionResponseCommandResult> Info(string name)
{
using var _ = _metrics.MeasureDuration();
_metrics.IncrementCounter();
await using var scope = Bot.Services.CreateAsyncScope();
var service = scope.ServiceProvider.GetRequiredService<IClubCommandService>();
var response = await service.Info(Context.GuildId, name);
Expand Down
18 changes: 18 additions & 0 deletions Hanekawa.Bot/Commands/Slash/Setting/GreetCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Disqord.Bot.Commands.Interaction;
using Disqord.Extensions.Interactivity.Menus.Paged;
using Disqord.Gateway;
using Hanekawa.Application;
using Hanekawa.Application.Interfaces.Commands;
using Hanekawa.Bot.Mapper;
using Hanekawa.Entities.Configs;
Expand All @@ -17,10 +18,17 @@ namespace Hanekawa.Bot.Commands.Slash.Setting;
[RequireAuthorPermissions(Permissions.ManageChannels)]
public class GreetCommands : DiscordApplicationGuildModuleBase
{
private readonly Metrics<GreetCommands> _metrics;

public GreetCommands(Metrics<GreetCommands> metrics)
=> _metrics = metrics;

[SlashCommand("channel")]
[Description("Set the greet channel")]
public async Task<DiscordInteractionResponseCommandResult> Set(IChannel channel)
{
using var _ = _metrics.MeasureDuration();
_metrics.IncrementCounter();
if (channel is not TransientInteractionChannel { Type: ChannelType.Text } textChannel)
return Response(Localization.ChannelMustBeTextChannel);
await using var scope = Bot.Services.CreateAsyncScope();
Expand All @@ -33,6 +41,8 @@ public async Task<DiscordInteractionResponseCommandResult> Set(IChannel channel)
[Description("Set the greet message")]
public async Task<DiscordInteractionResponseCommandResult> Set(string message)
{
using var _ = _metrics.MeasureDuration();
_metrics.IncrementCounter();
await using var scope = Bot.Services.CreateAsyncScope();
var service = scope.ServiceProvider.GetRequiredService<IGreetService>();
var response = await service.SetMessage(Context.GuildId, message);
Expand All @@ -43,6 +53,8 @@ public async Task<DiscordInteractionResponseCommandResult> Set(string message)
[Description("Set the greet image url")]
public async Task<DiscordInteractionResponseCommandResult> SetImage(string url)
{
using var _ = _metrics.MeasureDuration();
_metrics.IncrementCounter();
await using var scope = Bot.Services.CreateAsyncScope();
var service = scope.ServiceProvider.GetRequiredService<IGreetService>();
var response = await service.SetImage(Context.GuildId, url, Context.AuthorId);
Expand All @@ -53,6 +65,8 @@ public async Task<DiscordInteractionResponseCommandResult> SetImage(string url)
[Description("List the greet images")]
public async Task<IResult> ListImages()
{
using var _ = _metrics.MeasureDuration();
_metrics.IncrementCounter();
await using var scope = Bot.Services.CreateAsyncScope();
var service = scope.ServiceProvider.GetRequiredService<IGreetService>();
var response = await service.ListImages(Context.GuildId);
Expand All @@ -76,6 +90,8 @@ public async Task<IResult> ListImages()
[Description("Remove the greet image")]
public async Task<DiscordInteractionResponseCommandResult> RemoveImage(int id)
{
using var _ = _metrics.MeasureDuration();
_metrics.IncrementCounter();
await using var scope = Bot.Services.CreateAsyncScope();
var service = scope.ServiceProvider.GetRequiredService<IGreetService>();
var response = await service.RemoveImage(Context.GuildId, id);
Expand All @@ -88,6 +104,8 @@ public async Task<DiscordInteractionResponseCommandResult> RemoveImage(int id)
[Description("Toggle the greet image")]
public async Task<DiscordInteractionResponseCommandResult> ToggleImage()
{
using var _ = _metrics.MeasureDuration();
_metrics.IncrementCounter();
await using var scope = Bot.Services.CreateAsyncScope();
var service = scope.ServiceProvider.GetRequiredService<IGreetService>();
var response = await service.ToggleImage(Context.GuildId);
Expand Down
14 changes: 14 additions & 0 deletions Hanekawa.Bot/Commands/Slash/Setting/LevelCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Disqord.Bot.Commands.Interaction;
using Disqord.Extensions.Interactivity.Menus.Paged;
using Disqord.Gateway;
using Hanekawa.Application;
using Hanekawa.Application.Interfaces.Commands;
using Hanekawa.Entities.Levels;
using Hanekawa.Localize;
Expand All @@ -16,10 +17,17 @@ namespace Hanekawa.Bot.Commands.Slash.Setting;
[RequireAuthorPermissions(Permissions.ManageGuild)]
public class LevelCommands : DiscordApplicationGuildModuleBase
{
private readonly Metrics<LevelCommands> _metrics;

public LevelCommands(Metrics<LevelCommands> metrics)
=> _metrics = metrics;

[SlashCommand("add")]
[Description("Add a level role")]
public async Task<DiscordInteractionResponseCommandResult> Add(int level, IRole role)
{
using var _ = _metrics.MeasureDuration();
_metrics.IncrementCounter();
await using var scope = Bot.Services.CreateAsyncScope();
var service = scope.ServiceProvider.GetRequiredService<ILevelCommandService>();
await service.AddAsync(Context.GuildId, role.Id, level, Context.CancellationToken);
Expand All @@ -30,6 +38,8 @@ public async Task<DiscordInteractionResponseCommandResult> Add(int level, IRole
[Description("Remove a level role")]
public async Task<DiscordInteractionResponseCommandResult> Remove(IRole role)
{
using var _ = _metrics.MeasureDuration();
_metrics.IncrementCounter();
await using var scope = Bot.Services.CreateAsyncScope();
var service = scope.ServiceProvider.GetRequiredService<ILevelCommandService>();
await service.RemoveAsync(Context.GuildId, role.Id, Context.CancellationToken);
Expand All @@ -40,6 +50,8 @@ public async Task<DiscordInteractionResponseCommandResult> Remove(IRole role)
[Description("List all level roles")]
public async Task<IDiscordCommandResult> List()
{
using var _ = _metrics.MeasureDuration();
_metrics.IncrementCounter();
await using var scope = Bot.Services.CreateAsyncScope();
var service = scope.ServiceProvider.GetRequiredService<ILevelCommandService>();
var response = await service.ListAsync(Context.GuildId, Context.CancellationToken);
Expand All @@ -51,6 +63,8 @@ public async Task<IDiscordCommandResult> List()
[Description("Modify a level role")]
public async Task<DiscordInteractionResponseCommandResult> Modify(int level, IRole role)
{
using var _ = _metrics.MeasureDuration();
_metrics.IncrementCounter();
await using var scope = Bot.Services.CreateAsyncScope();
var service = scope.ServiceProvider.GetRequiredService<ILevelCommandService>();
await service.ModifyAsync(Context.GuildId, role.Id, level, Context.CancellationToken);
Expand Down
Loading

0 comments on commit e4add3c

Please sign in to comment.