Skip to content

Commit

Permalink
Merge pull request #566 from alephium/jvm-metrics
Browse files Browse the repository at this point in the history
Jvm metrics
  • Loading branch information
tdroxler authored Sep 24, 2024
2 parents 7effff7 + 266e97c commit cd62a8f
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 29 deletions.
22 changes: 0 additions & 22 deletions app/src/main/scala/org/alephium/explorer/Metrics.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,8 @@ package org.alephium.explorer
import scala.concurrent.Future

import io.prometheus.metrics.model.registry.PrometheusRegistry
import io.vertx.ext.web._
import sttp.monad.MonadError
import sttp.tapir._
import sttp.tapir.server.ServerEndpoint
import sttp.tapir.server.metrics.MetricLabels
import sttp.tapir.server.metrics.prometheus.PrometheusMetrics
import sttp.tapir.server.metrics.prometheus.PrometheusMetrics._
import sttp.tapir.server.vertx.VertxFutureServerInterpreter

object Metrics {
val defaultRegistry: PrometheusRegistry = PrometheusRegistry.defaultRegistry
Expand All @@ -40,20 +34,4 @@ object Metrics {
PrometheusMetrics.requestActive(defaultRegistry, namespace, MetricLabels.Default)
)
)

/** Endpoint to expose the metrics
*
* @param callback
* a block of code to execute before serving the metrics, for example to reload
*/
def endpoint(callback: => Unit): Router => Route = VertxFutureServerInterpreter().route(
ServerEndpoint.public(
sttp.tapir.endpoint.get.in(prometheus.endpointPrefix).out(plainBody[PrometheusRegistry]),
(monad: MonadError[Future]) =>
(_: Unit) => {
callback
monad.eval(Right(defaultRegistry): Either[Unit, PrometheusRegistry])
}
)
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright 2018 The Alephium Authors
// This file is part of the alephium project.
//
// The library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the library. If not, see <http://www.gnu.org/licenses/>.

package org.alephium.explorer.api

import sttp.tapir._

import org.alephium.api.alphPlainTextBody

trait MetricsEndpoints extends BaseEndpoint with QueryParams {

val metrics: BaseEndpoint[Unit, String] =
baseEndpoint
.in("metrics")
.out(alphPlainTextBody)
.summary("Exports all prometheus metrics")
}
37 changes: 30 additions & 7 deletions app/src/main/scala/org/alephium/explorer/web/MetricsServer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,32 +16,55 @@

package org.alephium.explorer.web

import java.io.StringWriter

import scala.collection.immutable.ArraySeq
import scala.concurrent.ExecutionContext
import scala.concurrent.{ExecutionContext, Future}
import scala.util.Using

import io.prometheus.client.CollectorRegistry
import io.prometheus.client.exporter.common.TextFormat
import io.prometheus.metrics.core.metrics.Gauge
import io.vertx.ext.web._
import sttp.tapir.server.metrics.prometheus.PrometheusMetrics.prometheusRegistryCodec

import org.alephium.explorer.Metrics
import org.alephium.explorer.api.MetricsEndpoints
import org.alephium.explorer.cache.MetricCache
import org.alephium.util.discard

class MetricsServer(cache: MetricCache)(implicit
val executionContext: ExecutionContext
) extends Server {
) extends Server
with MetricsEndpoints {

val routes: ArraySeq[Router => Route] =
ArraySeq(
Metrics.endpoint(reloadMetrics())
route(metrics.serverLogicSuccess[Future] { _ =>
Future.successful {
// Reload metrics cache on request
discard(reloadMetrics())
Using(new StringWriter()) { writer =>
// Scrape metrics from CollectorRegistry (jvm)
TextFormat.write004(writer, CollectorRegistry.defaultRegistry.metricFamilySamples())
// Scrape metrics from PrometheusRegistry (tapir, HikariCP, etc.)
writer.write(prometheusRegistryCodec.encode(Metrics.defaultRegistry))
writer.toString
}.getOrElse("")

}

})
)

def reloadMetrics(): Unit = {
MetricServer.fungibleCountGauge.set(cache.getFungibleCount().toDouble)
MetricServer.nftCountGauge.set(cache.getNFTCount().toDouble)
MetricServer.eventCountGauge.set(cache.getEventCount().toDouble)
MetricsServer.fungibleCountGauge.set(cache.getFungibleCount().toDouble)
MetricsServer.nftCountGauge.set(cache.getNFTCount().toDouble)
MetricsServer.eventCountGauge.set(cache.getEventCount().toDouble)
}
}

object MetricServer {
object MetricsServer {
val fungibleCountGauge: Gauge = Gauge
.builder()
.name(
Expand Down
1 change: 1 addition & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ lazy val app = mainProject("app")
slickHikaricp,
postgresql,
prometheusSimpleClient,
prometheusSimpleClientCommon,
prometheusSimpleClientHotspot,
tapirPrometheusMetrics,
micrometerCore,
Expand Down
2 changes: 2 additions & 0 deletions project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ object Dependencies {
lazy val slickHikaricp = "com.typesafe.slick" %% "slick-hikaricp" % Version.slick

lazy val prometheusSimpleClient = "io.prometheus" % "simpleclient" % Version.prometheus
lazy val prometheusSimpleClientCommon =
"io.prometheus" % "simpleclient_common" % Version.prometheus
lazy val prometheusSimpleClientHotspot =
"io.prometheus" % "simpleclient_hotspot" % Version.prometheus
lazy val tapirPrometheusMetrics =
Expand Down

0 comments on commit cd62a8f

Please sign in to comment.