You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by or...@apache.org on 2020/09/02 19:32:55 UTC
[qpid-broker-j] 08/10: QPID-8454:[Broker-J] Add Prometheus
integration
This is an automated email from the ASF dual-hosted git repository.
orudyy pushed a commit to branch 7.1.x
in repository https://gitbox.apache.org/repos/asf/qpid-broker-j.git
commit d547af8d35306a22207d6ae25af3ff94cd7c6893
Author: Dedeepya T <de...@yahoo.co.in>
AuthorDate: Tue Aug 25 15:11:50 2020 +0100
QPID-8454:[Broker-J] Add Prometheus integration
(cherry picked from commit e9794802df6ee22bd3b9dc55faffc26d74459c71)
---
.../dependency-verification/DEPENDENCIES_REFERENCE | 6 +
.../java/org/apache/qpid/server/model/Broker.java | 80 +++--
.../qpid/server/model/BrokerAttributeInjector.java | 20 +-
.../org/apache/qpid/server/model/BrokerLogger.java | 4 +-
.../model/ConfiguredObjectInjectedStatistic.java | 20 +-
.../model/ConfiguredObjectMethodStatistic.java | 12 +
.../server/model/ConfiguredObjectStatistic.java | 4 +
.../org/apache/qpid/server/model/Connection.java | 14 +-
.../org/apache/qpid/server/model/Consumer.java | 4 +-
.../org/apache/qpid/server/model/Exchange.java | 10 +-
.../apache/qpid/server/model/ManagedStatistic.java | 2 +
.../java/org/apache/qpid/server/model/Queue.java | 36 +--
.../java/org/apache/qpid/server/model/Session.java | 2 +-
.../qpid/server/model/VirtualHostLogger.java | 4 +-
.../apache/qpid/server/model/port/AmqpPort.java | 12 +-
.../apache/qpid/server/plugin/ContentFactory.java} | 13 +-
.../virtualhost/QueueManagingVirtualHost.java | 28 +-
.../hierarchy/InjectedAttributeTest.java | 16 +-
.../testmodels/hierarchy/TestAbstractCarImpl.java | 21 ++
.../hierarchy/TestAbstractEngineImpl.java | 7 +
.../hierarchy/TestAbstractInstrumentPanelImpl.java | 2 +
.../hierarchy/TestAbstractSensorImpl.java | 9 +
.../server/model/testmodels/hierarchy/TestCar.java | 14 +
.../model/testmodels/hierarchy/TestEngine.java | 6 +
.../model/testmodels/hierarchy/TestSensor.java | 5 +
.../hierarchy/TestTemperatureSensorImpl.java | 9 +
broker-plugins/management-http/pom.xml | 5 +
.../server/management/plugin/HttpManagement.java | 18 ++
.../plugin/HttpManagementConfiguration.java | 5 +
.../management/plugin/servlet/ContentServlet.java | 95 ++++++
.../plugin/servlet/rest/AbstractServlet.java | 21 +-
broker-plugins/prometheus-exporter/pom.xml | 70 ++++
.../IncludeDisabledStatisticPredicate.java | 26 +-
.../server/prometheus/IncludeMetricPredicate.java | 26 +-
.../prometheus/PrometheusContentFactory.java | 105 ++++++
.../qpid/server/prometheus/QpidCollector.java | 257 +++++++++++++++
.../prometheus/PrometheusContentFactoryTest.java | 176 ++++++++++
.../qpid/server/prometheus/QpidCollectorTest.java | 359 +++++++++++++++++++++
.../src/docbkx/Java-Broker-Management-Channels.xml | 1 +
.../channels/Java-Broker-Management-Metrics.xml | 48 +++
pom.xml | 20 ++
.../metrics/BrokerMetricsAuthenticationTest.java | 52 +++
.../qpid/tests/http/metrics/BrokerMetricsTest.java | 80 +++++
.../qpid/tests/http/metrics/TestMetricsHelper.java | 93 ++++++
.../tests/http/metrics/VirtualHostMetricsTest.java | 25 +-
45 files changed, 1712 insertions(+), 130 deletions(-)
diff --git a/apache-qpid-broker-j/src/main/assembly/dependency-verification/DEPENDENCIES_REFERENCE b/apache-qpid-broker-j/src/main/assembly/dependency-verification/DEPENDENCIES_REFERENCE
index c50e39d..d92150c 100644
--- a/apache-qpid-broker-j/src/main/assembly/dependency-verification/DEPENDENCIES_REFERENCE
+++ b/apache-qpid-broker-j/src/main/assembly/dependency-verification/DEPENDENCIES_REFERENCE
@@ -27,6 +27,10 @@ Apache Qpid Broker-J Bundles
From: 'an unknown organization'
- Guava: Google Core Libraries for Java (https://github.com/google/guava/guava) com.google.guava:guava:bundle:27.0-jre
License: The Apache Software License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt)
+ - Prometheus Java Simpleclient (http://github.com/prometheus/client_java/simpleclient) io.prometheus:simpleclient:bundle:0.9.0
+ License: The Apache Software License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt)
+ - Prometheus Java Simpleclient Common (http://github.com/prometheus/client_java/simpleclient_common) io.prometheus:simpleclient_common:bundle:0.9.0
+ License: The Apache Software License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0.txt)
- dgrid (http://webjars.org) org.webjars.bower:dgrid:jar:1.2.1
License: BSD 3-Clause (https://spdx.org/licenses/BSD 3-Clause#licenseText)
- dstore (http://webjars.org) org.webjars.bower:dstore:jar:1.1.2
@@ -111,6 +115,8 @@ From: 'The Apache Software Foundation' (https://www.apache.org/)
License: Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0.txt)
- Apache Qpid Broker-J Memory Message Store Plug-in (http://qpid.apache.org/components/broker-plugins/qpid-broker-plugins-memory-store) org.apache.qpid:qpid-broker-plugins-memory-store:jar
License: Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0.txt)
+ - qpid-broker-plugins-prometheus-exporter (http://qpid.apache.org/components/broker-plugins/qpid-broker-plugins-prometheus-exporter) org.apache.qpid:qpid-broker-plugins-prometheus-exporter:jar
+ License: Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0.txt)
- Apache Qpid Broker-J WebSocket Plug-in (http://qpid.apache.org/components/broker-plugins/qpid-broker-plugins-websocket) org.apache.qpid:qpid-broker-plugins-websocket:jar
License: Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0.txt)
diff --git a/broker-core/src/main/java/org/apache/qpid/server/model/Broker.java b/broker-core/src/main/java/org/apache/qpid/server/model/Broker.java
index 62e2519..24bdccb 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/model/Broker.java
+++ b/broker-core/src/main/java/org/apache/qpid/server/model/Broker.java
@@ -189,36 +189,52 @@ public interface Broker<X extends Broker<X>> extends ConfiguredObject<X>, EventL
String getModelVersion();
@SuppressWarnings("unused")
- @ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.BYTES, label = "Inbound",
- description = "Total size of all messages received by the Broker.")
+ @ManagedStatistic(statisticType = StatisticType.CUMULATIVE,
+ units = StatisticUnit.BYTES,
+ label = "Inbound",
+ description = "Total size of all messages received by the Broker.",
+ metricName = "inbound_bytes_count")
long getBytesIn();
@SuppressWarnings("unused")
- @ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.BYTES, label = "Outbound",
- description = "Total size of all messages delivered by the Broker.")
+ @ManagedStatistic(statisticType = StatisticType.CUMULATIVE,
+ units = StatisticUnit.BYTES,
+ label = "Outbound",
+ description = "Total size of all messages delivered by the Broker.",
+ metricName = "outbound_bytes_count")
long getBytesOut();
@SuppressWarnings("unused")
- @ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.MESSAGES, label = "Inbound",
- description = "Total number of messages received by the Broker.")
+ @ManagedStatistic(statisticType = StatisticType.CUMULATIVE,
+ units = StatisticUnit.MESSAGES,
+ label = "Inbound",
+ description = "Total number of messages received by the Broker.",
+ metricName = "inbound_messages_count")
long getMessagesIn();
@SuppressWarnings("unused")
- @ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.MESSAGES, label = "Outbound",
- description = "Total number of messages delivered by the Broker.")
+ @ManagedStatistic(statisticType = StatisticType.CUMULATIVE,
+ units = StatisticUnit.MESSAGES,
+ label = "Outbound",
+ description = "Total number of messages delivered by the Broker.",
+ metricName = "outbound_messages_count")
long getMessagesOut();
@SuppressWarnings("unused")
- @ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.MESSAGES,
+ @ManagedStatistic(statisticType = StatisticType.CUMULATIVE,
+ units = StatisticUnit.MESSAGES,
label = "Transacted Inbound",
- description = "Total number of messages delivered by the Broker within a transaction.")
+ description = "Total number of messages delivered by the Broker within a transaction.",
+ metricName = "inbound_transacted_messages_count")
long getTransactedMessagesIn();
@SuppressWarnings("unused")
- @ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.MESSAGES,
+ @ManagedStatistic(statisticType = StatisticType.CUMULATIVE,
+ units = StatisticUnit.MESSAGES,
label = "Transacted Outbound",
- description = "Total number of messages received by the Broker within a transaction.")
+ description = "Total number of messages received by the Broker within a transaction.",
+ metricName = "outbound_transacted_messages_count")
long getTransactedMessagesOut();
@ManagedOperation(nonModifying = true,
@@ -249,37 +265,44 @@ public interface Broker<X extends Broker<X>> extends ConfiguredObject<X>, EventL
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.POINT_IN_TIME,
- units = StatisticUnit.COUNT,
- label = "Live threads",
- description = "Number of live threads")
+ units = StatisticUnit.COUNT,
+ label = "Live threads",
+ description = "Number of live threads",
+ metricName = "live_threads_total",
+ metricDisabled = true)
int getNumberOfLiveThreads();
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.POINT_IN_TIME,
- units = StatisticUnit.BYTES,
- label = "Used Heap Memory Size",
- description = "Size of used heap memory")
+ units = StatisticUnit.BYTES,
+ label = "Used Heap Memory Size",
+ description = "Size of used heap memory",
+ metricDisabled = true)
long getUsedHeapMemorySize();
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.POINT_IN_TIME,
- units = StatisticUnit.BYTES,
- label = "Used Direct Memory Size",
- description = "Size of used direct memory")
+ units = StatisticUnit.BYTES,
+ label = "Used Direct Memory Size",
+ description = "Size of used direct memory",
+ metricDisabled = true)
long getUsedDirectMemorySize();
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.POINT_IN_TIME,
- units = StatisticUnit.BYTES,
- label = "Direct Memory Total Capacity",
- description = "Total capacity of direct memory allocated for the Broker process")
+ units = StatisticUnit.BYTES,
+ label = "Direct Memory Total Capacity",
+ description = "Total capacity of direct memory allocated for the Broker process",
+ metricName = "direct_memory_capacity_bytes_total",
+ metricDisabled = true)
long getDirectMemoryTotalCapacity();
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.POINT_IN_TIME,
- units = StatisticUnit.COUNT,
- label = "Number Of Object Pending Finalization",
- description = "Number of objects pending finalization")
+ units = StatisticUnit.COUNT,
+ label = "Number Of Object Pending Finalization",
+ description = "Number of objects pending finalization",
+ metricDisabled = true)
int getNumberOfObjectsPendingFinalization();
@SuppressWarnings("unused")
@@ -300,7 +323,8 @@ public interface Broker<X extends Broker<X>> extends ConfiguredObject<X>, EventL
@ManagedStatistic(statisticType = StatisticType.POINT_IN_TIME,
units = StatisticUnit.BYTES,
label = "Maximum recorded size of inbound messages",
- description = "Maximum size of messages published into the Broker since start-up.")
+ description = "Maximum size of messages published into the Broker since start-up.",
+ metricName = "inbound_message_size_high_watermark")
long getInboundMessageSizeHighWatermark();
@ManagedOperation(nonModifying = true,
diff --git a/broker-core/src/main/java/org/apache/qpid/server/model/BrokerAttributeInjector.java b/broker-core/src/main/java/org/apache/qpid/server/model/BrokerAttributeInjector.java
index 61be55f..a80a041 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/model/BrokerAttributeInjector.java
+++ b/broker-core/src/main/java/org/apache/qpid/server/model/BrokerAttributeInjector.java
@@ -167,7 +167,9 @@ public class BrokerAttributeInjector implements ConfiguredObjectAttributeInjecto
_typeValidator,
StatisticUnit.BYTES,
StatisticType.POINT_IN_TIME,
- memoryPoolMXBean.getName() + " Memory Used");
+ memoryPoolMXBean.getName() + " Memory Used",
+ null,
+ true);
statistics.add(injectedStatistic);
}
catch (NoSuchMethodException e)
@@ -195,7 +197,9 @@ public class BrokerAttributeInjector implements ConfiguredObjectAttributeInjecto
StatisticUnit.COUNT,
StatisticType.CUMULATIVE,
garbageCollectorMXBean.getName()
- + " GC Collection Time");
+ + " GC Collection Time",
+ null,
+ true);
statistics.add(injectedStatistic);
}
catch (NoSuchMethodException e)
@@ -218,7 +222,9 @@ public class BrokerAttributeInjector implements ConfiguredObjectAttributeInjecto
StatisticUnit.COUNT,
StatisticType.CUMULATIVE,
garbageCollectorMXBean.getName()
- + " GC Collection Count");
+ + " GC Collection Count",
+ null,
+ true);
statistics.add(injectedStatistic);
}
catch (NoSuchMethodException e)
@@ -263,7 +269,9 @@ public class BrokerAttributeInjector implements ConfiguredObjectAttributeInjecto
StatisticUnit.TIME_DURATION,
StatisticType.CUMULATIVE,
_operatingSystemMXBeanClass.getName()
- + " Process CPU Time");
+ + " Process CPU Time",
+ "process_cpu_time_nanoseconds",
+ true);
statistics.add(injectedStatistic);
}
@@ -308,7 +316,9 @@ public class BrokerAttributeInjector implements ConfiguredObjectAttributeInjecto
StatisticUnit.COUNT,
StatisticType.POINT_IN_TIME,
_operatingSystemMXBean.getName()
- + " Process CPU Load");
+ + " Process CPU Load",
+ null,
+ true);
statistics.add(injectedStatistic);
}
diff --git a/broker-core/src/main/java/org/apache/qpid/server/model/BrokerLogger.java b/broker-core/src/main/java/org/apache/qpid/server/model/BrokerLogger.java
index 07e61cd..8c4ad5b 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/model/BrokerLogger.java
+++ b/broker-core/src/main/java/org/apache/qpid/server/model/BrokerLogger.java
@@ -28,9 +28,9 @@ public interface BrokerLogger<X extends BrokerLogger<X>> extends ConfiguredObjec
void stopLogging();
- @ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.COUNT, label = "Errors")
+ @ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.COUNT, label = "Errors", metricName = "errors_count")
long getErrorCount();
- @ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.COUNT, label = "Warnings")
+ @ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.COUNT, label = "Warnings", metricName = "warnings_count")
long getWarnCount();
}
diff --git a/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObjectInjectedStatistic.java b/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObjectInjectedStatistic.java
index 95c8507..f1d9c6b 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObjectInjectedStatistic.java
+++ b/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObjectInjectedStatistic.java
@@ -41,6 +41,8 @@ final public class ConfiguredObjectInjectedStatistic<C extends ConfiguredObject,
private final StatisticType _type;
private final String _label;
private final Object[] _staticParams;
+ private final String _metricName;
+ private final boolean _metricDisabled;
public ConfiguredObjectInjectedStatistic(final String name,
final Method method,
@@ -49,7 +51,9 @@ final public class ConfiguredObjectInjectedStatistic<C extends ConfiguredObject,
final TypeValidator typeValidator,
final StatisticUnit units,
final StatisticType type,
- final String label)
+ final String label,
+ final String metricName,
+ final boolean metricDisabled)
{
super(name,
(Class<T>) AttributeValueConverter.getTypeFromMethod(method), method.getGenericReturnType(), typeValidator);
@@ -57,6 +61,8 @@ final public class ConfiguredObjectInjectedStatistic<C extends ConfiguredObject,
_type = type;
_label = label;
_staticParams = staticParams == null ? new Object[0] : staticParams;
+ _metricName = metricName;
+ _metricDisabled = metricDisabled;
if(!(method.getParameterTypes().length == 1 + _staticParams.length
&& ConfiguredObject.class.isAssignableFrom(method.getParameterTypes()[0])
&& Modifier.isStatic(method.getModifiers())
@@ -147,4 +153,16 @@ final public class ConfiguredObjectInjectedStatistic<C extends ConfiguredObject,
}
}
+
+ @Override
+ public String getMetricName()
+ {
+ return _metricName;
+ }
+
+ @Override
+ public boolean isMetricDisabled()
+ {
+ return _metricDisabled;
+ }
}
diff --git a/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObjectMethodStatistic.java b/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObjectMethodStatistic.java
index 2cc2fe9..528d80e 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObjectMethodStatistic.java
+++ b/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObjectMethodStatistic.java
@@ -66,4 +66,16 @@ public final class ConfiguredObjectMethodStatistic<C extends ConfiguredObject, T
{
return _annotation.label();
}
+
+ @Override
+ public String getMetricName()
+ {
+ return _annotation.metricName();
+ }
+
+ @Override
+ public boolean isMetricDisabled()
+ {
+ return _annotation.metricDisabled();
+ }
}
diff --git a/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObjectStatistic.java b/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObjectStatistic.java
index cc22d62..4585b42 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObjectStatistic.java
+++ b/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObjectStatistic.java
@@ -29,4 +29,8 @@ public interface ConfiguredObjectStatistic<C extends ConfiguredObject, T extends
StatisticType getStatisticType();
String getLabel();
+
+ String getMetricName();
+
+ boolean isMetricDisabled();
}
diff --git a/broker-core/src/main/java/org/apache/qpid/server/model/Connection.java b/broker-core/src/main/java/org/apache/qpid/server/model/Connection.java
index 62cefc1..0638e53 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/model/Connection.java
+++ b/broker-core/src/main/java/org/apache/qpid/server/model/Connection.java
@@ -117,24 +117,24 @@ public interface Connection<X extends Connection<X>> extends ConfiguredObject<X>
// See also QPID-7689: https://issues.apache.org/jira/browse/QPID-7689?focusedCommentId=16022923#comment-16022923
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.BYTES, label = "Inbound",
- description = "Total size of all messages received by this connection.")
+ description = "Total size of all messages received by this connection.", metricName = "inbound_bytes_count")
long getBytesIn();
// currently this reports outbound message content size without header.
// See also QPID-7689: https://issues.apache.org/jira/browse/QPID-7689?focusedCommentId=16022923#comment-16022923
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.BYTES, label = "Outbound",
- description = "Total size of all messages delivered by this connection.")
+ description = "Total size of all messages delivered by this connection.", metricName = "outbound_bytes_count")
long getBytesOut();
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.MESSAGES, label = "Inbound",
- description = "Total number of messages delivered by this connection.")
+ description = "Total number of messages delivered by this connection.", metricName = "inbound_messages_count")
long getMessagesIn();
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.MESSAGES, label = "Outbound",
- description = "Total number of messages received by this connection.")
+ description = "Total number of messages received by this connection.", metricName = "outbound_messages_count")
long getMessagesOut();
@SuppressWarnings("unused")
@@ -162,7 +162,7 @@ public interface Connection<X extends Connection<X>> extends ConfiguredObject<X>
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.POINT_IN_TIME, units = StatisticUnit.COUNT, label = "Sessions",
- description = "Current number of sessions belonging to this connection.")
+ description = "Current number of sessions belonging to this connection.", metricName = "sessions_total")
int getSessionCount();
@SuppressWarnings("unused")
@@ -188,12 +188,12 @@ public interface Connection<X extends Connection<X>> extends ConfiguredObject<X>
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.MESSAGES, label = "Transacted Inbound",
- description = "Total number of messages delivered by this connection within a transaction.")
+ description = "Total number of messages delivered by this connection within a transaction.", metricName = "transacted_inbound_messages_count")
long getTransactedMessagesIn();
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.MESSAGES, label = "Transacted Outbound",
- description = "Total number of messages received by this connection within a transaction.")
+ description = "Total number of messages received by this connection within a transaction.", metricName = "transacted_outbound_messages_count")
long getTransactedMessagesOut();
//children
diff --git a/broker-core/src/main/java/org/apache/qpid/server/model/Consumer.java b/broker-core/src/main/java/org/apache/qpid/server/model/Consumer.java
index 9db4b21..240d8a6 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/model/Consumer.java
+++ b/broker-core/src/main/java/org/apache/qpid/server/model/Consumer.java
@@ -70,10 +70,10 @@ public interface Consumer<X extends Consumer<X,T>, T extends ConsumerTarget> ext
+ "consumers. Priority 2147483647 is the highest priority.")
int getPriority();
- @ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.BYTES, label = "Outbound")
+ @ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.BYTES, label = "Outbound", metricName = "outbound_bytes_count")
long getBytesOut();
- @ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.MESSAGES, label = "Outbound")
+ @ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.MESSAGES, label = "Outbound", metricName = "outbound_messages_count")
long getMessagesOut();
@ManagedStatistic(statisticType = StatisticType.POINT_IN_TIME, units = StatisticUnit.BYTES, label = "Prefetch")
diff --git a/broker-core/src/main/java/org/apache/qpid/server/model/Exchange.java b/broker-core/src/main/java/org/apache/qpid/server/model/Exchange.java
index 800f57c..5e2d68f 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/model/Exchange.java
+++ b/broker-core/src/main/java/org/apache/qpid/server/model/Exchange.java
@@ -88,27 +88,27 @@ public interface Exchange<X extends Exchange<X>> extends ConfiguredObject<X>, Me
// Statistics
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.POINT_IN_TIME, units = StatisticUnit.COUNT, label = "Bindings",
- description = "Current number of bindings to this exchange.")
+ description = "Current number of bindings to this exchange.", metricName = "bindings_total")
long getBindingCount();
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.BYTES, label = "Dropped",
- description = "Total size of all unroutable messages dropped by this exchange.")
+ description = "Total size of all unroutable messages dropped by this exchange.", metricName = "dropped_bytes_count")
long getBytesDropped();
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.BYTES, label = "Inbound",
- description = "Total size of messages received by this exchange.")
+ description = "Total size of messages received by this exchange.", metricName = "inbound_bytes_count")
long getBytesIn();
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.MESSAGES, label = "Dropped",
- description = "Number of unroutable messages dropped by this exchange.")
+ description = "Number of unroutable messages dropped by this exchange.", metricName = "dropped_messages_count")
long getMessagesDropped();
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.MESSAGES, label = "Inbound",
- description = "Number of messages received by this exchange.")
+ description = "Number of messages received by this exchange.", metricName = "inbound_messages_count")
long getMessagesIn();
diff --git a/broker-core/src/main/java/org/apache/qpid/server/model/ManagedStatistic.java b/broker-core/src/main/java/org/apache/qpid/server/model/ManagedStatistic.java
index d9bb129..1bcd52e 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/model/ManagedStatistic.java
+++ b/broker-core/src/main/java/org/apache/qpid/server/model/ManagedStatistic.java
@@ -33,4 +33,6 @@ public @interface ManagedStatistic
String label() default "";
StatisticUnit units();
StatisticType statisticType();
+ String metricName() default "";
+ boolean metricDisabled() default false;
}
diff --git a/broker-core/src/main/java/org/apache/qpid/server/model/Queue.java b/broker-core/src/main/java/org/apache/qpid/server/model/Queue.java
index 3920a14..bfb7e16 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/model/Queue.java
+++ b/broker-core/src/main/java/org/apache/qpid/server/model/Queue.java
@@ -370,17 +370,17 @@ public interface Queue<X extends Queue<X>> extends ConfiguredObject<X>,
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.POINT_IN_TIME, units = StatisticUnit.COUNT, label = "Bindings",
- description = "Current number of bindings to this queue.")
+ description = "Current number of bindings to this queue.", metricName = "bindings_total")
int getBindingCount();
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.POINT_IN_TIME, units = StatisticUnit.COUNT, label = "Consumers",
- description = "Current number of consumers attached to this queue.")
+ description = "Current number of consumers attached to this queue.", metricName = "consumers_total")
int getConsumerCount();
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.POINT_IN_TIME, units = StatisticUnit.COUNT, label = "Consumers with credit",
- description = "Current number of consumers attached to this queue with credit")
+ description = "Current number of consumers attached to this queue with credit", metricName = "consumers_with_credit_total")
int getConsumerCountWithCredit();
@SuppressWarnings("unused")
@@ -405,42 +405,42 @@ public interface Queue<X extends Queue<X>> extends ConfiguredObject<X>,
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.POINT_IN_TIME, units = StatisticUnit.BYTES, label = "Queue Depth",
- description = "Current size of all messages enqueued by this queue.")
+ description = "Current size of all messages enqueued by this queue.", metricName = "depth_bytes_total")
long getQueueDepthBytes();
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.POINT_IN_TIME, units = StatisticUnit.MESSAGES, label = "Queue Depth",
- description = "Current number of messages enqueued by this queue.")
+ description = "Current number of messages enqueued by this queue.", metricName = "depth_messages_total")
int getQueueDepthMessages();
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.BYTES, label = "Delivered",
- description = "Total size of all messages delivered by this queue.")
+ description = "Total size of all messages delivered by this queue.", metricName = "dequeued_bytes_count")
long getTotalDequeuedBytes();
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.MESSAGES, label = "Delivered",
- description = "Total number of messages delivered by this queue.")
+ description = "Total number of messages delivered by this queue.", metricName = "dequeued_messages_count")
long getTotalDequeuedMessages();
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.BYTES, label = "Enqueued",
- description = "Total size of all messages received by this queue.")
+ description = "Total size of all messages received by this queue.", metricName = "enqueue_bytes_count")
long getTotalEnqueuedBytes();
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.MESSAGES, label = "Enqueued",
- description = "Total number of messages received by this queue.")
+ description = "Total number of messages received by this queue.", metricName = "enqueued_messages_count")
long getTotalEnqueuedMessages();
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.BYTES, label = "Expired",
- description = "Total size of all messages expired by message time-to-live on this queue.")
+ description = "Total size of all messages expired by message time-to-live on this queue.", metricName = "expired_bytes_count")
long getTotalExpiredBytes();
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.MESSAGES, label = "Expired",
- description = "Total number of messages expired by message time-to-live on this queue.")
+ description = "Total number of messages expired by message time-to-live on this queue.", metricName = "expired_messages_count")
long getTotalExpiredMessages();
@@ -466,37 +466,37 @@ public interface Queue<X extends Queue<X>> extends ConfiguredObject<X>,
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.POINT_IN_TIME, units = StatisticUnit.BYTES, label = "Available HWM",
- description = "Maximum recorded size of available messages.")
+ description = "Maximum recorded size of available messages.", metricName = "available_bytes_high_water_mark")
long getAvailableBytesHighWatermark();
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.POINT_IN_TIME, units = StatisticUnit.MESSAGES, label = "Available HWM",
- description = "Maximum recorded number of available messages.")
+ description = "Maximum recorded number of available messages.", metricName = "available_messages_high_water_mark")
int getAvailableMessagesHighWatermark();
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.POINT_IN_TIME, units = StatisticUnit.BYTES, label = "Queue Depth HWM",
- description = "Maximum recorded size of enqueued messages.")
+ description = "Maximum recorded size of enqueued messages.", metricName = "depth_bytes_high_water_mark")
long getQueueDepthBytesHighWatermark();
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.POINT_IN_TIME, units = StatisticUnit.MESSAGES, label = "Queue Depth HWM",
- description = "Maximum recorded number of enqueued messages.")
+ description = "Maximum recorded number of enqueued messages.", metricName = "depth_messages_high_water_mark")
int getQueueDepthMessagesHighWatermark();
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.POINT_IN_TIME, units = StatisticUnit.TIME_DURATION, label = "Oldest Message",
- description = "Current age of oldest message on the queue.")
+ description = "Current age of oldest message on the queue.", metricName = "oldest_message_age_milliseconds")
long getOldestMessageAge();
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.BYTES, label = "Malformed",
- description = "Total size of enqueued malformed messages.")
+ description = "Total size of enqueued malformed messages.", metricName = "malformed_bytes_count")
long getTotalMalformedBytes();
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.MESSAGES, label = "Malformed",
- description = "Total number of enqueued malformed messages.")
+ description = "Total number of enqueued malformed messages.", metricName = "malformed_messages_count")
long getTotalMalformedMessages();
@ManagedOperation(description = "move messages from this queue to another", changesConfiguredObjectState = false)
diff --git a/broker-core/src/main/java/org/apache/qpid/server/model/Session.java b/broker-core/src/main/java/org/apache/qpid/server/model/Session.java
index af1084e..343e5ba 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/model/Session.java
+++ b/broker-core/src/main/java/org/apache/qpid/server/model/Session.java
@@ -55,7 +55,7 @@ public interface Session<X extends Session<X>> extends ConfiguredObject<X>
boolean isProducerFlowBlocked();
- @ManagedStatistic(statisticType = StatisticType.POINT_IN_TIME, units = StatisticUnit.COUNT, label = "Consumers")
+ @ManagedStatistic(statisticType = StatisticType.POINT_IN_TIME, units = StatisticUnit.COUNT, label = "Consumers", metricName = "consumers_total")
long getConsumerCount();
@ManagedStatistic(statisticType = StatisticType.POINT_IN_TIME, units = StatisticUnit.MESSAGES, label = "Prefetched")
diff --git a/broker-core/src/main/java/org/apache/qpid/server/model/VirtualHostLogger.java b/broker-core/src/main/java/org/apache/qpid/server/model/VirtualHostLogger.java
index c30f6a7..6599a0f 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/model/VirtualHostLogger.java
+++ b/broker-core/src/main/java/org/apache/qpid/server/model/VirtualHostLogger.java
@@ -25,9 +25,9 @@ public interface VirtualHostLogger <X extends VirtualHostLogger<X>> extends Conf
{
void stopLogging();
- @ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.COUNT, label = "Errors")
+ @ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.COUNT, label = "Errors", metricName = "errors_count")
long getErrorCount();
- @ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.COUNT, label = "Warnings")
+ @ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.COUNT, label = "Warnings", metricName = "warnings_count")
long getWarnCount();
}
diff --git a/broker-core/src/main/java/org/apache/qpid/server/model/port/AmqpPort.java b/broker-core/src/main/java/org/apache/qpid/server/model/port/AmqpPort.java
index f88b99a..530d603 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/model/port/AmqpPort.java
+++ b/broker-core/src/main/java/org/apache/qpid/server/model/port/AmqpPort.java
@@ -159,12 +159,16 @@ public interface AmqpPort<X extends AmqpPort<X>> extends Port<X>
@ManagedAttribute( defaultValue = "${" + PORT_MAX_OPEN_CONNECTIONS + "}" )
int getMaxOpenConnections();
- @ManagedStatistic(statisticType = StatisticType.POINT_IN_TIME, units = StatisticUnit.COUNT, label = "Open Connections",
- description = "Current number of connections made through this port")
+ @ManagedStatistic(statisticType = StatisticType.POINT_IN_TIME, units = StatisticUnit.COUNT,
+ label = "Open Connections",
+ description = "Current number of connections made through this port",
+ metricName = "open_connections_total")
int getConnectionCount();
- @ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.COUNT, label = "Total Connections",
- description = "Total number of connections made through this port since broker startup")
+ @ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.COUNT,
+ label = "Total Connections",
+ description = "Total number of connections made through this port since broker startup",
+ metricName = "aggregate_connection_count")
long getTotalConnectionCount();
@DerivedAttribute(description = "Maximum time allowed for a new connection to send a protocol header."
diff --git a/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestSensor.java b/broker-core/src/main/java/org/apache/qpid/server/plugin/ContentFactory.java
similarity index 75%
copy from broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestSensor.java
copy to broker-core/src/main/java/org/apache/qpid/server/plugin/ContentFactory.java
index 41e07f2..bc0ad33 100644
--- a/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestSensor.java
+++ b/broker-core/src/main/java/org/apache/qpid/server/plugin/ContentFactory.java
@@ -1,5 +1,4 @@
/*
- *
* 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
@@ -18,13 +17,15 @@
* under the License.
*
*/
-package org.apache.qpid.server.model.testmodels.hierarchy;
+
+package org.apache.qpid.server.plugin;
+
+import java.util.Map;
import org.apache.qpid.server.model.ConfiguredObject;
-import org.apache.qpid.server.model.ManagedObject;
+import org.apache.qpid.server.model.Content;
-@ManagedObject( defaultType = TestTemperatureSensorImpl.TEST_TEMPERATURE_SENSOR_TYPE)
-public interface TestSensor<X extends TestSensor<X>> extends ConfiguredObject<X>
+public interface ContentFactory extends Pluggable
{
-
+ Content createContent(ConfiguredObject<?> object, Map<String, String[]> filter);
}
diff --git a/broker-core/src/main/java/org/apache/qpid/server/virtualhost/QueueManagingVirtualHost.java b/broker-core/src/main/java/org/apache/qpid/server/virtualhost/QueueManagingVirtualHost.java
index 9f1b318..4cd276a 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/virtualhost/QueueManagingVirtualHost.java
+++ b/broker-core/src/main/java/org/apache/qpid/server/virtualhost/QueueManagingVirtualHost.java
@@ -210,69 +210,69 @@ public interface QueueManagingVirtualHost<X extends QueueManagingVirtualHost<X>>
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.POINT_IN_TIME, units = StatisticUnit.COUNT, label = "Queues",
- description = "Current number of queues on this virtualhost.")
+ description = "Current number of queues on this virtualhost.", metricName = "queues_total")
long getQueueCount();
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.POINT_IN_TIME, units = StatisticUnit.COUNT, label = "Exchanges",
- description = "Current number of exchanges on this virtualhost.")
+ description = "Current number of exchanges on this virtualhost.", metricName = "exchanges_total")
long getExchangeCount();
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.POINT_IN_TIME, units = StatisticUnit.COUNT, label = "Connections",
- description = "Current number of messaging connections made to this virtualhost.")
+ description = "Current number of messaging connections made to this virtualhost.", metricName = "connections_total")
long getConnectionCount();
@ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.COUNT, label = "Total Connections",
- description = "Total number of messaging connections made to this virtualhost since broker startup")
+ description = "Total number of messaging connections made to this virtualhost since broker startup", metricName = "aggregate_connection_count")
long getTotalConnectionCount();
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.BYTES, label = "Inbound",
- description = "Total size of all messages received by this virtualhost.")
+ description = "Total size of all messages received by this virtualhost.", metricName = "inbound_bytes_count")
long getBytesIn();
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.BYTES, label = "Outbound",
- description = "Total size of all messages delivered by this virtualhost.")
+ description = "Total size of all messages delivered by this virtualhost.", metricName = "outbound_bytes_count")
long getBytesOut();
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.MESSAGES, label = "Inbound",
- description = "Total number of messages received by this virtualhost.")
+ description = "Total number of messages received by this virtualhost.", metricName = "inbound_messages_count")
long getMessagesIn();
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.MESSAGES, label = "Outbound",
- description = "Total number of messages delivered by this virtualhost.")
+ description = "Total number of messages delivered by this virtualhost.", metricName = "outbound_messages_count")
long getMessagesOut();
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.MESSAGES,
label = "Transacted Inbound",
- description = "Total number of messages delivered by this virtualhost within a transaction.")
+ description = "Total number of messages delivered by this virtualhost within a transaction.", metricName = "inbound_transacted_messages_count")
long getTransactedMessagesIn();
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.MESSAGES,
label = "Transacted Outbound",
- description = "Total number of messages received by this virtualhost within a transaction.")
+ description = "Total number of messages received by this virtualhost within a transaction.", metricName = "outbound_transacted_messages_count")
long getTransactedMessagesOut();
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.POINT_IN_TIME, units = StatisticUnit.BYTES, label = "Queue Depth",
- description = "Current size of all messages enqueued by this virtualhost.")
+ description = "Current size of all messages enqueued by this virtualhost.", metricName = "queue_depth_bytes_total")
long getTotalDepthOfQueuesBytes();
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.POINT_IN_TIME, units = StatisticUnit.MESSAGES, label = "Queue Depth",
- description = "Current number of messages enqueued by this virtualhost.")
+ description = "Current number of messages enqueued by this virtualhost.", metricName = "queue_depth_messages_total")
long getTotalDepthOfQueuesMessages();
@SuppressWarnings("unused")
@ManagedStatistic(statisticType = StatisticType.POINT_IN_TIME, units = StatisticUnit.BYTES, label = "In-Memory Message Bytes",
- description="Current size of all messages cached in-memory.")
+ description="Current size of all messages cached in-memory.", metricName = "in_memory_message_size_bytes_total")
long getInMemoryMessageSize();
@SuppressWarnings("unused")
@@ -284,7 +284,7 @@ public interface QueueManagingVirtualHost<X extends QueueManagingVirtualHost<X>>
@ManagedStatistic(statisticType = StatisticType.POINT_IN_TIME,
units = StatisticUnit.BYTES,
label = "Maximum recorded size of inbound messages",
- description = "Maximum size of message published into the Virtual Host since start-up.")
+ description = "Maximum size of message published into the Virtual Host since start-up.", metricName = "inbound_message_size_high_water_mark")
long getInboundMessageSizeHighWatermark();
@Override
diff --git a/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/InjectedAttributeTest.java b/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/InjectedAttributeTest.java
index 90ce83e..8afa95e 100644
--- a/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/InjectedAttributeTest.java
+++ b/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/InjectedAttributeTest.java
@@ -292,14 +292,16 @@ public class InjectedAttributeTest extends UnitTestBase
TYPE_VALIDATOR,
StatisticUnit.COUNT,
StatisticType.POINT_IN_TIME,
- "What is 6 x 9?");
+ "What is 6 x 9?",
+ null,
+ false);
TestModel model = new TestModel(null, new TestInjector(statInjector));
TestCar<?> testCar = new TestStandardCarImpl(Collections.<String,Object>singletonMap("name", "Arthur"), model);
final Map<String, Object> statistics = testCar.getStatistics();
- assertEquals("incorrect number of statistics", (long) 1, (long) statistics.size());
+ assertEquals("incorrect number of statistics", (long) 3, (long) statistics.size());
assertEquals("incorrect statistic value", 42, statistics.get("meaningOfLife"));
}
@@ -317,7 +319,9 @@ public class InjectedAttributeTest extends UnitTestBase
TYPE_VALIDATOR,
StatisticUnit.COUNT,
StatisticType.POINT_IN_TIME,
- "One");
+ "One",
+ null,
+ false);
final ConfiguredObjectInjectedStatistic<?, ?> statInjector2 =
new ConfiguredObjectInjectedStatistic<TestCar<?>, Integer>("whatISent2",
method,
@@ -325,13 +329,15 @@ public class InjectedAttributeTest extends UnitTestBase
TYPE_VALIDATOR,
StatisticUnit.COUNT,
StatisticType.POINT_IN_TIME,
- "Two");
+ "Two",
+ null,
+ false);
TestModel model = new TestModel(null, new TestInjector(statInjector1, statInjector2));
TestCar<?> testCar = new TestStandardCarImpl(Collections.<String,Object>singletonMap("name", "Arthur"), model);
final Map<String, Object> statistics = testCar.getStatistics();
- assertEquals("incorrect number of statistics", (long) 2, (long) statistics.size());
+ assertEquals("incorrect number of statistics", (long) 4, (long) statistics.size());
assertEquals("incorrect statistic value", 1, statistics.get("whatISent1"));
assertEquals("incorrect statistic value", 2, statistics.get("whatISent2"));
}
diff --git a/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestAbstractCarImpl.java b/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestAbstractCarImpl.java
index 447c324..f01a6a4 100644
--- a/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestAbstractCarImpl.java
+++ b/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestAbstractCarImpl.java
@@ -22,6 +22,7 @@ package org.apache.qpid.server.model.testmodels.hierarchy;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
import org.apache.qpid.server.configuration.IllegalConfigurationException;
import org.apache.qpid.server.configuration.updater.CurrentThreadTaskExecutor;
@@ -37,6 +38,8 @@ public abstract class TestAbstractCarImpl<X extends TestAbstractCarImpl<X>> exte
@ManagedAttributeField
private Colour _interiorColour;
+ private AtomicInteger _mileage = new AtomicInteger();
+
private volatile boolean _rejectStateChange;
public TestAbstractCarImpl(final Map<String, Object> attributes)
@@ -102,4 +105,22 @@ public abstract class TestAbstractCarImpl<X extends TestAbstractCarImpl<X>> exte
{
_rejectStateChange = rejectStateChange;
}
+
+ @Override
+ public int getMileage()
+ {
+ return _mileage.get();
+ }
+
+ @Override
+ public int move(final int value)
+ {
+ return _mileage.addAndGet(value);
+ }
+
+ @Override
+ public int getAge()
+ {
+ return 0;
+ }
}
diff --git a/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestAbstractEngineImpl.java b/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestAbstractEngineImpl.java
index 722ddc8..5e84a12 100644
--- a/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestAbstractEngineImpl.java
+++ b/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestAbstractEngineImpl.java
@@ -34,6 +34,7 @@ import org.apache.qpid.server.model.StateTransition;
public class TestAbstractEngineImpl<X extends TestAbstractEngineImpl<X>> extends AbstractConfiguredObject<X> implements TestEngine<X>
{
+ public static final int TEST_TEMPERATURE = 50;
@ManagedAttributeField
private ListenableFuture<Void> _beforeCloseFuture = Futures.immediateFuture(null);
@@ -109,4 +110,10 @@ public class TestAbstractEngineImpl<X extends TestAbstractEngineImpl<X>> extends
setState(State.ACTIVE);
return (ListenableFuture<Void>) _stateChangeFuture;
}
+
+ @Override
+ public int getTemperature()
+ {
+ return TEST_TEMPERATURE;
+ }
}
diff --git a/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestAbstractInstrumentPanelImpl.java b/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestAbstractInstrumentPanelImpl.java
index 5ef7001..73db9d3 100644
--- a/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestAbstractInstrumentPanelImpl.java
+++ b/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestAbstractInstrumentPanelImpl.java
@@ -21,6 +21,7 @@
package org.apache.qpid.server.model.testmodels.hierarchy;
import java.util.Map;
+import java.util.Random;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
@@ -51,4 +52,5 @@ public class TestAbstractInstrumentPanelImpl<X extends TestAbstractInstrumentPan
setState(State.ACTIVE);
return Futures.immediateFuture(null);
}
+
}
diff --git a/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestAbstractSensorImpl.java b/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestAbstractSensorImpl.java
index dc41c23..00ff611 100644
--- a/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestAbstractSensorImpl.java
+++ b/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestAbstractSensorImpl.java
@@ -21,6 +21,7 @@
package org.apache.qpid.server.model.testmodels.hierarchy;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
@@ -33,6 +34,8 @@ public class TestAbstractSensorImpl<X extends TestAbstractSensorImpl<X>> extends
implements TestSensor<X>
{
+ private AtomicInteger _alertCount;
+
protected TestAbstractSensorImpl(final TestInstrumentPanel<?> parent,
final Map<String, Object> attributes)
{
@@ -51,4 +54,10 @@ public class TestAbstractSensorImpl<X extends TestAbstractSensorImpl<X>> extends
setState(State.ACTIVE);
return Futures.immediateFuture(null);
}
+
+ @Override
+ public int getNumberOfAlerts()
+ {
+ return _alertCount.getAndIncrement();
+ }
}
diff --git a/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestCar.java b/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestCar.java
index a184227..8588d60 100644
--- a/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestCar.java
+++ b/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestCar.java
@@ -25,7 +25,10 @@ import org.apache.qpid.server.model.ManagedAttribute;
import org.apache.qpid.server.model.ManagedContextDefault;
import org.apache.qpid.server.model.ManagedObject;
import org.apache.qpid.server.model.ManagedOperation;
+import org.apache.qpid.server.model.ManagedStatistic;
import org.apache.qpid.server.model.Param;
+import org.apache.qpid.server.model.StatisticType;
+import org.apache.qpid.server.model.StatisticUnit;
@ManagedObject( defaultType = TestStandardCarImpl.TEST_STANDARD_CAR_TYPE)
public interface TestCar<X extends TestCar<X>> extends ConfiguredObject<X>
@@ -60,4 +63,15 @@ public interface TestCar<X extends TestCar<X>> extends ConfiguredObject<X>
void setRejectStateChange(boolean b);
+ @ManagedStatistic(statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.COUNT)
+ int getMileage();
+
+ @ManagedStatistic(metricName = "age",
+ statisticType = StatisticType.CUMULATIVE,
+ units = StatisticUnit.TIME_DURATION,
+ metricDisabled = true)
+ int getAge();
+
+ @ManagedOperation(changesConfiguredObjectState = false)
+ int move(@Param(name = "mileage", mandatory = true) int mileage);
}
diff --git a/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestEngine.java b/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestEngine.java
index d5284cb..97229b2 100644
--- a/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestEngine.java
+++ b/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestEngine.java
@@ -25,6 +25,9 @@ import com.google.common.util.concurrent.ListenableFuture;
import org.apache.qpid.server.model.ConfiguredObject;
import org.apache.qpid.server.model.ManagedAttribute;
import org.apache.qpid.server.model.ManagedObject;
+import org.apache.qpid.server.model.ManagedStatistic;
+import org.apache.qpid.server.model.StatisticType;
+import org.apache.qpid.server.model.StatisticUnit;
@ManagedObject(category = true, defaultType = TestElecEngineImpl.TEST_ELEC_ENGINE_TYPE)
public interface TestEngine<X extends TestEngine<X>> extends ConfiguredObject<X>
@@ -52,4 +55,7 @@ public interface TestEngine<X extends TestEngine<X>> extends ConfiguredObject<X>
Object getStateChangeException();
void setStateChangeException(RuntimeException exception);
+ @ManagedStatistic(statisticType = StatisticType.POINT_IN_TIME, units = StatisticUnit.COUNT)
+ int getTemperature();
+
}
diff --git a/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestSensor.java b/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestSensor.java
index 41e07f2..c2bbc79 100644
--- a/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestSensor.java
+++ b/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestSensor.java
@@ -22,9 +22,14 @@ package org.apache.qpid.server.model.testmodels.hierarchy;
import org.apache.qpid.server.model.ConfiguredObject;
import org.apache.qpid.server.model.ManagedObject;
+import org.apache.qpid.server.model.ManagedStatistic;
+import org.apache.qpid.server.model.StatisticType;
+import org.apache.qpid.server.model.StatisticUnit;
@ManagedObject( defaultType = TestTemperatureSensorImpl.TEST_TEMPERATURE_SENSOR_TYPE)
public interface TestSensor<X extends TestSensor<X>> extends ConfiguredObject<X>
{
+ @ManagedStatistic(metricName = "alert_count", statisticType = StatisticType.CUMULATIVE, units = StatisticUnit.COUNT)
+ int getNumberOfAlerts();
}
diff --git a/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestTemperatureSensorImpl.java b/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestTemperatureSensorImpl.java
index f2c176b..96f1fcf 100644
--- a/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestTemperatureSensorImpl.java
+++ b/broker-core/src/test/java/org/apache/qpid/server/model/testmodels/hierarchy/TestTemperatureSensorImpl.java
@@ -21,6 +21,7 @@
package org.apache.qpid.server.model.testmodels.hierarchy;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
import org.apache.qpid.server.model.ManagedObject;
import org.apache.qpid.server.model.ManagedObjectFactoryConstructor;
@@ -32,9 +33,17 @@ public class TestTemperatureSensorImpl extends TestAbstractSensorImpl<TestTemper
public static final String TEST_TEMPERATURE_SENSOR_TYPE = "temperature";
+ private AtomicInteger _alertCount = new AtomicInteger();
+
@ManagedObjectFactoryConstructor
protected TestTemperatureSensorImpl(final Map<String, Object> attributes,final TestInstrumentPanel<?> parent)
{
super(parent, attributes);
}
+
+ @Override
+ public int getNumberOfAlerts()
+ {
+ return _alertCount.getAndIncrement();
+ }
}
diff --git a/broker-plugins/management-http/pom.xml b/broker-plugins/management-http/pom.xml
index 9fa84d0..fa5a233 100644
--- a/broker-plugins/management-http/pom.xml
+++ b/broker-plugins/management-http/pom.xml
@@ -101,6 +101,11 @@
</exclusions>
</dependency>
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-broker-plugins-prometheus-exporter</artifactId>
+ </dependency>
+
<!-- test dependencies -->
<dependency>
<groupId>org.apache.qpid</groupId>
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java
index 39709df..43da9db 100644
--- a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagement.java
@@ -90,6 +90,7 @@ import org.apache.qpid.server.management.plugin.filter.RewriteRequestForUncompre
import org.apache.qpid.server.management.plugin.portunification.TlsOrPlainConnectionFactory;
import org.apache.qpid.server.management.plugin.servlet.FileServlet;
import org.apache.qpid.server.management.plugin.servlet.RootServlet;
+import org.apache.qpid.server.management.plugin.servlet.ContentServlet;
import org.apache.qpid.server.management.plugin.servlet.rest.ApiDocsServlet;
import org.apache.qpid.server.management.plugin.servlet.rest.BrokerQueryServlet;
import org.apache.qpid.server.management.plugin.servlet.rest.JsonValueServlet;
@@ -119,6 +120,8 @@ import org.apache.qpid.server.model.TrustStore;
import org.apache.qpid.server.model.adapter.AbstractPluginAdapter;
import org.apache.qpid.server.model.port.HttpPort;
import org.apache.qpid.server.model.port.PortManager;
+import org.apache.qpid.server.plugin.ContentFactory;
+import org.apache.qpid.server.plugin.QpidServiceLoader;
import org.apache.qpid.server.transport.PortBindFailureException;
import org.apache.qpid.server.transport.network.security.ssl.SSLUtil;
import org.apache.qpid.server.util.DaemonThreadFactory;
@@ -412,6 +415,21 @@ public class HttpManagement extends AbstractPluginAdapter<HttpManagement> implem
root.addServlet(new ServletHolder(new TimeZoneServlet()), "/service/timezones");
+ final Iterable<ContentFactory> contentFactories = new QpidServiceLoader().instancesOf(ContentFactory.class);
+ contentFactories.forEach(f->{
+ ServletHolder metricsServlet = new ServletHolder(new ContentServlet(f));
+ String path = f.getType().toLowerCase();
+ root.addServlet(metricsServlet, "/" + path);
+ root.addServlet(metricsServlet, "/" + path + "/*");
+
+ if (getContextValue(Boolean.class, HTTP_MANAGEMENT_ENABLE_CONTENT_AUTHENTICATION))
+ {
+ root.addFilter(restAuthorizationFilter, "/" + path, EnumSet.of(DispatcherType.REQUEST));
+ root.addFilter(restAuthorizationFilter, "/" + path + "/*", EnumSet.of(DispatcherType.REQUEST));
+ }
+
+ });
+
root.getSessionHandler().getSessionCookieConfig().setName(JSESSIONID_COOKIE_PREFIX + lastPort);
root.getSessionHandler().getSessionCookieConfig().setHttpOnly(true);
root.getSessionHandler().setMaxInactiveInterval(getSessionTimeout());
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagementConfiguration.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagementConfiguration.java
index a5aadae..d33f2b8 100644
--- a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagementConfiguration.java
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/HttpManagementConfiguration.java
@@ -85,6 +85,11 @@ public interface HttpManagementConfiguration<X extends HttpManagementConfigurati
@ManagedContextDefault( name = SASL_EXCHANGE_EXPIRY_CONTEXT_NAME)
long DEFAULT_SASL_EXCHANGE_EXPIRY = 60000L;
+ String HTTP_MANAGEMENT_ENABLE_CONTENT_AUTHENTICATION = "qpid.httpManagement.enableMetricContentAuthentication";
+ @SuppressWarnings("unused")
+ @ManagedContextDefault(name = HTTP_MANAGEMENT_ENABLE_CONTENT_AUTHENTICATION)
+ boolean DEFAULT_HTTP_MANAGEMENT_ENABLE_CONTENT_AUTHENTICATION = false;
+
AuthenticationProvider getAuthenticationProvider(HttpServletRequest request);
Port<?> getPort(HttpServletRequest request);
}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/ContentServlet.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/ContentServlet.java
new file mode 100644
index 0000000..36ea0a7
--- /dev/null
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/ContentServlet.java
@@ -0,0 +1,95 @@
+/*
+ * 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.
+ *
+ */
+package org.apache.qpid.server.management.plugin.servlet;
+
+import java.io.IOException;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.qpid.server.management.plugin.servlet.rest.AbstractServlet;
+import org.apache.qpid.server.model.Broker;
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.server.model.ConfiguredObjectFinder;
+import org.apache.qpid.server.model.Content;
+import org.apache.qpid.server.model.VirtualHost;
+import org.apache.qpid.server.plugin.ContentFactory;
+
+public class ContentServlet extends AbstractServlet
+{
+ private static final long serialVersionUID = 1L;
+ private final ContentFactory _contentFactory;
+
+ public ContentServlet(final ContentFactory contentFactory)
+ {
+ super();
+ _contentFactory = contentFactory;
+ }
+
+ @Override
+ public void doGet(HttpServletRequest request,
+ HttpServletResponse response,
+ final ConfiguredObject<?> managedObject) throws IOException
+ {
+
+ ConfiguredObject root = managedObject;
+ String pathInfo = request.getPathInfo();
+ if (managedObject instanceof Broker && null != pathInfo && !pathInfo.isEmpty())
+ {
+ final ConfiguredObjectFinder finder = getConfiguredObjectFinder(managedObject);
+ final ConfiguredObject virtualHost = finder.findObjectFromPath(pathInfo.substring(1), VirtualHost.class);
+ if (null == virtualHost)
+ {
+ sendError(response, HttpServletResponse.SC_NOT_FOUND);
+ return;
+ }
+ else
+ {
+ root = virtualHost;
+ }
+ }
+ else if (managedObject instanceof VirtualHost && null != pathInfo && !pathInfo.isEmpty())
+ {
+ sendError(response, HttpServletResponse.SC_BAD_REQUEST);
+ return;
+ }
+ final Map<String, String[]> parameters = request.getParameterMap();
+ Content content = _contentFactory.createContent(root, parameters);
+ try
+ {
+ writeContent(content, request, response);
+ }
+ finally
+ {
+ content.release();
+ }
+ }
+
+ @Override
+ protected void doPost(final HttpServletRequest req,
+ final HttpServletResponse resp,
+ final ConfiguredObject<?> managedObject)
+ throws IOException
+ {
+ doGet(req, resp, managedObject);
+ }
+
+}
diff --git a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java
index 76d87f1..e324c7a 100644
--- a/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java
+++ b/broker-plugins/management-http/src/main/java/org/apache/qpid/server/management/plugin/servlet/rest/AbstractServlet.java
@@ -284,8 +284,22 @@ public abstract class AbstractServlet extends HttpServlet
protected void writeTypedContent(Content content, HttpServletRequest request, HttpServletResponse response)
throws IOException
{
- Map<String, Object> headers = getResponseHeaders(content);
+ try
+ {
+ writeContent(content, request, response);
+ }
+ catch (IOException e)
+ {
+ LOGGER.warn("Unexpected exception processing request", e);
+ sendJsonErrorResponse(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
+ }
+ }
+ protected void writeContent(final Content content,
+ final HttpServletRequest request,
+ final HttpServletResponse response) throws IOException
+ {
+ Map<String, Object> headers = new HashMap<>(getResponseHeaders(content));
try (OutputStream os = getOutputStream(request, response, headers))
{
response.setStatus(HttpServletResponse.SC_OK);
@@ -295,11 +309,6 @@ public abstract class AbstractServlet extends HttpServlet
}
content.write(os);
}
- catch (IOException e)
- {
- LOGGER.warn("Unexpected exception processing request", e);
- sendJsonErrorResponse(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
- }
}
private OutputStream getOutputStream(final HttpServletRequest request,
diff --git a/broker-plugins/prometheus-exporter/pom.xml b/broker-plugins/prometheus-exporter/pom.xml
new file mode 100644
index 0000000..9f9885f
--- /dev/null
+++ b/broker-plugins/prometheus-exporter/pom.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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.
+ ~
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>qpid-broker-parent</artifactId>
+ <groupId>org.apache.qpid</groupId>
+ <version>7.1.9-SNAPSHOT</version>
+ <relativePath>../../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>qpid-broker-plugins-prometheus-exporter</artifactId>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-broker-codegen</artifactId>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-broker-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.prometheus</groupId>
+ <artifactId>simpleclient</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>io.prometheus</groupId>
+ <artifactId>simpleclient_common</artifactId>
+ </dependency>
+
+ <!-- test dependencies -->
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-test-utils</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-broker-core</artifactId>
+ <classifier>tests</classifier>
+ <scope>test</scope>
+ </dependency>
+
+ </dependencies>
+
+</project>
diff --git a/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObjectStatistic.java b/broker-plugins/prometheus-exporter/src/main/java/org/apache/qpid/server/prometheus/IncludeDisabledStatisticPredicate.java
similarity index 59%
copy from broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObjectStatistic.java
copy to broker-plugins/prometheus-exporter/src/main/java/org/apache/qpid/server/prometheus/IncludeDisabledStatisticPredicate.java
index cc22d62..f675e55 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObjectStatistic.java
+++ b/broker-plugins/prometheus-exporter/src/main/java/org/apache/qpid/server/prometheus/IncludeDisabledStatisticPredicate.java
@@ -1,5 +1,4 @@
/*
- *
* 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
@@ -18,15 +17,26 @@
* under the License.
*
*/
-package org.apache.qpid.server.model;
-public interface ConfiguredObjectStatistic<C extends ConfiguredObject, T extends Object> extends ConfiguredObjectAttributeOrStatistic<C,T>
-{
- String getDescription();
+package org.apache.qpid.server.prometheus;
+
+import java.util.function.Predicate;
- StatisticUnit getUnits();
+import org.apache.qpid.server.model.ConfiguredObjectStatistic;
+
+public class IncludeDisabledStatisticPredicate implements Predicate<ConfiguredObjectStatistic<?,?>>
+
+{
+ private final boolean _includeDisabled;
- StatisticType getStatisticType();
+ IncludeDisabledStatisticPredicate(final boolean includeDisabled)
+ {
+ _includeDisabled = includeDisabled;
+ }
- String getLabel();
+ @Override
+ public boolean test(final ConfiguredObjectStatistic s)
+ {
+ return _includeDisabled || !s.isMetricDisabled();
+ }
}
diff --git a/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObjectStatistic.java b/broker-plugins/prometheus-exporter/src/main/java/org/apache/qpid/server/prometheus/IncludeMetricPredicate.java
similarity index 60%
copy from broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObjectStatistic.java
copy to broker-plugins/prometheus-exporter/src/main/java/org/apache/qpid/server/prometheus/IncludeMetricPredicate.java
index cc22d62..e921352 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObjectStatistic.java
+++ b/broker-plugins/prometheus-exporter/src/main/java/org/apache/qpid/server/prometheus/IncludeMetricPredicate.java
@@ -1,5 +1,4 @@
/*
- *
* 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
@@ -18,15 +17,26 @@
* under the License.
*
*/
-package org.apache.qpid.server.model;
-public interface ConfiguredObjectStatistic<C extends ConfiguredObject, T extends Object> extends ConfiguredObjectAttributeOrStatistic<C,T>
-{
- String getDescription();
+package org.apache.qpid.server.prometheus;
- StatisticUnit getUnits();
+import java.util.Set;
+import java.util.function.Predicate;
+
+public class IncludeMetricPredicate implements Predicate<String>
+{
+ private final Set<String> _allowedNames;
+ private final boolean _isEmpty;
- StatisticType getStatisticType();
+ public IncludeMetricPredicate(final Set<String> allowedNames)
+ {
+ _allowedNames = allowedNames;
+ _isEmpty = _allowedNames.isEmpty();
+ }
- String getLabel();
+ @Override
+ public boolean test(final String name)
+ {
+ return _isEmpty || _allowedNames.contains(name);
+ }
}
diff --git a/broker-plugins/prometheus-exporter/src/main/java/org/apache/qpid/server/prometheus/PrometheusContentFactory.java b/broker-plugins/prometheus-exporter/src/main/java/org/apache/qpid/server/prometheus/PrometheusContentFactory.java
new file mode 100644
index 0000000..af98978
--- /dev/null
+++ b/broker-plugins/prometheus-exporter/src/main/java/org/apache/qpid/server/prometheus/PrometheusContentFactory.java
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ *
+ */
+
+package org.apache.qpid.server.prometheus;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Map;
+
+import io.prometheus.client.exporter.common.TextFormat;
+
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.server.model.Content;
+import org.apache.qpid.server.model.RestContentHeader;
+import org.apache.qpid.server.plugin.ContentFactory;
+import org.apache.qpid.server.plugin.PluggableService;
+
+@PluggableService
+public class PrometheusContentFactory implements ContentFactory
+{
+ static final String INCLUDE_DISABLED = "includeDisabled";
+ static final String INCLUDE_METRIC = "name[]";
+ static final String INCLUDE_DISABLED_CONTEXT_VARIABLE = "qpid.metrics.includeDisabled";
+
+ @Override
+ public Content createContent(final ConfiguredObject<?> object, final Map<String, String[]> filter)
+ {
+ final String[] includeDisabledValues = filter.get(INCLUDE_DISABLED);
+ boolean includeDisabled = includeDisabledValues!= null && includeDisabledValues.length == 1 && Boolean.parseBoolean(includeDisabledValues[0]);
+ if (!includeDisabled)
+ {
+ Boolean val = object.getContextValue(Boolean.class, INCLUDE_DISABLED_CONTEXT_VARIABLE);
+ if (val != null)
+ {
+ includeDisabled = val;
+ }
+ }
+
+ final String[] includedMetricNames = filter.get(INCLUDE_METRIC);
+
+ final IncludeMetricPredicate metricIncludeFilter =
+ new IncludeMetricPredicate(includedMetricNames == null || includedMetricNames.length == 0
+ ? Collections.emptySet()
+ : new HashSet<>(Arrays.asList(includedMetricNames)));
+ final QpidCollector qpidCollector = new QpidCollector(object,
+ new IncludeDisabledStatisticPredicate(includeDisabled),
+ metricIncludeFilter);
+
+ return new Content()
+ {
+ @Override
+ public void write(final OutputStream outputStream) throws IOException
+ {
+ try (final Writer writer = new OutputStreamWriter(outputStream))
+ {
+ TextFormat.write004(writer, Collections.enumeration(qpidCollector.collect()));
+ writer.flush();
+ }
+ }
+
+ @Override
+ public void release()
+ {
+
+ }
+
+ @SuppressWarnings("unused")
+ @RestContentHeader("Content-Type")
+ public String getContentType()
+ {
+ return TextFormat.CONTENT_TYPE_004;
+ }
+
+ };
+
+ }
+
+ @Override
+ public String getType()
+ {
+ return "metrics";
+ }
+}
diff --git a/broker-plugins/prometheus-exporter/src/main/java/org/apache/qpid/server/prometheus/QpidCollector.java b/broker-plugins/prometheus-exporter/src/main/java/org/apache/qpid/server/prometheus/QpidCollector.java
new file mode 100644
index 0000000..3997700
--- /dev/null
+++ b/broker-plugins/prometheus-exporter/src/main/java/org/apache/qpid/server/prometheus/QpidCollector.java
@@ -0,0 +1,257 @@
+/*
+ * 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.
+ *
+ */
+package org.apache.qpid.server.prometheus;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Predicate;
+
+import io.prometheus.client.Collector;
+import io.prometheus.client.CounterMetricFamily;
+import io.prometheus.client.GaugeMetricFamily;
+
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.server.model.ConfiguredObjectStatistic;
+import org.apache.qpid.server.model.Model;
+import org.apache.qpid.server.model.StatisticType;
+import org.apache.qpid.server.model.StatisticUnit;
+
+public class QpidCollector extends Collector
+{
+ private final static MetricFamilySamples IGNORED = new MetricFamilySamples(null, null, null, null);
+ static final String COUNT_SUFFIX = "count";
+ static final String TOTAL_SUFFIX = "total";
+ private final Predicate<ConfiguredObjectStatistic<?,?>> _includeStatisticFilter;
+ private final Predicate<String> _includeMetricFilter;
+ private ConfiguredObject<?> _root;
+ private Model _model;
+
+
+ QpidCollector(final ConfiguredObject<?> root,
+ final Predicate<ConfiguredObjectStatistic<?,?>> includeStatisticFilter,
+ final Predicate<String> includeMetricFilter)
+ {
+ _root = root;
+ _model = _root.getModel();
+ _includeStatisticFilter = includeStatisticFilter;
+ _includeMetricFilter = includeMetricFilter;
+ }
+
+ @Override
+ public List<MetricFamilySamples> collect()
+ {
+ final List<MetricFamilySamples> metricFamilySamples = new ArrayList<>();
+ addObjectMetrics(_root, Collections.emptyList(), new HashMap<>(), metricFamilySamples);
+ addChildrenMetrics(metricFamilySamples, _root, Collections.singletonList("name"));
+ return metricFamilySamples;
+ }
+
+ private void addObjectMetrics(final ConfiguredObject<?> object,
+ final List<String> labelNames,
+ final Map<String, MetricFamilySamples> metricFamilyMap,
+ final List<MetricFamilySamples> metricFamilySamples)
+ {
+ final Map<String, Object> statsMap = object.getStatistics();
+ for (final Map.Entry<String, Object> entry : statsMap.entrySet())
+ {
+ MetricFamilySamples family = metricFamilyMap.get(entry.getKey());
+ if (family == null)
+ {
+ family = createMetricFamilySamples(entry.getKey(), object, labelNames);
+ metricFamilyMap.put(entry.getKey(), family);
+ if (family != IGNORED)
+ {
+ metricFamilySamples.add(family);
+ }
+ }
+ if (family != IGNORED)
+ {
+ final List<String> labelsValues = buildLabelValues(object);
+ final double doubleValue = toDoubleValue(entry.getValue());
+ family.samples.add(new MetricFamilySamples.Sample(family.name, labelNames, labelsValues, doubleValue));
+ }
+ }
+ }
+
+ private MetricFamilySamples createMetricFamilySamples(final String statisticName,
+ final ConfiguredObject<?> object,
+ final List<String> labelNames)
+ {
+ final ConfiguredObjectStatistic<?, ?> configuredObjectStatistic =
+ findConfiguredObjectStatistic(statisticName, object.getTypeClass());
+ if (configuredObjectStatistic == null || !_includeStatisticFilter.test(configuredObjectStatistic))
+ {
+ return IGNORED;
+ }
+ final StatisticType type = configuredObjectStatistic.getStatisticType();
+ final String familyName = getFamilyName(object.getCategoryClass(), configuredObjectStatistic);
+
+ if (!_includeMetricFilter.test(familyName))
+ {
+ return IGNORED;
+ }
+
+ if (type == StatisticType.CUMULATIVE)
+ {
+ return new CounterMetricFamily(familyName, configuredObjectStatistic.getDescription(), labelNames);
+ }
+ else
+ {
+ return new GaugeMetricFamily(familyName, configuredObjectStatistic.getDescription(), labelNames);
+ }
+ }
+
+ private ConfiguredObjectStatistic<?, ?> findConfiguredObjectStatistic(final String statisticName,
+ final Class<? extends ConfiguredObject> typeClass)
+ {
+ final Collection<ConfiguredObjectStatistic<?, ?>> statisticsDefinitions =
+ _model.getTypeRegistry().getStatistics(typeClass);
+ return statisticsDefinitions.stream()
+ .filter(s -> statisticName.equals(s.getName()))
+ .findFirst()
+ .orElse(null);
+ }
+
+ private List<String> buildLabelValues(final ConfiguredObject<?> object)
+ {
+ final List<String> labelsValues = new ArrayList<>();
+ ConfiguredObject o = object;
+ while (o != null && o != _root)
+ {
+ labelsValues.add(o.getName());
+ o = o.getParent();
+ }
+ return labelsValues;
+ }
+
+ private void addChildrenMetrics(final List<MetricFamilySamples> metricFamilySamples,
+ final ConfiguredObject<?> object,
+ final List<String> childLabelNames)
+ {
+ final Class<? extends ConfiguredObject> category = object.getCategoryClass();
+ for (final Class<? extends ConfiguredObject> childClass : _model.getChildTypes(category))
+ {
+ final Collection<? extends ConfiguredObject> children = object.getChildren(childClass);
+ if (children != null && !children.isEmpty())
+ {
+ final Map<String, MetricFamilySamples> childrenMetricFamilyMap = new HashMap<>();
+ for (final ConfiguredObject<?> child : children)
+ {
+ addObjectMetrics(child, childLabelNames, childrenMetricFamilyMap, metricFamilySamples);
+ final List<String> labelNames = new ArrayList<>(childLabelNames);
+ final String label = String.format("%s_name", toSnakeCase(childClass.getSimpleName()));
+ labelNames.add(label);
+ addChildrenMetrics(metricFamilySamples, child, labelNames);
+ }
+ }
+ }
+ }
+
+ static String toSnakeCase(final String simpleName)
+ {
+ final StringBuilder sb = new StringBuilder();
+ final char[] chars = simpleName.toCharArray();
+ for (int i = 0; i < chars.length; i++)
+ {
+ final char ch = chars[i];
+ if (Character.isUpperCase(ch))
+ {
+ if (i > 0)
+ {
+ sb.append('_');
+ }
+ sb.append(Character.toLowerCase(ch));
+ }
+ else
+ {
+ sb.append(ch);
+ }
+ }
+ return sb.toString();
+ }
+
+ private double toDoubleValue(final Object value)
+ {
+ if (value instanceof Number)
+ {
+ return ((Number) value).doubleValue();
+ }
+ return 0;
+ }
+
+ static String getFamilyName(final Class<? extends ConfiguredObject> categoryClass,
+ ConfiguredObjectStatistic<?, ?> statistics)
+ {
+ String metricName = statistics.getMetricName();
+ if (metricName == null || metricName.isEmpty())
+ {
+ metricName = generateMetricName(statistics);
+ }
+
+ return String.format("qpid_%s_%s",
+ toSnakeCase(categoryClass.getSimpleName()),
+ metricName);
+ }
+
+ private static String generateMetricName(final ConfiguredObjectStatistic<?, ?> statistics)
+ {
+ String metricName = toSnakeCase(statistics.getName());
+ String suffix;
+ switch (statistics.getStatisticType())
+ {
+ case CUMULATIVE:
+ suffix = generateMetricSuffix(statistics, COUNT_SUFFIX, metricName);
+ break;
+ case POINT_IN_TIME:
+ suffix = generateMetricSuffix(statistics, TOTAL_SUFFIX, metricName);
+ break;
+ default:
+ suffix = "";
+ }
+
+ return metricName + suffix;
+ }
+
+ private static String generateMetricSuffix(final ConfiguredObjectStatistic<?, ?> statistics,
+ final String typeSuffix,
+ final String metricName)
+ {
+ String suffix = "";
+ if (!statistics.getName().toLowerCase().contains(typeSuffix)
+ && statistics.getUnits() != StatisticUnit.ABSOLUTE_TIME
+ && statistics.getUnits() != StatisticUnit.TIME_DURATION)
+ {
+ if (statistics.getUnits() == StatisticUnit.MESSAGES || statistics.getUnits() == StatisticUnit.BYTES)
+ {
+ final String units = statistics.getUnits().toString() + "s";
+ if (!metricName.contains(units))
+ {
+ suffix = "_" + units;
+ }
+ }
+ suffix = suffix + "_" + typeSuffix;
+ }
+ return suffix;
+ }
+}
diff --git a/broker-plugins/prometheus-exporter/src/test/java/org/apache/qpid/server/prometheus/PrometheusContentFactoryTest.java b/broker-plugins/prometheus-exporter/src/test/java/org/apache/qpid/server/prometheus/PrometheusContentFactoryTest.java
new file mode 100644
index 0000000..7959a77
--- /dev/null
+++ b/broker-plugins/prometheus-exporter/src/test/java/org/apache/qpid/server/prometheus/PrometheusContentFactoryTest.java
@@ -0,0 +1,176 @@
+/*
+ * 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.
+ *
+ */
+
+package org.apache.qpid.server.prometheus;
+
+import static org.apache.qpid.server.prometheus.PrometheusContentFactory.INCLUDE_DISABLED;
+import static org.apache.qpid.server.prometheus.PrometheusContentFactory.INCLUDE_METRIC;
+import static org.apache.qpid.server.prometheus.PrometheusContentFactory.INCLUDE_DISABLED_CONTEXT_VARIABLE;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.startsWith;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.server.model.Content;
+import org.apache.qpid.server.model.Model;
+import org.apache.qpid.server.model.testmodels.hierarchy.TestCar;
+import org.apache.qpid.server.model.testmodels.hierarchy.TestKitCarImpl;
+import org.apache.qpid.server.model.testmodels.hierarchy.TestModel;
+
+public class PrometheusContentFactoryTest
+{
+ public static final String QPID_TEST_CAR_MILEAGE_COUNT = "qpid_test_car_mileage_count";
+ public static final String QPID_TEST_CAR_AGE = "qpid_test_car_age";
+ public static final String PROMETHEUS_COMMENT = "#";
+ private ConfiguredObject _root;
+ private PrometheusContentFactory _prometheusContentFactory;
+
+ @Before
+ public void setUp()
+ {
+ final Model model = TestModel.getInstance();
+ final Map<String, Object> carAttributes = new HashMap<>();
+ carAttributes.put(ConfiguredObject.NAME, "MyPrometheusCar");
+ carAttributes.put(ConfiguredObject.TYPE, TestKitCarImpl.TEST_KITCAR_TYPE);
+
+ @SuppressWarnings("unchecked") final TestCar<?> car =
+ model.getObjectFactory().create(TestCar.class, carAttributes, null);
+ _root = car;
+ _prometheusContentFactory = new PrometheusContentFactory();
+ }
+
+ @Test
+ public void testCreateContent() throws Exception
+ {
+ final Content content = _prometheusContentFactory.createContent(_root, Collections.emptyMap());
+ assertThat(content, is(notNullValue()));
+ Collection<String> metrics;
+ try (final ByteArrayOutputStream output = new ByteArrayOutputStream())
+ {
+ content.write(output);
+ metrics = getMetricLines(output.toByteArray());
+ }
+ assertThat(metrics, is(notNullValue()));
+
+ assertThat(metrics.size(), is(equalTo(1)));
+ String metric = metrics.iterator().next();
+ assertThat(metric, startsWith(QPID_TEST_CAR_MILEAGE_COUNT));
+ }
+
+ @Test
+ public void testCreateContentIncludeDisabled() throws Exception
+ {
+ final Content content = _prometheusContentFactory.createContent(_root, Collections.singletonMap(INCLUDE_DISABLED, new String[]{"true"}));
+ assertThat(content, is(notNullValue()));
+ Collection<String> metrics;
+ try (final ByteArrayOutputStream output = new ByteArrayOutputStream())
+ {
+ content.write(output);
+ metrics = getMetricLines(output.toByteArray());
+ }
+ assertThat(metrics, is(notNullValue()));
+
+ assertThat(metrics.size(), is(equalTo(2)));
+ Map<String, String> metricsMap = convertMetricsToMap(metrics);
+ assertThat(metricsMap.containsKey(QPID_TEST_CAR_MILEAGE_COUNT), equalTo(Boolean.TRUE));
+ assertThat(metricsMap.containsKey(QPID_TEST_CAR_AGE), equalTo(Boolean.TRUE));
+ }
+
+ @Test
+ public void testCreateContentIncludeDisabledUsingContextVariable() throws Exception
+ {
+ _root.setContextVariable(INCLUDE_DISABLED_CONTEXT_VARIABLE, "true");
+ final Content content = _prometheusContentFactory.createContent(_root, Collections.emptyMap());
+ assertThat(content, is(notNullValue()));
+ Collection<String> metrics;
+ try (final ByteArrayOutputStream output = new ByteArrayOutputStream())
+ {
+ content.write(output);
+ metrics = getMetricLines(output.toByteArray());
+ }
+ assertThat(metrics, is(notNullValue()));
+ assertThat(metrics.size(), is(equalTo(2)));
+ Map<String, String> metricsMap = convertMetricsToMap(metrics);
+ assertThat(metricsMap.containsKey(QPID_TEST_CAR_MILEAGE_COUNT), equalTo(Boolean.TRUE));
+ assertThat(metricsMap.containsKey(QPID_TEST_CAR_AGE), equalTo(Boolean.TRUE));
+ }
+
+ @Test
+ public void testCreateContentIncludeName() throws Exception
+ {
+ final Map<String, String[]> filter = new HashMap<>();
+ filter.put(INCLUDE_DISABLED, new String[]{"true"});
+ filter.put(INCLUDE_METRIC, new String[]{QPID_TEST_CAR_AGE});
+ final Content content = _prometheusContentFactory.createContent(_root, filter);
+ assertThat(content, is(notNullValue()));
+ Collection<String> metrics;
+ try (final ByteArrayOutputStream output = new ByteArrayOutputStream())
+ {
+ content.write(output);
+ metrics = getMetricLines(output.toByteArray());
+ }
+ assertThat(metrics, is(notNullValue()));
+
+ assertThat(metrics.size(), is(equalTo(1)));
+ String metric = metrics.iterator().next();
+ assertThat(metric, startsWith(QPID_TEST_CAR_AGE));
+ }
+
+ private static Collection<String> getMetricLines(final byte[] metricsBytes) throws IOException
+ {
+ final List<String> results = new ArrayList<>();
+ try (BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(metricsBytes))))
+ {
+ String line;
+ while ((line = reader.readLine()) != null)
+ {
+ if (!(line.startsWith(PROMETHEUS_COMMENT) || line.isEmpty()))
+ {
+ results.add(line);
+ }
+ }
+ }
+ return results;
+ }
+
+ private Map<String, String> convertMetricsToMap(final Collection<String> metrics)
+ {
+ return metrics.stream().map(m -> m.split(" ")).collect(Collectors.toMap(m -> m[0], m -> m[1]));
+ }
+
+}
diff --git a/broker-plugins/prometheus-exporter/src/test/java/org/apache/qpid/server/prometheus/QpidCollectorTest.java b/broker-plugins/prometheus-exporter/src/test/java/org/apache/qpid/server/prometheus/QpidCollectorTest.java
new file mode 100644
index 0000000..5efbc59
--- /dev/null
+++ b/broker-plugins/prometheus-exporter/src/test/java/org/apache/qpid/server/prometheus/QpidCollectorTest.java
@@ -0,0 +1,359 @@
+/*
+ * 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.
+ *
+ */
+
+package org.apache.qpid.server.prometheus;
+
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.closeTo;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import io.prometheus.client.Collector;
+import org.hamcrest.Matchers;
+import org.junit.Before;
+import org.junit.Test;
+
+import org.apache.qpid.server.model.ConfiguredObject;
+import org.apache.qpid.server.model.ConfiguredObjectStatistic;
+import org.apache.qpid.server.model.Model;
+import org.apache.qpid.server.model.StatisticType;
+import org.apache.qpid.server.model.StatisticUnit;
+import org.apache.qpid.server.model.testmodels.hierarchy.TestAbstractEngineImpl;
+import org.apache.qpid.server.model.testmodels.hierarchy.TestCar;
+import org.apache.qpid.server.model.testmodels.hierarchy.TestDigitalInstrumentPanelImpl;
+import org.apache.qpid.server.model.testmodels.hierarchy.TestElecEngineImpl;
+import org.apache.qpid.server.model.testmodels.hierarchy.TestEngine;
+import org.apache.qpid.server.model.testmodels.hierarchy.TestInstrumentPanel;
+import org.apache.qpid.server.model.testmodels.hierarchy.TestKitCarImpl;
+import org.apache.qpid.server.model.testmodels.hierarchy.TestModel;
+import org.apache.qpid.server.model.testmodels.hierarchy.TestPetrolEngineImpl;
+import org.apache.qpid.server.model.testmodels.hierarchy.TestSensor;
+import org.apache.qpid.server.model.testmodels.hierarchy.TestTemperatureSensorImpl;
+import org.apache.qpid.test.utils.UnitTestBase;
+
+public class QpidCollectorTest extends UnitTestBase
+{
+ private static final String CAR_NAME = "myCar";
+ private static final String ELECTRIC_ENGINE_NAME = "myEngine";
+ private static final String INSTRUMENT_PANEL_NAME = "instrumentPanel";
+ private static final String PETROL_ENGINE_NAME = "myPetrolModel";
+ private static final String SENSOR = "sensor";
+ private static final int DESIRED_MILEAGE = 100;
+ private static final String QPID_TEST_CAR_MILEAGE_COUNT = "qpid_test_car_mileage_count";
+ private static final String QPID_TEST_ENGINE_TEMPERATURE_TOTAL = "qpid_test_engine_temperature_total";
+ private static final String QPID_TEST_SENSOR_ALERT_COUNT = "qpid_test_sensor_alert_count";
+ private static final String QPID_TEST_CAR_AGE_COUNT = "qpid_test_car_age";
+
+ private TestCar<?> _root;
+ private QpidCollector _qpidCollector;
+ private static final StatisticUnit[] UNITS = new StatisticUnit[]{
+ StatisticUnit.BYTES,
+ StatisticUnit.MESSAGES,
+ StatisticUnit.COUNT,
+ StatisticUnit.ABSOLUTE_TIME,
+ StatisticUnit.TIME_DURATION};
+ private static final String[] UNIT_SUFFIXES = new String[]{"_bytes", "_messages", "", "", ""};
+
+ @Before
+ public void setUp()
+ {
+ final Model model = TestModel.getInstance();
+ final Map<String, Object> carAttributes = new HashMap<>();
+ carAttributes.put(ConfiguredObject.NAME, CAR_NAME);
+ carAttributes.put(ConfiguredObject.TYPE, TestKitCarImpl.TEST_KITCAR_TYPE);
+
+ @SuppressWarnings("unchecked") final TestCar<?> car =
+ model.getObjectFactory().create(TestCar.class, carAttributes, null);
+ _root = car;
+ _qpidCollector = new QpidCollector(_root, new IncludeDisabledStatisticPredicate(false), s->true);
+ }
+
+ @Test
+ public void testCollectForHierarchyOfTwoObjects()
+ {
+ createTestEngine(ELECTRIC_ENGINE_NAME, TestElecEngineImpl.TEST_ELEC_ENGINE_TYPE);
+ _root.move(DESIRED_MILEAGE);
+
+ final List<Collector.MetricFamilySamples> metrics = _qpidCollector.collect();
+
+ final String[] expectedFamilyNames = {QPID_TEST_CAR_MILEAGE_COUNT, QPID_TEST_ENGINE_TEMPERATURE_TOTAL};
+ final Map<String, Collector.MetricFamilySamples> metricsMap =
+ convertMetricFamilySamplesIntoMapAndAssert(metrics, expectedFamilyNames);
+
+ final Collector.MetricFamilySamples carMetricFamilySamples = metricsMap.get(QPID_TEST_CAR_MILEAGE_COUNT);
+ assertMetricFamilySamplesSize(carMetricFamilySamples, 1);
+ final Collector.MetricFamilySamples.Sample carSample = carMetricFamilySamples.samples.get(0);
+ assertThat(carSample.value, closeTo(DESIRED_MILEAGE, 0.01));
+ assertThat(carSample.labelNames.size(), is(equalTo(0)));
+
+ final Collector.MetricFamilySamples engineMetricFamilySamples = metricsMap.get(
+ QPID_TEST_ENGINE_TEMPERATURE_TOTAL);
+ assertMetricFamilySamplesSize(engineMetricFamilySamples, 1);
+ final Collector.MetricFamilySamples.Sample engineSample = engineMetricFamilySamples.samples.get(0);
+ assertThat(engineSample.labelNames, is(equalTo(Collections.singletonList("name"))));
+ assertThat(engineSample.labelValues, is(equalTo(Collections.singletonList(ELECTRIC_ENGINE_NAME))));
+ assertThat(engineSample.value, Matchers.closeTo(TestAbstractEngineImpl.TEST_TEMPERATURE, 0.01));
+ }
+
+ @Test
+ public void testCollectForHierarchyOfThreeObjects()
+ {
+ final TestInstrumentPanel instrumentPanel = getTestInstrumentPanel();
+ createTestSensor(instrumentPanel);
+
+ final List<Collector.MetricFamilySamples> metrics = _qpidCollector.collect();
+
+ final String[] expectedFamilyNames =
+ {QPID_TEST_CAR_MILEAGE_COUNT, QPID_TEST_SENSOR_ALERT_COUNT};
+ final Map<String, Collector.MetricFamilySamples> metricsMap =
+ convertMetricFamilySamplesIntoMapAndAssert(metrics, expectedFamilyNames);
+
+ final Collector.MetricFamilySamples carMetricFamilySamples = metricsMap.get(QPID_TEST_CAR_MILEAGE_COUNT);
+ assertMetricFamilySamplesSize(carMetricFamilySamples, 1);
+ final Collector.MetricFamilySamples.Sample carSample = carMetricFamilySamples.samples.get(0);
+ assertThat(carSample.labelNames.size(), is(equalTo(0)));
+ assertThat(carSample.labelValues.size(), is(equalTo(0)));
+ assertThat(carSample.value, closeTo(0, 0.01));
+
+ final Collector.MetricFamilySamples sensorlMetricFamilySamples =
+ metricsMap.get(QPID_TEST_SENSOR_ALERT_COUNT);
+ assertMetricFamilySamplesSize(sensorlMetricFamilySamples, 1);
+ final Collector.MetricFamilySamples.Sample sensorSample = sensorlMetricFamilySamples.samples.get(0);
+ assertThat(sensorSample.labelNames, is(equalTo(Arrays.asList("name", "test_instrument_panel_name"))));
+ assertThat(sensorSample.labelValues, is(equalTo(Arrays.asList(SENSOR, INSTRUMENT_PANEL_NAME))));
+ }
+
+ @Test
+ public void testCollectForSiblingObjects()
+ {
+ createTestEngine(ELECTRIC_ENGINE_NAME, TestElecEngineImpl.TEST_ELEC_ENGINE_TYPE);
+ createTestEngine(PETROL_ENGINE_NAME, TestPetrolEngineImpl.TEST_PETROL_ENGINE_TYPE);
+
+ final List<Collector.MetricFamilySamples> metrics = _qpidCollector.collect();
+
+ final String[] expectedFamilyNames = {QPID_TEST_CAR_MILEAGE_COUNT, QPID_TEST_ENGINE_TEMPERATURE_TOTAL};
+ final Map<String, Collector.MetricFamilySamples> metricsMap =
+ convertMetricFamilySamplesIntoMapAndAssert(metrics, expectedFamilyNames);
+
+ final Collector.MetricFamilySamples carMetricFamilySamples = metricsMap.get(QPID_TEST_CAR_MILEAGE_COUNT);
+ assertMetricFamilySamplesSize(carMetricFamilySamples, 1);
+ final Collector.MetricFamilySamples.Sample carSample = carMetricFamilySamples.samples.get(0);
+ assertThat(carSample.labelNames.size(), is(equalTo(0)));
+ assertThat(carSample.labelValues.size(), is(equalTo(0)));
+
+ final Collector.MetricFamilySamples engineMetricFamilySamples = metricsMap.get(
+ QPID_TEST_ENGINE_TEMPERATURE_TOTAL);
+ assertMetricFamilySamplesSize(engineMetricFamilySamples, 2);
+ final String[] engineNames = {PETROL_ENGINE_NAME, ELECTRIC_ENGINE_NAME};
+ for (String engineName : engineNames)
+ {
+ final Collector.MetricFamilySamples.Sample sample =
+ findSampleByLabelValue(engineMetricFamilySamples, engineName);
+ assertThat(sample.labelNames, is(equalTo(Collections.singletonList("name"))));
+ assertThat(sample.labelValues, is(equalTo(Collections.singletonList(engineName))));
+ assertThat(sample.value, Matchers.closeTo(TestAbstractEngineImpl.TEST_TEMPERATURE, 0.01));
+ }
+ }
+
+ @Test
+ public void testCollectWithFilter(){
+ createTestEngine(ELECTRIC_ENGINE_NAME, TestElecEngineImpl.TEST_ELEC_ENGINE_TYPE);
+ _root.move(DESIRED_MILEAGE);
+
+ _qpidCollector = new QpidCollector(_root,
+ new IncludeDisabledStatisticPredicate(true),
+ new IncludeMetricPredicate(Collections.singleton(QPID_TEST_CAR_AGE_COUNT)));
+ final List<Collector.MetricFamilySamples> metrics = _qpidCollector.collect();
+
+ final String[] expectedFamilyNames = {QPID_TEST_CAR_AGE_COUNT};
+ final Map<String, Collector.MetricFamilySamples> metricsMap =
+ convertMetricFamilySamplesIntoMapAndAssert(metrics, expectedFamilyNames);
+
+
+ final Collector.MetricFamilySamples engineMetricFamilySamples = metricsMap.get(QPID_TEST_CAR_AGE_COUNT);
+ assertMetricFamilySamplesSize(engineMetricFamilySamples, 1);
+ final Collector.MetricFamilySamples.Sample engineSample = engineMetricFamilySamples.samples.get(0);
+ assertThat(engineSample.labelNames, is(equalTo(Collections.emptyList())));
+ assertThat(engineSample.labelValues, is(equalTo(Collections.emptyList())));
+ assertThat(engineSample.value, Matchers.closeTo(0.0, 0.01));
+
+ }
+
+
+ private Collector.MetricFamilySamples.Sample findSampleByLabelValue(final Collector.MetricFamilySamples metricFamilySamples,
+ final String nameLabelValue)
+ {
+ final List<Collector.MetricFamilySamples.Sample> found = metricFamilySamples.samples
+ .stream()
+ .filter(s -> s.labelValues != null
+ && s.labelValues.size() > 0
+ && nameLabelValue.equals(s.labelValues.get(0)))
+ .collect(Collectors.toList());
+ assertThat(found.size(), is(equalTo(1)));
+ return found.get(0);
+ }
+
+ private void createTestEngine(final String engineName, final String engineType)
+ {
+ final Map<String, Object> engineAttributes = new HashMap<>();
+ engineAttributes.put(ConfiguredObject.NAME, engineName);
+ engineAttributes.put(ConfiguredObject.TYPE, engineType);
+ _root.createChild(TestEngine.class, engineAttributes);
+ }
+
+ private void createTestSensor(final TestInstrumentPanel instrumentPanel)
+ {
+ final Map<String, Object> sensorAttributes = new HashMap<>();
+ sensorAttributes.put(ConfiguredObject.NAME, SENSOR);
+ sensorAttributes.put(ConfiguredObject.TYPE, TestTemperatureSensorImpl.TEST_TEMPERATURE_SENSOR_TYPE);
+ instrumentPanel.createChild(TestSensor.class, sensorAttributes);
+ }
+
+ private TestInstrumentPanel getTestInstrumentPanel()
+ {
+ final Map<String, Object> instrumentPanelAttributes = new HashMap<>();
+ instrumentPanelAttributes.put(ConfiguredObject.NAME, INSTRUMENT_PANEL_NAME);
+ instrumentPanelAttributes.put(ConfiguredObject.TYPE,
+ TestDigitalInstrumentPanelImpl.TEST_DIGITAL_INSTRUMENT_PANEL_TYPE);
+ return _root.createChild(TestInstrumentPanel.class, instrumentPanelAttributes);
+ }
+
+ private Map<String, Collector.MetricFamilySamples> convertMetricFamilySamplesIntoMap(List<Collector.MetricFamilySamples> metricFamilySamples)
+ {
+ Map<String, Collector.MetricFamilySamples> result = new HashMap<>();
+ for (Collector.MetricFamilySamples metricFamilySample : metricFamilySamples)
+ {
+ final String name = metricFamilySample.name;
+
+ if (result.put(name, metricFamilySample) != null)
+ {
+ fail(String.format("Duplicate family name : %s", name));
+ }
+ }
+ return result;
+ }
+
+ private void assertMetricFamilySamples(final Collector.MetricFamilySamples metricFamilySamples)
+ {
+ assertThat(metricFamilySamples, is(notNullValue()));
+ assertThat(metricFamilySamples.samples, is(notNullValue()));
+
+ for (final Collector.MetricFamilySamples.Sample sample : metricFamilySamples.samples)
+ {
+ assertThat(sample, is(notNullValue()));
+ assertThat(sample.name, is(equalTo(metricFamilySamples.name)));
+ }
+ }
+
+
+ private void assertMetricFamilySamplesSize(final Collector.MetricFamilySamples metricFamilySamples,
+ final int expectedSamplesSize)
+ {
+ assertThat(metricFamilySamples.samples.size(), equalTo(expectedSamplesSize));
+ }
+
+ private Map<String, Collector.MetricFamilySamples> convertMetricFamilySamplesIntoMapAndAssert(final List<Collector.MetricFamilySamples> metrics,
+ final String[] expectedFamilyNames)
+ {
+ assertThat(metrics.size(), equalTo(expectedFamilyNames.length));
+ final Map<String, Collector.MetricFamilySamples> metricsMap = convertMetricFamilySamplesIntoMap(metrics);
+
+ for (String expectedFamily : expectedFamilyNames)
+ {
+ assertMetricFamilySamples(metricsMap.get(expectedFamily));
+ }
+ return metricsMap;
+ }
+
+ @Test
+ public void testToSnakeCase()
+ {
+ assertThat(QpidCollector.toSnakeCase("carEngineOilChanges"), is(equalTo("car_engine_oil_changes")));
+ }
+
+ @Test
+ public void getFamilyNameForCumulativeStatistic()
+ {
+ for (int i = 0; i < UNITS.length; i++)
+ {
+ final ConfiguredObjectStatistic<?, ?> statistics = mock(ConfiguredObjectStatistic.class);
+ when(statistics.getUnits()).thenReturn(UNITS[i]);
+ when(statistics.getStatisticType()).thenReturn(StatisticType.CUMULATIVE);
+ when(statistics.getName()).thenReturn("diagnosticData");
+ final String familyName = QpidCollector.getFamilyName(TestCar.class, statistics);
+ final String expectedName =
+ String.format("qpid_test_car_diagnostic_data%s%s", UNIT_SUFFIXES[i], getSuffix(UNITS[i],QpidCollector.COUNT_SUFFIX));
+ assertThat(String.format("unexpected metric name for units %s", UNITS[i]),
+ familyName,
+ is(equalTo(expectedName)));
+ }
+ }
+
+ @Test
+ public void getFamilyNameForCumulativeStatisticContainingCountInName()
+ {
+ final ConfiguredObjectStatistic<?, ?> statistics = mock(ConfiguredObjectStatistic.class);
+ when(statistics.getUnits()).thenReturn(StatisticUnit.BYTES);
+ when(statistics.getStatisticType()).thenReturn(StatisticType.CUMULATIVE);
+ when(statistics.getName()).thenReturn("CountOfDiagnosticData");
+ final String familyName = QpidCollector.getFamilyName(TestCar.class, statistics);
+ assertThat(familyName, is(equalTo("qpid_test_car_count_of_diagnostic_data")));
+ }
+
+ @Test
+ public void getFamilyNameForPointInTimeStatistic()
+ {
+ for (int i = 0; i < UNITS.length; i++)
+ {
+ final ConfiguredObjectStatistic<?, ?> statistics = mock(ConfiguredObjectStatistic.class);
+ when(statistics.getUnits()).thenReturn(UNITS[i]);
+ when(statistics.getStatisticType()).thenReturn(StatisticType.POINT_IN_TIME);
+ when(statistics.getName()).thenReturn("diagnosticData");
+ final String familyName = QpidCollector.getFamilyName(TestCar.class, statistics);
+
+ final String expectedName =
+ String.format("qpid_test_car_diagnostic_data%s%s", UNIT_SUFFIXES[i], getSuffix(UNITS[i],QpidCollector.TOTAL_SUFFIX));
+ assertThat(String.format("unexpected metric name for units %s", UNITS[i]),
+ familyName,
+ is(equalTo(expectedName)));
+ }
+ }
+
+ String getSuffix(final StatisticUnit unit,final String requiredSuffix)
+ {
+ String suffix = "_" + requiredSuffix;
+ if(unit.equals(StatisticUnit.ABSOLUTE_TIME) || unit.equals(StatisticUnit.TIME_DURATION)){
+ suffix = "";
+ }
+ return suffix;
+ }
+}
diff --git a/doc/java-broker/src/docbkx/Java-Broker-Management-Channels.xml b/doc/java-broker/src/docbkx/Java-Broker-Management-Channels.xml
index 9211056..13e7fa2 100644
--- a/doc/java-broker/src/docbkx/Java-Broker-Management-Channels.xml
+++ b/doc/java-broker/src/docbkx/Java-Broker-Management-Channels.xml
@@ -37,5 +37,6 @@
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="management/channels/Java-Broker-Management-Channel-HTTP.xml"/>
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="management/channels/Java-Broker-Management-Channel-Web-Console.xml"/>
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="management/channels/Java-Broker-Management-Channel-REST-API.xml"/>
+ <xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="management/channels/Java-Broker-Management-Metrics.xml"/>
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="management/channels/Java-Broker-Management-Channel-AMQP-Intrinsic.xml"/>
</chapter>
diff --git a/doc/java-broker/src/docbkx/management/channels/Java-Broker-Management-Metrics.xml b/doc/java-broker/src/docbkx/management/channels/Java-Broker-Management-Metrics.xml
new file mode 100644
index 0000000..c8aeb9d
--- /dev/null
+++ b/doc/java-broker/src/docbkx/management/channels/Java-Broker-Management-Metrics.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0"?>
+<!--
+ ~ 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.
+ ~
+ -->
+
+<section xmlns="http://docbook.org/ns/docbook" version="5.0" xml:id="Java-Broker-Management-Metrics">
+ <title>Prometheus Metrics</title>
+ <para>This section describes the metrics endpoints exposing broker statistics in
+ <link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="https://prometheus.io/">Prometheus format</link>.
+ The metrics endpoint is intended for scraping by Prometheus server to collect the Broker telemetry.</para>
+ <para>The Prometheus metric endpoints are mapped under /metrics path and /metrics/*.
+ The latter allows to get the Virtual Host statistics by specify the path to the virtual host as
+ /metrics/<virtual host node name>/< virtual host name>.
+ The former allow to get all Broker statistics or Virtual Host statistics when called with HOST header
+ set to the Virtual Host name</para>
+ <para>
+ The metrics endpoints allow anonymous access by default. If required, an authentication can be enabled for the
+ metrics endpoints by setting http management context variable
+ <literal>qpid.httpManagement.enableMetricContentAuthentication</literal> to <literal>true</literal>.
+ </para>
+
+ <para>The Broker JVM statistics are disabled by default. The metrics endpoints can be called with parameter
+ <literal>includeDisabled</literal> set to <literal>true</literal> to include JVM broker metrics into endpoint
+ output.
+ </para>
+ <note>
+ <para>For more information about Prometheus, check out the
+ <link xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="https://prometheus.io/docs/introduction/overview/">prometheus documentation</link>.
+ </para>
+ </note>
+
+</section>
diff --git a/pom.xml b/pom.xml
index 274b957..c49bcb8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -154,6 +154,7 @@
<h2.version>1.4.199</h2.version>
<apache-directory-version>2.0.0-M23</apache-directory-version>
<kerby-version>1.0.1</kerby-version>
+ <prometheus-client-version>0.9.0</prometheus-client-version>
</properties>
<modules>
@@ -179,6 +180,7 @@
<module>broker-plugins/websocket</module>
<module>broker-plugins/amqp-1-0-bdb-store</module>
<module>broker-plugins/amqp-1-0-jdbc-store</module>
+ <module>broker-plugins/prometheus-exporter</module>
<module>tools</module>
<module>qpid-systests-parent</module>
@@ -469,6 +471,12 @@
<version>${project.version}</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.qpid</groupId>
+ <artifactId>qpid-broker-plugins-prometheus-exporter</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
<!-- External dependencies -->
<dependency>
<groupId>org.apache.qpid</groupId>
@@ -750,6 +758,18 @@
<scope>test</scope>
<version>${kerby-version}</version>
</dependency>
+
+ <!-- prometheus client dependencies -->
+ <dependency>
+ <groupId>io.prometheus</groupId>
+ <artifactId>simpleclient</artifactId>
+ <version>${prometheus-client-version}</version>
+ </dependency>
+ <dependency>
+ <groupId>io.prometheus</groupId>
+ <artifactId>simpleclient_common</artifactId>
+ <version>${prometheus-client-version}</version>
+ </dependency>
</dependencies>
</dependencyManagement>
diff --git a/systests/qpid-systests-http-management/src/test/java/org/apache/qpid/tests/http/metrics/BrokerMetricsAuthenticationTest.java b/systests/qpid-systests-http-management/src/test/java/org/apache/qpid/tests/http/metrics/BrokerMetricsAuthenticationTest.java
new file mode 100644
index 0000000..fcfca5e
--- /dev/null
+++ b/systests/qpid-systests-http-management/src/test/java/org/apache/qpid/tests/http/metrics/BrokerMetricsAuthenticationTest.java
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ *
+ */
+
+package org.apache.qpid.tests.http.metrics;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.junit.Test;
+
+import org.apache.qpid.server.management.plugin.HttpManagement;
+import org.apache.qpid.tests.http.HttpRequestConfig;
+import org.apache.qpid.tests.http.HttpTestBase;
+import org.apache.qpid.tests.http.HttpTestHelper;
+import org.apache.qpid.tests.utils.ConfigItem;
+
+@HttpRequestConfig(useVirtualHostAsHost = false)
+@ConfigItem(name = HttpManagement.HTTP_MANAGEMENT_ENABLE_CONTENT_AUTHENTICATION, value = "true")
+public class BrokerMetricsAuthenticationTest extends HttpTestBase
+{
+ @Test
+ public void testBrokerMetricsForAuthenticatedUser() throws Exception
+ {
+ getHelper().submitRequest("/metrics", "GET", HttpServletResponse.SC_OK);
+ }
+
+ @Test
+ public void testBrokerMetricsForUnauthenticatedUser() throws Exception
+ {
+ final HttpTestHelper helper = new HttpTestHelper(getBrokerAdmin(), null);
+ helper.setUserName(null);
+ helper.setPassword(null);
+ helper.submitRequest("/metrics", "GET", HttpServletResponse.SC_UNAUTHORIZED);
+ }
+
+}
diff --git a/systests/qpid-systests-http-management/src/test/java/org/apache/qpid/tests/http/metrics/BrokerMetricsTest.java b/systests/qpid-systests-http-management/src/test/java/org/apache/qpid/tests/http/metrics/BrokerMetricsTest.java
new file mode 100644
index 0000000..7423588
--- /dev/null
+++ b/systests/qpid-systests-http-management/src/test/java/org/apache/qpid/tests/http/metrics/BrokerMetricsTest.java
@@ -0,0 +1,80 @@
+package org.apache.qpid.tests.http.metrics;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+import static org.apache.qpid.tests.http.metrics.TestMetricsHelper.QUEUE_NAME;
+import static org.apache.qpid.tests.http.metrics.TestMetricsHelper.assertMetricsInclusion;
+import static org.apache.qpid.tests.http.metrics.TestMetricsHelper.assertVirtualHostHierarchyMetrics;
+import static org.apache.qpid.tests.http.metrics.TestMetricsHelper.createQueueMetricPattern;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import java.util.Collection;
+import java.util.regex.Pattern;
+
+import org.junit.Test;
+
+import org.apache.qpid.tests.http.HttpRequestConfig;
+import org.apache.qpid.tests.http.HttpTestBase;
+
+@HttpRequestConfig(useVirtualHostAsHost = false)
+public class BrokerMetricsTest extends HttpTestBase
+{
+ private static final String[] EXPECTED_BROKER_METRIC_NAMES =
+ new String[]{"qpid_broker_inbound_bytes_count", "qpid_broker_outbound_bytes_count"};
+
+ @Test
+ public void testBrokerMetrics() throws Exception
+ {
+ final String[] unexpectedMetricNames =
+ {"qpid_broker_live_threads_total", "qpid_broker_direct_memory_capacity_bytes_total"};
+
+ final byte[] metricsBytes = getHelper().getBytes("/metrics");
+ final String metricsString = new String(metricsBytes, UTF_8);
+ assertMetricsInclusion(metricsString, EXPECTED_BROKER_METRIC_NAMES, true);
+ assertMetricsInclusion(metricsString, unexpectedMetricNames, false);
+
+ final byte[] metricsBytesIncludingDisabled = getHelper().getBytes("/metrics?includeDisabled=true");
+ final String metricsStringIncludingDisabled = new String(metricsBytesIncludingDisabled, UTF_8);
+ assertMetricsInclusion(metricsStringIncludingDisabled, unexpectedMetricNames, true);
+ assertMetricsInclusion(metricsStringIncludingDisabled, EXPECTED_BROKER_METRIC_NAMES, true);
+ }
+
+ @Test
+ public void testQueueMetrics() throws Exception
+ {
+ getBrokerAdmin().createQueue(QUEUE_NAME);
+ final byte[] metricsBytes = getHelper().getBytes("/metrics");
+ final String metricsString = new String(metricsBytes, UTF_8);
+
+ final Pattern[] expectedMetricPattens = {createQueueMetricPattern("qpid_queue_consumers_total"),
+ createQueueMetricPattern("qpid_queue_depth_messages_total")};
+
+ assertMetricsInclusion(metricsString, expectedMetricPattens, true);
+ }
+
+ @Test
+ public void testQueueMetricsIncludeOnlyMessageDepth() throws Exception
+ {
+ getBrokerAdmin().createQueue(QUEUE_NAME);
+ final byte[] metricsBytes = getHelper().getBytes("/metrics?name[]=qpid_queue_depth_messages_total&name[]=qpid_queue_depth_bytes_total");
+ Collection<String> metricLines = TestMetricsHelper.getMetricLines(metricsBytes);
+ assertThat(metricLines.size(), is(equalTo(2)));
+
+ final String metricsString = new String(metricsBytes, UTF_8);
+ final Pattern[] expectedMetricPattens = {createQueueMetricPattern("qpid_queue_depth_bytes_total"),
+ createQueueMetricPattern("qpid_queue_depth_messages_total")};
+
+ assertMetricsInclusion(metricsString, expectedMetricPattens, true);
+ }
+
+ @Test
+ public void testMappingForVirtualHost() throws Exception
+ {
+ getBrokerAdmin().createQueue(QUEUE_NAME);
+ final byte[] metricsBytes =
+ getHelper().getBytes(String.format("/metrics/%s/%s", getVirtualHostNode(), getVirtualHost()));
+
+ assertVirtualHostHierarchyMetrics(metricsBytes);
+ }
+}
diff --git a/systests/qpid-systests-http-management/src/test/java/org/apache/qpid/tests/http/metrics/TestMetricsHelper.java b/systests/qpid-systests-http-management/src/test/java/org/apache/qpid/tests/http/metrics/TestMetricsHelper.java
new file mode 100644
index 0000000..fa9b679
--- /dev/null
+++ b/systests/qpid-systests-http-management/src/test/java/org/apache/qpid/tests/http/metrics/TestMetricsHelper.java
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ *
+ */
+
+package org.apache.qpid.tests.http.metrics;
+
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.fail;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.function.Predicate;
+import java.util.regex.Pattern;
+
+class TestMetricsHelper
+{
+ static final String QUEUE_NAME = "foo";
+
+ static void assertMetricsInclusion(final String metricsString,
+ final String[] metricNames,
+ final boolean inclusionFlag)
+ {
+ for (String expected : metricNames)
+ {
+ assertThat(metricsString.contains(expected), equalTo(inclusionFlag));
+ }
+ }
+
+ static void assertMetricsInclusion(final String metricsString,
+ final Pattern[] expectedMetricPattens,
+ final boolean inclusionFlag)
+ {
+ for (Pattern expected : expectedMetricPattens)
+ {
+ assertThat(expected.matcher(metricsString).find(), equalTo(inclusionFlag));
+ }
+ }
+
+ static Pattern createQueueMetricPattern(final String metricName)
+ {
+ return Pattern.compile(String.format("%s\\s*\\{.*name\\s*=\\s*\"%s\"\\s*,.*\\}\\s*0\\.0",
+ metricName,
+ QUEUE_NAME));
+ }
+
+ static void assertVirtualHostHierarchyMetrics(final byte[] metricsBytes) throws IOException
+ {
+ final Predicate<String> unexpectedMetricPredicate = line -> !(line.startsWith("qpid_virtual_host")
+ || line.startsWith("qpid_queue")
+ || line.startsWith("qpid_exchange"));
+ getMetricLines(metricsBytes).stream().filter(unexpectedMetricPredicate)
+ .findFirst().ifPresent(found -> fail("Unexpected metric: " + found));
+ }
+
+ static Collection<String> getMetricLines(final byte[] metricsBytes) throws IOException
+ {
+ final List<String> results = new ArrayList<>();
+ try(BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(metricsBytes))))
+ {
+ String line;
+ while ((line = reader.readLine()) != null)
+ {
+ if (!(line.startsWith("#") || line.isEmpty()))
+ {
+ results.add(line);
+ }
+ }
+ }
+ return results;
+ }
+}
diff --git a/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObjectStatistic.java b/systests/qpid-systests-http-management/src/test/java/org/apache/qpid/tests/http/metrics/VirtualHostMetricsTest.java
similarity index 54%
copy from broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObjectStatistic.java
copy to systests/qpid-systests-http-management/src/test/java/org/apache/qpid/tests/http/metrics/VirtualHostMetricsTest.java
index cc22d62..bd06588 100644
--- a/broker-core/src/main/java/org/apache/qpid/server/model/ConfiguredObjectStatistic.java
+++ b/systests/qpid-systests-http-management/src/test/java/org/apache/qpid/tests/http/metrics/VirtualHostMetricsTest.java
@@ -1,5 +1,4 @@
/*
- *
* 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
@@ -18,15 +17,25 @@
* under the License.
*
*/
-package org.apache.qpid.server.model;
-public interface ConfiguredObjectStatistic<C extends ConfiguredObject, T extends Object> extends ConfiguredObjectAttributeOrStatistic<C,T>
-{
- String getDescription();
+package org.apache.qpid.tests.http.metrics;
- StatisticUnit getUnits();
+import static org.apache.qpid.tests.http.metrics.TestMetricsHelper.QUEUE_NAME;
+import static org.apache.qpid.tests.http.metrics.TestMetricsHelper.assertVirtualHostHierarchyMetrics;
- StatisticType getStatisticType();
+import org.junit.Test;
- String getLabel();
+import org.apache.qpid.tests.http.HttpRequestConfig;
+import org.apache.qpid.tests.http.HttpTestBase;
+
+@HttpRequestConfig
+public class VirtualHostMetricsTest extends HttpTestBase
+{
+ @Test
+ public void testVirtualHostMetrics() throws Exception
+ {
+ getBrokerAdmin().createQueue(QUEUE_NAME);
+ final byte[] metricsBytes = getHelper().getBytes("/metrics");
+ assertVirtualHostHierarchyMetrics(metricsBytes);
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org