Skip to content

Commit 2be9f71

Browse files
author
Alexander Furer
committed
metrics
1 parent a6b1c47 commit 2be9f71

File tree

8 files changed

+176
-1
lines changed

8 files changed

+176
-1
lines changed

README.adoc

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,22 @@ public class GreeterService extends GreeterGrpc.GreeterImplBase{
237237
// ommited
238238
}
239239
----
240-
=== Spring Validation support
240+
241+
=== GRPC server metrics (Micrometer.io integration)
242+
243+
By including `org.springframework.boot:spring-boot-starter-actuator` dependency,
244+
the starter will collect gRPC server metrics , broken down by
245+
246+
. gRPC service method FQN (Fully Qualified Name)
247+
. https://grpc.github.io/grpc-java/javadoc/io/grpc/Status.Code.html[Response status code]
248+
249+
After configuring the exporter of your https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html#production-ready-metrics[choice],
250+
you should see the `timer` named `grpc.server.calls`.
251+
252+
253+
254+
255+
=== Spring Boot Validation support
241256

242257
The starter can be auto-configured to validate request/response gRPC service messages.
243258
Please continue to <<Implementing message validation>> for configuration details.

grpc-spring-boot-starter-demo/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ dependencies {
3939
testCompile 'com.github.stefanbirkner:system-rules:1.18.0'
4040
testCompile('org.springframework.cloud:spring-cloud-starter-consul-discovery')
4141
testCompile 'com.pszymczyk.consul:embedded-consul:2.1.4'
42+
testCompile 'org.awaitility:awaitility:4.0.3'
4243

4344
testCompile "org.springframework.cloud:spring-cloud-config-server:2.1.1.RELEASE"
4445
testCompile "org.springframework.cloud:spring-cloud-config-client:2.1.1.RELEASE"
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package org.lognet.springboot.grpc;
2+
3+
import io.micrometer.core.instrument.MeterRegistry;
4+
import io.micrometer.core.instrument.Timer;
5+
import io.micrometer.core.instrument.simple.SimpleConfig;
6+
import org.awaitility.Awaitility;
7+
import org.junit.Before;
8+
import org.junit.runner.RunWith;
9+
import org.lognet.springboot.grpc.demo.DemoApp;
10+
import org.springframework.beans.factory.annotation.Autowired;
11+
import org.springframework.boot.test.context.SpringBootTest;
12+
import org.springframework.test.context.ActiveProfiles;
13+
import org.springframework.test.context.junit4.SpringRunner;
14+
15+
import java.time.Duration;
16+
import java.util.concurrent.TimeUnit;
17+
18+
import static org.hamcrest.MatcherAssert.assertThat;
19+
import static org.hamcrest.Matchers.greaterThan;
20+
import static org.hamcrest.Matchers.notNullValue;
21+
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.NONE;
22+
23+
@RunWith(SpringRunner.class)
24+
@SpringBootTest(classes = {DemoApp.class}, webEnvironment = NONE, properties = {"grpc.port=0"})
25+
@ActiveProfiles("measure")
26+
public class GrpcMeterTest extends GrpcServerTestBase {
27+
@Autowired
28+
private MeterRegistry registry;
29+
30+
@Autowired
31+
private SimpleConfig registryConfig;
32+
33+
@Before
34+
public void setUp() {
35+
registry.clear();
36+
}
37+
38+
@Override
39+
protected void afterGreeting() {
40+
41+
42+
final Timer timer = registry.find("grpc.server.calls").timer();
43+
assertThat(timer,notNullValue(Timer.class));
44+
45+
Awaitility
46+
.waitAtMost(Duration.ofMillis(registryConfig.step().toMillis() * 2))
47+
.until(timer::count,greaterThan(0L));
48+
49+
assertThat(timer.max(TimeUnit.MILLISECONDS),greaterThan(0d));
50+
assertThat(timer.mean(TimeUnit.MILLISECONDS),greaterThan(0d));
51+
assertThat(timer.totalTime(TimeUnit.MILLISECONDS),greaterThan(0d));
52+
}
53+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
management:
2+
metrics:
3+
export:
4+
simple:
5+
step: 10s
6+
enabled: true

grpc-spring-boot-starter-demo/src/test/resources/application.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
management:
2+
metrics:
3+
export:
4+
simple:
5+
enabled: false
26
endpoints:
37
web:
48
base-path: "/"

grpc-spring-boot-starter/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ dependencies {
131131
compileOnly "org.springframework.security:spring-security-oauth2-resource-server"
132132
compileOnly "org.springframework.security:spring-security-oauth2-jose"
133133
compileOnly 'org.springframework.boot:spring-boot-starter-validation'
134+
compileOnly "org.springframework.boot:spring-boot-starter-actuator"
134135
compileOnly group: 'org.springframework.cloud', name: 'spring-cloud-starter-consul-discovery', version: '2.1.1.RELEASE'
135136

136137

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package org.lognet.springboot.grpc.autoconfigure.metrics;
2+
3+
import io.grpc.ForwardingServerCall;
4+
import io.grpc.Metadata;
5+
import io.grpc.ServerCall;
6+
import io.grpc.ServerCallHandler;
7+
import io.grpc.ServerInterceptor;
8+
import io.grpc.Status;
9+
import io.micrometer.core.instrument.MeterRegistry;
10+
import io.micrometer.core.instrument.Timer;
11+
import org.lognet.springboot.grpc.GRpcGlobalInterceptor;
12+
import org.lognet.springboot.grpc.GRpcService;
13+
import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration;
14+
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
15+
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
16+
import org.springframework.boot.autoconfigure.condition.AllNestedConditions;
17+
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
18+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
19+
import org.springframework.context.annotation.Bean;
20+
import org.springframework.context.annotation.Conditional;
21+
import org.springframework.context.annotation.Configuration;
22+
import org.springframework.core.Ordered;
23+
24+
@Configuration
25+
@AutoConfigureAfter({MetricsAutoConfiguration.class, CompositeMeterRegistryAutoConfiguration.class})
26+
@ConditionalOnClass({MeterRegistry.class})
27+
@Conditional(GRpcMetricsAutoConfiguration.OnGrpcAndMeterRegistryEnabledCondition.class)
28+
public class GRpcMetricsAutoConfiguration {
29+
30+
protected static class OnGrpcAndMeterRegistryEnabledCondition extends AllNestedConditions {
31+
32+
OnGrpcAndMeterRegistryEnabledCondition() {
33+
super(ConfigurationPhase.REGISTER_BEAN);
34+
}
35+
36+
@ConditionalOnBean(value = {MeterRegistry.class})
37+
static class MeterRegistryCondition {
38+
}
39+
40+
@ConditionalOnBean(annotation = {GRpcService.class})
41+
static class GrpcServiceCondition {
42+
}
43+
44+
}
45+
46+
static class MonitoringServerCall<ReqT, RespT> extends ForwardingServerCall.SimpleForwardingServerCall<ReqT, RespT> {
47+
48+
private MeterRegistry registry;
49+
final Timer.Sample start;
50+
protected MonitoringServerCall(ServerCall<ReqT, RespT> delegate, MeterRegistry registry) {
51+
super(delegate);
52+
this.start = Timer.start(registry);
53+
this.registry = registry;
54+
55+
56+
}
57+
@Override
58+
public void close(Status status, Metadata trailers) {
59+
start.stop(Timer.builder("grpc.server.calls")
60+
.tag("method",getMethodDescriptor().getFullMethodName())
61+
.tag("result",status.getCode().name())
62+
.register(registry));
63+
64+
super.close(status, trailers);
65+
}
66+
}
67+
68+
static class MonitoringServerInterceptor implements ServerInterceptor, Ordered {
69+
70+
71+
private MeterRegistry registry;
72+
73+
public MonitoringServerInterceptor(MeterRegistry registry) {
74+
this.registry = registry;
75+
}
76+
77+
@Override
78+
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
79+
return next.startCall(new MonitoringServerCall<>(call,registry), headers);
80+
81+
}
82+
83+
@Override
84+
public int getOrder() {
85+
return HIGHEST_PRECEDENCE+20;
86+
}
87+
}
88+
89+
@Bean
90+
@GRpcGlobalInterceptor
91+
public ServerInterceptor measure(MeterRegistry registry){
92+
return new MonitoringServerInterceptor(registry);
93+
}
94+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
22
org.lognet.springboot.grpc.autoconfigure.GRpcAutoConfiguration,\
3+
org.lognet.springboot.grpc.autoconfigure.metrics.GRpcMetricsAutoConfiguration,\
34
org.lognet.springboot.grpc.autoconfigure.consul.ConsulGrpcAutoConfiguration,\
45
org.lognet.springboot.grpc.autoconfigure.security.SecurityAutoConfiguration
56

0 commit comments

Comments
 (0)