diff --git a/docs/user/appendix/metrics.rst b/docs/user/appendix/metrics.rst
index a6ed3eec74d3..a3a10fe2b674 100644
--- a/docs/user/appendix/metrics.rst
+++ b/docs/user/appendix/metrics.rst
@@ -195,7 +195,7 @@ Configuration should be under the key ``geomesa.metrics``, and takes the followi
::
geomesa.metrics = {
- reporters = []
+ reporters = {}
instrumentations = {
# jvm classloader metrics
classloader = {
@@ -232,7 +232,8 @@ Prometheus
::
- {
+ # note: the top-level key here is only for uniqueness - it can be any string
+ "prometheus" = {
type = "prometheus"
enabled = true
# use prometheus "standard" names - see https://docs.micrometer.io/micrometer/reference/implementations/prometheus.html#_the_prometheus_rename_filter
@@ -255,7 +256,8 @@ Cloudwatch
::
- {
+ # note: the top-level key here is only for uniqueness - it can be any string
+ "cloudwatch" = {
type = "cloudwatch"
enabled = true
namespace = "geomesa"
diff --git a/docs/user/process.rst b/docs/user/process.rst
index 30b71a3c62f8..a4b7265eee54 100644
--- a/docs/user/process.rst
+++ b/docs/user/process.rst
@@ -481,9 +481,9 @@ the result as a json object.
========== ===========
Parameters Description
========== ===========
-features The data source feature collection to query. Reference as ``store:layername``.
- For an XML file enter ````
- For interactive WPS request builder select ``VECTOR_LAYER`` & choose ``store:layername``
+features The data source feature collection to query. Reference as ``workspace:layername``.
+ For an XML file enter ````
+ For interactive WPS request builder select ``VECTOR_LAYER`` & choose ``workspace:layername``
filter The filter to apply to the feature collection.
For an XML file enter:
@@ -565,9 +565,9 @@ which are returned as a json object.
=========== ===========
Parameters Description
=========== ===========
-features The data source feature collection to query. Reference as ``store:layername``.
- For an XML file enter ````
- For interactive WPS request builder select ``VECTOR_LAYER`` & choose ``store:layername``
+features The data source feature collection to query. Reference as ``workspace:layername``.
+ For an XML file enter ````
+ For interactive WPS request builder select ``VECTOR_LAYER`` & choose ``workspace:layername``
attribute The attribute for which unique values will be extracted. Attributes are expressed as a string.
For an XML file enter ``attribute-name``
diff --git a/geomesa-metrics/geomesa-metrics-micrometer/src/main/scala/org/locationtech/geomesa/metrics/micrometer/MicrometerSetup.scala b/geomesa-metrics/geomesa-metrics-micrometer/src/main/scala/org/locationtech/geomesa/metrics/micrometer/MicrometerSetup.scala
index 76bfb5b7feee..b15e45764025 100644
--- a/geomesa-metrics/geomesa-metrics-micrometer/src/main/scala/org/locationtech/geomesa/metrics/micrometer/MicrometerSetup.scala
+++ b/geomesa-metrics/geomesa-metrics-micrometer/src/main/scala/org/locationtech/geomesa/metrics/micrometer/MicrometerSetup.scala
@@ -18,6 +18,8 @@ import org.locationtech.geomesa.metrics.micrometer.prometheus.PrometheusFactory
import pureconfig.{ConfigReader, ConfigSource}
import java.util.Locale
+import scala.util.Try
+import scala.util.control.NonFatal
object MicrometerSetup extends StrictLogging {
@@ -35,20 +37,16 @@ object MicrometerSetup extends StrictLogging {
*
* @param conf conf
*/
- // noinspection ScalaUnusedSymbol
def configure(conf: Config = ConfigFactory.load()): Unit = synchronized {
if (conf.hasPath(ConfigPath)) {
- implicit val r0: ConfigReader[Instrumentation] = deriveReader[Instrumentation]
- implicit val r1: ConfigReader[JvmInstrumentations] = deriveReader[JvmInstrumentations]
- implicit val r2: ConfigReader[MetricsConfig] = deriveReader[MetricsConfig]
- implicit val r3: ConfigReader[RegistryConfig] = deriveReader[RegistryConfig]
- val metricsConfig = ConfigSource.fromConfig(conf.getConfig(ConfigPath)).loadOrThrow[MetricsConfig]
- metricsConfig.registries.foreach { registryConfig =>
+ implicit val r0: ConfigReader[RegistryConfig] = deriveReader[RegistryConfig]
+ val MetricsConfig(registries, instrumentations) = MetricsConfig(conf.getConfig(ConfigPath))
+ registries.foreach { case (_, registryConfig) =>
val config = ConfigSource.fromConfig(registryConfig).loadOrThrow[RegistryConfig]
if (config.enabled) {
val configString = registryConfig.root().render(ConfigRenderOptions.concise())
- if (registries.contains(config.`type`)) {
- val existing = registries(config.`type`)
+ if (this.registries.contains(config.`type`)) {
+ val existing = this.registries(config.`type`)
if (existing != configString) {
throw new IllegalArgumentException(
s"Registry type ${config.`type`} already registered with a different configuration:" +
@@ -58,34 +56,34 @@ object MicrometerSetup extends StrictLogging {
val registry = createRegistry(registryConfig)
sys.addShutdownHook(registry.close())
Metrics.addRegistry(registry)
- registries.put(config.`type`, configString)
+ this.registries.put(config.`type`, configString)
}
}
}
- if (metricsConfig.instrumentations.classloader.enabled && bindings.add("classloader")) {
+ if (instrumentations.classloader.enabled && bindings.add("classloader")) {
logger.debug("Enabling JVM classloader metrics")
- val tags = metricsConfig.instrumentations.classloader.tags.map { case (k, v) => Tag.of(k, v) }
+ val tags = instrumentations.classloader.tags.map { case (k, v) => Tag.of(k, v) }
new ClassLoaderMetrics(tags.asJava).bindTo(Metrics.globalRegistry)
}
- if (metricsConfig.instrumentations.memory.enabled && bindings.add("memory")) {
+ if (instrumentations.memory.enabled && bindings.add("memory")) {
logger.debug("Enabling JVM memory metrics")
- val tags = metricsConfig.instrumentations.memory.tags.map { case (k, v) => Tag.of(k, v) }
+ val tags = instrumentations.memory.tags.map { case (k, v) => Tag.of(k, v) }
new JvmMemoryMetrics(tags.asJava).bindTo(Metrics.globalRegistry)
}
- if (metricsConfig.instrumentations.gc.enabled && bindings.add("gc")) {
+ if (instrumentations.gc.enabled && bindings.add("gc")) {
logger.debug("Enabling JVM garbage collection metrics")
- val tags = metricsConfig.instrumentations.gc.tags.map { case (k, v) => Tag.of(k, v) }
+ val tags = instrumentations.gc.tags.map { case (k, v) => Tag.of(k, v) }
new JvmGcMetrics(tags.asJava).bindTo(Metrics.globalRegistry)
}
- if (metricsConfig.instrumentations.processor.enabled && bindings.add("processor")) {
+ if (instrumentations.processor.enabled && bindings.add("processor")) {
logger.debug("Enabling JVM processor metrics")
- val tags = metricsConfig.instrumentations.processor.tags.map { case (k, v) => Tag.of(k, v) }
+ val tags = instrumentations.processor.tags.map { case (k, v) => Tag.of(k, v) }
new ProcessorMetrics(tags.asJava).bindTo(Metrics.globalRegistry)
}
- if (metricsConfig.instrumentations.threads.enabled && bindings.add("threads")) {
+ if (instrumentations.threads.enabled && bindings.add("threads")) {
logger.debug("Enabling JVM thread metrics")
- val tags = metricsConfig.instrumentations.threads.tags.map { case (k, v) => Tag.of(k, v) }
+ val tags = instrumentations.threads.tags.map { case (k, v) => Tag.of(k, v) }
new JvmThreadMetrics(tags.asJava).bindTo(Metrics.globalRegistry)
}
}
@@ -107,14 +105,38 @@ object MicrometerSetup extends StrictLogging {
}
}
- private case class MetricsConfig(
+ case class MetricsConfig(
+ registries: Map[String, Config],
+ instrumentations: JvmInstrumentations,
+ )
+
+ object MetricsConfig {
+ // noinspection ScalaUnusedSymbol
+ def apply(conf: Config): MetricsConfig = {
+ implicit val r0: ConfigReader[Instrumentation] = deriveReader[Instrumentation]
+ implicit val r1: ConfigReader[JvmInstrumentations] = deriveReader[JvmInstrumentations]
+ implicit val r2: ConfigReader[MetricsConfig] = deriveReader[MetricsConfig]
+ val source = ConfigSource.fromConfig(conf)
+ try { source.loadOrThrow[MetricsConfig] } catch {
+ case NonFatal(e) =>
+ // back compatible loading attempt
+ implicit val r: ConfigReader[MetricsSeqConfig] = deriveReader[MetricsSeqConfig]
+ val c = Try(source.loadOrThrow[MetricsSeqConfig]).getOrElse(throw e)
+ val registries = Seq.tabulate(c.registries.length)(i => String.valueOf(i)).zip(c.registries).toMap
+ MetricsConfig(registries, c.instrumentations)
+ }
+ }
+ }
+
+ // back compatible structure
+ private case class MetricsSeqConfig(
registries: Seq[Config],
instrumentations: JvmInstrumentations,
)
- private case class Instrumentation(enabled: Boolean = false, tags: Map[String, String] = Map.empty)
+ case class Instrumentation(enabled: Boolean = false, tags: Map[String, String] = Map.empty)
- private case class JvmInstrumentations(
+ case class JvmInstrumentations(
classloader: Instrumentation = Instrumentation(),
memory: Instrumentation = Instrumentation(),
gc: Instrumentation = Instrumentation(),
diff --git a/geomesa-metrics/geomesa-metrics-micrometer/src/test/scala/org/locationtech/geomesa/metrics/micrometer/MetricsConfigTest.scala b/geomesa-metrics/geomesa-metrics-micrometer/src/test/scala/org/locationtech/geomesa/metrics/micrometer/MetricsConfigTest.scala
new file mode 100644
index 000000000000..9d78d99be87f
--- /dev/null
+++ b/geomesa-metrics/geomesa-metrics-micrometer/src/test/scala/org/locationtech/geomesa/metrics/micrometer/MetricsConfigTest.scala
@@ -0,0 +1,105 @@
+/***********************************************************************
+ * Copyright (c) 2013-2024 Commonwealth Computer Research, Inc.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Apache License, Version 2.0
+ * which accompanies this distribution and is available at
+ * http://www.opensource.org/licenses/apache2.0.php.
+ ***********************************************************************/
+
+package org.locationtech.geomesa.metrics.micrometer
+
+import com.typesafe.config.ConfigFactory
+import org.junit.runner.RunWith
+import org.locationtech.geomesa.metrics.micrometer.MicrometerSetup.MetricsConfig
+import org.specs2.mutable.Specification
+import org.specs2.runner.JUnitRunner
+
+@RunWith(classOf[JUnitRunner])
+class MetricsConfigTest extends Specification {
+
+ "MetricsConfig" should {
+ "parse configs" in {
+ val reg =
+ """
+ |{
+ | type = "prometheus"
+ | enabled = false
+ | rename = true
+ | common-tags = { "application" = "test" }
+ | port = 9090
+ |}
+ |""".stripMargin
+ val conf = ConfigFactory.parseString(
+ s"""{
+ | enabled = true
+ | instrumentations = {
+ | classloader = { enabled = true, tags = {} }
+ | memory = { enabled = true, tags = {} }
+ | gc = { enabled = false, tags = {} }
+ | processor = { enabled = true, tags = { "processor" = "foo" } }
+ | threads = { enabled = true, tags = {} }
+ | }
+ | registries = {
+ | prometheus = $reg
+ | }
+ |}
+ |""".stripMargin)
+ val config = MetricsConfig(conf)
+ config.instrumentations.classloader.enabled must beTrue
+ config.instrumentations.classloader.tags must beEmpty
+ config.instrumentations.memory.enabled must beTrue
+ config.instrumentations.memory.tags must beEmpty
+ config.instrumentations.gc.enabled must beFalse
+ config.instrumentations.gc.tags must beEmpty
+ config.instrumentations.processor.enabled must beTrue
+ config.instrumentations.processor.tags mustEqual Map("processor" -> "foo")
+ config.instrumentations.threads.enabled must beTrue
+ config.instrumentations.threads.tags must beEmpty
+ config.registries must haveSize(1)
+ config.registries.get("prometheus") must beSome
+ config.registries("prometheus") mustEqual ConfigFactory.parseString(reg)
+ }
+
+ "parse deprecated configs" in {
+ val reg =
+ """
+ |{
+ | type = "prometheus"
+ | enabled = false
+ | rename = true
+ | common-tags = { "application" = "test" }
+ | port = 9090
+ |}
+ |""".stripMargin
+ val conf = ConfigFactory.parseString(
+ s"""{
+ | enabled = true
+ | instrumentations = {
+ | classloader = { enabled = true, tags = {} }
+ | memory = { enabled = true, tags = {} }
+ | gc = { enabled = false, tags = {} }
+ | processor = { enabled = true, tags = { "processor" = "foo" } }
+ | threads = { enabled = true, tags = {} }
+ | }
+ | registries = [
+ | $reg
+ | ]
+ |}
+ |""".stripMargin)
+ val config = MetricsConfig(conf)
+ config.instrumentations.classloader.enabled must beTrue
+ config.instrumentations.classloader.tags must beEmpty
+ config.instrumentations.memory.enabled must beTrue
+ config.instrumentations.memory.tags must beEmpty
+ config.instrumentations.gc.enabled must beFalse
+ config.instrumentations.gc.tags must beEmpty
+ config.instrumentations.processor.enabled must beTrue
+ config.instrumentations.processor.tags mustEqual Map("processor" -> "foo")
+ config.instrumentations.threads.enabled must beTrue
+ config.instrumentations.threads.tags must beEmpty
+ config.registries must haveSize(1)
+ config.registries.get("0") must beSome
+ config.registries("0") mustEqual ConfigFactory.parseString(reg)
+ }
+ }
+}
diff --git a/geomesa-metrics/geomesa-metrics-micrometer/src/test/scala/org/locationtech/geomesa/metrics/micrometer/PrometheusReporterTest.scala b/geomesa-metrics/geomesa-metrics-micrometer/src/test/scala/org/locationtech/geomesa/metrics/micrometer/prometheus/PrometheusReporterTest.scala
similarity index 99%
rename from geomesa-metrics/geomesa-metrics-micrometer/src/test/scala/org/locationtech/geomesa/metrics/micrometer/PrometheusReporterTest.scala
rename to geomesa-metrics/geomesa-metrics-micrometer/src/test/scala/org/locationtech/geomesa/metrics/micrometer/prometheus/PrometheusReporterTest.scala
index 728b7f9880a8..bd4d46c11c7f 100644
--- a/geomesa-metrics/geomesa-metrics-micrometer/src/test/scala/org/locationtech/geomesa/metrics/micrometer/PrometheusReporterTest.scala
+++ b/geomesa-metrics/geomesa-metrics-micrometer/src/test/scala/org/locationtech/geomesa/metrics/micrometer/prometheus/PrometheusReporterTest.scala
@@ -7,6 +7,7 @@
***********************************************************************/
package org.locationtech.geomesa.metrics.micrometer
+package prometheus
import com.typesafe.config.ConfigFactory
import io.micrometer.core.instrument.util.IOUtils