Skip to content

Commit

Permalink
feat: [CO-1350] export http connection pool statistics to prometheus (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
sonersivri authored Jul 16, 2024
1 parent 7dcd419 commit ea87abb
Show file tree
Hide file tree
Showing 11 changed files with 288 additions and 17 deletions.
11 changes: 0 additions & 11 deletions Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -141,17 +141,6 @@ pipeline {
mvnCmd("$BUILD_PROPERTIES_PARAMS test -Dgroups=api")
}
}
stage('Publish Coverage') {
when {
expression {
params.SKIP_TEST_WITH_COVERAGE == false
}
}
steps {
recordCoverage(tools: [[parser: 'JACOCO']],sourceCodeRetention: 'MODIFIED')
junit allowEmptyResults: true, testResults: '**/target/surefire-reports/*.xml'
}
}

stage('Sonarqube Analysis') {
environment {
Expand Down
5 changes: 4 additions & 1 deletion common/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,10 @@
<artifactId>commons-lang</artifactId>
<groupId>commons-lang</groupId>
</dependency>

<dependency>
<groupId>io.prometheus</groupId>
<artifactId>simpleclient</artifactId>
</dependency>
<dependency>
<!-- TODO: this is also defined in zm-zcs-lib -->
<artifactId>nekohtml</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* SPDX-FileCopyrightText: 2024 Zextras <https://www.zextras.com>
*
* SPDX-License-Identifier: CC0-1.0
*/
package com.zimbra.common.metric;

import io.prometheus.client.Collector;

/**
* Carbonio Collector
*/
public abstract class CarbonioCollector extends Collector {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* SPDX-FileCopyrightText: 2024 Zextras <https://www.zextras.com>
*
* SPDX-License-Identifier: CC0-1.0
*/
package com.zimbra.common.util;

import com.zimbra.common.metric.CarbonioCollector;
import io.prometheus.client.GaugeMetricFamily;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.pool.PoolStats;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
* For export HttpConnectionPools metrics
*
*/
public class HttpConnectionManagerMetricsExport extends CarbonioCollector {

private static final String POOL = "pool";

/**
* @return List<MetricFamilySamples>
*/
@Override
public List<MetricFamilySamples> collect() {
List<MetricFamilySamples> mfs = new ArrayList<>();
GaugeMetricFamily internalGaugeMetricFamily = getGaugeMetricFamily("internal");

setMetrics(ZimbraHttpConnectionManager.getInternalHttpConnMgr(), internalGaugeMetricFamily);
mfs.add(internalGaugeMetricFamily);

GaugeMetricFamily externalGaugeMetricFamily = getGaugeMetricFamily("external");
setMetrics(ZimbraHttpConnectionManager.getExternalHttpConnMgr(), externalGaugeMetricFamily);
mfs.add(externalGaugeMetricFamily);
return mfs;
}

/**
* get Gauge Metric Family
*/
GaugeMetricFamily getGaugeMetricFamily(String type) {
return new GaugeMetricFamily(
"http_connection_" + POOL + "_" + type,
"Metrics for " + type + " " + POOL,
Collections.singletonList(POOL));
}

/**
* set metrics
*/
void setMetrics(ZimbraHttpConnectionManager zimbraHttpConnectionManager, GaugeMetricFamily gaugeMetricFamily) {
PoolStats poolStats = null;
if (zimbraHttpConnectionManager != null && zimbraHttpConnectionManager.getHttpConnMgr() instanceof PoolingHttpClientConnectionManager) {
poolStats = ((PoolingHttpClientConnectionManager) zimbraHttpConnectionManager.getHttpConnMgr()).getTotalStats();
}
if (poolStats == null) {
return;
}
gaugeMetricFamily.addMetric(Collections.singletonList("available"), poolStats.getAvailable());
gaugeMetricFamily.addMetric(Collections.singletonList("leased"), poolStats.getLeased());
gaugeMetricFamily.addMetric(Collections.singletonList("max"), poolStats.getMax());
gaugeMetricFamily.addMetric(Collections.singletonList("pending"), poolStats.getPending());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.zimbra.common.util;

import io.prometheus.client.GaugeMetricFamily;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;


class HttpConnectionManagerMetricsExportTest {

private HttpConnectionManagerMetricsExport export;

@BeforeEach
void setUp() {
export = new HttpConnectionManagerMetricsExport();
}
@Test
void test_collect() {
Assertions.assertDoesNotThrow(() ->export.collect());
}

@Test
void test_setMetrics_when_zimbraHttpConnectionManager_is_null_then_do_nothing() {
GaugeMetricFamily gaugeMetricFamily = Mockito.mock(GaugeMetricFamily.class);
export.setMetrics(null, gaugeMetricFamily);
Mockito.verify(gaugeMetricFamily, Mockito.times(0))
.addMetric(Mockito.anyList(), Mockito.anyInt());
}

@Test
void test_setMetrics_when_zimbraHttpConnectionManager_is_not_instanceof_PoolingHttpClientConnectionManager_then_do_nothing() {
GaugeMetricFamily gaugeMetricFamily = Mockito.mock(GaugeMetricFamily.class);
ZimbraHttpConnectionManager zimbraHttpConnectionManager = Mockito.mock(ZimbraHttpConnectionManager.class);
export.setMetrics(zimbraHttpConnectionManager, gaugeMetricFamily);
Mockito.verify(gaugeMetricFamily, Mockito.times(0))
.addMetric(Mockito.anyList(), Mockito.anyInt());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* SPDX-FileCopyrightText: 2024 Zextras <https://www.zextras.com>
*
* SPDX-License-Identifier: CC0-1.0
*/
package com.zextras.mailbox.metric;

import com.zimbra.common.util.HttpConnectionManagerMetricsExport;
import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.hotspot.DefaultExports;

/**
* For register DefaultExports and project specific exports
*/
public final class CarbonioMetricRegisterer {

private CarbonioMetricRegisterer() {}
/**
*
* @param registry CollectorRegistry
*/
public static void register(CollectorRegistry registry) {
DefaultExports.register(registry);
registry.register(new HttpConnectionManagerMetricsExport());
}
}
12 changes: 11 additions & 1 deletion store/src/main/java/com/zextras/mailbox/metric/Metrics.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.prometheus.PrometheusConfig;
import io.micrometer.prometheus.PrometheusMeterRegistry;
import io.prometheus.client.Collector;
import io.prometheus.client.CollectorRegistry;

/**
Expand All @@ -20,12 +21,21 @@
* @since 23.4.0
* @author davidefrison
*/
public class Metrics {
public final class Metrics {
public static final CollectorRegistry COLLECTOR_REGISTRY = new CollectorRegistry();

private Metrics() {}
/**
* Binds a Prometheus-compatible registry to the registry
*/
public static final MeterRegistry METER_REGISTRY =
new PrometheusMeterRegistry(PrometheusConfig.DEFAULT, COLLECTOR_REGISTRY, Clock.SYSTEM);

/**
* registers collector
*/
public static void register(Collector collector) {
COLLECTOR_REGISTRY.register(collector);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import com.google.inject.Provides;
import com.google.inject.servlet.ServletModule;
import com.zextras.mailbox.metric.CarbonioMetricRegisterer;
import com.zextras.mailbox.metric.Metrics;
import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.exporter.MetricsServlet;
Expand Down Expand Up @@ -42,9 +43,8 @@ public MetricsServlet provideMetricsServlet(CollectorRegistry collectorRegistry)
@Provides
@Singleton
public CollectorRegistry provideCollector() {
// TODO: it would be nice to get all collectors and register them programmatically
final CollectorRegistry metricRegistry = Metrics.COLLECTOR_REGISTRY;
DefaultExports.register(metricRegistry);
return metricRegistry;
CarbonioMetricRegisterer.register(Metrics.COLLECTOR_REGISTRY);
return Metrics.COLLECTOR_REGISTRY;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package com.zextras.mailbox.domain.usecase.metric;

import com.zextras.mailbox.metric.CarbonioMetricRegisterer;
import com.zimbra.common.util.HttpConnectionManagerMetricsExport;
import io.prometheus.client.CollectorRegistry;
import io.prometheus.client.hotspot.BufferPoolsExports;
import io.prometheus.client.hotspot.ClassLoadingExports;
import io.prometheus.client.hotspot.GarbageCollectorExports;
import io.prometheus.client.hotspot.MemoryAllocationExports;
import io.prometheus.client.hotspot.MemoryPoolsExports;
import io.prometheus.client.hotspot.StandardExports;
import io.prometheus.client.hotspot.ThreadExports;
import io.prometheus.client.hotspot.VersionInfoExports;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

class CarbonioMetricRegistererTest {


@Test
void test_register_registers_StandardExports() {
CollectorRegistry collectorRegistry = Mockito.mock(CollectorRegistry.class);
CarbonioMetricRegisterer.register(collectorRegistry);
Mockito.verify(collectorRegistry, Mockito.times(1))
.register(Mockito.any(StandardExports.class));

}

@Test
void test_register_registers_MemoryPoolsExports() {
CollectorRegistry collectorRegistry = Mockito.mock(CollectorRegistry.class);
CarbonioMetricRegisterer.register(collectorRegistry);
Mockito.verify(collectorRegistry, Mockito.times(1))
.register(Mockito.any(MemoryPoolsExports.class));
}

@Test
void test_register_registers_MemoryAllocationExports() {
CollectorRegistry collectorRegistry = Mockito.mock(CollectorRegistry.class);
CarbonioMetricRegisterer.register(collectorRegistry);
Mockito.verify(collectorRegistry, Mockito.times(1))
.register(Mockito.any(MemoryAllocationExports.class));
}

@Test
void test_register_registers_BufferPoolsExports() {
CollectorRegistry collectorRegistry = Mockito.mock(CollectorRegistry.class);
CarbonioMetricRegisterer.register(collectorRegistry);
Mockito.verify(collectorRegistry, Mockito.times(1))
.register(Mockito.any(BufferPoolsExports.class));
}

@Test
void test_register_registers_GarbageCollectorExports() {
CollectorRegistry collectorRegistry = Mockito.mock(CollectorRegistry.class);
CarbonioMetricRegisterer.register(collectorRegistry);
Mockito.verify(collectorRegistry, Mockito.times(1))
.register(Mockito.any(GarbageCollectorExports.class));
}

@Test
void test_register_registers_ThreadExports() {
CollectorRegistry collectorRegistry = Mockito.mock(CollectorRegistry.class);
CarbonioMetricRegisterer.register(collectorRegistry);
Mockito.verify(collectorRegistry, Mockito.times(1))
.register(Mockito.any(ThreadExports.class));
}

@Test
void test_register_registers_ClassLoadingExports() {
CollectorRegistry collectorRegistry = Mockito.mock(CollectorRegistry.class);
CarbonioMetricRegisterer.register(collectorRegistry);
Mockito.verify(collectorRegistry, Mockito.times(1))
.register(Mockito.any(ClassLoadingExports.class));
}

@Test
void test_register_registers_VersionInfoExports() {
CollectorRegistry collectorRegistry = Mockito.mock(CollectorRegistry.class);
CarbonioMetricRegisterer.register(collectorRegistry);
Mockito.verify(collectorRegistry, Mockito.times(1))
.register(Mockito.any(VersionInfoExports.class));
}

@Test
void test_register_registers_HttpConnectionManagerMetricsExport() {
CollectorRegistry collectorRegistry = Mockito.mock(CollectorRegistry.class);
CarbonioMetricRegisterer.register(collectorRegistry);
Mockito.verify(collectorRegistry, Mockito.times(1))
.register(Mockito.any(HttpConnectionManagerMetricsExport.class));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.zextras.mailbox.domain.usecase.metric;

import com.zextras.mailbox.metric.Metrics;
import io.prometheus.client.Collector;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

class MetricsTest {

@Test
void test_register() {
Collector collector = Mockito.mock(Collector.class);
Metrics.register(collector);
Assertions.assertDoesNotThrow(() -> Metrics.COLLECTOR_REGISTRY.unregister(collector));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.zextras.mailbox.servlet;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

class MetricsServletModuleTest {

@Test
void test_provideCollector() {
MetricsServletModule module = new MetricsServletModule();
Assertions.assertNotNull(module.provideCollector());
}
}

0 comments on commit ea87abb

Please sign in to comment.