Skip to content

Commit

Permalink
Merge branch '1.3.x' into 1.5.x
Browse files Browse the repository at this point in the history
  • Loading branch information
shakuzen committed Sep 17, 2020
2 parents cd257b1 + 49d9201 commit 71ef292
Show file tree
Hide file tree
Showing 3 changed files with 157 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

Expand Down Expand Up @@ -90,9 +91,9 @@ public class ElasticMeterRegistry extends StepMeterRegistry {
" \"type\": \"double\"\n" +
" }\n" +
"}";
private static final String TEMPLATE_BODY_BEFORE_VERSION_7 = "{\"template\":\"metrics*\",\"mappings\":{\"_default_\":{\"_all\":{\"enabled\":false}," + TEMPLATE_PROPERTIES + "}}}";
private static final String TEMPLATE_BODY_AFTER_VERSION_7 = "{\n" +
" \"index_patterns\": [\"metrics*\"],\n" +
private static final Function<String, String> TEMPLATE_BODY_BEFORE_VERSION_7 = (indexPrefix) -> "{\"template\":\"" + indexPrefix + "*\",\"mappings\":{\"_default_\":{\"_all\":{\"enabled\":false}," + TEMPLATE_PROPERTIES + "}}}";
private static final Function<String, String> TEMPLATE_BODY_AFTER_VERSION_7 = (indexPrefix) -> "{\n" +
" \"index_patterns\": [\"" + indexPrefix + "*\"],\n" +
" \"mappings\": {\n" +
" \"_source\": {\n" +
" \"enabled\": false\n" +
Expand Down Expand Up @@ -191,7 +192,7 @@ private void createIndexTemplateIfNeeded() {

@SuppressWarnings("ConstantConditions")
private String getTemplateBody() {
return majorVersion == null || majorVersion < 7 ? TEMPLATE_BODY_BEFORE_VERSION_7 : TEMPLATE_BODY_AFTER_VERSION_7;
return majorVersion == null || majorVersion < 7 ? TEMPLATE_BODY_BEFORE_VERSION_7.apply(config.index()) : TEMPLATE_BODY_AFTER_VERSION_7.apply(config.index());
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import javax.management.*;
import java.lang.management.ManagementFactory;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
Expand Down Expand Up @@ -135,7 +134,7 @@ private void registerSessionMetrics(MeterRegistry registry) {
}

private void registerThreadPoolMetrics(MeterRegistry registry) {
registerMetricsEventually("type", "ThreadPool", (name, allTags) -> {
registerMetricsEventually(":type=ThreadPool,name=*", (name, allTags) -> {
Gauge.builder("tomcat.threads.config.max", mBeanServer,
s -> safeDouble(() -> s.getAttribute(name, "maxThreads")))
.tags(allTags)
Expand All @@ -157,7 +156,7 @@ private void registerThreadPoolMetrics(MeterRegistry registry) {
}

private void registerCacheMetrics(MeterRegistry registry) {
registerMetricsEventually("type", "StringCache", (name, allTags) -> {
registerMetricsEventually(":type=StringCache", (name, allTags) -> {
FunctionCounter.builder("tomcat.cache.access", mBeanServer,
s -> safeDouble(() -> s.getAttribute(name, "accessCount")))
.tags(allTags)
Expand All @@ -167,11 +166,11 @@ private void registerCacheMetrics(MeterRegistry registry) {
s -> safeDouble(() -> s.getAttribute(name, "hitCount")))
.tags(allTags)
.register(registry);
}, false);
});
}

private void registerServletMetrics(MeterRegistry registry) {
registerMetricsEventually("j2eeType", "Servlet", (name, allTags) -> {
registerMetricsEventually(":j2eeType=Servlet,name=*,*", (name, allTags) -> {
FunctionCounter.builder("tomcat.servlet.error", mBeanServer,
s -> safeDouble(() -> s.getAttribute(name, "errorCount")))
.tags(allTags)
Expand All @@ -191,7 +190,7 @@ private void registerServletMetrics(MeterRegistry registry) {
}

private void registerGlobalRequestMetrics(MeterRegistry registry) {
registerMetricsEventually("type", "GlobalRequestProcessor", (name, allTags) -> {
registerMetricsEventually(":type=GlobalRequestProcessor,name=*", (name, allTags) -> {
FunctionCounter.builder("tomcat.global.sent", mBeanServer,
s -> safeDouble(() -> s.getAttribute(name, "bytesSent")))
.tags(allTags)
Expand Down Expand Up @@ -222,28 +221,17 @@ private void registerGlobalRequestMetrics(MeterRegistry registry) {
});
}

private void registerMetricsEventually(String key, String value, BiConsumer<ObjectName, Iterable<Tag>> perObject) {
registerMetricsEventually(key, value, perObject, true);
}

/**
* If the MBean already exists, register metrics immediately. Otherwise register an MBean registration listener
* with the MBeanServer and register metrics when/if the MBean becomes available.
* If the Tomcat MBeans already exist, register metrics immediately. Otherwise register an MBean registration listener
* with the MBeanServer and register metrics when/if the MBeans becomes available.
*/
private void registerMetricsEventually(String key, String value, BiConsumer<ObjectName, Iterable<Tag>> perObject, boolean hasName) {
private void registerMetricsEventually(String namePatternSuffix, BiConsumer<ObjectName, Iterable<Tag>> perObject) {
if (getJmxDomain() != null) {
try {
String name = getJmxDomain() + ":" + key + "=" + value + (hasName ? ",name=*,*" : "");
Set<ObjectName> objectNames = this.mBeanServer.queryNames(new ObjectName(name), null);
if (!objectNames.isEmpty()) {
// MBean is present, so we can register metrics now.
objectNames.stream().sorted(Comparator.reverseOrder()).findFirst()
.ifPresent(objectName -> perObject.accept(objectName, Tags.concat(tags, nameTag(objectName))));
return;
}
} catch (MalformedObjectNameException e) {
// should never happen
throw new RuntimeException("Error registering Tomcat JMX based metrics", e);
Set<ObjectName> objectNames = this.mBeanServer.queryNames(getNamePattern(namePatternSuffix), null);
if (!objectNames.isEmpty()) {
// MBeans are present, so we can register metrics now.
objectNames.forEach(objectName -> perObject.accept(objectName, Tags.concat(tags, nameTag(objectName))));
return;
}
}

Expand All @@ -255,6 +243,10 @@ public void handleNotification(Notification notification, Object handback) {
MBeanServerNotification mBeanServerNotification = (MBeanServerNotification) notification;
ObjectName objectName = mBeanServerNotification.getMBeanName();
perObject.accept(objectName, Tags.concat(tags, nameTag(objectName)));
if (getNamePattern(namePatternSuffix).isPattern()) {
// patterns can match multiple MBeans so don't remove listener
return;
}
try {
mBeanServer.removeNotificationListener(MBeanServerDelegate.DELEGATE_NAME, this);
notificationListeners.remove(this);
Expand All @@ -272,7 +264,7 @@ public void handleNotification(Notification notification, Object handback) {

// we can safely downcast now
ObjectName objectName = ((MBeanServerNotification) notification).getMBeanName();
return objectName.getDomain().equals(getJmxDomain()) && objectName.getKeyProperty(key).equals(value);
return getNamePattern(namePatternSuffix).apply(objectName);
};

try {
Expand All @@ -283,6 +275,15 @@ public void handleNotification(Notification notification, Object handback) {
}
}

private ObjectName getNamePattern(String namePatternSuffix) {
try {
return new ObjectName(getJmxDomain() + namePatternSuffix);
} catch (MalformedObjectNameException e) {
// should never happen
throw new RuntimeException("Error registering Tomcat JMX based metrics", e);
}
}

private String getJmxDomain() {
if (this.jmxDomain == null) {
if (hasObjectName(OBJECT_NAME_SERVER_EMBEDDED)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*/
package io.micrometer.core.instrument.binder.tomcat;

import io.micrometer.core.Issue;
import io.micrometer.core.instrument.FunctionTimer;
import io.micrometer.core.instrument.MockClock;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.Tags;
Expand All @@ -36,10 +38,15 @@
import org.apache.http.impl.client.HttpClients;
import org.junit.jupiter.api.Test;

import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
Expand Down Expand Up @@ -120,7 +127,7 @@ private void sleep() {
}

@Test
void mbeansAvailableAfterBinder() throws Exception {
void whenTomcatMetricsBoundBeforeTomcatStarted_mbeanMetricsRegisteredEventually() throws Exception {
TomcatMetrics.monitor(registry, null);

CountDownLatch latch = new CountDownLatch(1);
Expand All @@ -139,17 +146,17 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws I
};

runTomcat(servlet, () -> {
assertThat(latch.await(10, TimeUnit.SECONDS)).isTrue();
assertThat(latch.await(5, TimeUnit.SECONDS)).isTrue();

checkMbeansInitialState();

try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpPost post = new HttpPost("http://localhost:" + this.port + "/");
HttpPost post = new HttpPost("http://localhost:" + this.port + "/0");
post.setEntity(new StringEntity("you there?"));
CloseableHttpResponse response1 = httpClient.execute(post);

CloseableHttpResponse response2 = httpClient.execute(
new HttpGet("http://localhost:" + this.port + "/nowhere"));
new HttpGet("http://localhost:" + this.port + "/0/no-get"));

long expectedSentBytes = response1.getEntity().getContentLength()
+ response2.getEntity().getContentLength();
Expand All @@ -161,7 +168,7 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws I
}

@Test
void mbeansAvailableBeforeBinder() throws Exception {
void whenTomcatMetricsBoundAfterTomcatStarted_mbeanMetricsRegisteredImmediately() throws Exception {
HttpServlet servlet = new HttpServlet() {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
Expand All @@ -177,12 +184,12 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws I
checkMbeansInitialState();

try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpPost post = new HttpPost("http://localhost:" + this.port + "/");
HttpPost post = new HttpPost("http://localhost:" + this.port + "/0");
post.setEntity(new StringEntity("you there?"));
CloseableHttpResponse response1 = httpClient.execute(post);

CloseableHttpResponse response2 = httpClient.execute(
new HttpGet("http://localhost:" + this.port + "/nowhere"));
new HttpGet("http://localhost:" + this.port + "/0/no-get"));

long expectedSentBytes = response1.getEntity().getContentLength()
+ response2.getEntity().getContentLength();
Expand All @@ -193,8 +200,113 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws I
});
}

@Test
@Issue("#1989")
void whenMultipleServlets_thenRegisterMetricsForAllServlets() throws Exception {
Collection<Servlet> servlets = Arrays.asList(new HttpServlet() {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
IOUtils.toString(req.getInputStream());
sleep();
resp.getOutputStream().write("yes".getBytes());
}
}, new HttpServlet() {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
IOUtils.toString(req.getInputStream());
sleep();
resp.getOutputStream().write("hi".getBytes());
}
});

runTomcat(servlets, () -> {
TomcatMetrics.monitor(registry, null);

checkMbeansInitialState();

try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpPost post = new HttpPost("http://localhost:" + this.port + "/0");
post.setEntity(new StringEntity("you there?"));
CloseableHttpResponse response1 = httpClient.execute(post);

CloseableHttpResponse response2 = httpClient.execute(
new HttpGet("http://localhost:" + this.port + "/1"));

FunctionTimer servlet0 = registry.get("tomcat.servlet.request").tag("name", "servlet0").functionTimer();
FunctionTimer servlet1 = registry.get("tomcat.servlet.request").tag("name", "servlet1").functionTimer();
assertThat(servlet0.count()).isEqualTo(1);
assertThat(servlet0.totalTime(TimeUnit.MILLISECONDS)).isGreaterThanOrEqualTo(PROCESSING_TIME_IN_MILLIS);
assertThat(servlet1.count()).isEqualTo(1);
assertThat(servlet1.totalTime(TimeUnit.MILLISECONDS)).isGreaterThanOrEqualTo(PROCESSING_TIME_IN_MILLIS);
}

return null;
});
}

@Test
@Issue("#1989")
void whenMultipleServletsAndTomcatMetricsBoundBeforeTomcatStarted_thenEventuallyRegisterMetricsForAllServlets() throws Exception {
TomcatMetrics.monitor(registry, null);
CountDownLatch latch0 = new CountDownLatch(1);
CountDownLatch latch1 = new CountDownLatch(1);
registry.config().onMeterAdded(m -> {
if (m.getId().getName().equals("tomcat.servlet.error")) {
if ("servlet0".equals(m.getId().getTag("name"))) {
latch0.countDown();
} else if ("servlet1".equals(m.getId().getTag("name"))) {
latch1.countDown();
}
}
});

Collection<Servlet> servlets = Arrays.asList(new HttpServlet() {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
IOUtils.toString(req.getInputStream());
sleep();
resp.getOutputStream().write("yes".getBytes());
}
}, new HttpServlet() {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
IOUtils.toString(req.getInputStream());
sleep();
resp.getOutputStream().write("hi".getBytes());
}
});

runTomcat(servlets, () -> {
assertThat(latch0.await(3, TimeUnit.SECONDS)).isTrue();
assertThat(latch1.await(3, TimeUnit.SECONDS)).isTrue();

checkMbeansInitialState();

try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpPost post = new HttpPost("http://localhost:" + this.port + "/0");
post.setEntity(new StringEntity("you there?"));
CloseableHttpResponse response1 = httpClient.execute(post);

CloseableHttpResponse response2 = httpClient.execute(
new HttpGet("http://localhost:" + this.port + "/1"));

FunctionTimer servlet0 = registry.get("tomcat.servlet.request").tag("name", "servlet0").functionTimer();
FunctionTimer servlet1 = registry.get("tomcat.servlet.request").tag("name", "servlet1").functionTimer();
assertThat(servlet0.count()).isEqualTo(1);
assertThat(servlet0.totalTime(TimeUnit.MILLISECONDS)).isGreaterThanOrEqualTo(PROCESSING_TIME_IN_MILLIS);
assertThat(servlet1.count()).isEqualTo(1);
assertThat(servlet1.totalTime(TimeUnit.MILLISECONDS)).isGreaterThanOrEqualTo(PROCESSING_TIME_IN_MILLIS);
}

return null;
});
}

void runTomcat(HttpServlet servlet, Callable<Void> doWithTomcat) throws Exception {
runTomcat(Collections.singleton(servlet), doWithTomcat);
}

void runTomcat(Collection<Servlet> servlets, Callable<Void> doWithTomcat) throws Exception {
Tomcat server = new Tomcat();
try {
StandardHost host = new StandardHost();
Expand All @@ -206,8 +318,12 @@ void runTomcat(HttpServlet servlet, Callable<Void> doWithTomcat) throws Exceptio
this.port = server.getConnector().getLocalPort();

Context context = server.addContext("", null);
server.addServlet("", "servletname", servlet);
context.addServletMappingDecoded("/", "servletname");
int i = 0;
for (Servlet servlet : servlets) {
server.addServlet("", "servlet" + i, servlet);
context.addServletMappingDecoded("/" + i + "/*", "servlet" + i);
i++;
}

doWithTomcat.call();

Expand Down

0 comments on commit 71ef292

Please sign in to comment.