From 0c813ffda19684c5341147dbe404de8746d5d3be Mon Sep 17 00:00:00 2001 From: Thanh Le Date: Fri, 1 Nov 2024 21:27:43 +0100 Subject: [PATCH] Implement prometheus exporter routes for app --- modules/app/src/main/scala/app.scala | 31 ++++++++++--------- .../main/scala/http.routes.prometheus.scala | 13 ++++++++ modules/app/src/main/scala/http.server.scala | 2 +- .../app/src/test/scala/IntegrationSuite.scala | 7 +++-- modules/e2e/src/test/scala/CompatSuite.scala | 15 ++++++--- 5 files changed, 46 insertions(+), 22 deletions(-) create mode 100644 modules/app/src/main/scala/http.routes.prometheus.scala diff --git a/modules/app/src/main/scala/app.scala b/modules/app/src/main/scala/app.scala index 6f385abd..8b2f4c92 100644 --- a/modules/app/src/main/scala/app.scala +++ b/modules/app/src/main/scala/app.scala @@ -2,13 +2,15 @@ package lila.search package app import cats.effect.* +import cats.syntax.all.* import org.typelevel.log4cats.slf4j.{ Slf4jFactory, Slf4jLogger } import org.typelevel.log4cats.{ Logger, LoggerFactory } import org.typelevel.otel4s.experimental.metrics.* import org.typelevel.otel4s.metrics.Meter import org.typelevel.otel4s.sdk.* -import org.typelevel.otel4s.sdk.exporter.prometheus.autoconfigure.PrometheusMetricExporterAutoConfigure +import org.typelevel.otel4s.sdk.exporter.prometheus.PrometheusMetricExporter import org.typelevel.otel4s.sdk.metrics.SdkMetrics +import org.typelevel.otel4s.sdk.metrics.exporter.MetricExporter object App extends IOApp.Simple: @@ -19,22 +21,23 @@ object App extends IOApp.Simple: def app: Resource[IO, Unit] = for - config <- AppConfig.load.toResource - _ <- Logger[IO].info(s"Starting lila-search with config: $config").toResource - given Meter[IO] <- mkMeter - _ <- RuntimeMetrics.register[IO] - res <- AppResources.instance(config) - _ <- SearchApp(res, config).run() + given MetricExporter.Pull[IO] <- PrometheusMetricExporter.builder[IO].build.toResource + given Meter[IO] <- mkMeter + config <- AppConfig.load.toResource + _ <- Logger[IO].info(s"Starting lila-search with config: $config").toResource + _ <- RuntimeMetrics.register[IO] + res <- AppResources.instance(config) + _ <- mkServer(res, config) yield () - def mkMeter = SdkMetrics - .autoConfigured[IO](_.addExporterConfigurer(PrometheusMetricExporterAutoConfigure[IO])) + def mkMeter(using exporter: MetricExporter.Pull[IO]) = SdkMetrics + .autoConfigured[IO](_.addMeterProviderCustomizer((b, _) => b.registerMetricReader(exporter.metricReader))) .evalMap(_.meterProvider.get("lila-search")) -class SearchApp(res: AppResources, config: AppConfig)(using Logger[IO], LoggerFactory[IO], Meter[IO]): - def run(): Resource[IO, Unit] = + def mkServer(res: AppResources, config: AppConfig)(using MetricExporter.Pull[IO]): Resource[IO, Unit] = for - httpRoutes <- Routes(res, config.server) - server <- MkHttpServer.apply.newEmber(config.server, httpRoutes.orNotFound) - _ <- Logger[IO].info(s"Starting server on ${config.server.host}:${config.server.port}").toResource + apiRoutes <- Routes(res, config.server) + httpRoutes = apiRoutes <+> mkPrometheusRoutes + server <- MkHttpServer().newEmber(config.server, httpRoutes.orNotFound) + _ <- Logger[IO].info(s"Starting server on ${config.server.host}:${config.server.port}").toResource yield () diff --git a/modules/app/src/main/scala/http.routes.prometheus.scala b/modules/app/src/main/scala/http.routes.prometheus.scala new file mode 100644 index 00000000..e33763e9 --- /dev/null +++ b/modules/app/src/main/scala/http.routes.prometheus.scala @@ -0,0 +1,13 @@ +package lila.search +package app + +import cats.effect.IO +import org.http4s.HttpRoutes +import org.http4s.server.Router +import org.typelevel.otel4s.sdk.exporter.prometheus.* +import org.typelevel.otel4s.sdk.metrics.exporter.MetricExporter + +def mkPrometheusRoutes(using exporter: MetricExporter.Pull[IO]): HttpRoutes[IO] = + val writerConfig = PrometheusWriter.Config.default + val prometheusRoutes = PrometheusHttpRoutes.routes[IO](exporter, writerConfig) + Router("/metrics" -> prometheusRoutes) diff --git a/modules/app/src/main/scala/http.server.scala b/modules/app/src/main/scala/http.server.scala index 4bb9f1c9..e170028c 100644 --- a/modules/app/src/main/scala/http.server.scala +++ b/modules/app/src/main/scala/http.server.scala @@ -15,7 +15,7 @@ trait MkHttpServer: object MkHttpServer: - def apply(using server: MkHttpServer): MkHttpServer = server + def apply()(using server: MkHttpServer): MkHttpServer = server given forAsyncLogger(using Logger[IO]): MkHttpServer = new: diff --git a/modules/app/src/test/scala/IntegrationSuite.scala b/modules/app/src/test/scala/IntegrationSuite.scala index 8fdc1bbd..6e01d347 100644 --- a/modules/app/src/test/scala/IntegrationSuite.scala +++ b/modules/app/src/test/scala/IntegrationSuite.scala @@ -10,6 +10,8 @@ import org.http4s.Uri import org.typelevel.log4cats.noop.{ NoOpFactory, NoOpLogger } import org.typelevel.log4cats.{ Logger, LoggerFactory } import org.typelevel.otel4s.metrics.Meter +import org.typelevel.otel4s.sdk.exporter.prometheus.PrometheusMetricExporter +import org.typelevel.otel4s.sdk.metrics.exporter.MetricExporter import smithy4s.Timestamp import weaver.* @@ -31,8 +33,9 @@ object IntegrationSuite extends IOSuite: for elastic <- ElasticSearchContainer.start config = testAppConfig(elastic) - res <- AppResources.instance(config) - _ <- SearchApp(res, config).run() + res <- AppResources.instance(config) + given MetricExporter.Pull[IO] <- PrometheusMetricExporter.builder[IO].build.toResource + _ <- App.mkServer(res, config) yield res def testAppConfig(elastic: ElasticConfig) = AppConfig( diff --git a/modules/e2e/src/test/scala/CompatSuite.scala b/modules/e2e/src/test/scala/CompatSuite.scala index 952448ec..9552c504 100644 --- a/modules/e2e/src/test/scala/CompatSuite.scala +++ b/modules/e2e/src/test/scala/CompatSuite.scala @@ -5,12 +5,14 @@ import akka.actor.ActorSystem import cats.effect.{ IO, Resource } import com.comcast.ip4s.* import com.sksamuel.elastic4s.Indexable -import lila.search.app.{ AppConfig, AppResources, ElasticConfig, HttpServerConfig, SearchApp } +import lila.search.app.{ App, AppConfig, AppResources, ElasticConfig, HttpServerConfig } import lila.search.client.{ SearchClient, SearchError } import lila.search.spec.{ CountOutput, Query, SearchOutput, Source } import org.typelevel.log4cats.noop.{ NoOpFactory, NoOpLogger } import org.typelevel.log4cats.{ Logger, LoggerFactory } import org.typelevel.otel4s.metrics.Meter +import org.typelevel.otel4s.sdk.exporter.prometheus.PrometheusMetricExporter +import org.typelevel.otel4s.sdk.metrics.exporter.MetricExporter import play.api.libs.ws.* import play.api.libs.ws.ahc.* @@ -27,10 +29,13 @@ object CompatSuite extends weaver.IOSuite: override def sharedResource: Resource[IO, Res] = val res = AppResources(fakeClient) - SearchApp(res, testAppConfig) - .run() - .flatMap(_ => wsClient) - .map(SearchClient.play(_, "http://localhost:9999/api")) + for + given MetricExporter.Pull[IO] <- PrometheusMetricExporter.builder[IO].build.toResource + res <- App + .mkServer(res, testAppConfig) + .flatMap(_ => wsClient) + .map(SearchClient.play(_, "http://localhost:9999/api")) + yield res val from = From(0) val size = Size(12)