Skip to content

Commit 036b40e

Browse files
authored
Observation instrumentation for gRPC
Add Observation instrumentation for gRPC client and server. Closes micrometer-metrics#3427
1 parent 62ad361 commit 036b40e

18 files changed

+1602
-0
lines changed

dependencies.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ def VERSIONS = [
3636
'io.grpc:grpc-services:latest.release',
3737
'io.grpc:grpc-stubs:latest.release',
3838
'io.grpc:grpc-alts:latest.release',
39+
'io.grpc:grpc-testing-proto:latest.release',
3940
'info.ganglia.gmetric4j:gmetric4j:latest.release',
4041
'io.prometheus:simpleclient_common:latest.release',
4142
'io.prometheus:simpleclient_pushgateway:latest.release',

micrometer-core/build.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,10 @@ dependencies {
157157
}
158158
testImplementation("org.apache.maven.resolver:maven-resolver-connector-basic:latest.release")
159159
testImplementation("org.springframework:spring-core:latest.release")
160+
161+
// gRPC
162+
testImplementation("io.grpc:grpc-core")
163+
testImplementation("io.grpc:grpc-testing-proto")
160164
}
161165

162166
task shenandoahTest(type: Test) {
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright 2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.micrometer.core.instrument.binder.grpc;
17+
18+
import io.micrometer.common.KeyValue;
19+
import io.micrometer.common.KeyValues;
20+
import io.micrometer.core.instrument.binder.grpc.GrpcObservationDocumentation.LowCardinalityKeyNames;
21+
22+
import java.util.ArrayList;
23+
import java.util.List;
24+
25+
/**
26+
* Default convention for gRPC client. This class defines how to extract values from
27+
* {@link GrpcClientObservationContext}.
28+
*
29+
* @author Tadaya Tsuyukubo
30+
* @since 1.10.0
31+
*/
32+
public class DefaultGrpcClientObservationConvention implements GrpcClientObservationConvention {
33+
34+
@Override
35+
public String getName() {
36+
return "grpc.client";
37+
}
38+
39+
@Override
40+
public String getContextualName(GrpcClientObservationContext context) {
41+
return context.getFullMethodName();
42+
}
43+
44+
@Override
45+
public KeyValues getLowCardinalityKeyValues(GrpcClientObservationContext context) {
46+
List<KeyValue> keyValues = new ArrayList<>();
47+
keyValues.add(LowCardinalityKeyNames.METHOD.withValue(context.getMethodName()));
48+
keyValues.add(LowCardinalityKeyNames.SERVICE.withValue(context.getServiceName()));
49+
keyValues.add(LowCardinalityKeyNames.METHOD_TYPE.withValue(context.getMethodType().name()));
50+
if (context.getStatusCode() != null) {
51+
keyValues.add(LowCardinalityKeyNames.STATUS_CODE.withValue(context.getStatusCode().name()));
52+
}
53+
return KeyValues.of(keyValues);
54+
}
55+
56+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright 2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.micrometer.core.instrument.binder.grpc;
17+
18+
import io.micrometer.common.KeyValue;
19+
import io.micrometer.common.KeyValues;
20+
import io.micrometer.core.instrument.binder.grpc.GrpcObservationDocumentation.LowCardinalityKeyNames;
21+
22+
import java.util.ArrayList;
23+
import java.util.List;
24+
25+
/**
26+
* Default convention for gRPC server. This class defines how to extract values from
27+
* {@link GrpcServerObservationContext}.
28+
*
29+
* @author Tadaya Tsuyukubo
30+
* @since 1.10.0
31+
*/
32+
public class DefaultGrpcServerObservationConvention implements GrpcServerObservationConvention {
33+
34+
@Override
35+
public String getName() {
36+
return "grpc.server";
37+
}
38+
39+
@Override
40+
public String getContextualName(GrpcServerObservationContext context) {
41+
return context.getFullMethodName();
42+
}
43+
44+
@Override
45+
public KeyValues getLowCardinalityKeyValues(GrpcServerObservationContext context) {
46+
List<KeyValue> keyValues = new ArrayList<>();
47+
keyValues.add(LowCardinalityKeyNames.METHOD.withValue(context.getMethodName()));
48+
keyValues.add(LowCardinalityKeyNames.SERVICE.withValue(context.getServiceName()));
49+
keyValues.add(LowCardinalityKeyNames.METHOD_TYPE.withValue(context.getMethodType().name()));
50+
if (context.getStatusCode() != null) {
51+
keyValues.add(LowCardinalityKeyNames.STATUS_CODE.withValue(context.getStatusCode().name()));
52+
}
53+
return KeyValues.of(keyValues);
54+
}
55+
56+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
* Copyright 2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package io.micrometer.core.instrument.binder.grpc;
18+
19+
import io.grpc.Metadata;
20+
import io.grpc.MethodDescriptor.MethodType;
21+
import io.grpc.Status.Code;
22+
import io.micrometer.common.lang.Nullable;
23+
import io.micrometer.observation.Observation;
24+
import io.micrometer.observation.transport.Propagator.Setter;
25+
import io.micrometer.observation.transport.RequestReplySenderContext;
26+
27+
/**
28+
* {@link Observation.Context} for gRPC client.
29+
*
30+
* @author Tadaya Tsuyukubo
31+
* @since 1.10.0
32+
*/
33+
public class GrpcClientObservationContext extends RequestReplySenderContext<Metadata, Object> {
34+
35+
private String serviceName;
36+
37+
private String methodName;
38+
39+
private String fullMethodName;
40+
41+
private MethodType methodType;
42+
43+
@Nullable
44+
private Code statusCode;
45+
46+
public GrpcClientObservationContext(Setter<Metadata> setter) {
47+
super(setter);
48+
}
49+
50+
public String getServiceName() {
51+
return this.serviceName;
52+
}
53+
54+
public void setServiceName(String serviceName) {
55+
this.serviceName = serviceName;
56+
}
57+
58+
public String getMethodName() {
59+
return this.methodName;
60+
}
61+
62+
public void setMethodName(String methodName) {
63+
this.methodName = methodName;
64+
}
65+
66+
public String getFullMethodName() {
67+
return this.fullMethodName;
68+
}
69+
70+
public void setFullMethodName(String fullMethodName) {
71+
this.fullMethodName = fullMethodName;
72+
}
73+
74+
public MethodType getMethodType() {
75+
return this.methodType;
76+
}
77+
78+
public void setMethodType(MethodType methodType) {
79+
this.methodType = methodType;
80+
}
81+
82+
@Nullable
83+
public Code getStatusCode() {
84+
return this.statusCode;
85+
}
86+
87+
public void setStatusCode(Code statusCode) {
88+
this.statusCode = statusCode;
89+
}
90+
91+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.micrometer.core.instrument.binder.grpc;
17+
18+
import io.micrometer.observation.Observation.Context;
19+
import io.micrometer.observation.ObservationConvention;
20+
21+
/**
22+
* {@link ObservationConvention} for gRPC client.
23+
*
24+
* @author Tadaya Tsuyukubo
25+
* @since 1.10.0
26+
*/
27+
public interface GrpcClientObservationConvention extends ObservationConvention<GrpcClientObservationContext> {
28+
29+
@Override
30+
default boolean supportsContext(Context context) {
31+
return context instanceof GrpcClientObservationContext;
32+
}
33+
34+
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/*
2+
* Copyright 2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.micrometer.core.instrument.binder.grpc;
17+
18+
import io.micrometer.common.docs.KeyName;
19+
import io.micrometer.observation.Observation.Context;
20+
import io.micrometer.observation.Observation.Event;
21+
import io.micrometer.observation.ObservationConvention;
22+
import io.micrometer.observation.docs.ObservationDocumentation;
23+
24+
/**
25+
* {@link ObservationDocumentation} for gRPC.
26+
*
27+
* @author Tadaya Tsuyukubo
28+
* @since 1.10.0
29+
*/
30+
public enum GrpcObservationDocumentation implements ObservationDocumentation {
31+
32+
CLIENT {
33+
@Override
34+
public Class<? extends ObservationConvention<? extends Context>> getDefaultConvention() {
35+
return GrpcClientObservationConvention.class;
36+
}
37+
38+
@Override
39+
public KeyName[] getLowCardinalityKeyNames() {
40+
return LowCardinalityKeyNames.values();
41+
}
42+
},
43+
SERVER {
44+
@Override
45+
public Class<? extends ObservationConvention<? extends Context>> getDefaultConvention() {
46+
return GrpcServerObservationConvention.class;
47+
}
48+
49+
@Override
50+
public KeyName[] getLowCardinalityKeyNames() {
51+
return LowCardinalityKeyNames.values();
52+
}
53+
};
54+
55+
public enum LowCardinalityKeyNames implements KeyName {
56+
57+
METHOD {
58+
@Override
59+
public String asString() {
60+
return "rpc.method";
61+
}
62+
},
63+
METHOD_TYPE {
64+
@Override
65+
public String asString() {
66+
return "rpc.type";
67+
}
68+
},
69+
SERVICE {
70+
@Override
71+
public String asString() {
72+
return "rpc.service";
73+
}
74+
},
75+
ERROR_CODE {
76+
@Override
77+
public String asString() {
78+
return "rpc.error_code";
79+
}
80+
},
81+
STATUS_CODE {
82+
@Override
83+
public String asString() {
84+
return "grpc.status_code";
85+
}
86+
}
87+
88+
}
89+
90+
public enum GrpcClientEvents implements Event {
91+
92+
MESSAGE_SENT {
93+
@Override
94+
public String getName() {
95+
return "sent";
96+
}
97+
98+
},
99+
MESSAGE_RECEIVED {
100+
@Override
101+
public String getName() {
102+
return "received";
103+
}
104+
105+
}
106+
107+
}
108+
109+
public enum GrpcServerEvents implements Event {
110+
111+
MESSAGE_RECEIVED {
112+
@Override
113+
public String getName() {
114+
return "received";
115+
}
116+
117+
},
118+
MESSAGE_SENT {
119+
@Override
120+
public String getName() {
121+
return "sent";
122+
}
123+
124+
}
125+
126+
}
127+
128+
}

0 commit comments

Comments
 (0)