From d85ee024cb3f2fa944fc6c06551387a6d1987dae Mon Sep 17 00:00:00 2001 From: LamGC Date: Thu, 17 Feb 2022 19:06:28 +0800 Subject: [PATCH] =?UTF-8?q?feat(metrics):=20=E5=88=9D=E6=AD=A5=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E8=8E=B7=E5=8F=96=E8=BF=90=E8=A1=8C=E6=8C=87=E6=A0=87?= =?UTF-8?q?=E7=9A=84=E5=8A=9F=E8=83=BD(=E5=85=BC=E5=AE=B9=20Prometheus).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加兼容 Prometheus 的运行指标收集导出功能, 通过内置 HttpServer 将其导出. 默认关闭. --- scalabot-app/build.gradle.kts | 3 + scalabot-app/src/main/kotlin/AppConfigs.kt | 7 ++ scalabot-app/src/main/kotlin/AppMain.kt | 20 ++++++ scalabot-app/src/main/kotlin/ScalaBot.kt | 75 ++++++++++++++++++++++ 4 files changed, 105 insertions(+) diff --git a/scalabot-app/build.gradle.kts b/scalabot-app/build.gradle.kts index f49c820..8159a48 100644 --- a/scalabot-app/build.gradle.kts +++ b/scalabot-app/build.gradle.kts @@ -23,6 +23,9 @@ dependencies { implementation("org.telegram:telegrambots-abilities:5.6.0") implementation("org.telegram:telegrambots:5.6.0") + implementation("io.prometheus:simpleclient:0.15.0") + implementation("io.prometheus:simpleclient_httpserver:0.15.0") + testImplementation(kotlin("test")) } diff --git a/scalabot-app/src/main/kotlin/AppConfigs.kt b/scalabot-app/src/main/kotlin/AppConfigs.kt index bf493e5..192ddf6 100644 --- a/scalabot-app/src/main/kotlin/AppConfigs.kt +++ b/scalabot-app/src/main/kotlin/AppConfigs.kt @@ -60,6 +60,12 @@ internal data class ProxyConfig( val port: Int = 1080 ) +internal data class MetricsConfig( + val enable: Boolean = false, + val port: Int = 9386, + val bindAddress: String? = null +) + /** * ScalaBot App 配置. * @@ -68,6 +74,7 @@ internal data class ProxyConfig( */ internal data class AppConfig( val proxy: ProxyConfig = ProxyConfig(), + val metrics: MetricsConfig = MetricsConfig() ) /** diff --git a/scalabot-app/src/main/kotlin/AppMain.kt b/scalabot-app/src/main/kotlin/AppMain.kt index 0aded3a..aa23338 100644 --- a/scalabot-app/src/main/kotlin/AppMain.kt +++ b/scalabot-app/src/main/kotlin/AppMain.kt @@ -1,5 +1,6 @@ package net.lamgc.scalabot +import io.prometheus.client.exporter.HTTPServer import kotlinx.coroutines.runBlocking import mu.KotlinLogging import net.lamgc.scalabot.util.registerShutdownHook @@ -18,11 +19,30 @@ fun main(args: Array): Unit = runBlocking { log.info { "ScalaBot 正在启动中..." } log.debug { "启动参数: ${args.joinToString(prefix = "[", postfix = "]")}" } initialFiles() + if (Const.config.metrics.enable) { + startMetricsServer() + } if (!launcher.launch()) { exitProcess(1) } } +/** + * 启动运行指标服务器. + * 使用 Prometheus 指标格式. + */ +fun startMetricsServer() { + val builder = HTTPServer.Builder() + .withDaemonThreads(true) + .withPort(Const.config.metrics.port) + .withHostname(Const.config.metrics.bindAddress) + + val httpServer = builder + .build() + .registerShutdownHook() + log.info { "运行指标服务器已启动. (Port: ${httpServer.port})" } +} + internal class Launcher : AutoCloseable { companion object { diff --git a/scalabot-app/src/main/kotlin/ScalaBot.kt b/scalabot-app/src/main/kotlin/ScalaBot.kt index 9502d9e..1077e09 100644 --- a/scalabot-app/src/main/kotlin/ScalaBot.kt +++ b/scalabot-app/src/main/kotlin/ScalaBot.kt @@ -1,5 +1,8 @@ package net.lamgc.scalabot +import io.prometheus.client.Counter +import io.prometheus.client.Gauge +import io.prometheus.client.Summary import mu.KotlinLogging import org.eclipse.aether.artifact.Artifact import org.telegram.abilitybots.api.bot.AbilityBot @@ -7,6 +10,7 @@ import org.telegram.abilitybots.api.db.DBContext import org.telegram.abilitybots.api.toggle.BareboneToggle import org.telegram.abilitybots.api.toggle.DefaultToggle import org.telegram.telegrambots.bots.DefaultBotOptions +import org.telegram.telegrambots.meta.api.objects.Update internal class ScalaBot( name: String, @@ -22,6 +26,51 @@ internal class ScalaBot( companion object { @JvmStatic private val log = KotlinLogging.logger { } + + // ------------- Metrics ------------- + + @JvmStatic + private val botUpdateCounter = Counter.build() + .name("updates_total") + .help("Total number of updates received by all bots.") + .labelNames("bot_name") + .subsystem("telegrambots") + .register() + + @JvmStatic + private val botUpdateGauge = Gauge.build() + .name("updates_in_progress") + .help("Number of updates in process by all bots.") + .labelNames("bot_name") + .subsystem("telegrambots") + .register() + + @JvmStatic + private val onlineBotGauge = Gauge.build() + .name("bots_online") + .help("Number of bots Online.") + .subsystem("telegrambots") + .register() + + @JvmStatic + private val updateProcessTime = Summary.build() + .name("update_process_duration_seconds") + .help( + "Time to process update. (This indicator includes the pre-processing of update by TelegrammBots, " + + "so it may be different from the actual execution time of ability. " + + "It is not recommended to use it as the accurate execution time of ability)" + ) + .labelNames("bot_name") + .subsystem("telegrambots") + .register() + + @JvmStatic + private val exceptionHandlingCounter = Counter.build() + .name("updates_exception_handling") + .help("Number of exceptions during processing.") + .labelNames("bot_name") + .subsystem("telegrambots") + .register() } private val extensionLoader = ExtensionLoader(this) @@ -38,4 +87,30 @@ internal class ScalaBot( } override fun creatorId(): Long = creatorId + + override fun onUpdateReceived(update: Update?) { + botUpdateCounter.labels(botUsername).inc() + botUpdateGauge.labels(botUsername).inc() + + val timer = updateProcessTime.labels(botUsername).startTimer() + try { + super.onUpdateReceived(update) + } catch (e: Exception) { + exceptionHandlingCounter.labels(botUsername).inc() + throw e + } finally { + timer.observeDuration() + botUpdateGauge.labels(botUsername).dec() + } + } + + override fun onRegister() { + super.onRegister() + onlineBotGauge.inc() + } + + override fun onClosing() { + super.onClosing() + onlineBotGauge.dec() + } }