Add basic InfluxDB reporter
This commit is contained in:
		@@ -18,6 +18,7 @@ using Microsoft.Extensions.DependencyInjection;
 | 
				
			|||||||
using NodaTime;
 | 
					using NodaTime;
 | 
				
			||||||
using Npgsql;
 | 
					using Npgsql;
 | 
				
			||||||
using Npgsql.Logging;
 | 
					using Npgsql.Logging;
 | 
				
			||||||
 | 
					using PluralKit.Core;
 | 
				
			||||||
using Sentry;
 | 
					using Sentry;
 | 
				
			||||||
using Sentry.Extensibility;
 | 
					using Sentry.Extensibility;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -94,7 +95,15 @@ namespace PluralKit.Bot
 | 
				
			|||||||
                .AddTransient<MessageStore>()
 | 
					                .AddTransient<MessageStore>()
 | 
				
			||||||
                .AddTransient<SwitchStore>()
 | 
					                .AddTransient<SwitchStore>()
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
                .AddSingleton<IMetrics>(_ => AppMetrics.CreateDefaultBuilder().Build())
 | 
					                .AddSingleton<IMetrics>(svc =>
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    var cfg = svc.GetRequiredService<CoreConfig>();
 | 
				
			||||||
 | 
					                    var builder = AppMetrics.CreateDefaultBuilder();
 | 
				
			||||||
 | 
					                    if (cfg.InfluxUrl != null && cfg.InfluxDb != null)
 | 
				
			||||||
 | 
					                        builder.Report.ToInfluxDb(cfg.InfluxUrl, cfg.InfluxDb);
 | 
				
			||||||
 | 
					                    return builder.Build();
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                .AddSingleton<PeriodicStatCollector>()
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
                .BuildServiceProvider();
 | 
					                .BuildServiceProvider();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -106,14 +115,16 @@ namespace PluralKit.Bot
 | 
				
			|||||||
        private ProxyService _proxy;
 | 
					        private ProxyService _proxy;
 | 
				
			||||||
        private Timer _updateTimer;
 | 
					        private Timer _updateTimer;
 | 
				
			||||||
        private IMetrics _metrics;
 | 
					        private IMetrics _metrics;
 | 
				
			||||||
 | 
					        private PeriodicStatCollector _collector;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public Bot(IServiceProvider services, IDiscordClient client, CommandService commands, ProxyService proxy, IMetrics metrics)
 | 
					        public Bot(IServiceProvider services, IDiscordClient client, CommandService commands, ProxyService proxy, IMetrics metrics, PeriodicStatCollector collector)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            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;
 | 
					            _metrics = metrics;
 | 
				
			||||||
 | 
					            _collector = collector;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        public async Task Init()
 | 
					        public async Task Init()
 | 
				
			||||||
@@ -132,18 +143,28 @@ namespace PluralKit.Bot
 | 
				
			|||||||
            _client.MessageDeleted += async (message, channel) => _proxy.HandleMessageDeletedAsync(message, channel).CatchException(HandleRuntimeError);
 | 
					            _client.MessageDeleted += async (message, channel) => _proxy.HandleMessageDeletedAsync(message, channel).CatchException(HandleRuntimeError);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Method called every 60 seconds
 | 
				
			||||||
        private async Task UpdatePeriodic()
 | 
					        private async Task UpdatePeriodic()
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            // Method called every 60 seconds
 | 
					            // Change bot status
 | 
				
			||||||
            await _client.SetGameAsync($"pk;help | in {_client.Guilds.Count} servers");
 | 
					            await _client.SetGameAsync($"pk;help | in {_client.Guilds.Count} servers");
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            await _collector.CollectStats();
 | 
				
			||||||
 | 
					            await Task.WhenAll(((IMetricsRoot) _metrics).ReportRunner.RunAllAsync());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private async Task ShardReady(DiscordSocketClient shardClient)
 | 
					        private async Task ShardReady(DiscordSocketClient shardClient)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            //_updateTimer = new Timer((_) => UpdatePeriodic(), null, 0, 60*1000);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            Console.WriteLine($"Shard #{shardClient.ShardId} connected to {shardClient.Guilds.Sum(g => g.Channels.Count)} channels in {shardClient.Guilds.Count} guilds.");
 | 
					            Console.WriteLine($"Shard #{shardClient.ShardId} connected to {shardClient.Guilds.Sum(g => g.Channels.Count)} channels in {shardClient.Guilds.Count} guilds.");
 | 
				
			||||||
            //Console.WriteLine($"PluralKit started as {_client.CurrentUser.Username}#{_client.CurrentUser.Discriminator} ({_client.CurrentUser.Id}).");
 | 
					
 | 
				
			||||||
 | 
					            if (shardClient.ShardId == 0)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                _updateTimer = new Timer((_) => UpdatePeriodic().CatchException(HandleRuntimeError), null, 0, 60*1000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                Console.WriteLine(
 | 
				
			||||||
 | 
					                    $"PluralKit started as {_client.CurrentUser.Username}#{_client.CurrentUser.Discriminator} ({_client.CurrentUser.Id}).");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private async Task CommandExecuted(Optional<CommandInfo> cmd, ICommandContext ctx, IResult _result)
 | 
					        private async Task CommandExecuted(Optional<CommandInfo> cmd, ICommandContext ctx, IResult _result)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,7 +9,10 @@ namespace PluralKit.Bot
 | 
				
			|||||||
        public static MeterOptions MessagesReceived => new MeterOptions {Name = "Messages processed", MeasurementUnit = Unit.Events, RateUnit = TimeUnit.Seconds, Context = "Bot"};
 | 
					        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 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 MeterOptions CommandsRun => new MeterOptions {Name = "Commands run", MeasurementUnit = Unit.Commands, RateUnit = TimeUnit.Seconds, Context = "Bot"};
 | 
				
			||||||
 | 
					        public static GaugeOptions MembersTotal => new GaugeOptions {Name = "Members total", MeasurementUnit = Unit.None, Context = "Bot"};
 | 
				
			||||||
        public static GaugeOptions MembersOnline => new GaugeOptions {Name = "Members online", MeasurementUnit = Unit.None, Context = "Bot"};
 | 
					        public static GaugeOptions MembersOnline => new GaugeOptions {Name = "Members online", MeasurementUnit = Unit.None, Context = "Bot"};
 | 
				
			||||||
 | 
					        public static GaugeOptions Guilds => new GaugeOptions {Name = "Guilds", MeasurementUnit = Unit.None, Context = "Bot"};
 | 
				
			||||||
 | 
					        public static GaugeOptions Channels => new GaugeOptions {Name = "Channels", MeasurementUnit = Unit.None, Context = "Bot"};
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        public static GaugeOptions DatabasePoolSize => new GaugeOptions { Name = "Database pool size", Context = "Database" };
 | 
					        public static GaugeOptions DatabasePoolSize => new GaugeOptions { Name = "Database pool size", Context = "Database" };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										57
									
								
								PluralKit.Bot/Services/PeriodicStatCollector.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								PluralKit.Bot/Services/PeriodicStatCollector.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,57 @@
 | 
				
			|||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using App.Metrics;
 | 
				
			||||||
 | 
					using Discord;
 | 
				
			||||||
 | 
					using Discord.WebSocket;
 | 
				
			||||||
 | 
					using PluralKit.Core;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace PluralKit.Bot
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public class PeriodicStatCollector
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        private DiscordShardedClient _client;
 | 
				
			||||||
 | 
					        private IMetrics _metrics;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private SystemStore _systems;
 | 
				
			||||||
 | 
					        private MemberStore _members;
 | 
				
			||||||
 | 
					        private SwitchStore _switches;
 | 
				
			||||||
 | 
					        private MessageStore _messages;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public PeriodicStatCollector(IDiscordClient client, IMetrics metrics, SystemStore systems, MemberStore members, SwitchStore switches, MessageStore messages)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            _client = (DiscordShardedClient) client;
 | 
				
			||||||
 | 
					            _metrics = metrics;
 | 
				
			||||||
 | 
					            _systems = systems;
 | 
				
			||||||
 | 
					            _members = members;
 | 
				
			||||||
 | 
					            _switches = switches;
 | 
				
			||||||
 | 
					            _messages = messages;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async Task CollectStats()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            // Aggregate guild/channel stats
 | 
				
			||||||
 | 
					            _metrics.Measure.Gauge.SetValue(BotMetrics.Guilds, _client.Guilds.Count);
 | 
				
			||||||
 | 
					            _metrics.Measure.Gauge.SetValue(BotMetrics.Channels, _client.Guilds.Sum(g => g.TextChannels.Count));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Aggregate member stats
 | 
				
			||||||
 | 
					            var usersKnown = new HashSet<ulong>();
 | 
				
			||||||
 | 
					            var usersOnline = new HashSet<ulong>();
 | 
				
			||||||
 | 
					            foreach (var guild in _client.Guilds)
 | 
				
			||||||
 | 
					            foreach (var user in guild.Users)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                usersKnown.Add(user.Id);
 | 
				
			||||||
 | 
					                if (user.Status == UserStatus.Online) usersOnline.Add(user.Id);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            _metrics.Measure.Gauge.SetValue(BotMetrics.MembersTotal, usersKnown.Count);
 | 
				
			||||||
 | 
					            _metrics.Measure.Gauge.SetValue(BotMetrics.MembersOnline, usersOnline.Count);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // Aggregate DB stats
 | 
				
			||||||
 | 
					            _metrics.Measure.Gauge.SetValue(CoreMetrics.SystemCount, await _systems.Count());
 | 
				
			||||||
 | 
					            _metrics.Measure.Gauge.SetValue(CoreMetrics.MemberCount, await _members.Count());
 | 
				
			||||||
 | 
					            _metrics.Measure.Gauge.SetValue(CoreMetrics.SwitchCount, await _switches.Count());
 | 
				
			||||||
 | 
					            _metrics.Measure.Gauge.SetValue(CoreMetrics.MessageCount, await _messages.Count());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -4,5 +4,7 @@ namespace PluralKit
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        public string Database { get; set; }
 | 
					        public string Database { get; set; }
 | 
				
			||||||
        public string SentryUrl { get; set; }
 | 
					        public string SentryUrl { get; set; }
 | 
				
			||||||
 | 
					        public string InfluxUrl { get; set; }
 | 
				
			||||||
 | 
					        public string InfluxDb { get; set; }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -6,6 +6,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    <ItemGroup>
 | 
					    <ItemGroup>
 | 
				
			||||||
      <PackageReference Include="App.Metrics" Version="3.1.0" />
 | 
					      <PackageReference Include="App.Metrics" Version="3.1.0" />
 | 
				
			||||||
 | 
					      <PackageReference Include="App.Metrics.Reporting.InfluxDB" 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" />
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -72,6 +72,12 @@ namespace PluralKit {
 | 
				
			|||||||
            using (var conn = await _conn.Obtain())
 | 
					            using (var conn = await _conn.Obtain())
 | 
				
			||||||
                return await conn.QueryAsync<ulong>("select uid from accounts where system = @Id", new { Id = system.Id });
 | 
					                return await conn.QueryAsync<ulong>("select uid from accounts where system = @Id", new { Id = system.Id });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        public async Task<ulong> Count()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            using (var conn = await _conn.Obtain())
 | 
				
			||||||
 | 
					                return await conn.ExecuteScalarAsync<ulong>("select count(id) from systems");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public class MemberStore {
 | 
					    public class MemberStore {
 | 
				
			||||||
@@ -138,6 +144,12 @@ namespace PluralKit {
 | 
				
			|||||||
            using (var conn = await _conn.Obtain())
 | 
					            using (var conn = await _conn.Obtain())
 | 
				
			||||||
                return await conn.ExecuteScalarAsync<int>("select count(*) from members where system = @Id", system);
 | 
					                return await conn.ExecuteScalarAsync<int>("select count(*) from members where system = @Id", system);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        public async Task<ulong> Count()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            using (var conn = await _conn.Obtain())
 | 
				
			||||||
 | 
					                return await conn.ExecuteScalarAsync<ulong>("select count(id) from members");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public class MessageStore {
 | 
					    public class MessageStore {
 | 
				
			||||||
@@ -185,6 +197,12 @@ namespace PluralKit {
 | 
				
			|||||||
            using (var conn = await _conn.Obtain())
 | 
					            using (var conn = await _conn.Obtain())
 | 
				
			||||||
                await conn.ExecuteAsync("delete from messages where mid = @Id", new { Id = id });
 | 
					                await conn.ExecuteAsync("delete from messages where mid = @Id", new { Id = id });
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        public async Task<ulong> Count()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            using (var conn = await _conn.Obtain())
 | 
				
			||||||
 | 
					                return await conn.ExecuteScalarAsync<ulong>("select count(mid) from messages");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public class SwitchStore
 | 
					    public class SwitchStore
 | 
				
			||||||
@@ -258,6 +276,12 @@ namespace PluralKit {
 | 
				
			|||||||
                await conn.ExecuteAsync("delete from switches where id = @Id", new {Id = sw.Id});
 | 
					                await conn.ExecuteAsync("delete from switches where id = @Id", new {Id = sw.Id});
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
 | 
					        public async Task<ulong> Count()
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            using (var conn = await _conn.Obtain())
 | 
				
			||||||
 | 
					                return await conn.ExecuteScalarAsync<ulong>("select count(id) from switches");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
        public struct SwitchListEntry
 | 
					        public struct SwitchListEntry
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            public ICollection<PKMember> Members;
 | 
					            public ICollection<PKMember> Members;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,10 +5,13 @@ services:
 | 
				
			|||||||
    entrypoint: ["dotnet", "run", "--project", "PluralKit.Bot"]
 | 
					    entrypoint: ["dotnet", "run", "--project", "PluralKit.Bot"]
 | 
				
			||||||
    environment:
 | 
					    environment:
 | 
				
			||||||
      - "PluralKit:Database=Host=db;Username=postgres;Password=postgres;Database=postgres"
 | 
					      - "PluralKit:Database=Host=db;Username=postgres;Password=postgres;Database=postgres"
 | 
				
			||||||
 | 
					      - "PluralKit:InfluxUrl=http://influx:8086"
 | 
				
			||||||
 | 
					      - "PluralKit:InfluxDb=pluralkit
 | 
				
			||||||
    volumes:
 | 
					    volumes:
 | 
				
			||||||
      - "./pluralkit.conf:/app/pluralkit.conf:ro"
 | 
					      - "./pluralkit.conf:/app/pluralkit.conf:ro"
 | 
				
			||||||
    links:
 | 
					    links:
 | 
				
			||||||
      - db
 | 
					      - db
 | 
				
			||||||
 | 
					      - influx
 | 
				
			||||||
    restart: always
 | 
					    restart: always
 | 
				
			||||||
  web:
 | 
					  web:
 | 
				
			||||||
    build: .
 | 
					    build: .
 | 
				
			||||||
@@ -35,6 +38,14 @@ services:
 | 
				
			|||||||
    volumes:
 | 
					    volumes:
 | 
				
			||||||
      - "db_data:/var/lib/postgresql/data"
 | 
					      - "db_data:/var/lib/postgresql/data"
 | 
				
			||||||
    restart: always
 | 
					    restart: always
 | 
				
			||||||
 | 
					  influx:
 | 
				
			||||||
 | 
					    image: influxdb:alpine
 | 
				
			||||||
 | 
					    volumes:
 | 
				
			||||||
 | 
					      - "influx_data:/var/lib/influxdb"
 | 
				
			||||||
 | 
					    ports:
 | 
				
			||||||
 | 
					      - 2839:8086
 | 
				
			||||||
 | 
					    restart: always
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
volumes:
 | 
					volumes:
 | 
				
			||||||
  db_data:
 | 
					  db_data:
 | 
				
			||||||
 | 
					  influx_data:
 | 
				
			||||||
		Reference in New Issue
	
	Block a user