From ad1f20065a7a4666b103414752335400847eee23 Mon Sep 17 00:00:00 2001 From: Thanh Le Date: Thu, 14 Nov 2024 20:20:40 +0100 Subject: [PATCH 1/4] prometheus exporter: add shutdown timeout config Allow users to customize the shutdown timeout of exporter server. Default value is 10 seconds --- .../prometheus/PrometheusMetricExporter.scala | 12 +++++++++++ ...rometheusMetricExporterAutoConfigure.scala | 20 ++++++++++++++++++- ...heusMetricExporterAutoConfigureSuite.scala | 11 ++++++---- 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/sdk-exporter/prometheus/src/main/scala/org/typelevel/otel4s/sdk/exporter/prometheus/PrometheusMetricExporter.scala b/sdk-exporter/prometheus/src/main/scala/org/typelevel/otel4s/sdk/exporter/prometheus/PrometheusMetricExporter.scala index 55e8b8d2a..03a0439c8 100644 --- a/sdk-exporter/prometheus/src/main/scala/org/typelevel/otel4s/sdk/exporter/prometheus/PrometheusMetricExporter.scala +++ b/sdk-exporter/prometheus/src/main/scala/org/typelevel/otel4s/sdk/exporter/prometheus/PrometheusMetricExporter.scala @@ -30,6 +30,8 @@ import org.http4s.server.Router import org.typelevel.otel4s.sdk.metrics.data.MetricData import org.typelevel.otel4s.sdk.metrics.exporter._ +import scala.concurrent.duration.FiniteDuration + /** Exports metrics on request (pull based). * * @see @@ -97,6 +99,7 @@ object PrometheusMetricExporter { private[prometheus] object Defaults { val Host: Host = host"localhost" val Port: Port = port"9464" + val ShutdownTimeout: FiniteDuration = FiniteDuration(10, "seconds") } /** Builder for [[PrometheusMetricExporter]] */ @@ -121,6 +124,9 @@ object PrometheusMetricExporter { /** Sets the port that metrics are served on. */ def withPort(port: Port): HttpServerBuilder[F] + /** Sets the port that metrics are served on. */ + def withShutdownTimeout(shutdownTimeout: FiniteDuration): HttpServerBuilder[F] + /** Sets the default aggregation selector to use. */ def withDefaultAggregationSelector( selector: AggregationSelector @@ -150,6 +156,7 @@ object PrometheusMetricExporter { new HttpServerBuilderImpl[F]( host = Defaults.Host, port = Defaults.Port, + shutdownTimeout = Defaults.ShutdownTimeout, defaultAggregationSelector = AggregationSelector.default, writerConfig = PrometheusWriter.Config.default ) @@ -174,6 +181,7 @@ object PrometheusMetricExporter { private final case class HttpServerBuilderImpl[F[_]: Async: Network: Compression: Console]( host: Host, port: Port, + shutdownTimeout: FiniteDuration, defaultAggregationSelector: AggregationSelector, writerConfig: PrometheusWriter.Config ) extends HttpServerBuilder[F] { @@ -183,6 +191,9 @@ object PrometheusMetricExporter { def withPort(port: Port): HttpServerBuilder[F] = copy(port = port) + def withShutdownTimeout(shutdowntimeout: FiniteDuration): HttpServerBuilder[F] = + copy(shutdownTimeout = shutdownTimeout) + def withDefaultAggregationSelector( selector: AggregationSelector ): HttpServerBuilder[F] = copy(defaultAggregationSelector = selector) @@ -204,6 +215,7 @@ object PrometheusMetricExporter { .default[F] .withHost(host) .withPort(port) + .withShutdownTimeout(shutdownTimeout) .withHttpApp(Router("metrics" -> routes).orNotFound) .build .evalTap { _ => diff --git a/sdk-exporter/prometheus/src/main/scala/org/typelevel/otel4s/sdk/exporter/prometheus/autoconfigure/PrometheusMetricExporterAutoConfigure.scala b/sdk-exporter/prometheus/src/main/scala/org/typelevel/otel4s/sdk/exporter/prometheus/autoconfigure/PrometheusMetricExporterAutoConfigure.scala index 6fffc9fbb..c0e24672b 100644 --- a/sdk-exporter/prometheus/src/main/scala/org/typelevel/otel4s/sdk/exporter/prometheus/autoconfigure/PrometheusMetricExporterAutoConfigure.scala +++ b/sdk-exporter/prometheus/src/main/scala/org/typelevel/otel4s/sdk/exporter/prometheus/autoconfigure/PrometheusMetricExporterAutoConfigure.scala @@ -30,6 +30,7 @@ import org.typelevel.otel4s.sdk.autoconfigure.ConfigurationError import org.typelevel.otel4s.sdk.metrics.exporter.AggregationSelector import org.typelevel.otel4s.sdk.metrics.exporter.MetricExporter +import scala.concurrent.duration.FiniteDuration import scala.util.chaining._ /** Autoconfigures Prometheus [[MetricExporter]]. @@ -45,6 +46,7 @@ import scala.util.chaining._ * | otel.exporter.prometheus.without.type.suffix | OTEL_EXPORTER_PROMETHEUS_WITHOUT_TYPE_SUFFIX | If metrics are produced without a type suffix. Default is `false`. | * | otel.exporter.prometheus.without.scope.info | OTEL_EXPORTER_PROMETHEUS_WITHOUT_SCOPE_INFO | If metrics are produced without a scope info metric or scope labels. Default is `false`. | * | otel.exporter.prometheus.without.target.info | OTEL_EXPORTER_PROMETHEUS_WITHOUT_TARGET_INFO | If metrics are produced without a target info metric. Default is `false`. | + * | otel.exporter.prometheus.shutdown.timeout | OTEL_EXPORTER_PROMETHEUS_SHUTDOWN_TIMEOUT | The time to wait for provider to do any cleanup required. Default is `10 seconds`. | * }}} * * @see @@ -85,10 +87,14 @@ private final class PrometheusMetricExporterAutoConfigure[ withoutTargetInfo <- F .fromEither(config.getOrElse(ConfigKeys.WithoutTargetInfo, defaultConfig.targetInfoDisabled)) .toResource + shutdownTimeout <- F + .fromEither(config.getOrElse(ConfigKeys.ShutdownTimeout, Defaults.ShutdownTimeout)) + .toResource exporter <- PrometheusMetricExporter .serverBuilder[F] .withHost(host) .withPort(port) + .withShutdownTimeout(shutdownTimeout) .withDefaultAggregationSelector(defaultAggregation) .withWriterConfig(mkWriterConfig(withoutUnits, withoutTypeSuffixes, withoutScopeInfo, withoutTargetInfo)) .build @@ -153,8 +159,20 @@ object PrometheusMetricExporterAutoConfigure { val WithoutTargetInfo: Config.Key[Boolean] = Config.Key("otel.exporter.prometheus.without.target.info") + val ShutdownTimeout: Config.Key[FiniteDuration] = + Config.Key("otel.exporter.prometheus.shutdown.timeout") + val All: Set[Config.Key[_]] = - Set(Host, Port, DefaultAggregation, WithoutUnits, WithoutTypeSuffixes, WithoutScopeInfo, WithoutTargetInfo) + Set( + Host, + Port, + DefaultAggregation, + WithoutUnits, + WithoutTypeSuffixes, + WithoutScopeInfo, + WithoutTargetInfo, + ShutdownTimeout + ) } /** Returns [[org.typelevel.otel4s.sdk.autoconfigure.AutoConfigure.Named]] that configures Prometheus diff --git a/sdk-exporter/prometheus/src/test/scala/org/typelevel/otel4s/sdk/exporter/prometheus/autoconfigure/PrometheusMetricExporterAutoConfigureSuite.scala b/sdk-exporter/prometheus/src/test/scala/org/typelevel/otel4s/sdk/exporter/prometheus/autoconfigure/PrometheusMetricExporterAutoConfigureSuite.scala index effd2b7b2..57a7d77d1 100644 --- a/sdk-exporter/prometheus/src/test/scala/org/typelevel/otel4s/sdk/exporter/prometheus/autoconfigure/PrometheusMetricExporterAutoConfigureSuite.scala +++ b/sdk-exporter/prometheus/src/test/scala/org/typelevel/otel4s/sdk/exporter/prometheus/autoconfigure/PrometheusMetricExporterAutoConfigureSuite.scala @@ -59,6 +59,7 @@ class PrometheusMetricExporterAutoConfigureSuite extends CatsEffectSuite with Su Map( "otel.exporter.prometheus.host" -> "", "otel.exporter.prometheus.port" -> "", + "otel.exporter.prometheus.shutdown.timeout" -> "", "otel.exporter.prometheus.default.aggregation" -> "", "otel.exporter.prometheus.without.units" -> "", "otel.exporter.prometheus.without.type.suffix" -> "", @@ -98,6 +99,7 @@ class PrometheusMetricExporterAutoConfigureSuite extends CatsEffectSuite with Su Map( "otel.exporter.prometheus.host" -> "127.0.0.2", "otel.exporter.prometheus.port" -> "9465", + "otel.exporter.prometheus.shutdown.timeout" -> "10000", "otel.exporter.prometheus.without.units" -> "true", "otel.exporter.prometheus.without.type.suffix" -> "true", "otel.exporter.prometheus.without.scope.info" -> "true", @@ -148,10 +150,11 @@ class PrometheusMetricExporterAutoConfigureSuite extends CatsEffectSuite with Su |1) `otel.exporter.prometheus.default.aggregation` - unspecified |2) `otel.exporter.prometheus.host` - N/A |3) `otel.exporter.prometheus.port` - N/A - |4) `otel.exporter.prometheus.without.scope.info` - N/A - |5) `otel.exporter.prometheus.without.target.info` - N/A - |6) `otel.exporter.prometheus.without.type.suffix` - N/A - |7) `otel.exporter.prometheus.without.units` - N/A""".stripMargin + |4) `otel.exporter.prometheus.shutdown.timeout` - N/A + |5) `otel.exporter.prometheus.without.scope.info` - N/A + |6) `otel.exporter.prometheus.without.target.info` - N/A + |7) `otel.exporter.prometheus.without.type.suffix` - N/A + |8) `otel.exporter.prometheus.without.units` - N/A""".stripMargin ) ) } From 93ecd4b7e75ef40a2360fb74d0df1aecc7afc9e5 Mon Sep 17 00:00:00 2001 From: Maksym Ochenashko Date: Fri, 15 Nov 2024 09:55:32 +0200 Subject: [PATCH 2/4] Filter out non-breaking issues --- build.sbt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/build.sbt b/build.sbt index 9bd41ec37..bbf39f1c6 100644 --- a/build.sbt +++ b/build.sbt @@ -1,3 +1,5 @@ +import com.typesafe.tools.mima.core._ + ThisBuild / tlBaseVersion := "0.11" ThisBuild / organization := "org.typelevel" @@ -446,6 +448,18 @@ lazy val `sdk-exporter-prometheus` = startYear := Some(2024), libraryDependencies ++= Seq( "org.http4s" %%% "http4s-ember-server" % Http4sVersion + ), + mimaBinaryIssueFilters ++= Seq( + // see #838 + ProblemFilters.exclude[DirectMissingMethodProblem]( + "org.typelevel.otel4s.sdk.exporter.prometheus.PrometheusMetricExporter#HttpServerBuilderImpl.*" + ), + ProblemFilters.exclude[IncompatibleResultTypeProblem]( + "org.typelevel.otel4s.sdk.exporter.prometheus.PrometheusMetricExporter#HttpServerBuilderImpl.*" + ), + ProblemFilters.exclude[ReversedMissingMethodProblem]( + "org.typelevel.otel4s.sdk.exporter.prometheus.PrometheusMetricExporter#HttpServerBuilder.withShutdownTimeout" + ) ) ) .jsSettings(scalaJSLinkerSettings) From 3510f0c86b07e298afb94b2499287c998473e579 Mon Sep 17 00:00:00 2001 From: Thanh Le Date: Fri, 15 Nov 2024 13:35:41 +0100 Subject: [PATCH 3/4] Fix docs Co-authored-by: Maksym Ochenashko --- .../sdk/exporter/prometheus/PrometheusMetricExporter.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk-exporter/prometheus/src/main/scala/org/typelevel/otel4s/sdk/exporter/prometheus/PrometheusMetricExporter.scala b/sdk-exporter/prometheus/src/main/scala/org/typelevel/otel4s/sdk/exporter/prometheus/PrometheusMetricExporter.scala index 03a0439c8..2deab8dda 100644 --- a/sdk-exporter/prometheus/src/main/scala/org/typelevel/otel4s/sdk/exporter/prometheus/PrometheusMetricExporter.scala +++ b/sdk-exporter/prometheus/src/main/scala/org/typelevel/otel4s/sdk/exporter/prometheus/PrometheusMetricExporter.scala @@ -124,7 +124,7 @@ object PrometheusMetricExporter { /** Sets the port that metrics are served on. */ def withPort(port: Port): HttpServerBuilder[F] - /** Sets the port that metrics are served on. */ + /** Sets the shutdown timeout of the HTTP server. */ def withShutdownTimeout(shutdownTimeout: FiniteDuration): HttpServerBuilder[F] /** Sets the default aggregation selector to use. */ From ceb560b1cbbde4aa7f20aec31a276efda886e463 Mon Sep 17 00:00:00 2001 From: Thanh Le Date: Fri, 15 Nov 2024 13:42:38 +0100 Subject: [PATCH 4/4] Add new configuration to doc tables --- docs/sdk/configuration.md | 3 ++- .../autoconfigure/PrometheusMetricExporterAutoConfigure.scala | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/sdk/configuration.md b/docs/sdk/configuration.md index 688f4bb33..754c243b5 100644 --- a/docs/sdk/configuration.md +++ b/docs/sdk/configuration.md @@ -105,7 +105,7 @@ Target-specific properties are prioritized. E.g. `otel.exporter.otlp.metrics.end ### Prometheus exporter -The exporter launches an HTTP server which responds to the HTTP requests with Prometheus metrics in the appropriate format. +The exporter launches an HTTP server which responds to the HTTP requests with Prometheus metrics in the appropriate format. | System property | Environment variable | Description | |----------------------------------------------|-------------------------------------------------------|------------------------------------------------------------------------------------------| @@ -116,6 +116,7 @@ The exporter launches an HTTP server which responds to the HTTP requests with Pr | otel.exporter.prometheus.without.type.suffix | OTEL\\_EXPORTER\\_PROMETHEUS\_WITHOUT\\_TYPE\\_SUFFIX | If metrics are produced without a type suffix. Default is `false`. | | otel.exporter.prometheus.without.scope.info | OTEL\\_EXPORTER\\_PROMETHEUS\_WITHOUT\\_SCOPE\\_INFO | If metrics are produced without a scope info metric or scope labels. Default is `false`. | | otel.exporter.prometheus.without.target.info | OTEL\\_EXPORTER\\_PROMETHEUS\_WITHOUT\\_TARGET\\_INFO | If metrics are produced without a target info metric. Default is `false`. | +| otel.exporter.prometheus.shutdown.timeout | OTEL\\_EXPORTER\\_PROMETHEUS\_SHUTDOWN\\_TIMEOUT | The time to wait for provider to do any cleanup required. Default is `10 seconds`. | ### Period metric reader diff --git a/sdk-exporter/prometheus/src/main/scala/org/typelevel/otel4s/sdk/exporter/prometheus/autoconfigure/PrometheusMetricExporterAutoConfigure.scala b/sdk-exporter/prometheus/src/main/scala/org/typelevel/otel4s/sdk/exporter/prometheus/autoconfigure/PrometheusMetricExporterAutoConfigure.scala index c0e24672b..ece6a7f1b 100644 --- a/sdk-exporter/prometheus/src/main/scala/org/typelevel/otel4s/sdk/exporter/prometheus/autoconfigure/PrometheusMetricExporterAutoConfigure.scala +++ b/sdk-exporter/prometheus/src/main/scala/org/typelevel/otel4s/sdk/exporter/prometheus/autoconfigure/PrometheusMetricExporterAutoConfigure.scala @@ -189,6 +189,7 @@ object PrometheusMetricExporterAutoConfigure { * | otel.exporter.prometheus.without.type.suffix | OTEL_EXPORTER_PROMETHEUS_WITHOUT_TYPE_SUFFIX | If metrics are produced without a type suffix. Default is `false`. | * | otel.exporter.prometheus.without.scope.info | OTEL_EXPORTER_PROMETHEUS_WITHOUT_SCOPE_INFO | If metrics are produced without a scope info metric or scope labels. Default is `false`. | * | otel.exporter.prometheus.without.target.info | OTEL_EXPORTER_PROMETHEUS_WITHOUT_TARGET_INFO | If metrics are produced without a target info metric. Default is `false`. | + * | otel.exporter.prometheus.shutdown.timeout | OTEL_EXPORTER_PROMETHEUS_SHUTDOWN_TIMEOUT | The time to wait for provider to do any cleanup required. Default is `10 seconds`. | * }}} * * @see