2019-07-16 21:34:22 +00:00
using System.Collections.Generic ;
2019-07-18 15:13:42 +00:00
using System.Diagnostics ;
2019-07-16 21:34:22 +00:00
using System.Threading.Tasks ;
using App.Metrics ;
2020-04-17 21:10:01 +00:00
2020-06-13 17:15:29 +00:00
using Dapper ;
2020-04-17 21:10:01 +00:00
using DSharpPlus ;
using DSharpPlus.Entities ;
2019-07-18 15:13:42 +00:00
using NodaTime.Extensions ;
2019-07-16 21:34:22 +00:00
using PluralKit.Core ;
2020-02-12 14:16:19 +00:00
2019-07-18 15:13:42 +00:00
using Serilog ;
2019-07-16 21:34:22 +00:00
namespace PluralKit.Bot
{
public class PeriodicStatCollector
{
private DiscordShardedClient _client ;
private IMetrics _metrics ;
2019-12-22 11:50:47 +00:00
private CpuStatService _cpu ;
2019-07-16 21:34:22 +00:00
2020-06-13 17:36:43 +00:00
private IDatabase _db ;
2019-07-16 21:34:22 +00:00
2019-07-21 02:15:47 +00:00
private WebhookCacheService _webhookCache ;
2019-08-11 20:56:20 +00:00
private DbConnectionCountHolder _countHolder ;
2019-07-18 15:13:42 +00:00
private ILogger _logger ;
2020-06-13 17:36:43 +00:00
public PeriodicStatCollector ( DiscordShardedClient client , IMetrics metrics , ILogger logger , WebhookCacheService webhookCache , DbConnectionCountHolder countHolder , CpuStatService cpu , IDatabase db )
2019-07-16 21:34:22 +00:00
{
2020-04-17 21:10:01 +00:00
_client = client ;
2019-07-16 21:34:22 +00:00
_metrics = metrics ;
2019-07-21 02:15:47 +00:00
_webhookCache = webhookCache ;
2019-08-11 20:56:20 +00:00
_countHolder = countHolder ;
2019-12-22 11:50:47 +00:00
_cpu = cpu ;
2020-06-13 17:15:29 +00:00
_db = db ;
2019-07-18 15:13:42 +00:00
_logger = logger . ForContext < PeriodicStatCollector > ( ) ;
2019-07-16 21:34:22 +00:00
}
public async Task CollectStats ( )
{
2019-07-18 15:13:42 +00:00
var stopwatch = new Stopwatch ( ) ;
stopwatch . Start ( ) ;
2019-07-16 21:34:22 +00:00
// Aggregate guild/channel stats
2020-04-17 21:10:01 +00:00
var guildCount = 0 ;
var channelCount = 0 ;
// No LINQ today, sorry
foreach ( var shard in _client . ShardClients . Values )
{
guildCount + = shard . Guilds . Count ;
foreach ( var guild in shard . Guilds . Values )
foreach ( var channel in guild . Channels . Values )
if ( channel . Type = = ChannelType . Text )
channelCount + + ;
}
_metrics . Measure . Gauge . SetValue ( BotMetrics . Guilds , guildCount ) ;
_metrics . Measure . Gauge . SetValue ( BotMetrics . Channels , channelCount ) ;
2019-07-16 21:34:22 +00:00
// Aggregate member stats
var usersKnown = new HashSet < ulong > ( ) ;
var usersOnline = new HashSet < ulong > ( ) ;
2020-04-17 21:10:01 +00:00
foreach ( var shard in _client . ShardClients . Values )
foreach ( var guild in shard . Guilds . Values )
foreach ( var user in guild . Members . Values )
2019-07-16 21:34:22 +00:00
{
usersKnown . Add ( user . Id ) ;
2020-04-30 21:55:12 +00:00
if ( user . Presence ? . Status = = UserStatus . Online )
usersOnline . Add ( user . Id ) ;
2019-07-16 21:34:22 +00:00
}
_metrics . Measure . Gauge . SetValue ( BotMetrics . MembersTotal , usersKnown . Count ) ;
_metrics . Measure . Gauge . SetValue ( BotMetrics . MembersOnline , usersOnline . Count ) ;
// Aggregate DB stats
2020-08-25 16:26:30 +00:00
var counts = await _db . Execute ( c = > c . QueryFirstAsync < Counts > ( "select (select count(*) from systems) as systems, (select count(*) from members) as members, (select count(*) from switches) as switches, (select count(*) from messages) as messages, (select count(*) from groups) as groups" ) ) ;
2020-06-13 17:15:29 +00:00
_metrics . Measure . Gauge . SetValue ( CoreMetrics . SystemCount , counts . Systems ) ;
_metrics . Measure . Gauge . SetValue ( CoreMetrics . MemberCount , counts . Members ) ;
_metrics . Measure . Gauge . SetValue ( CoreMetrics . SwitchCount , counts . Switches ) ;
_metrics . Measure . Gauge . SetValue ( CoreMetrics . MessageCount , counts . Messages ) ;
2020-08-25 16:26:30 +00:00
_metrics . Measure . Gauge . SetValue ( CoreMetrics . GroupCount , counts . Groups ) ;
2019-07-20 22:01:02 +00:00
// Process info
var process = Process . GetCurrentProcess ( ) ;
_metrics . Measure . Gauge . SetValue ( CoreMetrics . ProcessPhysicalMemory , process . WorkingSet64 ) ;
_metrics . Measure . Gauge . SetValue ( CoreMetrics . ProcessVirtualMemory , process . VirtualMemorySize64 ) ;
_metrics . Measure . Gauge . SetValue ( CoreMetrics . ProcessPrivateMemory , process . PrivateMemorySize64 ) ;
_metrics . Measure . Gauge . SetValue ( CoreMetrics . ProcessThreads , process . Threads . Count ) ;
_metrics . Measure . Gauge . SetValue ( CoreMetrics . ProcessHandles , process . HandleCount ) ;
2019-12-22 11:50:47 +00:00
_metrics . Measure . Gauge . SetValue ( CoreMetrics . CpuUsage , await _cpu . EstimateCpuUsage ( ) ) ;
2019-07-21 02:15:47 +00:00
2019-08-11 20:56:20 +00:00
// Database info
_metrics . Measure . Gauge . SetValue ( CoreMetrics . DatabaseConnections , _countHolder . ConnectionCount ) ;
2019-07-21 02:15:47 +00:00
// Other shiz
_metrics . Measure . Gauge . SetValue ( BotMetrics . WebhookCacheSize , _webhookCache . CacheSize ) ;
2019-07-18 15:13:42 +00:00
stopwatch . Stop ( ) ;
_logger . Information ( "Updated metrics in {Time}" , stopwatch . ElapsedDuration ( ) ) ;
2019-07-16 21:34:22 +00:00
}
2020-06-13 17:15:29 +00:00
public class Counts
{
public int Systems { get ; }
public int Members { get ; }
public int Switches { get ; }
public int Messages { get ; }
2020-08-25 16:26:30 +00:00
public int Groups { get ; }
2020-06-13 17:15:29 +00:00
}
2019-07-16 21:34:22 +00:00
}
}