diff --git a/jmx-metrics/src/integrationTest/java/io/opentelemetry/contrib/jmxmetrics/target_systems/SolrIntegrationTest.java b/jmx-metrics/src/integrationTest/java/io/opentelemetry/contrib/jmxmetrics/target_systems/SolrIntegrationTest.java index 57dba7a5c..78fc82acd 100644 --- a/jmx-metrics/src/integrationTest/java/io/opentelemetry/contrib/jmxmetrics/target_systems/SolrIntegrationTest.java +++ b/jmx-metrics/src/integrationTest/java/io/opentelemetry/contrib/jmxmetrics/target_systems/SolrIntegrationTest.java @@ -43,18 +43,18 @@ void endToEnd() { metric, "solr.document.count", "The total number of indexed documents.", - "{documents}", + "{document}", attrs -> attrs.containsOnly(entry("core", "gettingstarted"))), metric -> assertSumWithAttributes( metric, "solr.index.size", "The total index size.", - "by", + "By", attrs -> attrs.containsOnly(entry("core", "gettingstarted"))), metric -> assertSolrRequestSumMetric( - metric, "solr.request.count", "The number of queries made.", "{queries}"), + metric, "solr.request.count", "The number of queries made.", "{query}"), metric -> assertSolrRequestGaugeMetric( metric, @@ -66,37 +66,31 @@ void endToEnd() { metric, "solr.request.error.count", "The number of queries resulting in an error.", - "{queries}"), + "{query}"), metric -> assertSolrRequestSumMetric( metric, "solr.request.timeout.count", "The number of queries resulting in a timeout.", - "{queries}"), + "{query}"), metric -> assertSolrCacheSumMetric( metric, "solr.cache.eviction.count", "The number of evictions from a cache.", - "{evictions}"), + "{eviction}"), metric -> assertSolrCacheSumMetric( - metric, "solr.cache.hit.count", "The number of hits for a cache.", "{hits}"), + metric, "solr.cache.hit.count", "The number of hits for a cache.", "{hit}"), metric -> assertSolrCacheSumMetric( - metric, - "solr.cache.insert.count", - "The number of inserts to a cache.", - "{inserts}"), + metric, "solr.cache.insert.count", "The number of inserts to a cache.", "{insert}"), metric -> assertSolrCacheSumMetric( - metric, - "solr.cache.lookup.count", - "The number of lookups to a cache.", - "{lookups}"), + metric, "solr.cache.lookup.count", "The number of lookups to a cache.", "{lookup}"), metric -> assertSolrCacheSumMetric( - metric, "solr.cache.size", "The size of the cache occupied in memory.", "by")); + metric, "solr.cache.size", "The size of the cache occupied in memory.", "By")); } private void assertSolrRequestSumMetric( diff --git a/jmx-metrics/src/main/resources/target-systems/solr.groovy b/jmx-metrics/src/main/resources/target-systems/solr.groovy index 91c5bc14b..48467deec 100644 --- a/jmx-metrics/src/main/resources/target-systems/solr.groovy +++ b/jmx-metrics/src/main/resources/target-systems/solr.groovy @@ -15,18 +15,18 @@ */ def beanSolrCoreSearcherNumDocs = otel.mbeans("solr:dom1=core,dom2=*,category=SEARCHER,scope=searcher,name=numDocs") -otel.instrument(beanSolrCoreSearcherNumDocs, "solr.document.count", "The total number of indexed documents.", "{documents}", +otel.instrument(beanSolrCoreSearcherNumDocs, "solr.document.count", "The total number of indexed documents.", "{document}", ["core" : { mbean -> mbean.name().getKeyProperty("dom2") }], "Value", otel.&longUpDownCounterCallback) def beanSolrCoreIndexSize = otel.mbeans("solr:dom1=core,dom2=*,category=INDEX,name=sizeInBytes") -otel.instrument(beanSolrCoreIndexSize, "solr.index.size", "The total index size.", "by", +otel.instrument(beanSolrCoreIndexSize, "solr.index.size", "The total index size.", "By", ["core" : { mbean -> mbean.name().getKeyProperty("dom2") }], "Value", otel.&longUpDownCounterCallback) def beanSolrCoreRequests = otel.mbeans(["solr:dom1=core,dom2=*,category=QUERY,scope=*,name=requests", "solr:dom1=core,dom2=*,category=UPDATE,scope=*,name=requests"]) -otel.instrument(beanSolrCoreRequests, "solr.request.count", "The number of queries made.", "{queries}", +otel.instrument(beanSolrCoreRequests, "solr.request.count", "The number of queries made.", "{query}", ["core" : { mbean -> mbean.name().getKeyProperty("dom2") }, "type" : { mbean -> mbean.name().getKeyProperty("category") }, "handler" : { mbean -> mbean.name().getKeyProperty("scope") }], @@ -44,7 +44,7 @@ otel.instrument(beanSolrCoreRequestTimes, "solr.request.time.average", def beanSolrCoreErrors = otel.mbeans(["solr:dom1=core,dom2=*,category=QUERY,scope=*,name=errors", "solr:dom1=core,dom2=*,category=UPDATE,scope=*,name=errors"]) -otel.instrument(beanSolrCoreErrors, "solr.request.error.count", "The number of queries resulting in an error.", "{queries}", +otel.instrument(beanSolrCoreErrors, "solr.request.error.count", "The number of queries resulting in an error.", "{query}", ["core" : { mbean -> mbean.name().getKeyProperty("dom2") }, "type" : { mbean -> mbean.name().getKeyProperty("category") }, "handler" : { mbean -> mbean.name().getKeyProperty("scope") }], @@ -52,30 +52,30 @@ otel.instrument(beanSolrCoreErrors, "solr.request.error.count", "The number of q def beanSolrCoreTimeouts = otel.mbeans(["solr:dom1=core,dom2=*,category=QUERY,scope=*,name=timeouts", "solr:dom1=core,dom2=*,category=UPDATE,scope=*,name=timeouts"]) -otel.instrument(beanSolrCoreTimeouts, "solr.request.timeout.count", "The number of queries resulting in a timeout.", "{queries}", +otel.instrument(beanSolrCoreTimeouts, "solr.request.timeout.count", "The number of queries resulting in a timeout.", "{query}", ["core" : { mbean -> mbean.name().getKeyProperty("dom2") }, "type" : { mbean -> mbean.name().getKeyProperty("category") }, "handler" : { mbean -> mbean.name().getKeyProperty("scope") }], "Count", otel.&longCounterCallback) def beanSolrCoreQueryResultsCache = otel.mbeans("solr:dom1=core,dom2=*,category=CACHE,scope=*,name=queryResultCache") -otel.instrument(beanSolrCoreQueryResultsCache, "solr.cache.eviction.count", "The number of evictions from a cache.", "{evictions}", +otel.instrument(beanSolrCoreQueryResultsCache, "solr.cache.eviction.count", "The number of evictions from a cache.", "{eviction}", ["core" : { mbean -> mbean.name().getKeyProperty("dom2") }, "cache" : { mbean -> mbean.name().getKeyProperty("scope") }], "cumulative_evictions", otel.&longCounterCallback) -otel.instrument(beanSolrCoreQueryResultsCache, "solr.cache.hit.count", "The number of hits for a cache.", "{hits}", +otel.instrument(beanSolrCoreQueryResultsCache, "solr.cache.hit.count", "The number of hits for a cache.", "{hit}", ["core" : { mbean -> mbean.name().getKeyProperty("dom2") }, "cache" : { mbean -> mbean.name().getKeyProperty("scope") }], "cumulative_hits", otel.&longCounterCallback) -otel.instrument(beanSolrCoreQueryResultsCache, "solr.cache.insert.count", "The number of inserts to a cache.", "{inserts}", +otel.instrument(beanSolrCoreQueryResultsCache, "solr.cache.insert.count", "The number of inserts to a cache.", "{insert}", ["core" : { mbean -> mbean.name().getKeyProperty("dom2") }, "cache" : { mbean -> mbean.name().getKeyProperty("scope") }], "cumulative_inserts", otel.&longCounterCallback) -otel.instrument(beanSolrCoreQueryResultsCache, "solr.cache.lookup.count", "The number of lookups to a cache.", "{lookups}", +otel.instrument(beanSolrCoreQueryResultsCache, "solr.cache.lookup.count", "The number of lookups to a cache.", "{lookup}", ["core" : { mbean -> mbean.name().getKeyProperty("dom2") }, "cache" : { mbean -> mbean.name().getKeyProperty("scope") }], "cumulative_lookups", otel.&longCounterCallback) -otel.instrument(beanSolrCoreQueryResultsCache, "solr.cache.size", "The size of the cache occupied in memory.", "by", +otel.instrument(beanSolrCoreQueryResultsCache, "solr.cache.size", "The size of the cache occupied in memory.", "By", ["core" : { mbean -> mbean.name().getKeyProperty("dom2") }, "cache" : { mbean -> mbean.name().getKeyProperty("scope") }], "ramBytesUsed", otel.&longUpDownCounterCallback) diff --git a/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/SolrIntegrationTest.java b/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/SolrIntegrationTest.java new file mode 100644 index 000000000..fbec81e2e --- /dev/null +++ b/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/SolrIntegrationTest.java @@ -0,0 +1,150 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.contrib.jmxscraper.target_systems; + +import static io.opentelemetry.contrib.jmxscraper.assertions.DataPointAttributes.attribute; +import static io.opentelemetry.contrib.jmxscraper.assertions.DataPointAttributes.attributeGroup; +import static io.opentelemetry.contrib.jmxscraper.assertions.DataPointAttributes.attributeWithAnyValue; + +import io.opentelemetry.contrib.jmxscraper.JmxScraperContainer; +import io.opentelemetry.contrib.jmxscraper.assertions.AttributeMatcher; +import io.opentelemetry.contrib.jmxscraper.assertions.AttributeMatcherGroup; +import java.nio.file.Path; +import java.time.Duration; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.Network; +import org.testcontainers.containers.wait.strategy.Wait; + +public class SolrIntegrationTest extends TargetSystemIntegrationTest { + + @Override + protected GenericContainer createTargetContainer(int jmxPort) { + return new GenericContainer<>("solr:8.8.2") + .withNetwork(Network.SHARED) + .withEnv("LOCAL_JMX", "no") + .withEnv("ENABLE_REMOTE_JMX_OPTS", "true") + .withEnv("RMI_PORT", Integer.toString(jmxPort)) + .withCommand("solr-precreate", "gettingstarted") + .withExposedPorts(jmxPort) + .withStartupTimeout(Duration.ofMinutes(2)) + .waitingFor(Wait.forListeningPort()); + } + + @Override + protected JmxScraperContainer customizeScraperContainer( + JmxScraperContainer scraper, GenericContainer target, Path tempDir) { + return scraper.withTargetSystem("solr"); + } + + @Override + protected MetricsVerifier createMetricsVerifier() { + + AttributeMatcher coreAttribute = attribute("core", "gettingstarted"); + AttributeMatcherGroup[] requestAttributes = requestAttributes(coreAttribute); + AttributeMatcherGroup cacheAttributes = + attributeGroup(coreAttribute, attribute("cache", "searcher")); + + return MetricsVerifier.create() + .add( + "solr.document.count", + metric -> + metric + .hasDescription("The total number of indexed documents.") + .hasUnit("{document}") + .isUpDownCounter() + .hasDataPointsWithOneAttribute(coreAttribute)) + .add( + "solr.index.size", + metric -> + metric + .hasDescription("The total index size.") + .hasUnit("By") + .isUpDownCounter() + .hasDataPointsWithOneAttribute(coreAttribute)) + .add( + "solr.request.count", + metric -> + metric + .hasDescription("The number of queries made.") + .hasUnit("{query}") + .isCounter() + .hasDataPointsWithAttributes(requestAttributes)) + .add( + "solr.request.time.average", + metric -> + metric + .hasDescription( + "The average time of a query, based on Solr's histogram configuration.") + .hasUnit("ms") + .isGauge() + .hasDataPointsWithAttributes(requestAttributes)) + .add( + "solr.request.error.count", + metric -> + metric + .hasDescription("The number of queries resulting in an error.") + .hasUnit("{query}") + .isCounter() + .hasDataPointsWithAttributes(requestAttributes)) + .add( + "solr.request.timeout.count", + metric -> + metric + .hasDescription("The number of queries resulting in a timeout.") + .hasUnit("{query}") + .isCounter() + .hasDataPointsWithAttributes(requestAttributes)) + .add( + "solr.cache.eviction.count", + metric -> + metric + .hasDescription("The number of evictions from a cache.") + .hasUnit("{eviction}") + .isCounter() + .hasDataPointsWithAttributes(cacheAttributes)) + .add( + "solr.cache.hit.count", + metric -> + metric + .hasDescription("The number of hits for a cache.") + .hasUnit("{hit}") + .isCounter() + .hasDataPointsWithAttributes(cacheAttributes)) + .add( + "solr.cache.insert.count", + metric -> + metric + .hasDescription("The number of inserts to a cache.") + .hasUnit("{insert}") + .isCounter() + .hasDataPointsWithAttributes(cacheAttributes)) + .add( + "solr.cache.lookup.count", + metric -> + metric + .hasDescription("The number of lookups to a cache.") + .hasUnit("{lookup}") + .isCounter() + .hasDataPointsWithAttributes(cacheAttributes)) + .add( + "solr.cache.size", + metric -> + metric + .hasDescription("The size of the cache occupied in memory.") + .hasUnit("By") + .isUpDownCounter() + .hasDataPointsWithAttributes(cacheAttributes)); + } + + private static AttributeMatcherGroup[] requestAttributes(AttributeMatcher coreAttribute) { + // ignore actual 'handler' values due to high cardinality (10+) and an exact matching makes test + // flaky + return new AttributeMatcherGroup[] { + attributeGroup(coreAttribute, attributeWithAnyValue("handler"), attribute("type", "QUERY")), + attributeGroup(coreAttribute, attributeWithAnyValue("handler"), attribute("type", "UPDATE")) + }; + } +} diff --git a/jmx-scraper/src/main/resources/jvm.yaml b/jmx-scraper/src/main/resources/jvm.yaml index b706ff7c0..733e679fd 100644 --- a/jmx-scraper/src/main/resources/jvm.yaml +++ b/jmx-scraper/src/main/resources/jvm.yaml @@ -7,7 +7,7 @@ rules: LoadedClassCount: metric: jvm.classes.loaded type: gauge - unit: '{class}' + unit: "{class}" desc: number of loaded classes - bean: java.lang:type=GarbageCollector,name=* @@ -15,7 +15,7 @@ rules: CollectionCount: metric: jvm.gc.collections.count type: counter - unit: '{collection}' + unit: "{collection}" desc: total number of collections that have occurred metricAttribute: name: param(name) @@ -87,5 +87,5 @@ rules: mapping: ThreadCount: metric: jvm.threads.count - unit: '{thread}' + unit: "{thread}" desc: number of threads diff --git a/jmx-scraper/src/main/resources/solr.yaml b/jmx-scraper/src/main/resources/solr.yaml new file mode 100644 index 000000000..0a414ee27 --- /dev/null +++ b/jmx-scraper/src/main/resources/solr.yaml @@ -0,0 +1,110 @@ +--- + +rules: + + - bean: solr:dom1=core,dom2=*,category=SEARCHER,scope=searcher,name=numDocs + mapping: + Value: + metric: solr.document.count + type: updowncounter + unit: "{document}" + desc: The total number of indexed documents. + metricAttribute: + core: param(dom2) + + - bean: solr:dom1=core,dom2=*,category=INDEX,name=sizeInBytes + mapping: + Value: + metric: solr.index.size + type: updowncounter + unit: By + desc: The total index size. + metricAttribute: + core: param(dom2) + + - beans: + - solr:dom1=core,dom2=*,category=QUERY,scope=*,name=requests + - solr:dom1=core,dom2=*,category=UPDATE,scope=*,name=requests + mapping: + Count: + metric: solr.request.count + type: counter + unit: "{query}" + desc: The number of queries made. + metricAttribute: + core: param(dom2) + type: param(category) + handler: param(scope) + + - beans: + - solr:dom1=core,dom2=*,category=QUERY,scope=*,name=requestTimes + - solr:dom1=core,dom2=*,category=UPDATE,scope=*,name=requestTimes + mapping: + Mean: + metric: solr.request.time.average + type: gauge + unit: ms + desc: "The average time of a query, based on Solr's histogram configuration." + metricAttribute: + core: param(dom2) + type: param(category) + handler: param(scope) + + - beans: + - solr:dom1=core,dom2=*,category=QUERY,scope=*,name=errors + - solr:dom1=core,dom2=*,category=UPDATE,scope=*,name=errors + mapping: + Count: + metric: solr.request.error.count + type: counter + unit: "{query}" + desc: The number of queries resulting in an error. + metricAttribute: + core: param(dom2) + type: param(category) + handler: param(scope) + + - beans: + - solr:dom1=core,dom2=*,category=QUERY,scope=*,name=timeouts + - solr:dom1=core,dom2=*,category=UPDATE,scope=*,name=timeouts + mapping: + Count: + metric: solr.request.timeout.count + type: counter + unit: "{query}" + desc: The number of queries resulting in a timeout. + metricAttribute: + core: param(dom2) + type: param(category) + handler: param(scope) + + - bean: solr:dom1=core,dom2=*,category=CACHE,scope=*,name=queryResultCache + prefix: solr.cache. + type: counter + metricAttribute: + core: param(dom2) + cache: param(scope) + mapping: + cumulative_evictions: + metric: eviction.count + unit: "{eviction}" + desc: The number of evictions from a cache. + cumulative_hits: + metric: hit.count + unit: "{hit}" + desc: The number of hits for a cache. + cumulative_inserts: + metric: insert.count + unit: "{insert}" + desc: The number of inserts to a cache. + cumulative_lookups: + metric: lookup.count + unit: "{lookup}" + desc: The number of lookups to a cache. + ramBytesUsed: + type: updowncounter + metric: size + unit: By + desc: The size of the cache occupied in memory. + +