- gRPC Service Metrics
Helidon gRPC Server has built-in support for metrics capture, which allows service developers to easily enable application-level metrics for their services.
Enabling Metrics Capture
By default, gRPC Server only captures two vendor-level metrics: grpc.request.count and grpc.request.meter. These metrics provide aggregate view of requests across all services, and serve as an indication of the overall server load.
However, users can enable more fine grained metrics by simply configuring a built-in GrpcMetrics interceptor within the routing:
private static GrpcRouting createRouting(Config config) {
return GrpcRouting.builder()
.intercept(GrpcMetrics.timed())
.register(new GreetService(config))
.register(new EchoService())
.build();
}- Capture metrics for all methods of all services as a
timer
In the example above we have chosen to create and keep a timer metric type for each method of each service. Alternatively, we could’ve chosen to use a counter, meter or a histogram instead.
Overriding Metrics Capture
While global metrics capture is certainly useful, it is not always sufficient. Keeping a separate timer for each gRPC method may be an overkill, so the user could decide to use a lighter-weight metric type, such as counter or a meter.
However, she may still want to enable histogram or a timer for some services, or even only some methods of some services.
This can be easily accomplished by overriding the type of the captured metric at either service or the method level:
private static GrpcRouting createRouting(Config config) {
return GrpcRouting.builder()
.intercept(GrpcMetrics.counted())
.register(new MyService())
.build();
}
public static class MyService implements GrpcService {
@Override
public void update(ServiceDescriptor.Rules rules) {
rules
.intercept(GrpcMetrics.metered())
.unary("MyMethod", this::myMethod,
cfg -> cfg.intercept(GrpcMetrics.timer()))
}
private <ReqT, ResT> void myMethod(ReqT request, StreamObserver<ResT> observer) {
// do something
}
}- Use
counterfor all methods of all services, unless overridden - Use
meterfor all methods ofMyService - Use
timerforMyService::MyMethod
Exposing Metrics Externally
Collected metrics are stored in the standard Helidon Metric Registries, such as vendor and application registry, and can be exposed via standard /metrics REST API.
Routing routing = Routing.builder()
.register(MetricsSupport.create())
.build();
WebServer.create(webServerConfig(), routing)
.start()- Add
MetricsSupportinstance to web server routing - Create and start Helidon web server
See Helidon Metrics documentation for more details.
Specifying Metric Meta-data
Helidon metrics contain meta-data such as tags, a description, units etc. It is possible to add this additional meta-data when specifying the metrics.
Adding Tags
To add tags to a metric a Map of key/value tags can be supplied. For example:
Map<String, String> tagMap = new HashMap<>();
tagMap.put("keyOne", "valueOne");
tagMap.put("keyTwo", "valueTwo");
GrpcRouting routing = GrpcRouting.builder()
.intercept(GrpcMetrics.counted().tags(tagMap))
.register(new MyService())
.build();- the
tags()method is used to add theMapof tags to the metric.
Adding a Description
A meaningful description can be added to a metric: For example:
GrpcRouting routing = GrpcRouting.builder()
.intercept(GrpcMetrics.counted().description("Something useful"))
.register(new MyService())
.build();- the
description()method is used to add the description to the metric.
Adding Metric Units
A units value can be added to the Metric: For example:
GrpcRouting routing = GrpcRouting.builder()
.intercept(GrpcMetrics.timed().units(MetricUnits.SECONDS))
.register(new MyService())
.build();- the
units()method is used to add the metric units to the metric. Typically the units value is one of the constants fromorg.eclipse.microprofile.metrics.MetricUnitsclass.
Overriding the Metric Name
By default the metric name is the gRPC service name followed by a dot ('.') followed by the method name. It is possible to supply a function that can be used to override the default behaviour.
The function should implement the io.helidon.grpc.metrics.GrpcMetrics.NamingFunction interface
@FunctionalInterface
public interface NamingFunction {
/**
* Create a metric name.
*
* @param service the service descriptor
* @param methodName the method name
* @param metricType the metric type
* @return the metric name
*/
String createName(ServiceDescriptor service, String methodName, MetricType metricType);
}This is a functional interface so lambda can be used too.
For example:
GrpcRouting routing = GrpcRouting.builder()
.intercept(GrpcMetrics.counted()
.nameFunction((svc, method, metric) -> "grpc." + service.name() + '.' + method) - the
NamingFunctionis just a lambda that returns the concatenated service name and method name with the prefixgrpc.So for a service "Foo", method "bar" the above example would produce a name "grpc.Foo.bar".