Skip to content

Commit

Permalink
Make Dropwizard Metrics and Prometheus optional for Spring Boot integ…
Browse files Browse the repository at this point in the history
…ration (line#2107)

Motivation:

A user may not want to pull in Dropwizard Metrics at all.

Modifications:

- Access anything related with Dropwizard using the reflection API.
- Make `io.dropwizard.metrics:metrics-json` optional.

Result:

- Fixes line#2106
- A user can exclude `io.dropwizard.metrics:metrics-core` as well as
  `metrics-json`.
  • Loading branch information
trustin authored Sep 25, 2019
1 parent f17355d commit 893497d
Show file tree
Hide file tree
Showing 7 changed files with 179 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,6 @@ public void hello() throws Exception {
public void healthCheck() throws Exception {
final AggregatedHttpResponse res = client.get("/internal/healthcheck").aggregate().join();
assertThat(res.status()).isEqualTo(HttpStatus.OK);
assertThat(res.contentUtf8()).isEqualTo("ok");
assertThat(res.contentUtf8()).isEqualTo("{\"healthy\":true}");
}
}
8 changes: 6 additions & 2 deletions spring/boot-autoconfigure/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@ dependencies {
}
compile project(':logback')

compile 'io.micrometer:micrometer-registry-prometheus'
compile 'io.dropwizard.metrics:metrics-json'
compile('io.micrometer:micrometer-registry-prometheus') {
ext.optional = true
}
compile('io.dropwizard.metrics:metrics-json') {
ext.optional = true
}
compile 'javax.inject:javax.inject'
compileOnly 'javax.validation:validation-api'
compile 'org.springframework.boot:spring-boot-starter'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,6 @@
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
Expand All @@ -54,10 +52,6 @@
import org.slf4j.LoggerFactory;
import org.springframework.util.ResourceUtils;

import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.json.MetricsModule;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.google.common.base.Ascii;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
Expand All @@ -67,21 +61,17 @@
import com.linecorp.armeria.common.HttpHeaders;
import com.linecorp.armeria.common.HttpRequest;
import com.linecorp.armeria.common.HttpResponse;
import com.linecorp.armeria.common.HttpStatus;
import com.linecorp.armeria.common.MediaType;
import com.linecorp.armeria.common.SessionProtocol;
import com.linecorp.armeria.server.AbstractHttpService;
import com.linecorp.armeria.server.ServerBuilder;
import com.linecorp.armeria.server.ServerPort;
import com.linecorp.armeria.server.Service;
import com.linecorp.armeria.server.ServiceRequestContext;
import com.linecorp.armeria.server.ServiceWithRoutes;
import com.linecorp.armeria.server.docs.DocServiceBuilder;
import com.linecorp.armeria.server.encoding.HttpEncodingService;
import com.linecorp.armeria.server.healthcheck.HealthCheckService;
import com.linecorp.armeria.server.healthcheck.HealthChecker;
import com.linecorp.armeria.server.healthcheck.HttpHealthCheckService;
import com.linecorp.armeria.server.metric.MetricCollectingService;
import com.linecorp.armeria.server.metric.PrometheusExpositionService;
import com.linecorp.armeria.spring.AbstractServiceRegistrationBean;
import com.linecorp.armeria.spring.AnnotatedExampleRequest;
import com.linecorp.armeria.spring.AnnotatedServiceRegistrationBean;
Expand All @@ -95,14 +85,10 @@
import com.linecorp.armeria.spring.ThriftServiceRegistrationBean;

import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
import io.micrometer.core.instrument.dropwizard.DropwizardMeterRegistry;
import io.micrometer.prometheus.PrometheusMeterRegistry;
import io.netty.handler.ssl.ClientAuth;
import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.SupportedCipherSuiteFilter;
import io.netty.util.NetUtil;
import io.prometheus.client.CollectorRegistry;

/**
* A utility class which is used to configure a {@link ServerBuilder} with the {@link ArmeriaSettings} and
Expand All @@ -111,7 +97,6 @@
public final class ArmeriaConfigurationUtil {
private static final Logger logger = LoggerFactory.getLogger(ArmeriaConfigurationUtil.class);

private static final HealthChecker[] EMPTY_HEALTH_CHECKERS = new HealthChecker[0];
private static final String[] EMPTY_PROTOCOL_NAMES = new String[0];

private static final String METER_TYPE = "server";
Expand Down Expand Up @@ -144,39 +129,31 @@ public static void configureServerWithArmeriaSettings(ServerBuilder server, Arme

final String healthCheckPath = settings.getHealthCheckPath();
if (!Strings.isNullOrEmpty(healthCheckPath)) {
server.service(healthCheckPath,
new HttpHealthCheckService(healthCheckers.toArray(EMPTY_HEALTH_CHECKERS)));
server.service(healthCheckPath, HealthCheckService.of(healthCheckers));
}

server.meterRegistry(meterRegistry);

if (settings.isEnableMetrics() && !Strings.isNullOrEmpty(settings.getMetricsPath())) {
if (meterRegistry instanceof CompositeMeterRegistry) {
final Set<MeterRegistry> childRegistries =
((CompositeMeterRegistry) meterRegistry).getRegistries();
childRegistries.stream()
.filter(PrometheusMeterRegistry.class::isInstance)
.map(PrometheusMeterRegistry.class::cast)
.findAny()
.ifPresent(r -> addPrometheusExposition(settings, server, r));
} else if (meterRegistry instanceof PrometheusMeterRegistry) {
addPrometheusExposition(settings, server, (PrometheusMeterRegistry) meterRegistry);
} else if (meterRegistry instanceof DropwizardMeterRegistry) {
final MetricRegistry dropwizardRegistry =
((DropwizardMeterRegistry) meterRegistry).getDropwizardRegistry();
final ObjectMapper objectMapper = new ObjectMapper()
.enable(SerializationFeature.INDENT_OUTPUT)
.registerModule(new MetricsModule(TimeUnit.SECONDS, TimeUnit.MILLISECONDS, true));
server.service(
settings.getMetricsPath(),
new AbstractHttpService() {
@Override
protected HttpResponse doGet(ServiceRequestContext ctx, HttpRequest req)
throws Exception {
return HttpResponse.of(HttpStatus.OK, MediaType.JSON_UTF_8,
objectMapper.writeValueAsBytes(dropwizardRegistry));
}
});
final boolean hasPrometheus = hasAllClasses(
"io.micrometer.prometheus.PrometheusMeterRegistry",
"io.prometheus.client.CollectorRegistry");

final boolean addedPrometheusExposition;
if (hasPrometheus) {
addedPrometheusExposition = PrometheusSupport.addExposition(settings, server, meterRegistry);
} else {
addedPrometheusExposition = false;
}

if (!addedPrometheusExposition) {
final boolean hasDropwizard = hasAllClasses(
"io.micrometer.core.instrument.dropwizard.DropwizardMeterRegistry",
"com.codahale.metrics.MetricRegistry",
"com.codahale.metrics.json.MetricsModule");
if (hasDropwizard) {
DropwizardSupport.addExposition(settings, server, meterRegistry);
}
}
}

Expand All @@ -192,6 +169,17 @@ protected HttpResponse doGet(ServiceRequestContext ctx, HttpRequest req)
}
}

private static boolean hasAllClasses(String... classNames) {
for (String className : classNames) {
try {
Class.forName(className, false, ArmeriaConfigurationUtil.class.getClassLoader());
} catch (Throwable t) {
return false;
}
}
return true;
}

/**
* Adds {@link Port}s to the specified {@link ServerBuilder}.
*/
Expand Down Expand Up @@ -405,21 +393,6 @@ MetricCollectingService<HttpRequest, HttpResponse>> metricCollectingServiceDecor
meterIdPrefixFunctionFactory.get(METER_TYPE, bean.getServiceName()));
}

private static void addPrometheusExposition(ArmeriaSettings armeriaSettings, ServerBuilder server,
PrometheusMeterRegistry registry) {
requireNonNull(armeriaSettings, "armeriaSettings");
requireNonNull(server, "server");
requireNonNull(registry, "registry");

final String metricsPath = armeriaSettings.getMetricsPath();
if (metricsPath == null) {
return;
}

final CollectorRegistry prometheusRegistry = registry.getPrometheusRegistry();
server.service(metricsPath, new PrometheusExpositionService(prometheusRegistry));
}

/**
* Adds SSL/TLS context to the specified {@link ServerBuilder}.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright 2019 LINE Corporation
*
* LINE Corporation 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:
*
* https://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 com.linecorp.armeria.internal.spring;

import java.util.concurrent.TimeUnit;

import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.json.MetricsModule;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

import com.linecorp.armeria.common.HttpResponse;
import com.linecorp.armeria.common.HttpStatus;
import com.linecorp.armeria.common.MediaType;
import com.linecorp.armeria.server.ServerBuilder;
import com.linecorp.armeria.spring.ArmeriaSettings;

import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.dropwizard.DropwizardMeterRegistry;

final class DropwizardSupport {

static boolean addExposition(ArmeriaSettings settings, ServerBuilder server,
MeterRegistry meterRegistry) {
final String metricsPath = settings.getMetricsPath();
assert metricsPath != null;

if (!(meterRegistry instanceof DropwizardMeterRegistry)) {
return false;
}
final MetricRegistry dropwizardRegistry =
((DropwizardMeterRegistry) meterRegistry).getDropwizardRegistry();
final ObjectMapper objectMapper = new ObjectMapper()
.enable(SerializationFeature.INDENT_OUTPUT)
.registerModule(new MetricsModule(TimeUnit.SECONDS, TimeUnit.MILLISECONDS, true));

server.route()
.get(settings.getMetricsPath())
.build((ctx, req) -> HttpResponse.of(HttpStatus.OK, MediaType.JSON_UTF_8,
objectMapper.writeValueAsBytes(dropwizardRegistry)));
return true;
}

private DropwizardSupport() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright 2019 LINE Corporation
*
* LINE Corporation 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:
*
* https://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 com.linecorp.armeria.internal.spring;

import java.util.Optional;
import java.util.Set;

import com.linecorp.armeria.server.ServerBuilder;
import com.linecorp.armeria.server.metric.PrometheusExpositionService;
import com.linecorp.armeria.spring.ArmeriaSettings;

import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.composite.CompositeMeterRegistry;
import io.micrometer.prometheus.PrometheusMeterRegistry;
import io.prometheus.client.CollectorRegistry;

final class PrometheusSupport {

static boolean addExposition(ArmeriaSettings settings, ServerBuilder server,
MeterRegistry meterRegistry) {

final String metricsPath = settings.getMetricsPath();
assert metricsPath != null;

for (;;) {
if (meterRegistry instanceof PrometheusMeterRegistry) {
final CollectorRegistry prometheusRegistry =
((PrometheusMeterRegistry) meterRegistry).getPrometheusRegistry();
server.service(metricsPath, new PrometheusExpositionService(prometheusRegistry));
return true;
}

if (meterRegistry instanceof CompositeMeterRegistry) {
final Set<MeterRegistry> childRegistries =
((CompositeMeterRegistry) meterRegistry).getRegistries();
final Optional<PrometheusMeterRegistry> opt =
childRegistries.stream()
.filter(PrometheusMeterRegistry.class::isInstance)
.map(PrometheusMeterRegistry.class::cast)
.findAny();

if (!opt.isPresent()) {
return false;
}

meterRegistry = opt.get();
continue;
}

return false;
}
}

private PrometheusSupport() {}
}
10 changes: 8 additions & 2 deletions spring/boot-webflux-autoconfigure/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@ dependencies {
}
compile project(':logback')

compile 'io.micrometer:micrometer-registry-prometheus'
compile 'io.dropwizard.metrics:metrics-json'
compile('io.micrometer:micrometer-registry-prometheus') {
ext.optional = true
}
compile('io.dropwizard.metrics:metrics-json') {
ext.optional = true
}
compile 'javax.inject:javax.inject'
compileOnly 'javax.validation:validation-api'
compile 'org.springframework.boot:spring-boot-starter-webflux'
Expand All @@ -34,7 +38,9 @@ task copyFiles(type: Copy) {
include '**/ArmeriaServerConfigurator.java'
include '**/ArmeriaSettings.java'
include '**/CustomAlias*KeyManager*.java'
include '**/DropwizardSupport.java'
include '**/MeterIdPrefixFunctionFactory.java'
include '**/PrometheusSupport.java'
include '**/Ssl.java'
include '**/ThriftServiceUtils.java'
}
Expand Down
8 changes: 6 additions & 2 deletions spring/boot1-autoconfigure/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@ dependencies {
compile project(':logback')

compile 'io.micrometer:micrometer-spring-legacy'
compile 'io.micrometer:micrometer-registry-prometheus'
compile 'io.dropwizard.metrics:metrics-json'
compile('io.micrometer:micrometer-registry-prometheus') {
ext.optional = true
}
compile('io.dropwizard.metrics:metrics-json') {
ext.optional = true
}
compile 'javax.inject:javax.inject'
compileOnly 'javax.validation:validation-api'
compile 'org.springframework.boot:spring-boot-starter:1.5.22.RELEASE'
Expand Down

0 comments on commit 893497d

Please sign in to comment.