Можно ли определить дополнительные теги для метрик Spring Boot 2 по умолчанию?

Недавно я перешел на Spring Boot 2 с Micrometer, Когда я получил эти блестящие новые метрики, я поговорил с нашими парнями из DevOps, и мы начали экспортировать их в Telegraf,

Чтобы различать разные приложения и узлы приложений, мы решили использовать теги. Это отлично работает для пользовательских метрик, но теперь я начал думать о предопределенных. Чтобы добиться того же для показателей по умолчанию, мне нужна возможность добавить дополнительные теги для них.

Можно ли этого добиться? Я делаю это правильно?

РЕДАКТИРОВАТЬ: я попробовал следующий подход:

@Component
public class MyMetricsImpl implements MyMetrics {

    @Autowired
    protected MyProperties myProperties;
    @Autowired
    protected MeterRegistry meterRegistry;

    @PostConstruct
    public void initialize() {
        this.meterRegistry.config()
                .commonTags(commonTags());
    }

    @Override
    public List<Tag> commonTags() {
        List<Tag> tags = new ArrayList<>();
        tags.add(Tag.of("application", myProperties.getApplicationName()));
        tags.add(Tag.of("node", myProperties.getNodeName()));
        return tags;
    }
}

Проблема в том, что мои метрики ведут себя правильно, и даже некоторые из метрик загрузки (по крайней мере, http.server.requests) увидеть мои теги. Но jvm.*, system.*, tomcat.* и многие другие до сих пор не имеют необходимых тегов.

4 ответа

Решение

Если вы ищете поддержку общих тегов, вы можете сделать это, зарегистрировав MeterFilter Делать это.

Смотрите этот коммит или эту ветку для примера.

В новой версии Spring Boot 2.1.0.M1 вы можете использовать следующие свойства:

management.metrics.tags.*= # Common tags that are applied to every meter.

Смотрите ссылку для деталей.


ОБНОВЛЕНО:

Поскольку вопрос был обновлен, я проверил обновленный вопрос с этим MeterFilter и подтвердил, что работает следующим образом:

Запрос: http://localhost:8080/actuator/metrics/jvm.gc.memory.allocated

Отклик:

{
  "name" : "jvm.gc.memory.allocated",
  "measurements" : [ {
    "statistic" : "COUNT",
    "value" : 1.98180864E8
  } ],
  "availableTags" : [ {
    "tag" : "stack",
    "values" : [ "prod" ]
  }, {
    "tag" : "region",
    "values" : [ "us-east-1" ]
  } ]
}

Я не проверял подход, который был представлен в обновленном вопросе, но я бы просто использовал проверенный MeterFilter подход, если нет никаких оснований придерживаться подхода.


2-е ОБНОВЛЕНИЕ:

Я посмотрел в подход и смог воспроизвести его с этой веткой.

Слишком поздно применять общие теги в @PostConstruct так как некоторые показатели уже зарегистрированы. Причина по которой http.server.requests работает то, что он будет зарегистрирован с первым запросом. Попробуйте поставить точку останова на точке применения фильтров, если вы заинтересованы в этом.

Вкратце, попробуйте описанный выше подход, который похож на готовую поддержку Spring Boot.

Для сервера:
(т.е. @RestController)
Чтобы добавить пользовательские теги метрик для приложения reactive-spring-boot в дополнение к тегам по умолчанию, предоставляемым платформой spring-boot.
предоставить один или несколько @Beans которые реализуют WebFluxTagsContributor
ИЛИ
Чтобы заменить теги по умолчанию, укажите, что implements WebFluxTagsProvider.
Эталонная реализация, использующая Spring-WebMVCкак один из ответов.
Ссылка:

Следующая реализация находится в отличном состоянии.

      import io.micrometer.core.instrument.Tag
import org.springframework.boot.actuate.metrics.web.reactive.server.WebFluxTagsContributor
import org.springframework.http.HttpMethod
import org.springframework.http.HttpStatus
import org.springframework.stereotype.Component
import org.springframework.web.server.ServerWebExchange

@Component
class CustomWebClientExchangeTagsProvider implements WebFluxTagsContributor {

    final KEY = "key"

    /**
     * Provides tags to be associated with metrics for the given {@code exchange}.
     * @param exchange the exchange
     * @param ex the current exception (maybe {@code null})
     * @return tags to associate with metrics for the request and response exchange
     */
    @Override
    Iterable<Tag> httpRequestTags(ServerWebExchange exchange, Throwable ex) {
        String apiKey = exchange.request.queryParams[KEY] ?: "default_api_key"
        HttpStatus status = exchange.response.statusCode ?: HttpStatus.INTERNAL_SERVER_ERROR
        HttpMethod method = exchange.request.method ?: HttpMethod.OPTIONS
        String group = (status.value() >= 500 ? "5XX" : (status.value() >= 400) ? "4XX" : (status.value() >= 300) ? "3XX" : "NONE")

        Tag statusTag = Tag.of("status", status.value().toString())
        Tag methodTag = Tag.of("method", method.toString())
        Tag apiKeyTag = Tag.of(KEY, apiKey)
        Tag groupTag = Tag.of("group", group)

        return Arrays.asList(statusTag, methodTag, apiKeyTag, groupTag)
    }
}

Теги метрик:

Подтвердить для клиента: метрика http.client.requests.percentile или же http.client.requests.

      http://localhost:8010/metrics/http.client.requests

Проверить для сервера:

      http://localhost:8010/metrics/http.server.requests

аналогично будут добавлены теги для http.server.requests.percentile метрики

      {
    "name": "http.server.requests",
    "description": null,
    "base_unit": "milliseconds",
    "measurements": [
        {
            "statistic": "COUNT",
            "value": 17.0
        },
        {
            "statistic": "TOTAL_TIME",
            "value": 8832.186054
        },
        {
            "statistic": "MAX",
            "value": 6.514132
        }
    ],
    "available_tags": [
        {
            "tag": "exception",
            "values": [
                "None",
                "ResponseStatusException"
            ]
        },
        {
            "tag": "method",
            "values": [
                "GET"
            ]
        },
        {
            "tag": "application",
            "values": [
                "myapplication"
            ]
        },
        {
            "tag": "uri",
            "values": [
                "/myapplication/v1/abcd",
                "/manage/metrics/{requiredMetricName}",
                "/manage",
                "/manage/metrics",
                "/myapplication/v1/windows",
                "/**"
            ]
        },
        {
            "tag": "outcome",
            "values": [
                "CLIENT_ERROR",
                "SERVER_ERROR",
                "SUCCESS"
            ]
        },
        {
            "tag": "key",
            "values": [
                "default_api_key",
                "[abcd]"
            ]
        },
        {
            "tag": "status",
            "values": [
                "404",
                "200",
                "502"
            ]
        },
        {
            "tag": "group",
            "values": [
                "4XX",
                "NONE",
                "5XX"
            ]
        }
    ]
}

ИЛИ ЖЕ

Если вы используете prometheus
вы можете проверить пользовательские теги следующим образом

      http://localhost:8010/prometheus

Клиент:

      http_client_requests_seconds_count{application="myapplication",clientName="myhostname.com",method="POST",outcome="CLIENT_ERROR",status="403",uri="/urlIamTryingToHit/v1/list",} 1.0

Не добавлено для клиента, поэтому нет настраиваемых тегов, только существующие теги, предоставляемые spring -boot.

Сервер:

      http_server_requests_seconds_sum{application="myapplication",exception="None",group="4XX",key="default_api_key",method="GET",outcome="CLIENT_ERROR",status="404",uri="/manage/metrics/{requiredMetricName}",} 0.004154207

Мы можем наблюдать group="4XX", key="default_api_key", method="GET", status="404" вместе с существующими тегами по умолчанию.


Для клиента:
чтобы настроить теги, и в зависимости от вашего выбора клиента, вы можете предоставить @Bean который implements RestTemplateExchangeTagsProvider или же WebClientExchangeTagsProvider.
Есть удобные статические функции в RestTemplateExchangeTags и WebClientExchangeTags.
Ссылка:

Если вы используете spring-boot-actator и вам нужно добавить динамические теги к метрикам по умолчанию, просто определите be an-компонент WebMvcTagsProvider:
реализация WebMvcTagsProvider по умолчанию.
@author Джон Шнайдер
@since 2.0.0

public class DefaultWebMvcTagsProvider implements WebMvcTagsProvider {

@Override
public Iterable<Tag> getTags(HttpServletRequest request, HttpServletResponse response, Object handler,
        Throwable exception) {
    return Tags.of(WebMvcTags.method(request), WebMvcTags.uri(request, response), WebMvcTags.exception(exception),
            WebMvcTags.status(response), WebMvcTags.outcome(response));
}

@Override
public Iterable<Tag> getLongRequestTags(HttpServletRequest request, Object handler) {
    return Tags.of(WebMvcTags.method(request), WebMvcTags.uri(request, null));
}

}

В ответ на комментарий под моим первоначальным вопросом, вот небольшой пример добавления пользовательских метрик:

package io.github.stepio.examples.metrics;

import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

@Component
public class MyImportantComponent {

    protected static final String CONNECTIONS_CURRENT = "connections.created";
    protected static final String CONNECTIONS_IDLE = "connections.idle";
    protected static final String CONNECTIONS_MAX = "connections.max";

    protected static final String PREFIX_REQUESTS_FOR_SERVICE = "requests";

    protected static final String SERVICE = "service";

    protected AtomicInteger connectionMax = new AtomicInteger();

    @Autowired
    protected MeterRegistry meterRegistry;

    @Override
    public List<Tag> tags() {
        return new ArrayList<>();
    }

    @Override
    public void trackConnectorsCurrent(AtomicInteger counter) {
        this.meterRegistry.gauge(CONNECTIONS_CURRENT, tags(), counter);
    }

    @Override
    public void trackConnectorsIdle(Collection<?> collection) {
        this.meterRegistry.gaugeCollectionSize(CONNECTIONS_IDLE, tags(), collection);
    }

    @Override
    public void submitConnectorsMax(final int value) {
        this.meterRegistry.gauge(CONNECTIONS_MAX, tags(), this.connectionMax).set(value);
    }

    @Override
    public void incrementRequestsForService(String serviceName) {
        this.meterRegistry.counter(PREFIX_REQUESTS_FOR_SERVICE, tags(SERVICE, serviceName)).increment();
    }

}
Другие вопросы по тегам