166 lines
5.2 KiB
Dart
166 lines
5.2 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:nyxx/nyxx.dart';
|
|
import 'package:prometheus_client/prometheus_client.dart';
|
|
import 'package:prometheus_client_shelf/shelf_handler.dart';
|
|
import 'package:shelf/shelf.dart';
|
|
import 'package:shelf/shelf_io.dart' as io;
|
|
import 'package:shelf_router/shelf_router.dart';
|
|
import 'package:prometheus_client_shelf/shelf_metrics.dart' as shelf_metrics;
|
|
import 'package:prometheus_client/runtime_metrics.dart' as runtime_metrics;
|
|
|
|
// final logger = Logger('Analytics');
|
|
|
|
/// A global instance of the [Analytics] plugin.
|
|
final analytics = Analytics();
|
|
|
|
class Analytics extends NyxxPlugin<NyxxRest> {
|
|
@override
|
|
String get name => 'Analytics';
|
|
|
|
/// Whether to include runtime metrics in the analytics.
|
|
final bool includeDefaultMetrics;
|
|
|
|
/// The port to bind the analytics server to.
|
|
final int port;
|
|
|
|
/// The analytics route.
|
|
final String route;
|
|
|
|
/// The refresh interval for the analytics.
|
|
final Duration refreshInterval;
|
|
|
|
/// Custom gauges for the analytics. It's up to you to update these.
|
|
final List<Gauge> gauges;
|
|
|
|
/// Custom counters for the analytics.
|
|
final List<Counter> counters;
|
|
|
|
/// Custom summaries for the analytics.
|
|
final List<Summary> summaries;
|
|
|
|
/// Custom histograms for the analytics.
|
|
final List<Histogram> histograms;
|
|
|
|
Analytics({
|
|
this.includeDefaultMetrics = true,
|
|
this.port = 4242,
|
|
this.route = '/analytics',
|
|
this.refreshInterval = const Duration(seconds: 5),
|
|
this.gauges = const [],
|
|
this.counters = const [],
|
|
this.summaries = const [],
|
|
this.histograms = const [],
|
|
});
|
|
|
|
@override
|
|
Future<void> beforeConnect(
|
|
ApiOptions apiOptions,
|
|
ClientOptions clientOptions,
|
|
) async {
|
|
final router = Router();
|
|
|
|
router.get('/', (req) => Response.found(route));
|
|
router.get(route, prometheusHandler());
|
|
|
|
final pipeline = const Pipeline();
|
|
|
|
if (includeDefaultMetrics) {
|
|
runtime_metrics.register();
|
|
pipeline.addMiddleware(shelf_metrics.register());
|
|
}
|
|
|
|
final server = await io.serve(
|
|
pipeline.addHandler(router.call),
|
|
'0.0.0.0',
|
|
port,
|
|
);
|
|
|
|
// this.s
|
|
|
|
logger.info('Serving analytics at http://${server.address.host}:${server.port}$route');
|
|
}
|
|
|
|
@override
|
|
NyxxPluginState<NyxxRest, Analytics> createState() => AnalyticsPluginState(this);
|
|
}
|
|
|
|
class AnalyticsPluginState extends NyxxPluginState<NyxxRest, Analytics> {
|
|
late final Timer timer;
|
|
|
|
AnalyticsPluginState(super.plugin);
|
|
|
|
@override
|
|
Future<void> afterConnect(NyxxRest client) async {
|
|
await super.afterConnect(client);
|
|
registerPeriodicMetrics(client);
|
|
registerCustomMetrics();
|
|
}
|
|
|
|
void registerCustomMetrics() {
|
|
final m = [plugin.gauges, plugin.counters, plugin.summaries, plugin.histograms].expand((e) => e);
|
|
|
|
if (m.isEmpty) return;
|
|
|
|
for (dynamic metric in m) {
|
|
metric.register();
|
|
}
|
|
}
|
|
|
|
void registerPeriodicMetrics(NyxxRest client) {
|
|
final cachedUsers = Gauge(name: 'nyxx_analytics_cached_users', help: 'The number of cached users')..register();
|
|
final cachedGuilds = Gauge(name: 'nyxx_analytics_cached_guilds', help: 'The number of cached guilds')..register();
|
|
final cachedChannels = Gauge(name: 'nyxx_analytics_cached_channels', help: 'The number of cached channels')
|
|
..register();
|
|
final cachedMessages = Gauge(name: 'nyxx_analytics_cached_messages', help: 'The total number of cached messages')
|
|
..register();
|
|
final restLatency = Gauge(
|
|
name: 'nyxx_analytics_rest_latency',
|
|
help: 'The average REST latency',
|
|
labelNames: ['client_id'],
|
|
)..register();
|
|
final realRestLatency = Gauge(
|
|
name: 'nyxx_analytics_real_rest_latency',
|
|
help: 'The real average REST latency',
|
|
labelNames: ['client_id'],
|
|
)..register();
|
|
|
|
Gauge? gatewayLatency;
|
|
Gauge? shardGatewayLatency;
|
|
|
|
if (client is NyxxGateway) {
|
|
gatewayLatency = Gauge(
|
|
name: 'nyxx_analytics_gateway_latency',
|
|
help: 'The average gateway latency',
|
|
labelNames: ['client_id'],
|
|
)..register();
|
|
shardGatewayLatency = Gauge(
|
|
name: 'nyxx_analytics_shard_gateway_latency',
|
|
help: 'The average gateway latency per shard',
|
|
labelNames: ['client_id', 'shard_id'],
|
|
)..register();
|
|
}
|
|
|
|
Timer.periodic(plugin.refreshInterval, (timer) {
|
|
cachedUsers.value = client.users.cache.length.toDouble();
|
|
cachedGuilds.value = client.guilds.cache.length.toDouble();
|
|
cachedChannels.value = client.channels.cache.length.toDouble();
|
|
cachedMessages.value = client.channels.cache.values
|
|
.whereType<TextChannel>()
|
|
.fold(0, (count, channel) => count + channel.messages.cache.length)
|
|
.toDouble();
|
|
restLatency.labels([client.user.id.toString()]).value = client.httpHandler.latency.inMilliseconds.toDouble();
|
|
realRestLatency.labels([client.user.id.toString()]).value =
|
|
client.httpHandler.realLatency.inMilliseconds.toDouble();
|
|
|
|
if (client case final NyxxGateway client) {
|
|
gatewayLatency!.labels([client.user.id.toString()]).value = client.gateway.latency.inMilliseconds.toDouble();
|
|
for (final shard in client.gateway.shards) {
|
|
shardGatewayLatency!.labels([client.user.id.toString(), shard.id.toString()]).value =
|
|
shard.latency.inMilliseconds.toDouble();
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|