Collecting metrics

Armeria has built-in support for collecting metrics both on the server and client side. This page describes how to enable this.

Collecting server-side metrics

You can use MetricCollectingService with MeterIdPrefixFunction for collecting metrics of your services.

import com.linecorp.armeria.common.MeterIdPrefixFunction;
import com.linecorp.armeria.server.Server;
import com.linecorp.armeria.server.ServerBuilder;
import com.linecorp.armeria.server.metric.MetricCollectingService;

Server.builder()
      .http(new InetSocketAddress(address, port))
      .service(new MyHttpService())
      .decorator(MetricCollectingService.newDecorator(
              MeterIdPrefixFunction.ofDefault("http.service")))
      .build();

If you are interested in monitoring gRPC status for a gRPC service, you can use GrpcMeterIdPrefixFunction instead of MeterIdPrefixFunction.

import com.linecorp.armeria.common.grpc.GrpcMeterIdPrefixFunction;
import com.linecorp.armeria.server.grpc.GrpcService;

Server.builder()
      .http(new InetSocketAddress(address, port))
      .service(GrpcService.builder()
                          .addService(new MyHelloService())
                          .build())
      .decorator(MetricCollectingService.newDecorator(
              GrpcMeterIdPrefixFunction.of("grpc.service")))
      .build();

You can provide GrpcMeterIdPrefixFunction as a bean if you are using a Spring integration module:

@Bean
public MeterIdPrefixFunction grpcMeterIdPrefixFunction() {
    return GrpcMeterIdPrefixFunction.of("grpc.service");
}

In cases where more sophisticated filtering and/or mangling of the generated metrics are required, you can use the ServerBuilder.meterRegistry() method like this:

Server.builder()
      // ...
      .meterRegistry(myMeterRegistry);

Collecting client-side metrics

This approach can be used to collect metrics for a WebClient and a gRPC client:

import com.linecorp.armeria.client.WebClient;
import com.linecorp.armeria.client.grpc.GrpcClients;
import com.linecorp.armeria.client.metric.MetricCollectingClient;

// Decorate a WebClient with MetricCollectingClient
WebClient.builder(httpUrl)
         .decorator(MetricCollectingClient.newDecorator(
                 MeterIdPrefixFunction.ofDefault("http.client")))
         .build();

// Decorate a gRPC client with MetricCollectingClient
GrpcClients.builder(grpcUrl)
           .decorator(MetricCollectingClient.newDecorator(
                   GrpcMeterIdPrefixFunction.of("grpc.client")))
           .build(HelloServiceBlockingStub.class);

Like the server-side metrics described above, certain scenarios might require you to provide a custom meter registry. To accomplish this, override the ClientFactory in this way:

import com.linecorp.armeria.client.ClientFactory;
import com.linecorp.armeria.client.Clients;

ClientFactory clientFactory =
      ClientFactory.builder()
                   .meterRegistry(myMeterRegistry)
                   .build();

// Set a custom ClientFactory to a WebClient
WebClient.builder(httpUrl)
         .factory(clientFactory)
          // ...
         .build();

// Set a custom ClientFactory to a gRPC client
Clients.builder(grpcUrl)
       .factory(clientFactory)
        // ...
       .build(HelloServiceBlockingStub.class);

Changing the default distribution summary config

A distribution summary is used to track the distribution of events. Armeria provides a sensible default DistributionStatisticConfig for measuring the following metrics:

  • A length of the request content
  • A length of the response content
  • A duration of request
  • A duration of response

If you want to override the default config, you can set your own DistributionStatisticConfig using MoreMeters.setDistributionStatisticConfig().

import com.linecorp.armeria.common.metric.MoreMeters;
import io.micrometer.core.instrument.distribution.DistributionStatisticConfig;

DistributionStatisticConfig myDistStatCfg =
        DistributionStatisticConfig.builder()
                                   .percentilesHistogram(false)
                                   .sla()
                                   .percentiles({ 0, 0.5, 0.75, 0.9, 0.95, 0.99, 1.0 })
                                   .percentilePrecision(10)
                                   .minimumExpectedValue(1L)
                                   .maximumExpectedValue(Long.MAX_VALUE)
                                   .expiry(Duration.ofMinutes(10))
                                   .bufferLength(10)
                                   .build();
MoreMeters.setDistributionStatisticConfig(myDistStatCfg);

Excluding certain meters created by Armeria

Micrometer's MeterRegistry can be configured with meter filters. If you need to control the exported meters, you can apply sophisticated filters to the MeterRegistry.

import com.linecorp.armeria.common.Flags;

Flags.meterRegistry()
     .config()
     .meterFilter(MeterFilter.deny(id ->
                  id.getTag("service").equals("MyHealthCheckService")));
     .meterFilter(MeterFilter.denyNameStartsWith("jvm"));

Please refer to MeterIdPrefixFunction to learn what kinds of tags are used for request metrics.