Skip to content

Commit

Permalink
Implemented micrometer-metrics#1704 - add a binder for Jetty connecto…
Browse files Browse the repository at this point in the history
…r metrics
  • Loading branch information
tomakehurst authored and Jon Schneider committed Jan 3, 2020
1 parent 8253b5a commit b219c7f
Show file tree
Hide file tree
Showing 4 changed files with 204 additions and 6 deletions.
9 changes: 5 additions & 4 deletions micrometer-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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'
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/**
* Copyright 2017 Pivotal Software, Inc.
* <p>
* 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
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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.<br><br>
*
* Usage example:
*
* <pre>{@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 });
* }</pre>
*
* @author Tom Akehurst
*
*/
public class JettyConnectionMetrics implements MeterBinder {

private final ConnectionStatistics connectionStatistics;
private final Iterable<Tag> tags;

public JettyConnectionMetrics(ConnectionStatistics connectionStatistics) {
this(connectionStatistics, emptyList());
}

public JettyConnectionMetrics(ConnectionStatistics connectionStatistics, Iterable<Tag> tags) {
this.connectionStatistics = connectionStatistics;
this.tags = tags;
}

@Override
public void bindTo(MeterRegistry registry) {
Gauge.builder("jetty.connector.connections.current", connectionStatistics, ConnectionStatistics::getConnections)
.tags(tags)
.description("The current number of open connections")
.register(registry);
Gauge.builder("jetty.connector.connections.max", connectionStatistics, ConnectionStatistics::getConnectionsMax)
.tags(tags)
.description("The maximum number of connections")
.register(registry);
FunctionCounter.builder("jetty.connector.connections.total", connectionStatistics, ConnectionStatistics::getConnectionsTotal)
.tags(tags)
.description("The total number of connections")
.register(registry);

FunctionCounter.builder("jetty.connector.received", connectionStatistics, ConnectionStatistics::getReceivedBytesRate)
.tags(tags)
.description("The rate of bytes received")
.baseUnit("bytes")
.register(registry);
FunctionCounter.builder("jetty.connector.sent", connectionStatistics, ConnectionStatistics::getSentBytesRate)
.tags(tags)
.description("The rate of bytes sent")
.baseUnit("bytes")
.register(registry);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/**
* Copyright 2017 Pivotal Software, Inc.
* <p>
* 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
* <p>
* https://www.apache.org/licenses/LICENSE-2.0
* <p>
* 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();
}
}
6 changes: 4 additions & 2 deletions micrometer-test/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down

0 comments on commit b219c7f

Please sign in to comment.