Skip to content

Commit

Permalink
Observation instrumentation for gRPC
Browse files Browse the repository at this point in the history
Add Observation instrumentation for gRPC client and server.

Closes #3427
  • Loading branch information
ttddyy committed Oct 3, 2022
1 parent 62ad361 commit 036b40e
Show file tree
Hide file tree
Showing 18 changed files with 1,602 additions and 0 deletions.
1 change: 1 addition & 0 deletions dependencies.gradle
Expand Up @@ -36,6 +36,7 @@ def VERSIONS = [
'io.grpc:grpc-services:latest.release',
'io.grpc:grpc-stubs:latest.release',
'io.grpc:grpc-alts:latest.release',
'io.grpc:grpc-testing-proto:latest.release',
'info.ganglia.gmetric4j:gmetric4j:latest.release',
'io.prometheus:simpleclient_common:latest.release',
'io.prometheus:simpleclient_pushgateway:latest.release',
Expand Down
4 changes: 4 additions & 0 deletions micrometer-core/build.gradle
Expand Up @@ -157,6 +157,10 @@ dependencies {
}
testImplementation("org.apache.maven.resolver:maven-resolver-connector-basic:latest.release")
testImplementation("org.springframework:spring-core:latest.release")

// gRPC
testImplementation("io.grpc:grpc-core")
testImplementation("io.grpc:grpc-testing-proto")
}

task shenandoahTest(type: Test) {
Expand Down
@@ -0,0 +1,56 @@
/*
* Copyright 2022 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 io.micrometer.core.instrument.binder.grpc;

import io.micrometer.common.KeyValue;
import io.micrometer.common.KeyValues;
import io.micrometer.core.instrument.binder.grpc.GrpcObservationDocumentation.LowCardinalityKeyNames;

import java.util.ArrayList;
import java.util.List;

/**
* Default convention for gRPC client. This class defines how to extract values from
* {@link GrpcClientObservationContext}.
*
* @author Tadaya Tsuyukubo
* @since 1.10.0
*/
public class DefaultGrpcClientObservationConvention implements GrpcClientObservationConvention {

@Override
public String getName() {
return "grpc.client";
}

@Override
public String getContextualName(GrpcClientObservationContext context) {
return context.getFullMethodName();
}

@Override
public KeyValues getLowCardinalityKeyValues(GrpcClientObservationContext context) {
List<KeyValue> keyValues = new ArrayList<>();
keyValues.add(LowCardinalityKeyNames.METHOD.withValue(context.getMethodName()));
keyValues.add(LowCardinalityKeyNames.SERVICE.withValue(context.getServiceName()));
keyValues.add(LowCardinalityKeyNames.METHOD_TYPE.withValue(context.getMethodType().name()));
if (context.getStatusCode() != null) {
keyValues.add(LowCardinalityKeyNames.STATUS_CODE.withValue(context.getStatusCode().name()));
}
return KeyValues.of(keyValues);
}

}
@@ -0,0 +1,56 @@
/*
* Copyright 2022 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 io.micrometer.core.instrument.binder.grpc;

import io.micrometer.common.KeyValue;
import io.micrometer.common.KeyValues;
import io.micrometer.core.instrument.binder.grpc.GrpcObservationDocumentation.LowCardinalityKeyNames;

import java.util.ArrayList;
import java.util.List;

/**
* Default convention for gRPC server. This class defines how to extract values from
* {@link GrpcServerObservationContext}.
*
* @author Tadaya Tsuyukubo
* @since 1.10.0
*/
public class DefaultGrpcServerObservationConvention implements GrpcServerObservationConvention {

@Override
public String getName() {
return "grpc.server";
}

@Override
public String getContextualName(GrpcServerObservationContext context) {
return context.getFullMethodName();
}

@Override
public KeyValues getLowCardinalityKeyValues(GrpcServerObservationContext context) {
List<KeyValue> keyValues = new ArrayList<>();
keyValues.add(LowCardinalityKeyNames.METHOD.withValue(context.getMethodName()));
keyValues.add(LowCardinalityKeyNames.SERVICE.withValue(context.getServiceName()));
keyValues.add(LowCardinalityKeyNames.METHOD_TYPE.withValue(context.getMethodType().name()));
if (context.getStatusCode() != null) {
keyValues.add(LowCardinalityKeyNames.STATUS_CODE.withValue(context.getStatusCode().name()));
}
return KeyValues.of(keyValues);
}

}
@@ -0,0 +1,91 @@
/*
* Copyright 2022 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 io.micrometer.core.instrument.binder.grpc;

import io.grpc.Metadata;
import io.grpc.MethodDescriptor.MethodType;
import io.grpc.Status.Code;
import io.micrometer.common.lang.Nullable;
import io.micrometer.observation.Observation;
import io.micrometer.observation.transport.Propagator.Setter;
import io.micrometer.observation.transport.RequestReplySenderContext;

/**
* {@link Observation.Context} for gRPC client.
*
* @author Tadaya Tsuyukubo
* @since 1.10.0
*/
public class GrpcClientObservationContext extends RequestReplySenderContext<Metadata, Object> {

private String serviceName;

private String methodName;

private String fullMethodName;

private MethodType methodType;

@Nullable
private Code statusCode;

public GrpcClientObservationContext(Setter<Metadata> setter) {
super(setter);
}

public String getServiceName() {
return this.serviceName;
}

public void setServiceName(String serviceName) {
this.serviceName = serviceName;
}

public String getMethodName() {
return this.methodName;
}

public void setMethodName(String methodName) {
this.methodName = methodName;
}

public String getFullMethodName() {
return this.fullMethodName;
}

public void setFullMethodName(String fullMethodName) {
this.fullMethodName = fullMethodName;
}

public MethodType getMethodType() {
return this.methodType;
}

public void setMethodType(MethodType methodType) {
this.methodType = methodType;
}

@Nullable
public Code getStatusCode() {
return this.statusCode;
}

public void setStatusCode(Code statusCode) {
this.statusCode = statusCode;
}

}
@@ -0,0 +1,34 @@
/*
* Copyright 2022 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 io.micrometer.core.instrument.binder.grpc;

import io.micrometer.observation.Observation.Context;
import io.micrometer.observation.ObservationConvention;

/**
* {@link ObservationConvention} for gRPC client.
*
* @author Tadaya Tsuyukubo
* @since 1.10.0
*/
public interface GrpcClientObservationConvention extends ObservationConvention<GrpcClientObservationContext> {

@Override
default boolean supportsContext(Context context) {
return context instanceof GrpcClientObservationContext;
}

}
@@ -0,0 +1,128 @@
/*
* Copyright 2022 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 io.micrometer.core.instrument.binder.grpc;

import io.micrometer.common.docs.KeyName;
import io.micrometer.observation.Observation.Context;
import io.micrometer.observation.Observation.Event;
import io.micrometer.observation.ObservationConvention;
import io.micrometer.observation.docs.ObservationDocumentation;

/**
* {@link ObservationDocumentation} for gRPC.
*
* @author Tadaya Tsuyukubo
* @since 1.10.0
*/
public enum GrpcObservationDocumentation implements ObservationDocumentation {

CLIENT {
@Override
public Class<? extends ObservationConvention<? extends Context>> getDefaultConvention() {
return GrpcClientObservationConvention.class;
}

@Override
public KeyName[] getLowCardinalityKeyNames() {
return LowCardinalityKeyNames.values();
}
},
SERVER {
@Override
public Class<? extends ObservationConvention<? extends Context>> getDefaultConvention() {
return GrpcServerObservationConvention.class;
}

@Override
public KeyName[] getLowCardinalityKeyNames() {
return LowCardinalityKeyNames.values();
}
};

public enum LowCardinalityKeyNames implements KeyName {

METHOD {
@Override
public String asString() {
return "rpc.method";
}
},
METHOD_TYPE {
@Override
public String asString() {
return "rpc.type";
}
},
SERVICE {
@Override
public String asString() {
return "rpc.service";
}
},
ERROR_CODE {
@Override
public String asString() {
return "rpc.error_code";
}
},
STATUS_CODE {
@Override
public String asString() {
return "grpc.status_code";
}
}

}

public enum GrpcClientEvents implements Event {

MESSAGE_SENT {
@Override
public String getName() {
return "sent";
}

},
MESSAGE_RECEIVED {
@Override
public String getName() {
return "received";
}

}

}

public enum GrpcServerEvents implements Event {

MESSAGE_RECEIVED {
@Override
public String getName() {
return "received";
}

},
MESSAGE_SENT {
@Override
public String getName() {
return "sent";
}

}

}

}

0 comments on commit 036b40e

Please sign in to comment.