Skip to content

Commit de2d98b

Browse files
author
Alexander Furer
committed
closes #191
1 parent 2103610 commit de2d98b

File tree

5 files changed

+211
-28
lines changed

5 files changed

+211
-28
lines changed

grpc-spring-boot-starter-demo/src/test/java/org/lognet/springboot/grpc/GrpcMeterTest.java

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,32 @@
11
package org.lognet.springboot.grpc;
22

3+
import io.grpc.Attributes;
4+
import io.grpc.MethodDescriptor;
5+
import io.grpc.Status;
36
import io.grpc.examples.GreeterGrpc;
7+
import io.grpc.examples.GreeterOuterClass;
48
import io.micrometer.core.instrument.MeterRegistry;
9+
import io.micrometer.core.instrument.Tag;
510
import io.micrometer.core.instrument.Timer;
611
import io.micrometer.core.instrument.simple.SimpleConfig;
712
import org.awaitility.Awaitility;
813
import org.junit.Before;
914
import org.junit.runner.RunWith;
15+
import org.lognet.springboot.grpc.autoconfigure.metrics.RequestAwareGRpcMetricsTagsContributor;
1016
import org.lognet.springboot.grpc.context.LocalRunningGrpcPort;
1117
import org.lognet.springboot.grpc.demo.DemoApp;
18+
import org.mockito.Mockito;
1219
import org.springframework.beans.factory.annotation.Autowired;
1320
import org.springframework.boot.test.context.SpringBootTest;
21+
import org.springframework.boot.test.context.TestConfiguration;
22+
import org.springframework.boot.test.mock.mockito.SpyBean;
23+
import org.springframework.context.annotation.Bean;
24+
import org.springframework.context.annotation.Import;
1425
import org.springframework.test.context.ActiveProfiles;
1526
import org.springframework.test.context.junit4.SpringRunner;
1627

1728
import java.time.Duration;
29+
import java.util.Collections;
1830
import java.util.concurrent.TimeUnit;
1931

2032
import static org.hamcrest.MatcherAssert.assertThat;
@@ -27,7 +39,43 @@
2739
@RunWith(SpringRunner.class)
2840
@SpringBootTest(classes = {DemoApp.class}, webEnvironment = NONE, properties = {"grpc.port=0"})
2941
@ActiveProfiles("measure")
42+
@Import(GrpcMeterTest.Config.class)
3043
public class GrpcMeterTest extends GrpcServerTestBase {
44+
@TestConfiguration
45+
static class Config{
46+
@Bean
47+
public RequestAwareGRpcMetricsTagsContributor<GreeterOuterClass.HelloRequest> helloContributor(){
48+
return new RequestAwareGRpcMetricsTagsContributor<GreeterOuterClass.HelloRequest>(GreeterOuterClass.HelloRequest.class) {
49+
@Override
50+
public Iterable<Tag> getTags(GreeterOuterClass.HelloRequest request, MethodDescriptor<?, ?> methodDescriptor, Attributes attributes) {
51+
return Collections.singletonList(Tag.of("hello",request.getName()));
52+
}
53+
54+
@Override
55+
public Iterable<Tag> getTags(Status status, MethodDescriptor<?, ?> methodDescriptor, Attributes attributes) {
56+
return Collections.singletonList(Tag.of("customTagName",status.getCode().name()));
57+
}
58+
};
59+
}
60+
@Bean
61+
public RequestAwareGRpcMetricsTagsContributor<GreeterOuterClass.Person> shouldNotBeInvoked(){
62+
return new RequestAwareGRpcMetricsTagsContributor<GreeterOuterClass.Person>(GreeterOuterClass.Person.class) {
63+
@Override
64+
public Iterable<Tag> getTags(GreeterOuterClass.Person request, MethodDescriptor<?, ?> methodDescriptor, Attributes attributes) {
65+
return Collections.emptyList();
66+
}
67+
68+
@Override
69+
public Iterable<Tag> getTags(Status status, MethodDescriptor<?, ?> methodDescriptor, Attributes attributes) {
70+
return Collections.emptyList();
71+
}
72+
};
73+
}
74+
}
75+
76+
@SpyBean
77+
private RequestAwareGRpcMetricsTagsContributor<GreeterOuterClass.Person> shouldNotBeInvoked;
78+
3179
@Autowired
3280
private MeterRegistry registry;
3381

@@ -65,5 +113,27 @@ protected void afterGreeting() {
65113
final String methodTag = timer.getId().getTag("method");
66114
assertThat(methodTag,notNullValue());
67115
assertThat(methodTag,is(GreeterGrpc.getSayHelloMethod().getFullMethodName()));
116+
117+
final String resultTag = timer.getId().getTag("result");
118+
assertThat(resultTag,notNullValue());
119+
assertThat(resultTag,is(Status.OK.getCode().name()));
120+
121+
//from contributor
122+
123+
final String helloTag = timer.getId().getTag("hello");
124+
assertThat(helloTag,notNullValue());
125+
assertThat(helloTag,is(name));
126+
127+
final String customTag = timer.getId().getTag("customTagName");
128+
assertThat(customTag,notNullValue());
129+
assertThat(customTag,is(Status.OK.getCode().name()));
130+
131+
Mockito.verify(shouldNotBeInvoked,Mockito.times(1)).getTags(Mockito.any(Status.class),Mockito.any(),Mockito.any());
132+
Mockito.verify(shouldNotBeInvoked,Mockito.never()).getTags(Mockito.any(GreeterOuterClass.Person.class),Mockito.any(),Mockito.any());
133+
134+
135+
136+
137+
68138
}
69139
}

grpc-spring-boot-starter-demo/src/test/java/org/lognet/springboot/grpc/GrpcServerTestBase.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ public abstract class GrpcServerTestBase {
4949
@Autowired
5050
protected GRpcServerProperties gRpcServerProperties;
5151

52+
protected String name="John";
53+
5254
@Before
5355
public void setupChannels() throws IOException {
5456
if(gRpcServerProperties.isEnabled()) {
@@ -102,7 +104,7 @@ public void shutdownChannels() {
102104
public void simpleGreeting() throws ExecutionException, InterruptedException {
103105

104106

105-
String name ="John";
107+
106108
final GreeterGrpc.GreeterFutureStub greeterFutureStub = GreeterGrpc.newFutureStub(selectedChanel);
107109
final GreeterOuterClass.HelloRequest helloRequest =GreeterOuterClass.HelloRequest.newBuilder().setName(name).build();
108110
final String reply = beforeGreeting(greeterFutureStub).sayHello(helloRequest).get().getMessage();
Lines changed: 100 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,23 @@
11
package org.lognet.springboot.grpc.autoconfigure.metrics;
22

33
import io.grpc.ForwardingServerCall;
4+
import io.grpc.ForwardingServerCallListener;
45
import io.grpc.Grpc;
56
import io.grpc.Metadata;
67
import io.grpc.ServerCall;
78
import io.grpc.ServerCallHandler;
89
import io.grpc.ServerInterceptor;
910
import io.grpc.Status;
1011
import io.micrometer.core.instrument.MeterRegistry;
12+
import io.micrometer.core.instrument.Tag;
1113
import io.micrometer.core.instrument.Timer;
1214
import lombok.Setter;
1315
import lombok.experimental.Accessors;
16+
import lombok.extern.slf4j.Slf4j;
1417
import org.lognet.springboot.grpc.GRpcGlobalInterceptor;
1518
import org.lognet.springboot.grpc.GRpcService;
1619
import org.lognet.springboot.grpc.autoconfigure.GRpcServerProperties;
20+
import org.springframework.beans.factory.annotation.Autowired;
1721
import org.springframework.boot.actuate.autoconfigure.metrics.CompositeMeterRegistryAutoConfiguration;
1822
import org.springframework.boot.actuate.autoconfigure.metrics.MetricsAutoConfiguration;
1923
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
@@ -27,7 +31,13 @@
2731
import org.springframework.core.Ordered;
2832

2933
import java.net.SocketAddress;
34+
import java.util.ArrayList;
35+
import java.util.Collection;
36+
import java.util.List;
3037
import java.util.Optional;
38+
import java.util.stream.Collectors;
39+
import java.util.stream.Stream;
40+
import java.util.stream.StreamSupport;
3141

3242
@Configuration
3343
@AutoConfigureAfter({MetricsAutoConfiguration.class, CompositeMeterRegistryAutoConfiguration.class})
@@ -52,73 +62,136 @@ static class GrpcServiceCondition {
5262

5363
}
5464

55-
static class MonitoringServerCall<ReqT, RespT> extends ForwardingServerCall.SimpleForwardingServerCall<ReqT, RespT> {
65+
static class MonitoringServerCall<ReqT, RespT> extends ForwardingServerCall.SimpleForwardingServerCall<ReqT, RespT> {
66+
5667

57-
private final boolean addAddressTag;
5868
private MeterRegistry registry;
69+
5970
final Timer.Sample start;
60-
protected MonitoringServerCall(ServerCall<ReqT, RespT> delegate, MeterRegistry registry, boolean addAddressTag) {
71+
72+
private Collection<GRpcMetricsTagsContributor> tagsContributors;
73+
private List<Tag> additionalTags;
74+
75+
76+
77+
protected MonitoringServerCall(ServerCall<ReqT, RespT> delegate, MeterRegistry registry, Collection<GRpcMetricsTagsContributor> tagsContributors) {
6178
super(delegate);
6279
this.start = Timer.start(registry);
6380
this.registry = registry;
64-
this.addAddressTag = addAddressTag;
65-
66-
81+
this.tagsContributors = tagsContributors;
6782
}
83+
6884
@Override
6985
public void close(Status status, Metadata trailers) {
70-
final Timer.Builder timerBuilder = Timer.builder("grpc.server.calls")
71-
.tag("method", getMethodDescriptor().getFullMethodName())
72-
.tag("result", status.getCode().name());
73-
if (addAddressTag){
74-
Optional.ofNullable(delegate().getAttributes())
75-
.map(a->a.get(Grpc.TRANSPORT_ATTR_LOCAL_ADDR))
76-
.map(SocketAddress::toString)
77-
.ifPresent(a->timerBuilder.tag("address",a));
78-
}
86+
87+
final Timer.Builder timerBuilder = Timer.builder("grpc.server.calls");
88+
tagsContributors.forEach(c->
89+
timerBuilder.tags(c.getTags(status,getMethodDescriptor(),getAttributes()))
90+
);
91+
Optional.ofNullable(additionalTags)
92+
.ifPresent(timerBuilder::tags);
93+
7994
start.stop(timerBuilder.register(registry));
8095

8196
super.close(status, trailers);
8297
}
98+
99+
public void addTags(List<Tag> tags) {
100+
additionalTags = tags;
101+
}
83102
}
84103

104+
@Slf4j
85105
static class MonitoringServerInterceptor implements ServerInterceptor, Ordered {
86106

87-
88107
private MeterRegistry registry;
89-
private boolean addAddressTag;
108+
109+
110+
111+
private Collection<GRpcMetricsTagsContributor> tagsContributors ;
112+
113+
@Autowired
114+
public void setTagsContributors(Collection<GRpcMetricsTagsContributor> tagsContributors) {
115+
this.tagsContributors = tagsContributors;
116+
}
117+
90118
@Setter
91119
@Accessors(fluent = true)
92120
private Integer order;
93121

94-
95-
public MonitoringServerInterceptor(MeterRegistry registry,boolean addAddressTag) {
122+
public MonitoringServerInterceptor(MeterRegistry registry) {
96123
this.registry = registry;
97-
this.addAddressTag = addAddressTag;
98124
}
99125

100126
@Override
101127
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
102-
return next.startCall(new MonitoringServerCall<>(call,registry,addAddressTag), headers);
103128

129+
final MonitoringServerCall<ReqT, RespT> monitoringServerCall = new MonitoringServerCall<>(call, registry,tagsContributors);
130+
final ServerCall.Listener<ReqT> measuredCall = next.startCall(monitoringServerCall, headers);
131+
if (call.getMethodDescriptor().getType().clientSendsOneMessage()) {
132+
return new ForwardingServerCallListener.SimpleForwardingServerCallListener<ReqT>(measuredCall) {
133+
@Override
134+
public void onMessage(ReqT message) {
135+
136+
final Stream<Tag> fd = tagsContributors
137+
.stream()
138+
.filter(RequestAwareGRpcMetricsTagsContributor.class::isInstance)
139+
.map(RequestAwareGRpcMetricsTagsContributor.class::cast)
140+
.filter(c -> c.accepts(message))
141+
.flatMap(c -> {
142+
try {
143+
return StreamSupport.stream(c.getTags(message,monitoringServerCall.getMethodDescriptor(),monitoringServerCall.getAttributes()).spliterator(), false);
144+
}catch (Throwable t){
145+
log.error("Failed to execute tag contributor",t);
146+
return Stream.empty();
147+
}
148+
});
149+
150+
monitoringServerCall.addTags(fd.collect(Collectors.toList()));
151+
152+
super.onMessage(message);
153+
154+
}
155+
};
156+
} else {
157+
return measuredCall;
158+
}
104159
}
105160

106-
107-
108161
@Override
109162
public int getOrder() {
110-
return Optional.ofNullable(order).orElse(HIGHEST_PRECEDENCE+20);
163+
return Optional.ofNullable(order).orElse(HIGHEST_PRECEDENCE + 20);
111164
}
112165
}
113166

114167
@Bean
115168
@GRpcGlobalInterceptor
116-
public ServerInterceptor measure(MeterRegistry registry, GRpcServerProperties properties,GRpcMetricsProperties metricsProperties){
169+
public ServerInterceptor measure(MeterRegistry registry, GRpcMetricsProperties metricsProperties) {
170+
171+
return new MonitoringServerInterceptor(registry)
172+
.order(metricsProperties.getInterceptorOrder());
173+
}
174+
175+
@Bean
176+
public GRpcMetricsTagsContributor defaultTagsContributor(GRpcServerProperties properties) {
117177
final Boolean hasMultipleAddresses = Optional.ofNullable(properties.getNettyServer())
118178
.map(GRpcServerProperties.NettyServerProperties::getAdditionalListenAddresses)
119179
.map(l -> !l.isEmpty())
120180
.orElse(false);
121-
return new MonitoringServerInterceptor(registry,hasMultipleAddresses)
122-
.order(metricsProperties.getInterceptorOrder());
181+
182+
return (status, methodDescriptor, attributes) -> {
183+
184+
final ArrayList<Tag> tags = new ArrayList<>();
185+
tags.add(Tag.of("result", status.getCode().name()));
186+
tags.add(Tag.of("method", methodDescriptor.getFullMethodName()));
187+
if (hasMultipleAddresses) {
188+
Optional.ofNullable(attributes)
189+
.map(a -> a.get(Grpc.TRANSPORT_ATTR_LOCAL_ADDR))
190+
.map(SocketAddress::toString)
191+
.map(a -> Tag.of("address", a))
192+
.ifPresent(tags::add);
193+
}
194+
return tags;
195+
};
123196
}
124197
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package org.lognet.springboot.grpc.autoconfigure.metrics;
2+
3+
import io.grpc.Attributes;
4+
import io.grpc.MethodDescriptor;
5+
import io.grpc.Status;
6+
import io.micrometer.core.instrument.Tag;
7+
@FunctionalInterface
8+
public interface GRpcMetricsTagsContributor {
9+
10+
Iterable<Tag> getTags(Status status, MethodDescriptor<?,?> methodDescriptor, Attributes attributes);
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package org.lognet.springboot.grpc.autoconfigure.metrics;
2+
3+
import io.grpc.Attributes;
4+
import io.grpc.MethodDescriptor;
5+
import io.grpc.Status;
6+
import io.micrometer.core.instrument.Tag;
7+
8+
import java.util.Collections;
9+
10+
public abstract class RequestAwareGRpcMetricsTagsContributor<ReqT> implements GRpcMetricsTagsContributor {
11+
private Class<ReqT> tClass;
12+
13+
protected RequestAwareGRpcMetricsTagsContributor(Class<ReqT> tClass) {
14+
this.tClass = tClass;
15+
}
16+
public final boolean accepts(Object o){
17+
return tClass.isInstance(o);
18+
}
19+
20+
@Override
21+
public Iterable<Tag> getTags(Status status, MethodDescriptor<?, ?> methodDescriptor, Attributes attributes) {
22+
return Collections.emptyList();
23+
}
24+
25+
public abstract Iterable<Tag> getTags(ReqT request,MethodDescriptor<?, ?> methodDescriptor, Attributes attributes);
26+
27+
}

0 commit comments

Comments
 (0)