diff --git a/micrometer-core/build.gradle b/micrometer-core/build.gradle index 04ebf2f836..ceb7adced3 100644 --- a/micrometer-core/build.gradle +++ b/micrometer-core/build.gradle @@ -29,8 +29,7 @@ dependencies { compile 'org.hibernate:hibernate-entitymanager:latest.release', optional // server runtime monitoring - // 9.4+ are incompatible with the latest version of wiremock as of 1/4/2019 - compile 'org.eclipse.jetty:jetty-server:9.2.+', optional + compile 'org.eclipse.jetty:jetty-server:9.4.+', optional compile 'org.apache.tomcat.embed:tomcat-embed-core:8.+', optional // apache httpcomponents monitoring @@ -87,8 +86,10 @@ dependencies { // Pin version temporarily to restore builds. testCompile 'org.apache.kafka:kafka-clients:2.3.1' - testCompile 'ru.lanwen.wiremock:wiremock-junit5:1.2.0' - testCompile 'com.github.tomakehurst:wiremock:latest.release' + testCompile 'ru.lanwen.wiremock:wiremock-junit5:1.2.0', { + exclude group: 'com.github.tomakehurst', module: 'wiremock' + } + testCompile 'com.github.tomakehurst:wiremock-jre8:latest.release' testCompile 'de.flapdoodle.embed:de.flapdoodle.embed.mongo:latest.release' } diff --git a/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/jetty/JettyConnectionMetrics.java b/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/jetty/JettyConnectionMetrics.java new file mode 100644 index 0000000000..ecdc2ecde7 --- /dev/null +++ b/micrometer-core/src/main/java/io/micrometer/core/instrument/binder/jetty/JettyConnectionMetrics.java @@ -0,0 +1,89 @@ +/** + * Copyright 2017 Pivotal Software, Inc. + *
+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *
+ * 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 io.micrometer.core.instrument.binder.jetty;
+
+import io.micrometer.core.instrument.FunctionCounter;
+import io.micrometer.core.instrument.Gauge;
+import io.micrometer.core.instrument.MeterRegistry;
+import io.micrometer.core.instrument.Tag;
+import io.micrometer.core.instrument.binder.MeterBinder;
+import org.eclipse.jetty.io.ConnectionStatistics;
+
+import static java.util.Collections.emptyList;
+
+/**
+ * {@link MeterBinder} for Jetty's connection metrics.
+ *
+ * Usage example:
+ *
+ *
{@code + * MeterRegistry registry = new SimpleMeterRegistry(SimpleConfig.DEFAULT, new MockClock()); + * + * ServerConnectionStatistics serverConnectionStatistics = new ServerConnectionStatistics(); + * JettyConnectionMetrics connectionMetrics = + * new JettyConnectionMetrics(serverConnectionStatistics, singletonList(Tag.of("protocol", "http"))); + * connectionMetrics.bindTo(registry); + * + * Server server = new Server(0); + * Connector connector = new ServerConnector(server); + * connector.addBean(serverConnectionStatistics); + * server.setConnectors(new Connector[] { connector }); + * }+ * + * @author Tom Akehurst + * + */ +public class JettyConnectionMetrics implements MeterBinder { + + private final ConnectionStatistics connectionStatistics; + private final Iterable
+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *
+ * 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 io.micrometer.core.instrument.binder.jetty; + +import io.micrometer.core.instrument.MockClock; +import io.micrometer.core.instrument.Tag; +import io.micrometer.core.instrument.simple.SimpleConfig; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpPost; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.eclipse.jetty.server.Connector; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnectionStatistics; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.util.component.AbstractLifeCycle; +import org.eclipse.jetty.util.component.LifeCycle; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.concurrent.CountDownLatch; + +import static java.util.Collections.singletonList; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class JettyConnectionMetricsTest { + + private SimpleMeterRegistry registry; + private ServerConnector connector; + private Server server; + private CloseableHttpClient client; + + @BeforeEach + void setup() throws Exception { + registry = new SimpleMeterRegistry(SimpleConfig.DEFAULT, new MockClock()); + + ServerConnectionStatistics serverConnectionStatistics = new ServerConnectionStatistics(); + JettyConnectionMetrics connectionMetrics = + new JettyConnectionMetrics(serverConnectionStatistics, singletonList(Tag.of("protocol", "http"))); + connectionMetrics.bindTo(registry); + + server = new Server(0); + connector = new ServerConnector(server); + connector.addBean(serverConnectionStatistics); + server.setConnectors(new Connector[] { connector }); + server.start(); + + client = HttpClients.createDefault(); + } + + @AfterEach + void teardown() throws Exception { + if (server.isRunning()) { + server.stop(); + } + } + + @Test + void contributesConnectorMetrics() throws Exception { + String url = getBaseUrl(); + HttpPost post = new HttpPost(url); + post.setEntity(new StringEntity("some blah whatever text")); + try (CloseableHttpResponse response = client.execute(post)) { + assertThat(registry.get("jetty.connector.connections.max").gauge().value()).isEqualTo(1.0); + assertThat(registry.get("jetty.connector.connections.current").gauge().value()).isEqualTo(1.0); + assertThat(registry.get("jetty.connector.connections.total").functionCounter().count()).isEqualTo(1.0); + } + + CountDownLatch latch = new CountDownLatch(1); + connector.addLifeCycleListener(new AbstractLifeCycle.AbstractLifeCycleListener() { + @Override + public void lifeCycleStopped(LifeCycle event) { + latch.countDown(); + } + }); + // Convenient way to get Jetty to flush its connections, which is required to update the sent/received bytes metrics + server.stop(); + + assertTrue(latch.await(10, SECONDS)); + + assertThat(registry.get("jetty.connector.sent").functionCounter().count()).isGreaterThan(0.0); + assertThat(registry.get("jetty.connector.received").functionCounter().count()).isGreaterThan(0.0); + } + + private String getBaseUrl() { + return "http://localhost:" + connector.getLocalPort(); + } +} diff --git a/micrometer-test/build.gradle b/micrometer-test/build.gradle index 58a0255180..a3837fb980 100644 --- a/micrometer-test/build.gradle +++ b/micrometer-test/build.gradle @@ -4,8 +4,10 @@ dependencies { compile 'org.junit.jupiter:junit-jupiter:latest.release' - compile 'ru.lanwen.wiremock:wiremock-junit5:latest.release' - compile 'com.github.tomakehurst:wiremock:latest.release' + compile 'ru.lanwen.wiremock:wiremock-junit5:latest.release', { + exclude group: 'com.github.tomakehurst', module: 'wiremock' + } + compile 'com.github.tomakehurst:wiremock-jre8:latest.release' compile 'org.mockito:mockito-core:latest.release' testCompile 'org.jsr107.ri:cache-ri-impl:1.0.0'