From 8a97c480a2da7ca57228148817db1bd3589a6b13 Mon Sep 17 00:00:00 2001 From: armstrmi Date: Tue, 11 Jan 2022 14:29:09 -0500 Subject: [PATCH] wip --- jmx-metrics/docs/target-systems/activemq.md | 54 ++++++++ .../jmxmetrics/AbstractIntegrationTest.java | 2 +- .../ActivemqIntegrationTest.java | 66 ++++++++++ .../integrationTest/resources/activemq/env | 117 +++++++++++++++++ .../target-systems/activemq.properties | 7 + .../contrib/jmxmetrics/JmxConfig.java | 3 +- .../resources/target-systems/activemq.groovy | 124 ++++++++++++++++++ .../contrib/jmxmetrics/JmxConfigTest.java | 5 +- 8 files changed, 374 insertions(+), 4 deletions(-) create mode 100644 jmx-metrics/docs/target-systems/activemq.md create mode 100644 jmx-metrics/src/integrationTest/java/io/opentelemetry/contrib/jmxmetrics/target_systems/ActivemqIntegrationTest.java create mode 100644 jmx-metrics/src/integrationTest/resources/activemq/env create mode 100644 jmx-metrics/src/integrationTest/resources/target-systems/activemq.properties create mode 100644 jmx-metrics/src/main/resources/target-systems/activemq.groovy diff --git a/jmx-metrics/docs/target-systems/activemq.md b/jmx-metrics/docs/target-systems/activemq.md new file mode 100644 index 000000000..4cb93e8d2 --- /dev/null +++ b/jmx-metrics/docs/target-systems/activemq.md @@ -0,0 +1,54 @@ +# ActiveMQ Metrics + +The JMX Metric Gatherer provides built in Tomcat metric gathering capabilities. +These metrics are sourced from: https://activemq.apache.org/jmx + +### Metrics + +* Name: `activemq.consumer.count` +* Description: The number of consumers currently reading from the broker. +* Unit: `{consumers}` +* Labels: `destination` +* Instrument Type: LongUpDownCounterCallback + + +* Name: `activemq.producer.count` +* Description: The number of producers currently attached to the broker. +* Unit: `{producers}` +* Labels: `destination` +* Instrument Type: LongUpDownCounterCallback + + +* Name: `activemq.memory.usage` +* Description: The percentage of configured memory used. +* Unit: `%` +* Labels: `destination` +* Instrument Type: DoubleValueCallback + + +* Name: `tomcat.traffic` +* Description: The number of bytes transmitted and received. +* Unit: `by` +* Labels: `proto_handler`, `direction` +* Instrument Type: LongCounterCallback + + +* Name: `tomcat.threads` +* Description: The number of threads. +* Unit: `threads` +* Labels: `proto_handler`, `state` +* Instrument Type: LongValueCallback + + +* Name: `tomcat.max_time` +* Description: Maximum time to process a request. +* Unit: `ms` +* Labels: `proto_handler` +* Instrument Type: LongCounterCallback + + +* Name: `tomcat.request_count` +* Description: The total requests. +* Unit: `requests` +* Labels: `proto_handler` +* Instrument Type: LongCounterCallback diff --git a/jmx-metrics/src/integrationTest/java/io/opentelemetry/contrib/jmxmetrics/AbstractIntegrationTest.java b/jmx-metrics/src/integrationTest/java/io/opentelemetry/contrib/jmxmetrics/AbstractIntegrationTest.java index f368ece4d..b14407abf 100644 --- a/jmx-metrics/src/integrationTest/java/io/opentelemetry/contrib/jmxmetrics/AbstractIntegrationTest.java +++ b/jmx-metrics/src/integrationTest/java/io/opentelemetry/contrib/jmxmetrics/AbstractIntegrationTest.java @@ -114,7 +114,7 @@ void beforeEach() { @SafeVarargs protected final void waitAndAssertMetrics(Consumer... assertions) { await() - .atMost(Duration.ofSeconds(30)) + .atMost(Duration.ofMinutes(2)) .untilAsserted( () -> { List metrics = diff --git a/jmx-metrics/src/integrationTest/java/io/opentelemetry/contrib/jmxmetrics/target_systems/ActivemqIntegrationTest.java b/jmx-metrics/src/integrationTest/java/io/opentelemetry/contrib/jmxmetrics/target_systems/ActivemqIntegrationTest.java new file mode 100644 index 000000000..14421f3be --- /dev/null +++ b/jmx-metrics/src/integrationTest/java/io/opentelemetry/contrib/jmxmetrics/target_systems/ActivemqIntegrationTest.java @@ -0,0 +1,66 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.contrib.jmxmetrics.target_systems; + +import static org.assertj.core.api.Assertions.entry; + +import io.opentelemetry.contrib.jmxmetrics.AbstractIntegrationTest; +import java.time.Duration; +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.containers.Network; +import org.testcontainers.containers.wait.strategy.Wait; +import org.testcontainers.images.builder.ImageFromDockerfile; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.utility.MountableFile; + +class ActivemqIntegrationTest extends AbstractIntegrationTest { + + ActivemqIntegrationTest() { + super(/* configFromStdin= */ false, "target-systems/activemq.properties"); + } + + @Container + GenericContainer activemq = + new GenericContainer<>( + new ImageFromDockerfile() + .withDockerfileFromBuilder( + builder -> + builder + .from("rmohr/activemq:5.15.9-alpine") + .expose(10991) + .env( + "ACTIVEMQ_JMX_OPTS", + "-Djava.rmi.server.hostname=localhost -Dcom.sun.management.jmxremote.port=10991 -Dcom.sun.management.jmxremote.rmi.port=10991 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false") + .env("ACTIVEMQ_JMX", "10991") + // .env("ACTIVEMQ_OPTS","$ACTIVEMQ_JMX_OPTS + // -Dhawtio.authenticationEnabled=false -Dhawtio.realm=activemq + // -Dhawtio.role=admins + // -Dhawtio.rolePrincipalClasses=org.apache.activemq.jaas.GroupPrincipal") + // + // .env("ACTIVEMQ_SUNJMX_START","-Dcom.sun.management.jmxremote") + .build())) + .withCopyFileToContainer( + MountableFile.forClasspathResource("activemq/env", 0400), "/opt/activemq/bin/env") + .withNetwork(Network.SHARED) + .withEnv("LOCAL_JMX", "no") + .withNetworkAliases("activemq") + .withExposedPorts(10991) + .withStartupTimeout(Duration.ofMinutes(2)) + .waitingFor(Wait.forListeningPort()); + + @Test + void endToEnd() { + waitAndAssertMetrics( + metric -> + assertGaugeWithAttributes( + metric, + "activemq.memory.usage", + "The percentage of configured memory used.", + "%", + attrs -> attrs.containsOnly(entry("destination", "client_test")))); + } +} diff --git a/jmx-metrics/src/integrationTest/resources/activemq/env b/jmx-metrics/src/integrationTest/resources/activemq/env new file mode 100644 index 000000000..bfcc87c6f --- /dev/null +++ b/jmx-metrics/src/integrationTest/resources/activemq/env @@ -0,0 +1,117 @@ + #!/bin/sh + # ------------------------------------------------------------------------ + # Licensed to the Apache Software Foundation (ASF) under one or more + # contributor license agreements. See the NOTICE file distributed with + # this work for additional information regarding copyright ownership. + # The ASF licenses this file to You under the Apache License, Version 2.0 + # (the "License"); you may not use this file except in compliance with + # the License. You may obtain a copy of the License at + # + # http://www.apache.org/licenses/LICENSE-2.0 + # + # Unless required by applicable law or agreed to in writing, software + # distributed under the License is distributed on an "AS IS" BASIS, + # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + # See the License for the specific language governing permissions and + # limitations under the License. + # ------------------------------------------------------------------------ + # + # Configuration file for running Apache Active MQ as standalone provider. + # + # This file overwrites the predefined settings of the sysv init-script. + # You can also use alternate location for default settings - + # invoke the init-script without a argument an review help section "Configuration of this script" + # /etc/default/activemq /.activemqrc /bin/env + + # Active MQ installation dirs + # ACTIVEMQ_HOME="/" + # ACTIVEMQ_BASE="$ACTIVEMQ_HOME" + # ACTIVEMQ_CONF="$ACTIVEMQ_BASE/conf" + # ACTIVEMQ_DATA="$ACTIVEMQ_BASE/data" + # ACTIVEMQ_TMP="$ACTIVEMQ_BASE/tmp" + + # Set jvm memory configuration (minimal/maximum amount of memory) + ACTIVEMQ_OPTS_MEMORY="-Xms64M -Xmx256M" + + if [ -z "$ACTIVEMQ_OPTS" ] ; then + ACTIVEMQ_OPTS="$ACTIVEMQ_OPTS_MEMORY -Djava.util.logging.config.file=logging.properties -Djava.security.auth.login.config=$ACTIVEMQ_CONF/login.config" + fi + + if [ -z "$ACTIVEMQ_OUT" ]; then + ACTIVEMQ_OUT="/dev/null" + fi + + # Uncomment to enable audit logging + #ACTIVEMQ_OPTS="$ACTIVEMQ_OPTS -Dorg.apache.activemq.audit=true" + + # Set jvm jmx configuration + # This enables jmx access over a configured jmx-tcp-port. + # You have to configure the first four settings if you run a ibm jvm, caused by the + # fact that IBM's jvm does not support VirtualMachine.attach(PID). + # JMX access is needed for quering a running activemq instance to gain data or to + # trigger management operations. + # + # Example for ${ACTIVEMQ_CONF}/jmx.access: + # --- + # # The "monitorRole" role has readonly access. + # # The "controlRole" role has readwrite access. + # monitorRole readonly + # controlRole readwrite + # --- + # + # Example for ${ACTIVEMQ_CONF}/jmx.password: + # --- + # # The "monitorRole" role has password "abc123". + # # # The "controlRole" role has password "abcd1234". + # monitorRole abc123 + # controlRole abcd1234 + # --- + # + # ACTIVEMQ_SUNJMX_START="$ACTIVEMQ_SUNJMX_START -Dcom.sun.management.jmxremote.port=11099 " + # ACTIVEMQ_SUNJMX_START="$ACTIVEMQ_SUNJMX_START -Dcom.sun.management.jmxremote.password.file=${ACTIVEMQ_CONF}/jmx.password" + # ACTIVEMQ_SUNJMX_START="$ACTIVEMQ_SUNJMX_START -Dcom.sun.management.jmxremote.access.file=${ACTIVEMQ_CONF}/jmx.access" + # ACTIVEMQ_SUNJMX_START="$ACTIVEMQ_SUNJMX_START -Dcom.sun.management.jmxremote.ssl=false" + # ACTIVEMQ_SUNJMX_START="$ACTIVEMQ_SUNJMX_START -Dcom.sun.management.jmxremote" + ACTIVEMQ_SUNJMX_START="$ACTIVEMQ_SUNJMX_START -Dcom.sun.management.jmxremote" + + # Set jvm jmx configuration for controlling the broker process + # You only have to configure the first four settings if you run a ibm jvm, caused by the + # fact that IBM's jvm does not support VirtualMachine.attach(PID) + # (see also com.sun.management.jmxremote.port, .jmx.password.file and .jmx.access.file ) + #ACTIVEMQ_SUNJMX_CONTROL="--jmxurl service:jmx:rmi:///jndi/rmi://127.0.0.1:1099/jmxrmi --jmxuser controlRole --jmxpassword abcd1234" + ACTIVEMQ_SUNJMX_CONTROL="" + + # Specify the queue manager URL for using "browse" option of sysv initscript + if [ -z "$ACTIVEMQ_QUEUEMANAGERURL" ]; then + ACTIVEMQ_QUEUEMANAGERURL="--amqurl tcp://localhost:61616" + fi + + # Set additional JSE arguments + if [ -z "$ACTIVEMQ_SSL_OPTS" ] ; then + #ACTIVEMQ_SSL_OPTS="-Djava.security.properties=$ACTIVEMQ_CONF/java.security" + ACTIVEMQ_SSL_OPTS="" + fi + + # Uncomment to enable remote debugging + #ACTIVEMQ_DEBUG_OPTS="-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005" + + # ActiveMQ tries to shutdown the broker by jmx, + # after a specified number of seconds send SIGKILL + if [ -z "$ACTIVEMQ_KILL_MAXSECONDS" ]; then + ACTIVEMQ_KILL_MAXSECONDS=30 + fi + + # Configure a user with non root privileges, if no user is specified do not change user + # (the entire activemq installation should be owned by this user) + ACTIVEMQ_USER="" + + # location of the pidfile + # ACTIVEMQ_PIDFILE="$ACTIVEMQ_DATA/activemq.pid" + + # Location of the java installation + # Specify the location of your java installation using JAVA_HOME, or specify the + # path to the "java" binary using JAVACMD + # (set JAVACMD to "auto" for automatic detection) + #JAVA_HOME="" + JAVACMD="auto" + ACTIVEMQ_OPTS="$ACTIVEMQ_OPTS $ACTIVEMQ_JMX_OPTS -Dhawtio.authenticationEnabled=false -Dhawtio.realm=activemq -Dhawtio.role=admins -Dhawtio.rolePrincipalClasses=org.apache.activemq.jaas.GroupPrincipal" diff --git a/jmx-metrics/src/integrationTest/resources/target-systems/activemq.properties b/jmx-metrics/src/integrationTest/resources/target-systems/activemq.properties new file mode 100644 index 000000000..e69b6ba2e --- /dev/null +++ b/jmx-metrics/src/integrationTest/resources/target-systems/activemq.properties @@ -0,0 +1,7 @@ +otel.jmx.interval.milliseconds = 3000 +otel.metrics.exporter = otlp +otel.jmx.service.url = service:jmx:rmi:///jndi/rmi://activemq:10991/jmxrmi +otel.jmx.target.system = activemq + +# these will be overridden by cmd line +otel.exporter.otlp.endpoint = http://host.testcontainers.internal diff --git a/jmx-metrics/src/main/groovy/io/opentelemetry/contrib/jmxmetrics/JmxConfig.java b/jmx-metrics/src/main/groovy/io/opentelemetry/contrib/jmxmetrics/JmxConfig.java index 29789d1c5..dbf630d76 100644 --- a/jmx-metrics/src/main/groovy/io/opentelemetry/contrib/jmxmetrics/JmxConfig.java +++ b/jmx-metrics/src/main/groovy/io/opentelemetry/contrib/jmxmetrics/JmxConfig.java @@ -33,7 +33,8 @@ class JmxConfig { static final String JMX_REALM = PREFIX + "jmx.realm"; static final List AVAILABLE_TARGET_SYSTEMS = - Arrays.asList("cassandra", "jvm", "kafka", "kafka-consumer", "kafka-producer", "tomcat"); + Arrays.asList( + "cassandra", "jvm", "kafka", "kafka-consumer", "kafka-producer", "tomcat", "activemq"); final String serviceUrl; final String groovyScript; diff --git a/jmx-metrics/src/main/resources/target-systems/activemq.groovy b/jmx-metrics/src/main/resources/target-systems/activemq.groovy new file mode 100644 index 000000000..cce7c5046 --- /dev/null +++ b/jmx-metrics/src/main/resources/target-systems/activemq.groovy @@ -0,0 +1,124 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +def activemqMetrics = otel.mbeans( + [ + "org.apache.activemq:type=Broker,brokerName=*,destinationType=Queue,destinationName=*", + "org.apache.activemq:type=Broker,brokerName=*,destinationType=Topic,destinationName=*" + ] +) + + +otel.instrument(activemqMetrics, + "activemq.producer.count", + "The number of producers currently attached to the broker.", + "{producers}", + ["destination" : { mbean -> mbean.name().getKeyProperty("destinationName") }], + "ProducerCount", + otel.&longUpDownCounterCallback) +// +//otel.instrument(activemqMetrics, +// "activemq.consumer.count", +// "The number of consumers currently reading from the broker.", +// "{consumers}", +// ["destination" : { mbean -> mbean.name().getKeyProperty("destinationName") }], +// "ConsumerCount", +// otel.&longUpDownCounterCallback) +// +//otel.instrument(activemqMetrics, +// "activemq.memory.usage", +// "The percentage of configured memory used.", +// "%", +// ["destination" : { mbean -> mbean.name().getKeyProperty("destinationName") }], +// "MemoryPercentUsage", +// otel.&doubleValueCallback) +// +//otel.instrument(activemqMetrics, +// "activemq.memory.usage", +// "The percentage of configured memory used.", +// "%", +// ["destination" : { mbean -> mbean.name().getKeyProperty("destinationName") }], +// "MemoryPercentUsage", +// otel.&doubleValueCallback) +// +//otel.instrument(activemqMetrics, +// "activemq.message.current", +// "The current number of messages waiting to be consumed.", +// "{messages}", +// ["destination" : { mbean -> mbean.name().getKeyProperty("destinationName") }], +// "QueueSize", +// otel.&longUpDownCounterCallback) +// +//otel.instrument(activemqMetrics, +// "activemq.message.expired", +// "The total number of messages not delivered because they expired.", +// "{messages}", +// ["destination" : { mbean -> mbean.name().getKeyProperty("destinationName") }], +// "ExpiredCount", +// otel.&longCounterCallback) +// +//otel.instrument(activemqMetrics, +// "activemq.message.enqueued", +// "The total number of messages received by the broker.", +// "{messages}", +// ["destination" : { mbean -> mbean.name().getKeyProperty("destinationName") }], +// "EnqueueCount", +// otel.&longCounterCallback) +// +//otel.instrument(activemqMetrics, +// "activemq.message.dequeued", +// "The total number of messages delivered to consumers.", +// "{messages}", +// ["destination" : { mbean -> mbean.name().getKeyProperty("destinationName") }], +// "DequeueCount", +// otel.&longCounterCallback) +// +//otel.instrument(activemqMetrics, +// "activemq.message.wait_time.avg", +// "The average time a message was held on a destination.", +// "ms", +// ["destination" : { mbean -> mbean.name().getKeyProperty("destinationName") }], +// "AverageEnqueueTime", +// otel.&doubleValueCallback) +// +// +// +// +//def activemqMetricsNoDestination = otel.mbean( +// "org.apache.activemq:type=Broker,brokerName=*" +//) +// +//otel.instrument(activemqMetricsNoDestination, +// "activemq.connection.count", +// "The total number of current connections.", +// "{connections}", +// "CurrentConnectionsCount", +// otel.&longUpDownCounterCallback) +// +//otel.instrument(activemqMetricsNoDestination, +// "activemq.disk.store_usage", +// "The percentage of configured disk used for persistent messages.", +// "%", +// "StorePercentUsage", +// otel.&doubleValueCallback) +// +//otel.instrument(activemqMetricsNoDestination, +// "activemq.disk.temp_usage", +// "The percentage of configured disk used for non-persistent messages.", +// "%", +// "TempPercentUsage", +// otel.&doubleValueCallback) \ No newline at end of file diff --git a/jmx-metrics/src/test/java/io/opentelemetry/contrib/jmxmetrics/JmxConfigTest.java b/jmx-metrics/src/test/java/io/opentelemetry/contrib/jmxmetrics/JmxConfigTest.java index 8a2cdc600..42c5ecd74 100644 --- a/jmx-metrics/src/test/java/io/opentelemetry/contrib/jmxmetrics/JmxConfigTest.java +++ b/jmx-metrics/src/test/java/io/opentelemetry/contrib/jmxmetrics/JmxConfigTest.java @@ -16,7 +16,8 @@ class JmxConfigTest { @Test void staticValues() { assertThat(JmxConfig.AVAILABLE_TARGET_SYSTEMS) - .containsOnly("cassandra", "jvm", "kafka", "kafka-consumer", "kafka-producer", "tomcat"); + .containsOnly( + "cassandra", "jvm", "kafka", "kafka-consumer", "kafka-producer", "tomcat", "activemq"); } @Test @@ -114,6 +115,6 @@ void invalidTargetSystem() { .isInstanceOf(ConfigurationException.class) .hasMessage( "[jvm, unavailabletargetsystem] must specify targets from " - + "[cassandra, jvm, kafka, kafka-consumer, kafka-producer, tomcat]"); + + "[cassandra, jvm, kafka, kafka-consumer, kafka-producer, tomcat, activemq]"); } }