Skip to content

Commit

Permalink
Fixes spring-cloudgh-3685: Collect metrics for application instances
Browse files Browse the repository at this point in the history
  • Loading branch information
heowc committed Mar 24, 2024
1 parent ad002b4 commit 55f4127
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright 2013-2024 the original author or authors.
*
* 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 org.springframework.cloud.netflix.eureka.server.metrics;

import com.netflix.eureka.registry.PeerAwareInstanceRegistry;
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration;
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfiguration;
import org.springframework.context.annotation.Bean;

/**
* @author heowc
*/
@ConditionalOnBean(MeterRegistry.class)
@AutoConfigureAfter({
MetricsAutoConfiguration.class,
CompositeMeterRegistryAutoConfiguration.class,
EurekaServerAutoConfiguration.class})
class EurekaInstanceMetricsAutoConfiguration {

@ConditionalOnMissingBean
@Bean
public EurekaInstanceMetricsBinder eurekaInstanceMetricsBinder(PeerAwareInstanceRegistry instanceRegistry) {
return new EurekaInstanceMetricsBinder(instanceRegistry);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright 2013-2024 the original author or authors.
*
* 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 org.springframework.cloud.netflix.eureka.server.metrics;

import com.netflix.eureka.registry.PeerAwareInstanceRegistry;
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 java.util.List;
import java.util.Objects;

/**
* @author heowc
*/
public class EurekaInstanceMetricsBinder implements MeterBinder {

private final PeerAwareInstanceRegistry instanceRegistry;

EurekaInstanceMetricsBinder(PeerAwareInstanceRegistry instanceRegistry) {
this.instanceRegistry = Objects.requireNonNull(instanceRegistry);
}

@Override
public void bindTo(MeterRegistry meterRegistry) {
instanceRegistry.getApplications()
.getRegisteredApplications()
.stream()
.flatMap(application -> application.getInstances().stream())
.forEach(instanceInfo -> {
Gauge.builder("eureka.server.application.instances", () -> 1L)
.description("Information about application instances registered on the Eureka server.")
.tags(List.of(
Tag.of("application", instanceInfo.getAppName()),
Tag.of("id", instanceInfo.getId()),
Tag.of("host", instanceInfo.getHostName()),
Tag.of("port", String.valueOf(instanceInfo.getPort())),
Tag.of("status", instanceInfo.getStatus().name())
))
.register(meterRegistry);
});
}
}
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfiguration
org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfiguration
org.springframework.cloud.netflix.eureka.server.metrics.EurekaInstanceMetricsAutoConfiguration
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package org.springframework.cloud.netflix.eureka.server;

import com.netflix.appinfo.InstanceInfo;
import com.netflix.eureka.registry.PeerAwareInstanceRegistry;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.cloud.netflix.eureka.server.metrics.EurekaInstanceMetricsBinder;

import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.cloud.netflix.eureka.server.InstanceRegistryTests.getInstanceInfo;
import static org.springframework.cloud.netflix.eureka.server.InstanceRegistryTests.getLeaseInfo;

@SpringBootTest(classes = InstanceRegistryTests.TestApplication.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
value = {"spring.application.name=eureka",
"logging.level.org.springframework.cloud.netflix.eureka.server.InstanceRegistry=DEBUG"})
class EurekaInstanceMetricsBinderTests {

private static final String APP_NAME = "MY-APP-NAME";

private static final String HOST_NAME = "my-host-name";

private static final String INSTANCE_ID = "my-host-name:8008";

private static final int PORT = 8008;

@SpyBean(PeerAwareInstanceRegistry.class)
private InstanceRegistry instanceRegistry;

@Autowired
private MeterRegistry meterRegistry;

@Autowired
private EurekaInstanceMetricsBinder binder;

@Test
void testMetrics() {
final InstanceInfo instanceInfo = getInstanceInfo(APP_NAME, HOST_NAME, INSTANCE_ID, PORT, getLeaseInfo());
instanceRegistry.register(instanceInfo, false);

binder.bindTo(meterRegistry);

assertThat(meterRegistry.get("eureka.server.application.instances").tags(List.of(
Tag.of("application", APP_NAME),
Tag.of("id", INSTANCE_ID),
Tag.of("host", HOST_NAME),
Tag.of("port", String.valueOf(PORT)),
Tag.of("status", InstanceInfo.InstanceStatus.UP.name())
))).isNotNull();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,12 @@

package org.springframework.cloud.netflix.eureka.server;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import com.netflix.appinfo.InstanceInfo;
import com.netflix.appinfo.LeaseInfo;
import com.netflix.discovery.shared.Application;
import com.netflix.eureka.registry.PeerAwareInstanceRegistry;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
Expand All @@ -40,15 +35,19 @@
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.SmartApplicationListener;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.doReturn;

/**
* @author Bartlomiej Slota
*/
@SpringBootTest(classes = TestApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
value = { "spring.application.name=eureka",
"logging.level.org.springframework." + "cloud.netflix.eureka.server.InstanceRegistry=DEBUG" })
value = {"spring.application.name=eureka",
"logging.level.org.springframework." + "cloud.netflix.eureka.server.InstanceRegistry=DEBUG"})
class InstanceRegistryTests {

private static final String APP_NAME = "MY-APP-NAME";
Expand Down Expand Up @@ -147,15 +146,15 @@ void testRenew() {
assertThat(event2.getInstanceInfo()).isEqualTo(instanceInfo2);
}

private LeaseInfo getLeaseInfo() {
static LeaseInfo getLeaseInfo() {
LeaseInfo.Builder leaseBuilder = LeaseInfo.Builder.newBuilder();
leaseBuilder.setRenewalIntervalInSecs(10);
leaseBuilder.setDurationInSecs(15);
return leaseBuilder.build();
}

private InstanceInfo getInstanceInfo(String appName, String hostName, String instanceId, int port,
LeaseInfo leaseInfo) {
static InstanceInfo getInstanceInfo(String appName, String hostName, String instanceId, int port,
LeaseInfo leaseInfo) {
InstanceInfo.Builder builder = InstanceInfo.Builder.newBuilder();
builder.setAppName(appName);
builder.setHostName(hostName);
Expand Down

0 comments on commit 55f4127

Please sign in to comment.