diff --git a/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/JmxScraperContainer.java b/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/JmxScraperContainer.java index a1deb1717..125b56a97 100644 --- a/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/JmxScraperContainer.java +++ b/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/JmxScraperContainer.java @@ -28,7 +28,7 @@ public class JmxScraperContainer extends GenericContainer { private final Set customYamlFiles; public JmxScraperContainer(String otlpEndpoint) { - super("openjdk:8u272-jre-slim"); + super("openjdk:8u342-jre-slim"); String scraperJarPath = System.getProperty("shadow.jar.path"); assertThat(scraperJarPath).isNotNull(); diff --git a/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/CassandraIntegrationTest.java b/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/CassandraIntegrationTest.java new file mode 100644 index 000000000..756cbd502 --- /dev/null +++ b/jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/target_systems/CassandraIntegrationTest.java @@ -0,0 +1,172 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.contrib.jmxscraper.target_systems; + +import static io.opentelemetry.contrib.jmxscraper.target_systems.MetricAssertions.assertGauge; +import static io.opentelemetry.contrib.jmxscraper.target_systems.MetricAssertions.assertSum; +import static io.opentelemetry.contrib.jmxscraper.target_systems.MetricAssertions.assertSumWithAttributes; +import static org.assertj.core.api.Assertions.entry; + +import io.opentelemetry.contrib.jmxscraper.JmxScraperContainer; +import java.time.Duration; +import java.util.Arrays; +import java.util.List; +import java.util.function.Consumer; +import org.assertj.core.api.MapAssert; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.images.builder.ImageFromDockerfile; + +public class CassandraIntegrationTest extends TargetSystemIntegrationTest { + + @Override + protected GenericContainer createTargetContainer(int jmxPort) { + return new GenericContainer<>( + new ImageFromDockerfile() + .withDockerfileFromBuilder(builder -> builder.from("cassandra:5.0.2").build())) + .withEnv( + "JVM_EXTRA_OPTS", + " -Dcassandra.jmx.remote.port=" + + jmxPort + + " -Dcom.sun.management.jmxremote.rmi.port=" + + jmxPort + + " -Dcom.sun.management.jmxremote.local.only=false" + + " -Dcom.sun.management.jmxremote.ssl=false" + + " -Dcom.sun.management.jmxremote.authenticate=false") + .withStartupTimeout(Duration.ofMinutes(2)) + .waitingFor(Wait.forLogMessage(".*Startup complete.*", 1)); + } + + @Override + protected JmxScraperContainer customizeScraperContainer(JmxScraperContainer scraper) { + return scraper.withTargetSystem("cassandra"); + } + + @Override + protected void verifyMetrics() { + waitAndAssertMetrics( + metric -> + assertGauge( + metric, + "cassandra.client.request.range_slice.latency.50p", + "Token range read request latency - 50th percentile", + "us"), + metric -> + assertGauge( + metric, + "cassandra.client.request.range_slice.latency.99p", + "Token range read request latency - 99th percentile", + "us"), + metric -> + assertGauge( + metric, + "cassandra.client.request.range_slice.latency.max", + "Maximum token range read request latency", + "us"), + metric -> + assertGauge( + metric, + "cassandra.client.request.read.latency.50p", + "Standard read request latency - 50th percentile", + "us"), + metric -> + assertGauge( + metric, + "cassandra.client.request.read.latency.99p", + "Standard read request latency - 99th percentile", + "us"), + metric -> + assertGauge( + metric, + "cassandra.client.request.read.latency.max", + "Maximum standard read request latency", + "us"), + metric -> + assertGauge( + metric, + "cassandra.client.request.write.latency.50p", + "Regular write request latency - 50th percentile", + "us"), + metric -> + assertGauge( + metric, + "cassandra.client.request.write.latency.99p", + "Regular write request latency - 99th percentile", + "us"), + metric -> + assertGauge( + metric, + "cassandra.client.request.write.latency.max", + "Maximum regular write request latency", + "us"), + metric -> + assertSum( + metric, + "cassandra.compaction.tasks.completed", + "Number of completed compactions since server [re]start", + "1"), + metric -> + assertGauge( + metric, + "cassandra.compaction.tasks.pending", + "Estimated number of compactions remaining to perform", + "1"), + metric -> + assertSum( + metric, + "cassandra.storage.load.count", + "Size of the on disk data size this node manages", + "by", + /* isMonotonic= */ false), + metric -> + assertSum( + metric, + "cassandra.storage.total_hints.count", + "Number of hint messages written to this node since [re]start", + "1"), + metric -> + assertSum( + metric, + "cassandra.storage.total_hints.in_progress.count", + "Number of hints attempting to be sent currently", + "1", + /* isMonotonic= */ false), + metric -> + assertSumWithAttributes( + metric, + "cassandra.client.request.count", + "Number of requests by operation", + "1", + attrs -> attrs.containsOnly(entry("operation", "RangeSlice")), + attrs -> attrs.containsOnly(entry("operation", "Read")), + attrs -> attrs.containsOnly(entry("operation", "Write"))), + metric -> + assertSumWithAttributes( + metric, + "cassandra.client.request.error.count", + "Number of request errors by operation", + "1", + getRequestErrorCountAttributes())); + } + + @SuppressWarnings("unchecked") + private static Consumer>[] getRequestErrorCountAttributes() { + List operations = Arrays.asList("RangeSlice", "Read", "Write"); + List statuses = Arrays.asList("Timeout", "Failure", "Unavailable"); + + return operations.stream() + .flatMap( + op -> + statuses.stream() + .map( + st -> + (Consumer>) + attrs -> + attrs.containsOnly( + entry("operation", op), entry("status", st)))) + .toArray(Consumer[]::new); + } +} diff --git a/jmx-scraper/src/main/resources/cassandra.yaml b/jmx-scraper/src/main/resources/cassandra.yaml new file mode 100644 index 000000000..74ffdf44e --- /dev/null +++ b/jmx-scraper/src/main/resources/cassandra.yaml @@ -0,0 +1,146 @@ +rules: + + # Compaction task + - bean: org.apache.cassandra.metrics:type=Compaction,name=CompletedTasks + mapping: + Value: + metric: cassandra.compaction.tasks.completed + type: counter + unit: "1" + desc: Number of completed compactions since server [re]start + + - bean: org.apache.cassandra.metrics:type=Compaction,name=PendingTasks + mapping: + Value: + metric: cassandra.compaction.tasks.pending + type: gauge + unit: "1" + desc: Estimated number of compactions remaining to perform + + # Storage + - bean: org.apache.cassandra.metrics:type=Storage,name=Load + mapping: + Count: + metric: cassandra.storage.load.count + type: updowncounter + unit: by + desc: Size of the on disk data size this node manages + + - bean: org.apache.cassandra.metrics:type=Storage,name=TotalHints + mapping: + Count: + metric: cassandra.storage.total_hints.count + type: counter + unit: "1" + desc: Number of hint messages written to this node since [re]start + + - bean: org.apache.cassandra.metrics:type=Storage,name=TotalHintsInProgress + mapping: + Count: + metric: cassandra.storage.total_hints.in_progress.count + type: updowncounter + unit: "1" + desc: Number of hints attempting to be sent currently + + # Client latency + - bean: org.apache.cassandra.metrics:type=ClientRequest,scope=RangeSlice,name=Latency + prefix: cassandra.client.request.range_slice.latency. + unit: us + type: gauge + mapping: + 50thPercentile: + metric: 50p + desc: Token range read request latency - 50th percentile + 99thPercentile: + metric: 99p + desc: Token range read request latency - 99th percentile + Max: + metric: max + desc: Maximum token range read request latency + + - bean: org.apache.cassandra.metrics:type=ClientRequest,scope=Read,name=Latency + prefix: cassandra.client.request.read.latency. + unit: us + type: gauge + mapping: + 50thPercentile: + metric: 50p + desc: Standard read request latency - 50th percentile + 99thPercentile: + metric: 99p + desc: Standard read request latency - 99th percentile + Max: + metric: max + desc: Maximum standard read request latency + + - bean: org.apache.cassandra.metrics:type=ClientRequest,scope=Write,name=Latency + prefix: cassandra.client.request.write.latency. + unit: us + type: gauge + mapping: + 50thPercentile: + metric: 50p + desc: Regular write request latency - 50th percentile + 99thPercentile: + metric: 99p + desc: Regular write request latency - 99th percentile + Max: + metric: max + desc: Maximum regular write request latency + + # Client request count + - beans: + - org.apache.cassandra.metrics:type=ClientRequest,scope=RangeSlice,name=Latency + - org.apache.cassandra.metrics:type=ClientRequest,scope=Read,name=Latency + - org.apache.cassandra.metrics:type=ClientRequest,scope=Write,name=Latency + metricAttribute: + operation: param(scope) + mapping: + Count: + metric: cassandra.client.request.count + type: counter + unit: "1" + desc: Number of requests by operation + + # Client error count + - beans: + - org.apache.cassandra.metrics:type=ClientRequest,scope=RangeSlice,name=Unavailables + - org.apache.cassandra.metrics:type=ClientRequest,scope=Read,name=Unavailables + - org.apache.cassandra.metrics:type=ClientRequest,scope=Write,name=Unavailables + metricAttribute: + operation: param(scope) + status: const(Unavailable) + mapping: + Count: + metric: cassandra.client.request.error.count + type: counter + unit: "1" + desc: Number of request errors by operation + + - beans: + - org.apache.cassandra.metrics:type=ClientRequest,scope=RangeSlice,name=Timeouts + - org.apache.cassandra.metrics:type=ClientRequest,scope=Read,name=Timeouts + - org.apache.cassandra.metrics:type=ClientRequest,scope=Write,name=Timeouts + metricAttribute: + operation: param(scope) + status: const(Timeout) + mapping: + Count: + metric: cassandra.client.request.error.count + type: counter + unit: "1" + desc: Number of request errors by operation + + - beans: + - org.apache.cassandra.metrics:type=ClientRequest,scope=RangeSlice,name=Failures + - org.apache.cassandra.metrics:type=ClientRequest,scope=Read,name=Failures + - org.apache.cassandra.metrics:type=ClientRequest,scope=Write,name=Failures + metricAttribute: + operation: param(scope) + status: const(Failure) + mapping: + Count: + metric: cassandra.client.request.error.count + type: counter + unit: "1" + desc: Number of request errors by operation