From 03d63f3cf12e9e11c6e4761390340386469272da Mon Sep 17 00:00:00 2001 From: bgrozev Date: Tue, 12 Mar 2024 09:48:36 -0500 Subject: [PATCH] feat: Add prometheus metrics. (#540) * ref: Move stats config to a separate file. * ref: Add an abstraction layer in front of statsd. * ref: Move statsd code under metrics, make private. * Add a prometheus interface at /metrics. * feat: Add "healthy" and "recording" metrics. * ref: Rename a function for clarity. --- pom.xml | 6 + .../kotlin/org/jitsi/jibri/JibriManager.kt | 55 ++---- src/main/kotlin/org/jitsi/jibri/Main.kt | 6 +- .../org/jitsi/jibri/api/http/HttpApi.kt | 30 ++++ .../org/jitsi/jibri/api/xmpp/XmppApi.kt | 23 +-- .../org/jitsi/jibri/metrics/JibriMetrics.kt | 166 ++++++++++++++++++ .../jibri/metrics/JibriMetricsContainer.kt | 21 +++ .../org/jitsi/jibri/metrics/StatsConfig.kt | 39 ++++ .../org/jitsi/jibri/metrics/StatsDEvents.kt | 36 ++++ .../jitsi/jibri/statsd/JibriStatsDClient.kt | 52 ------ .../org/jitsi/jibri/statsd/StatsDEvents.kt | 36 ---- src/main/resources/reference.conf | 4 + 12 files changed, 332 insertions(+), 142 deletions(-) create mode 100644 src/main/kotlin/org/jitsi/jibri/metrics/JibriMetrics.kt create mode 100644 src/main/kotlin/org/jitsi/jibri/metrics/JibriMetricsContainer.kt create mode 100644 src/main/kotlin/org/jitsi/jibri/metrics/StatsConfig.kt create mode 100644 src/main/kotlin/org/jitsi/jibri/metrics/StatsDEvents.kt delete mode 100644 src/main/kotlin/org/jitsi/jibri/statsd/JibriStatsDClient.kt delete mode 100644 src/main/kotlin/org/jitsi/jibri/statsd/StatsDEvents.kt diff --git a/pom.xml b/pom.xml index 0b87d1dc2..790ed65ad 100644 --- a/pom.xml +++ b/pom.xml @@ -270,6 +270,12 @@ 1.2.1 test + + org.jitsi + jicoco-metrics + 1.1-133-g768ef2e + compile + diff --git a/src/main/kotlin/org/jitsi/jibri/JibriManager.kt b/src/main/kotlin/org/jitsi/jibri/JibriManager.kt index a6d153dab..cbf46e35c 100644 --- a/src/main/kotlin/org/jitsi/jibri/JibriManager.kt +++ b/src/main/kotlin/org/jitsi/jibri/JibriManager.kt @@ -20,6 +20,7 @@ package org.jitsi.jibri import org.jitsi.jibri.config.Config import org.jitsi.jibri.config.XmppCredentials import org.jitsi.jibri.health.EnvironmentContext +import org.jitsi.jibri.metrics.JibriMetrics import org.jitsi.jibri.selenium.CallParams import org.jitsi.jibri.service.JibriService import org.jitsi.jibri.service.JibriServiceStatusHandler @@ -30,14 +31,6 @@ import org.jitsi.jibri.service.impl.SipGatewayJibriService import org.jitsi.jibri.service.impl.SipGatewayServiceParams import org.jitsi.jibri.service.impl.StreamingJibriService import org.jitsi.jibri.service.impl.StreamingParams -import org.jitsi.jibri.statsd.ASPECT_BUSY -import org.jitsi.jibri.statsd.ASPECT_ERROR -import org.jitsi.jibri.statsd.ASPECT_START -import org.jitsi.jibri.statsd.ASPECT_STOP -import org.jitsi.jibri.statsd.JibriStatsDClient -import org.jitsi.jibri.statsd.TAG_SERVICE_LIVE_STREAM -import org.jitsi.jibri.statsd.TAG_SERVICE_RECORDING -import org.jitsi.jibri.statsd.TAG_SERVICE_SIP_GATEWAY import org.jitsi.jibri.status.ComponentBusyStatus import org.jitsi.jibri.status.ComponentHealthStatus import org.jitsi.jibri.status.ComponentState @@ -99,29 +92,12 @@ class JibriManager : StatusPublisher() { private var pendingIdleFunc: () -> Unit = {} private var serviceTimeoutTask: ScheduledFuture<*>? = null - private val enableStatsD: Boolean by config { - "JibriConfig::enableStatsD" { Config.legacyConfigSource.enabledStatsD!! } - "jibri.stats.enable-stats-d".from(Config.configSource) - } - - private val statsdHost: String by config { - "jibri.stats.host".from(Config.configSource) - } - - private val statsdPort: Int by config { - "jibri.stats.port".from(Config.configSource) - } - private val singleUseMode: Boolean by config { "JibriConfig::singleUseMode" { Config.legacyConfigSource.singleUseMode!! } "jibri.single-use-mode".from(Config.configSource) } - val statsDClient: JibriStatsDClient? = if (enableStatsD) { - JibriStatsDClient(statsdHost, statsdPort) - } else { - null - } + val jibriMetrics = JibriMetrics() /** * Note: should only be called if the instance-wide lock is held (i.e. called from @@ -129,10 +105,10 @@ class JibriManager : StatusPublisher() { * TODO: instead of the synchronized decorators, use a synchronized(this) block * which we can also use here */ - private fun throwIfBusy() { + private fun throwIfBusy(sinkType: RecordingSinkType) { if (busy()) { logger.info("Jibri is busy, can't start service") - statsDClient?.incrementCounter(ASPECT_BUSY, TAG_SERVICE_RECORDING) + jibriMetrics.requestWhileBusy(sinkType) throw JibriBusyException() } } @@ -148,7 +124,7 @@ class JibriManager : StatusPublisher() { environmentContext: EnvironmentContext? = null, serviceStatusHandler: JibriServiceStatusHandler? = null ) { - throwIfBusy() + throwIfBusy(RecordingSinkType.FILE) logger.info("Starting a file recording with params: $fileRecordingRequestParams") val service = FileRecordingJibriService( FileRecordingParams( @@ -158,7 +134,7 @@ class JibriManager : StatusPublisher() { serviceParams.appData?.fileRecordingMetadata ) ) - statsDClient?.incrementCounter(ASPECT_START, TAG_SERVICE_RECORDING) + jibriMetrics.start(RecordingSinkType.FILE) startService(service, serviceParams, environmentContext, serviceStatusHandler) } @@ -174,9 +150,9 @@ class JibriManager : StatusPublisher() { serviceStatusHandler: JibriServiceStatusHandler? = null ) { logger.info("Starting a stream with params: $serviceParams $streamingParams") - throwIfBusy() + throwIfBusy(RecordingSinkType.STREAM) val service = StreamingJibriService(streamingParams) - statsDClient?.incrementCounter(ASPECT_START, TAG_SERVICE_LIVE_STREAM) + jibriMetrics.start(RecordingSinkType.STREAM) startService(service, serviceParams, environmentContext, serviceStatusHandler) } @@ -188,7 +164,7 @@ class JibriManager : StatusPublisher() { serviceStatusHandler: JibriServiceStatusHandler? = null ) { logger.info("Starting a SIP gateway with params: $serviceParams $sipGatewayServiceParams") - throwIfBusy() + throwIfBusy(RecordingSinkType.GATEWAY) val service = SipGatewayJibriService( SipGatewayServiceParams( sipGatewayServiceParams.callParams, @@ -196,7 +172,7 @@ class JibriManager : StatusPublisher() { sipGatewayServiceParams.sipClientParams ) ) - statsDClient?.incrementCounter(ASPECT_START, TAG_SERVICE_SIP_GATEWAY) + jibriMetrics.start(RecordingSinkType.GATEWAY) return startService(service, serviceParams, environmentContext, serviceStatusHandler) } @@ -219,7 +195,7 @@ class JibriManager : StatusPublisher() { when (it) { is ComponentState.Error -> { if (it.error.scope == ErrorScope.SYSTEM) { - statsDClient?.incrementCounter(ASPECT_ERROR, JibriStatsDClient.getTagForService(jibriService)) + jibriMetrics.error(jibriService.getSinkType()) publishStatus(ComponentHealthStatus.UNHEALTHY) } stopService() @@ -270,7 +246,7 @@ class JibriManager : StatusPublisher() { logger.info("No service active, ignoring stop") return } - statsDClient?.incrementCounter(ASPECT_STOP, JibriStatsDClient.getTagForService(currentService)) + jibriMetrics.stop(currentService.getSinkType()) logger.info("Stopping the current service") serviceTimeoutTask?.cancel(false) // Note that this will block until the service is completely stopped @@ -309,3 +285,10 @@ class JibriManager : StatusPublisher() { } } } + +private fun JibriService.getSinkType() = when (this) { + is FileRecordingJibriService -> RecordingSinkType.FILE + is StreamingJibriService -> RecordingSinkType.GATEWAY + is SipGatewayJibriService -> RecordingSinkType.GATEWAY + else -> throw IllegalArgumentException("JibriService of unsupported type: ${JibriService::class.java.name}") +} diff --git a/src/main/kotlin/org/jitsi/jibri/Main.kt b/src/main/kotlin/org/jitsi/jibri/Main.kt index 94894745e..ec23ffc73 100644 --- a/src/main/kotlin/org/jitsi/jibri/Main.kt +++ b/src/main/kotlin/org/jitsi/jibri/Main.kt @@ -78,6 +78,9 @@ fun main(args: Array) { jibriStatusManager.addStatusHandler { webhookClient.updateStatus(it) } + jibriStatusManager.addStatusHandler { + jibriManager.jibriMetrics.updateStatus(it) + } webhookSubscribers.forEach(webhookClient::addSubscriber) val statusUpdaterTask = TaskPools.recurringTasksPool.scheduleAtFixedRate( 1, @@ -144,8 +147,7 @@ fun main(args: Array) { val xmppApi = XmppApi( jibriManager = jibriManager, xmppConfigs = xmppEnvironments, - jibriStatusManager = jibriStatusManager, - jibriManager.statsDClient + jibriStatusManager = jibriStatusManager ) xmppApi.start() diff --git a/src/main/kotlin/org/jitsi/jibri/api/http/HttpApi.kt b/src/main/kotlin/org/jitsi/jibri/api/http/HttpApi.kt index d59cc0cd1..ab6f14839 100644 --- a/src/main/kotlin/org/jitsi/jibri/api/http/HttpApi.kt +++ b/src/main/kotlin/org/jitsi/jibri/api/http/HttpApi.kt @@ -16,6 +16,7 @@ package org.jitsi.jibri.api.http +import io.ktor.http.ContentType import io.ktor.http.HttpStatusCode import io.ktor.serialization.jackson.jackson import io.ktor.server.application.Application @@ -24,10 +25,12 @@ import io.ktor.server.application.install import io.ktor.server.plugins.contentnegotiation.ContentNegotiation import io.ktor.server.request.receive import io.ktor.server.response.respond +import io.ktor.server.response.respondText import io.ktor.server.routing.get import io.ktor.server.routing.post import io.ktor.server.routing.route import io.ktor.server.routing.routing +import io.prometheus.client.exporter.common.TextFormat import jakarta.ws.rs.core.Response import org.jitsi.jibri.FileRecordingRequestParams import org.jitsi.jibri.JibriBusyException @@ -36,6 +39,8 @@ import org.jitsi.jibri.RecordingSinkType import org.jitsi.jibri.config.Config import org.jitsi.jibri.config.XmppCredentials import org.jitsi.jibri.health.JibriHealth +import org.jitsi.jibri.metrics.JibriMetricsContainer +import org.jitsi.jibri.metrics.StatsConfig import org.jitsi.jibri.selenium.CallParams import org.jitsi.jibri.service.JibriServiceStatusHandler import org.jitsi.jibri.service.ServiceParams @@ -130,6 +135,31 @@ class HttpApi( call.respond(HttpStatusCode.OK) } } + if (StatsConfig.enablePrometheus) { + logger.info("Enabling prometheus interface at :$port/metrics") + get("/metrics") { + val accept = call.request.headers["Accept"] + when { + accept?.startsWith("application/openmetrics-text") == true -> + call.respondText( + JibriMetricsContainer.getPrometheusMetrics(TextFormat.CONTENT_TYPE_OPENMETRICS_100), + contentType = ContentType.parse(TextFormat.CONTENT_TYPE_OPENMETRICS_100) + ) + + accept?.startsWith("text/plain") == true -> + call.respondText( + JibriMetricsContainer.getPrometheusMetrics(TextFormat.CONTENT_TYPE_004), + contentType = ContentType.parse(TextFormat.CONTENT_TYPE_004) + ) + + else -> + call.respondText( + JibriMetricsContainer.jsonString, + contentType = ContentType.parse("application/json") + ) + } + } + } } } diff --git a/src/main/kotlin/org/jitsi/jibri/api/xmpp/XmppApi.kt b/src/main/kotlin/org/jitsi/jibri/api/xmpp/XmppApi.kt index 55c9232b7..38047f7bd 100644 --- a/src/main/kotlin/org/jitsi/jibri/api/xmpp/XmppApi.kt +++ b/src/main/kotlin/org/jitsi/jibri/api/xmpp/XmppApi.kt @@ -32,14 +32,6 @@ import org.jitsi.jibri.service.impl.SipGatewayServiceParams import org.jitsi.jibri.service.impl.StreamingParams import org.jitsi.jibri.service.impl.YOUTUBE_URL import org.jitsi.jibri.sipgateway.SipClientParams -import org.jitsi.jibri.statsd.JibriStatsDClient -import org.jitsi.jibri.statsd.STOPPED_ON_XMPP_CLOSED -import org.jitsi.jibri.statsd.XMPP_CLOSED -import org.jitsi.jibri.statsd.XMPP_CLOSED_ON_ERROR -import org.jitsi.jibri.statsd.XMPP_CONNECTED -import org.jitsi.jibri.statsd.XMPP_PING_FAILED -import org.jitsi.jibri.statsd.XMPP_RECONNECTING -import org.jitsi.jibri.statsd.XMPP_RECONNECTION_FAILED import org.jitsi.jibri.status.ComponentState import org.jitsi.jibri.status.JibriStatus import org.jitsi.jibri.status.JibriStatusManager @@ -78,22 +70,21 @@ class XmppApi( private val jibriManager: JibriManager, private val xmppConfigs: List, private val jibriStatusManager: JibriStatusManager, - private val statsDClient: JibriStatsDClient? = null ) : IQListener { private val logger = createLogger() private val connectionStateListener = object : ConnectionStateListener { override fun connected(mucClient: MucClient) { - statsDClient?.incrementCounter(XMPP_CONNECTED, mucClient.tags()) + jibriManager.jibriMetrics.xmppConnected(mucClient.tags()) } override fun reconnecting(mucClient: MucClient) { - statsDClient?.incrementCounter(XMPP_RECONNECTING, mucClient.tags()) + jibriManager.jibriMetrics.xmppReconnecting(mucClient.tags()) } override fun reconnectionFailed(mucClient: MucClient) { - statsDClient?.incrementCounter(XMPP_RECONNECTION_FAILED, mucClient.tags()) + jibriManager.jibriMetrics.xmppReconnectionFailed(mucClient.tags()) } override fun pingFailed(mucClient: MucClient) { - statsDClient?.incrementCounter(XMPP_PING_FAILED, mucClient.tags()) + jibriManager.jibriMetrics.xmppPingFailed(mucClient.tags()) } /** @@ -102,7 +93,7 @@ class XmppApi( * recording is stopped. */ override fun closed(mucClient: MucClient) { - statsDClient?.incrementCounter(XMPP_CLOSED, mucClient.tags()) + jibriManager.jibriMetrics.xmppClosed(mucClient.tags()) maybeStop(mucClient) } @@ -112,7 +103,7 @@ class XmppApi( * recording is stopped. */ override fun closedOnError(mucClient: MucClient) { - statsDClient?.incrementCounter(XMPP_CLOSED_ON_ERROR, mucClient.tags()) + jibriManager.jibriMetrics.xmppClosedOnError(mucClient.tags()) maybeStop(mucClient) } @@ -121,7 +112,7 @@ class XmppApi( val environmentContext = createEnvironmentContext(xmppEnvironment, mucClient) if (jibriManager.currentEnvironmentContext == environmentContext) { logger.warn("XMPP disconnected, stopping.") - statsDClient?.incrementCounter(STOPPED_ON_XMPP_CLOSED, mucClient.tags()) + jibriManager.jibriMetrics.stoppedOnXmppClosed(mucClient.tags()) jibriManager.stopService() } } diff --git a/src/main/kotlin/org/jitsi/jibri/metrics/JibriMetrics.kt b/src/main/kotlin/org/jitsi/jibri/metrics/JibriMetrics.kt new file mode 100644 index 000000000..959a00be9 --- /dev/null +++ b/src/main/kotlin/org/jitsi/jibri/metrics/JibriMetrics.kt @@ -0,0 +1,166 @@ +/* + * Copyright @ 2024-Present 8x8, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.jitsi.jibri.metrics + +import com.timgroup.statsd.NonBlockingStatsDClient +import org.jitsi.jibri.RecordingSinkType +import org.jitsi.jibri.status.ComponentBusyStatus +import org.jitsi.jibri.status.ComponentHealthStatus +import org.jitsi.jibri.status.JibriStatus +import org.jitsi.utils.logging2.createLogger + +class JibriMetrics { + private val logger = createLogger() + + private val statsd = if (StatsConfig.enableStatsD) { + NonBlockingStatsDClient( + "jibri", + StatsConfig.statsdHost, + StatsConfig.statsdPort + ) + } else { + null + } + + private fun incrementStatsDCounter(aspect: String, tag: String) { + statsd?.let { + logger.debug { "Incrementing statsd counter: $aspect:$tag" } + it.incrementCounter(aspect, tag) + } + } + + fun requestWhileBusy(type: RecordingSinkType) { + incrementStatsDCounter(ASPECT_BUSY, type.getTag()) + requestsWhileBusy.inc() + } + + fun start(type: RecordingSinkType) { + incrementStatsDCounter(ASPECT_START, type.getTag()) + sessionsStarted.inc() + } + + fun stop(type: RecordingSinkType) { + incrementStatsDCounter(ASPECT_STOP, type.getTag()) + sessionsStopped.inc() + } + + fun error(type: RecordingSinkType) { + incrementStatsDCounter(ASPECT_ERROR, type.getTag()) + errors.inc() + } + + fun xmppConnected(tags: String) { + incrementStatsDCounter(XMPP_CONNECTED, tags) + xmppConnected.inc() + } + + fun xmppReconnecting(tags: String) { + incrementStatsDCounter(XMPP_RECONNECTING, tags) + xmppReconnecting.inc() + } + + fun xmppReconnectionFailed(tags: String) { + incrementStatsDCounter(XMPP_RECONNECTION_FAILED, tags) + xmppReconnectionFailed.inc() + } + + fun xmppPingFailed(tags: String) { + incrementStatsDCounter(XMPP_PING_FAILED, tags) + xmppPingFailed.inc() + } + + fun xmppClosed(tags: String) { + incrementStatsDCounter(XMPP_CLOSED, tags) + xmppClosed.inc() + } + + fun xmppClosedOnError(tags: String) { + incrementStatsDCounter(XMPP_CLOSED_ON_ERROR, tags) + xmppClosedOnError.inc() + } + + fun stoppedOnXmppClosed(tags: String) { + incrementStatsDCounter(STOPPED_ON_XMPP_CLOSED, tags) + stoppedOnXmppClosed.inc() + } + + fun updateStatus(status: JibriStatus) { + healthy.set(status.health.healthStatus == ComponentHealthStatus.HEALTHY) + recording.set(status.busyStatus == ComponentBusyStatus.BUSY) + } + + companion object { + val sessionsStarted = JibriMetricsContainer.registerCounter( + "sessions_started", + "Number of times a session was started." + ) + val sessionsStopped = JibriMetricsContainer.registerCounter( + "sessions_stopped", + "Number of times a session was stopped." + ) + val errors = JibriMetricsContainer.registerCounter( + "errors", + "Number of errors." + ) + val requestsWhileBusy = JibriMetricsContainer.registerCounter( + "busy", + "Number of times a request was received while the instance was busy." + ) + val xmppConnected = JibriMetricsContainer.registerCounter( + "xmpp_connected", + "Number of times an XMPP connection connected." + ) + val xmppReconnecting = JibriMetricsContainer.registerCounter( + "xmpp_reconnecting", + "Number of times an XMPP connection started re-connecting." + ) + val xmppReconnectionFailed = JibriMetricsContainer.registerCounter( + "xmpp_reconnection_failed", + "Number of times an XMPP re-connection failed." + ) + val xmppPingFailed = JibriMetricsContainer.registerCounter( + "xmpp_ping_failed", + "Number of times an XMPP ping timed out." + ) + val xmppClosed = JibriMetricsContainer.registerCounter( + "xmpp_closed", + "Number of times an XMPP connection was closed." + ) + val xmppClosedOnError = JibriMetricsContainer.registerCounter( + "xmpp_closed_on_error", + "Number of times an XMPP connection was closed on error." + ) + val stoppedOnXmppClosed = JibriMetricsContainer.registerCounter( + "stopped_on_xmpp_closed", + "Number of times a session was stopped because XMPP disconnected." + ) + val healthy = JibriMetricsContainer.registerBooleanMetric( + "healthy", + "Whether the jibri instance is currently healthy." + ) + val recording = JibriMetricsContainer.registerBooleanMetric( + "recording", + "Whether the jibri instance is currently in use." + ) + } +} + +private fun RecordingSinkType.getTag() = when (this) { + RecordingSinkType.STREAM -> TAG_SERVICE_LIVE_STREAM + RecordingSinkType.FILE -> TAG_SERVICE_RECORDING + RecordingSinkType.GATEWAY -> TAG_SERVICE_SIP_GATEWAY +} diff --git a/src/main/kotlin/org/jitsi/jibri/metrics/JibriMetricsContainer.kt b/src/main/kotlin/org/jitsi/jibri/metrics/JibriMetricsContainer.kt new file mode 100644 index 000000000..4475d0f0a --- /dev/null +++ b/src/main/kotlin/org/jitsi/jibri/metrics/JibriMetricsContainer.kt @@ -0,0 +1,21 @@ +/* + * Copyright @ 2024-Present 8x8, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.jitsi.jibri.metrics + +import org.jitsi.metrics.MetricsContainer + +object JibriMetricsContainer : MetricsContainer(namespace = "jitsi_jibri") diff --git a/src/main/kotlin/org/jitsi/jibri/metrics/StatsConfig.kt b/src/main/kotlin/org/jitsi/jibri/metrics/StatsConfig.kt new file mode 100644 index 000000000..085243131 --- /dev/null +++ b/src/main/kotlin/org/jitsi/jibri/metrics/StatsConfig.kt @@ -0,0 +1,39 @@ +/* + * Copyright @ 2024-Present 8x8, Inc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.jitsi.jibri.metrics + +import org.jitsi.jibri.config.Config +import org.jitsi.metaconfig.config + +object StatsConfig { + val enableStatsD: Boolean by config { + "JibriConfig::enableStatsD" { Config.legacyConfigSource.enabledStatsD!! } + "jibri.stats.enable-stats-d".from(Config.configSource) + } + + val statsdHost: String by config { + "jibri.stats.host".from(Config.configSource) + } + + val statsdPort: Int by config { + "jibri.stats.port".from(Config.configSource) + } + + val enablePrometheus: Boolean by config { + "jibri.stats.prometheus.enabled".from(Config.configSource) + } +} diff --git a/src/main/kotlin/org/jitsi/jibri/metrics/StatsDEvents.kt b/src/main/kotlin/org/jitsi/jibri/metrics/StatsDEvents.kt new file mode 100644 index 000000000..9b7c374c8 --- /dev/null +++ b/src/main/kotlin/org/jitsi/jibri/metrics/StatsDEvents.kt @@ -0,0 +1,36 @@ +/* + * Copyright @ 2018 Atlassian Pty Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.jitsi.jibri.metrics + +internal const val ASPECT_START = "start" +internal const val ASPECT_STOP = "stop" +internal const val ASPECT_BUSY = "busy" +internal const val ASPECT_ERROR = "error" + +internal const val XMPP_CONNECTED = "xmpp-connected" +internal const val XMPP_RECONNECTING = "xmpp-reconnecting" +internal const val XMPP_RECONNECTION_FAILED = "xmpp-reconnection-failed" +internal const val XMPP_PING_FAILED = "xmpp-ping-failed" +internal const val XMPP_CLOSED = "xmpp-closed" +internal const val XMPP_CLOSED_ON_ERROR = "xmpp-closed-on-error" + +// A recording session was stopped because XMPP disconnected. +internal const val STOPPED_ON_XMPP_CLOSED = "stopped-on-xmpp-closed" + +internal const val TAG_SERVICE_RECORDING = "recording" +internal const val TAG_SERVICE_LIVE_STREAM = "live_stream" +internal const val TAG_SERVICE_SIP_GATEWAY = "sip_gateway" diff --git a/src/main/kotlin/org/jitsi/jibri/statsd/JibriStatsDClient.kt b/src/main/kotlin/org/jitsi/jibri/statsd/JibriStatsDClient.kt deleted file mode 100644 index 273d6d0d5..000000000 --- a/src/main/kotlin/org/jitsi/jibri/statsd/JibriStatsDClient.kt +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright @ 2018 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package org.jitsi.jibri.statsd - -import com.timgroup.statsd.NonBlockingStatsDClient -import org.jitsi.jibri.service.JibriService -import org.jitsi.jibri.service.impl.FileRecordingJibriService -import org.jitsi.jibri.service.impl.SipGatewayJibriService -import org.jitsi.jibri.service.impl.StreamingJibriService -import org.jitsi.utils.logging2.createLogger - -/** - * Client for pushing statsd values - */ -class JibriStatsDClient(hostname: String = "localhost", port: Int = 8125) { - private val logger = createLogger() - private val statsd = NonBlockingStatsDClient( - "jibri", - hostname, - port - ) - - fun incrementCounter(aspect: String, vararg tags: String) { - logger.debug { "Incrementing statsd counter: $aspect:${tags.joinToString(":")}" } - statsd.incrementCounter(aspect, *tags) - } - - companion object { - fun getTagForService(service: JibriService?): String { - return when (service) { - is FileRecordingJibriService -> TAG_SERVICE_RECORDING - is StreamingJibriService -> TAG_SERVICE_LIVE_STREAM - is SipGatewayJibriService -> TAG_SERVICE_SIP_GATEWAY - else -> "" - } - } - } -} diff --git a/src/main/kotlin/org/jitsi/jibri/statsd/StatsDEvents.kt b/src/main/kotlin/org/jitsi/jibri/statsd/StatsDEvents.kt deleted file mode 100644 index 0dd85726b..000000000 --- a/src/main/kotlin/org/jitsi/jibri/statsd/StatsDEvents.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright @ 2018 Atlassian Pty Ltd - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package org.jitsi.jibri.statsd - -const val ASPECT_START = "start" -const val ASPECT_STOP = "stop" -const val ASPECT_BUSY = "busy" -const val ASPECT_ERROR = "error" - -const val XMPP_CONNECTED = "xmpp-connected" -const val XMPP_RECONNECTING = "xmpp-reconnecting" -const val XMPP_RECONNECTION_FAILED = "xmpp-reconnection-failed" -const val XMPP_PING_FAILED = "xmpp-ping-failed" -const val XMPP_CLOSED = "xmpp-closed" -const val XMPP_CLOSED_ON_ERROR = "xmpp-closed-on-error" - -// A recording session was stopped because XMPP disconnected. -const val STOPPED_ON_XMPP_CLOSED = "stopped-on-xmpp-closed" - -const val TAG_SERVICE_RECORDING = "recording" -const val TAG_SERVICE_LIVE_STREAM = "live_stream" -const val TAG_SERVICE_SIP_GATEWAY = "sip_gateway" diff --git a/src/main/resources/reference.conf b/src/main/resources/reference.conf index 86d2a74b5..d947a81fa 100644 --- a/src/main/resources/reference.conf +++ b/src/main/resources/reference.conf @@ -71,6 +71,10 @@ jibri { enable-stats-d = true host = "localhost" port = 8125 + prometheus { + // Enable a prometheus interface at /metrics + enabled = false + } } webhook { // A list of subscribers interested in receiving webhook events