/proxy/core/src/main/scala/io/cloudstate/proxy/telemetry/PrometheusExporter.scala

https://github.com/cloudstateio/cloudstate · Scala · 67 lines · 42 code · 6 blank · 19 comment · 0 complexity · 22eb28199da92825b092a4b92fc86b91 MD5 · raw file

  1. /*
  2. * Copyright 2019 Lightbend Inc.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package io.cloudstate.proxy.telemetry
  17. import akka.actor.ActorSystem
  18. import akka.http.scaladsl.Http
  19. import akka.http.scaladsl.model._
  20. import akka.http.scaladsl.server.Directives._
  21. import akka.util.ByteString
  22. import io.prometheus.client.CollectorRegistry
  23. import io.prometheus.client.exporter.common.TextFormat
  24. import java.io.OutputStreamWriter
  25. import scala.util.{Failure, Success}
  26. /**
  27. * Serves Prometheus metrics using Akka HTTP
  28. */
  29. class PrometheusExporter(registry: CollectorRegistry, metricsHost: String, metricsPort: Int)(
  30. implicit system: ActorSystem
  31. ) {
  32. private[this] val PrometheusContentType = ContentType.parse(TextFormat.CONTENT_TYPE_004).right.get
  33. private def routes = get {
  34. (path("metrics") | pathSingleSlash) {
  35. encodeResponse {
  36. parameter(Symbol("name[]").*) { names =>
  37. complete {
  38. val namesSet = new java.util.HashSet[String]()
  39. names.foreach(namesSet.add)
  40. val builder = ByteString.newBuilder
  41. val writer = new OutputStreamWriter(builder.asOutputStream)
  42. TextFormat.write004(writer, registry.filteredMetricFamilySamples(namesSet))
  43. // Very important to flush the writer before we build the byte string!
  44. writer.flush()
  45. HttpEntity(PrometheusContentType, builder.result())
  46. }
  47. }
  48. }
  49. }
  50. }
  51. def start(): Unit = {
  52. import system.dispatcher
  53. Http().bindAndHandle(routes, metricsHost, metricsPort).onComplete {
  54. case Success(binding) =>
  55. system.log.info("Prometheus exporter started on {}", binding.localAddress)
  56. case Failure(error) =>
  57. system.log.error(error, "Error starting Prometheus exporter!")
  58. system.terminate()
  59. }
  60. }
  61. }