diff --git a/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/BufferPoolsExports.java b/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/BufferPoolsExports.java
index d456b5e7b..808b2b3c7 100644
--- a/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/BufferPoolsExports.java
+++ b/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/BufferPoolsExports.java
@@ -17,7 +17,7 @@
* Can be replaced with a simple access once JDK 1.7 compatibility is baseline.
*
*/
-public class BufferPoolsExports extends Collector {
+public class BufferPoolsExports extends Collector implements HotspotCollector {
private static final Logger LOGGER = Logger.getLogger(BufferPoolsExports.class.getName());
diff --git a/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/ClassLoadingExports.java b/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/ClassLoadingExports.java
index 65f86cfba..8c94a86ee 100644
--- a/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/ClassLoadingExports.java
+++ b/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/ClassLoadingExports.java
@@ -25,7 +25,7 @@
* jvm_classes_unloaded_total{} 500
*
*/
-public class ClassLoadingExports extends Collector {
+public class ClassLoadingExports extends Collector implements HotspotCollector {
private final ClassLoadingMXBean clBean;
public ClassLoadingExports() {
diff --git a/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/DeadlockExports.java b/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/DeadlockExports.java
new file mode 100644
index 000000000..d29bfd5e6
--- /dev/null
+++ b/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/DeadlockExports.java
@@ -0,0 +1,65 @@
+package io.prometheus.client.hotspot;
+
+import io.prometheus.client.Collector;
+import io.prometheus.client.GaugeMetricFamily;
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.ThreadMXBean;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Exports metrics about JVM thread deadlocks.
+ *
+ * Deadlock detection might be an expensive operation.
+ * Consider to avoid these metrics for performance-critical applications.
+ *
+ *
+ * Example usage:
+ *
+ * {@code
+ * new DeadlockExports().register();
+ * }
+ *
+ * Example metrics being exported:
+ *
+ * jvm_threads_deadlocked{} 4
+ * jvm_threads_deadlocked_monitor{} 2
+ *
+ */
+public class DeadlockExports extends Collector implements HotspotCollector {
+ private final ThreadMXBean threadBean;
+
+ public DeadlockExports() {
+ this(ManagementFactory.getThreadMXBean());
+ }
+
+ public DeadlockExports(ThreadMXBean threadBean) {
+ this.threadBean = threadBean;
+ }
+
+ void addDeadlockMetrics(List sampleFamilies) {
+ sampleFamilies.add(
+ new GaugeMetricFamily(
+ "jvm_threads_deadlocked",
+ "Cycles of JVM-threads that are in deadlock waiting to acquire object monitors or ownable synchronizers",
+ nullSafeArrayLength(threadBean.findDeadlockedThreads())));
+
+ sampleFamilies.add(
+ new GaugeMetricFamily(
+ "jvm_threads_deadlocked_monitor",
+ "Cycles of JVM-threads that are in deadlock waiting to acquire object monitors",
+ nullSafeArrayLength(threadBean.findMonitorDeadlockedThreads())));
+ }
+
+ private static double nullSafeArrayLength(long[] array) {
+ return null == array ? 0 : array.length;
+ }
+
+ @Override
+ public List collect() {
+ List mfs = new ArrayList();
+ addDeadlockMetrics(mfs);
+ return mfs;
+ }
+}
diff --git a/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/DefaultExports.java b/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/DefaultExports.java
index 79cd6ed5e..e10914186 100644
--- a/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/DefaultExports.java
+++ b/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/DefaultExports.java
@@ -2,6 +2,12 @@
import io.prometheus.client.CollectorRegistry;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
/**
* Registers the default Hotspot collectors.
*
@@ -14,6 +20,14 @@
* DefaultExports.initialize();
* }
*
+ * If some collector is undesirable for any reason
+ * (e.g. {@link DeadlockExports} for potential performance penalty)
+ * it may be explicitly excluded by using alternative registration approach:
+ *
+ * {@code
+ * DefaultExports.build().exclude(DeadlockExports.class).register();
+ * }
+ *
*/
public class DefaultExports {
private static boolean initialized = false;
@@ -34,14 +48,63 @@ public static synchronized void initialize() {
* Register the default Hotspot collectors with the given registry.
*/
public static void register(CollectorRegistry registry) {
- new StandardExports().register(registry);
- new MemoryPoolsExports().register(registry);
- new MemoryAllocationExports().register(registry);
- new BufferPoolsExports().register(registry);
- new GarbageCollectorExports().register(registry);
- new ThreadExports().register(registry);
- new ClassLoadingExports().register(registry);
- new VersionInfoExports().register(registry);
+ register(registry, Collections.>emptySet());
+ }
+
+ private static void register(CollectorRegistry registry, Set> exclusions) {
+ List extends HotspotCollector> collectors = Arrays.asList(
+ new StandardExports(),
+ new MemoryPoolsExports(),
+ new MemoryAllocationExports(),
+ new BufferPoolsExports(),
+ new GarbageCollectorExports(),
+ new ThreadExports(),
+ new DeadlockExports(),
+ new ClassLoadingExports(),
+ new VersionInfoExports());
+
+ for (HotspotCollector collector : collectors) {
+ if (!exclusions.contains(collector.getClass())) {
+ collector.register(registry);
+ }
+ }
+ }
+
+ /**
+ * Return a Builder to allow configuration of a DefaultExports.
+ */
+ public static Builder build() {
+ return new Builder();
+ }
+
+ public static class Builder {
+
+ private final Set> exclusions = new HashSet>();
+
+ private Builder() {
+ }
+
+ /**
+ * Exclude provided Hotspot collector from registration.
+ */
+ public Builder exclude(Class extends HotspotCollector> exclusion) {
+ exclusions.add(exclusion);
+ return this;
+ }
+
+ /**
+ * Register the default Hotspot collectors (except provided exclusions) with the default registry.
+ */
+ public void register() {
+ register(CollectorRegistry.defaultRegistry);
+ }
+
+ /**
+ * Register the default Hotspot collectors (except provided exclusions) with the given registry.
+ */
+ public void register(CollectorRegistry registry) {
+ DefaultExports.register(registry, exclusions);
+ }
}
}
diff --git a/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/GarbageCollectorExports.java b/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/GarbageCollectorExports.java
index ad0b1f029..7232f9eb5 100644
--- a/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/GarbageCollectorExports.java
+++ b/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/GarbageCollectorExports.java
@@ -24,7 +24,7 @@
* jvm_gc_collection_seconds_sum{gc="PS1"} 6.7
*
*/
-public class GarbageCollectorExports extends Collector {
+public class GarbageCollectorExports extends Collector implements HotspotCollector {
private final List garbageCollectors;
public GarbageCollectorExports() {
diff --git a/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/HotspotCollector.java b/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/HotspotCollector.java
new file mode 100644
index 000000000..ccb4d02a0
--- /dev/null
+++ b/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/HotspotCollector.java
@@ -0,0 +1,13 @@
+package io.prometheus.client.hotspot;
+
+import io.prometheus.client.Collector;
+import io.prometheus.client.CollectorRegistry;
+
+/**
+ * Intended mostly for exclusion of undesired collectors from {@link DefaultExports}
+ * (see {@link DefaultExports.Builder#exclude(Class)})
+ */
+public interface HotspotCollector {
+
+ T register(CollectorRegistry registry);
+}
diff --git a/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/MemoryAllocationExports.java b/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/MemoryAllocationExports.java
index b5a2754d7..7e502d7d1 100644
--- a/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/MemoryAllocationExports.java
+++ b/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/MemoryAllocationExports.java
@@ -16,7 +16,7 @@
import java.util.List;
import java.util.Map;
-public class MemoryAllocationExports extends Collector {
+public class MemoryAllocationExports extends Collector implements HotspotCollector {
private final Counter allocatedCounter = Counter.build()
.name("jvm_memory_pool_allocated_bytes_total")
.help("Total bytes allocated in a given JVM memory pool. Only updated after GC, not continuously.")
diff --git a/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/MemoryPoolsExports.java b/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/MemoryPoolsExports.java
index 0b37e72ba..c0035377b 100644
--- a/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/MemoryPoolsExports.java
+++ b/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/MemoryPoolsExports.java
@@ -28,7 +28,7 @@
* jvm_memory_pool_bytes_used{pool="PS Eden Space"} 2000
*
*/
-public class MemoryPoolsExports extends Collector {
+public class MemoryPoolsExports extends Collector implements HotspotCollector {
private final MemoryMXBean memoryBean;
private final List poolBeans;
diff --git a/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/StandardExports.java b/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/StandardExports.java
index 2013bc142..ced0cdb69 100644
--- a/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/StandardExports.java
+++ b/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/StandardExports.java
@@ -30,7 +30,7 @@
* }
*
*/
-public class StandardExports extends Collector {
+public class StandardExports extends Collector implements HotspotCollector {
private static final Logger LOGGER = Logger.getLogger(StandardExports.class.getName());
private final StatusReader statusReader;
diff --git a/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/ThreadExports.java b/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/ThreadExports.java
index 55c90a654..a5a7edd82 100644
--- a/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/ThreadExports.java
+++ b/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/ThreadExports.java
@@ -30,7 +30,7 @@
* jvm_threads_started_total{} 1200
*
*/
-public class ThreadExports extends Collector {
+public class ThreadExports extends Collector implements HotspotCollector {
private final ThreadMXBean threadBean;
public ThreadExports() {
@@ -66,18 +66,6 @@ void addThreadMetrics(List sampleFamilies) {
"Started thread count of a JVM",
threadBean.getTotalStartedThreadCount()));
- sampleFamilies.add(
- new GaugeMetricFamily(
- "jvm_threads_deadlocked",
- "Cycles of JVM-threads that are in deadlock waiting to acquire object monitors or ownable synchronizers",
- nullSafeArrayLength(threadBean.findDeadlockedThreads())));
-
- sampleFamilies.add(
- new GaugeMetricFamily(
- "jvm_threads_deadlocked_monitor",
- "Cycles of JVM-threads that are in deadlock waiting to acquire object monitors",
- nullSafeArrayLength(threadBean.findMonitorDeadlockedThreads())));
-
GaugeMetricFamily threadStateFamily = new GaugeMetricFamily(
"jvm_threads_state",
"Current count of threads by state",
@@ -114,10 +102,7 @@ private Map getThreadStateCountMap() {
return threadCounts;
}
- private static double nullSafeArrayLength(long[] array) {
- return null == array ? 0 : array.length;
- }
-
+ @Override
public List collect() {
List mfs = new ArrayList();
addThreadMetrics(mfs);
diff --git a/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/VersionInfoExports.java b/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/VersionInfoExports.java
index 8dd711080..5c59e4277 100644
--- a/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/VersionInfoExports.java
+++ b/simpleclient_hotspot/src/main/java/io/prometheus/client/hotspot/VersionInfoExports.java
@@ -3,8 +3,6 @@
import io.prometheus.client.Collector;
import io.prometheus.client.Info;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
/**
@@ -22,7 +20,7 @@
*
*/
-public class VersionInfoExports extends Collector {
+public class VersionInfoExports extends Collector implements HotspotCollector {
public List collect() {
diff --git a/simpleclient_hotspot/src/test/java/io/prometheus/client/hotspot/DeadlockExportsTest.java b/simpleclient_hotspot/src/test/java/io/prometheus/client/hotspot/DeadlockExportsTest.java
new file mode 100644
index 000000000..803701dfc
--- /dev/null
+++ b/simpleclient_hotspot/src/test/java/io/prometheus/client/hotspot/DeadlockExportsTest.java
@@ -0,0 +1,54 @@
+package io.prometheus.client.hotspot;
+
+import io.prometheus.client.CollectorRegistry;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.lang.management.ThreadMXBean;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.when;
+
+public class DeadlockExportsTest {
+
+ private ThreadMXBean mockThreadsBean = Mockito.mock(ThreadMXBean.class);
+ private CollectorRegistry registry = new CollectorRegistry();
+ private DeadlockExports collectorUnderTest;
+
+ private static final String[] EMPTY_LABEL = new String[0];
+
+ @Before
+ public void setUp() {
+ when(mockThreadsBean.findDeadlockedThreads()).thenReturn(new long[]{1L,2L,3L});
+ when(mockThreadsBean.findMonitorDeadlockedThreads()).thenReturn(new long[]{2L,3L,4L});
+ collectorUnderTest = new DeadlockExports(mockThreadsBean).register(registry);
+ }
+
+ @Test
+ public void testDeadlockExports() {
+ assertNull(
+ registry.getSampleValue(
+ "jvm_threads_current", EMPTY_LABEL, EMPTY_LABEL));
+ assertNull(
+ registry.getSampleValue(
+ "jvm_threads_daemon", EMPTY_LABEL, EMPTY_LABEL));
+ assertNull(
+ registry.getSampleValue(
+ "jvm_threads_peak", EMPTY_LABEL, EMPTY_LABEL));
+ assertNull(
+ registry.getSampleValue(
+ "jvm_threads_started_total", EMPTY_LABEL, EMPTY_LABEL));
+ assertEquals(
+ 3L,
+ registry.getSampleValue(
+ "jvm_threads_deadlocked", EMPTY_LABEL, EMPTY_LABEL),
+ .0000001);
+ assertEquals(
+ 3L,
+ registry.getSampleValue(
+ "jvm_threads_deadlocked_monitor", EMPTY_LABEL, EMPTY_LABEL),
+ .0000001);
+ }
+}
diff --git a/simpleclient_hotspot/src/test/java/io/prometheus/client/hotspot/ExampleExporter.java b/simpleclient_hotspot/src/test/java/io/prometheus/client/hotspot/ExampleExporter.java
index 7c7212c8b..59886306b 100644
--- a/simpleclient_hotspot/src/test/java/io/prometheus/client/hotspot/ExampleExporter.java
+++ b/simpleclient_hotspot/src/test/java/io/prometheus/client/hotspot/ExampleExporter.java
@@ -1,7 +1,6 @@
package io.prometheus.client.hotspot;
import io.prometheus.client.exporter.MetricsServlet;
-import io.prometheus.client.hotspot.StandardExports;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
diff --git a/simpleclient_hotspot/src/test/java/io/prometheus/client/hotspot/ThreadExportsTest.java b/simpleclient_hotspot/src/test/java/io/prometheus/client/hotspot/ThreadExportsTest.java
index 9bd6c7f45..908ffccfc 100644
--- a/simpleclient_hotspot/src/test/java/io/prometheus/client/hotspot/ThreadExportsTest.java
+++ b/simpleclient_hotspot/src/test/java/io/prometheus/client/hotspot/ThreadExportsTest.java
@@ -7,9 +7,9 @@
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
-import java.util.Arrays;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.when;
public class ThreadExportsTest {
@@ -34,8 +34,6 @@ public void setUp() {
when(mockThreadsBean.getDaemonThreadCount()).thenReturn(200);
when(mockThreadsBean.getPeakThreadCount()).thenReturn(301);
when(mockThreadsBean.getTotalStartedThreadCount()).thenReturn(503L);
- when(mockThreadsBean.findDeadlockedThreads()).thenReturn(new long[]{1L,2L,3L});
- when(mockThreadsBean.findMonitorDeadlockedThreads()).thenReturn(new long[]{2L,3L,4L});
when(mockThreadsBean.getAllThreadIds()).thenReturn(new long[]{3L,4L,5L});
when(mockThreadInfoBlocked.getThreadState()).thenReturn(Thread.State.BLOCKED);
when(mockThreadInfoRunnable1.getThreadState()).thenReturn(Thread.State.RUNNABLE);
@@ -68,16 +66,12 @@ public void testThreadPools() {
registry.getSampleValue(
"jvm_threads_started_total", EMPTY_LABEL, EMPTY_LABEL),
.0000001);
- assertEquals(
- 3L,
+ assertNull(
registry.getSampleValue(
- "jvm_threads_deadlocked", EMPTY_LABEL, EMPTY_LABEL),
- .0000001);
- assertEquals(
- 3L,
+ "jvm_threads_deadlocked", EMPTY_LABEL, EMPTY_LABEL));
+ assertNull(
registry.getSampleValue(
- "jvm_threads_deadlocked_monitor", EMPTY_LABEL, EMPTY_LABEL),
- .0000001);
+ "jvm_threads_deadlocked_monitor", EMPTY_LABEL, EMPTY_LABEL));
assertEquals(
1L,