There are three key additions for us:
- New
SimpleTimer
metric (combines a count and an elapsed time accumulator) @SimplyTimed
annotationREST.request
optional automatic metric inbase
registry for all REST endpoints. In MP, Helidon creates aSimpleTimer
for each endpoint annotated with a JAX-RS annotation (@GET
,@PUT
, etc.), with nameREST.request
and tagsclass
andmethod
identifying the endpoint.
The spec leaves open how an MP implementation enables and disables the REST.request
behavior. See below for our proposal.
In Helidon configuration, the metrics.rest-request.enabled
key controls whether the behavior is on or off. Defaults to false
.
The full (not bare) MP Quickstart example and the MP quickstart archetype enable this feature. Enabling this in the MP quickstart example emphasizes that we support this optional feature of MP. Leaving it out of the bare archetype keeps that bare.
Question: What about the MP database quickstart?
As with all other metrics in the MP spec, we implement SimpleTimer
for SE.
The question is whether and how SE should support REST.request
? We should not support REST.request
behavior in SE without the developer making some change, to preserve the "no magic" philosophy in SE.
This approach requires very little boilerplate while still not introducing any magic.
Using the SE quickstart as an example...
- The
Main
class saves in a static field theMetricsSupport
instance used in preparing the routing rules. (Currently, theMetricsSupport
instance is used only locally in thecreateRouting
method.
static MetricsSupport metrics;
- The
GreetService#update
method changes to add handlers for each of the endpoint paths:
rules
.get("/",
Main.metrics.restRequestMetricHandler(this, "getDefaultMessageHandler"), // added handler
this::getDefaultMessageHandler)
.get("/{name}",
Main.metrics.restRequestMetricHandler(this, "getMessageHandler"), // added handler
this::getMessageHandler)
.put("/greeting",
Main.metrics.restRequestMetricHandler(this, "updateGreetingHandler"), // added handler
this::updateGreetingHandler);
This requires little code and allows the developer to leave the endpoint code itself alone.
An annoying aspect of this approach is that the developer must repeat the "this" and the method name: once in the method reference from the original quickstart SE example and once again in the method invocation to get the handler new handler from MetricsSupport
. Unfortunately, there is no nice, solid, supported way to get the class name and method name from a method reference.
The config key metrics.rest-request.enabled
setting for SE defaults to true
because, given that the developer has added code to activate the REST.request
metrics, we should not require another action to enable them.
Instead doc will illustrate how developers can use the convenience methods.
- Add these instance field declarations to each resource class (this example is from the SE QuickStart):
private MetricRegistry appRegistry = RegistryFactory.getInstance().getRegistry(Type.APPLICATION);
private SimpleTimer getDefaultMessageSimpleTimer = appRegistry.simpleTimer("REST.request",
new Tag(getClass().getName(), "getDefaultMessageHandler");
private SimpleTimer getMessageSimpleTimer = appRegistry.simpleTimer("REST.request",
new Tag(getClass().getName(), "getMessageHandler");
private SimpleTimer updateGreetingSimpleTimer = appRegistry.simpleTimer("REST.request",
new Tag(getClass().getName(), "updategreetingHandler");
(Note that the spec for MP mandates the metadata for REST.request
but because the SE developer "owns" the REST.request
metric that's not up to us.
2. Wrap each endpoint method's code in simpleTimer.time
:
private void getDefaultMessageHandler(ServerRequest request,
ServerResponse response) {
getDefaultMessageSimpleTimer.time(sendResponse(response, "World"));
}
This involves considerable coding by the developer and intrudes into the endpoint logic which, ideally, would focus on business logic. (Note that declaring the metrics separately from the endpoint code makes sure that all the REST.request
metrics are created, apart from whether clients invoke the corresponding endpoints.)
For example (again from the SE QuickStart):
// Use the same declarations as above.
@Override
public void update(Routing.Rules rules) {
rules
.get("/", (req, resp) -> getDefaultMessageSimpleTimer.time(this::getDefaultMessageHandler))
.get("/{name}", (req, resp) -> getMessageSimpleTimer.time(this::getMessageHandler))
.put("/greeting", (req, resp) -> updateGreetingSimpleTimer.time(this::updateGreetingHandler));
}
This avoids changes in the endpoint methods themselves but still requires the developer to add quite a bit of code.