Быстрый старт#
Ниже приведен пример выставления метрик в формате Prometheus из прикладного приложения на Spring Boot.
1. Зависимости#
Добавить в Apache Maven зависимости на actuator и micrometer:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
2. Настройки#
В application.properties указать порт actuator (значение по умолчанию 8080) и фильтр включенных endpoint.
Пример application.properties
management.server.port=8081
management.endpoints.web.exposure.include=*
3. Создание прикладных метрик в коде#
Создать прикладные метрики в проекте с помощью micrometer. Подробнее см.: https://www.baeldung.com/micrometer. Если будут выставлены метрики со значением NaN, то есть "value": "NaN", они будут собраны, но валидацию не пройдут и не будут направлены в хранилище.
Ниже приведены примеры различных типов метрик.
Пример метрики Counter
@RestController
public class CounterController {
private static final String COUNTER_WITH_TAG_NAME = "simple.counterWithTags";
private static final String COUNTER_WITH_TAG_DESCRIPTION = "Just a simple counter with tags";
private static final String COUNTER_WITH_USER_TAG_NAME = "simple.counterWithUserTag";
private static final String TAG_NAME_1 = "terbank";
private static final String TAG_NAME_2 = "vsp";
private static final String TAG_USER = "userText";
private static final String TAG_RN = "rn";
private static final String RN = "unimon-sandbox-micrometer";
private static final String TAG_UNIMON_UD = "unimonId";
private static final String UNIMON_ID = "unimon-sandbox-micrometer";
@Autowired
MeterRegistry meterRegistry;
private Counter simpleCounter;
private Counter simpleCounterWithTags;
private Counter simpleCounterWithTags2;
private Counter simpleCounterWithUserTag;
/**
* Инициальзация метрик типа Counter
*/
@PostConstruct
public void init() {
simpleCounter = Counter.builder("simple.counter")
.description("Just a simple counter")
.tag(TAG_RN, RN)
.tag(TAG_UNIMON_UD, UNIMON_ID)
.register(meterRegistry);
simpleCounterWithTags = Counter.builder(COUNTER_WITH_TAG_NAME)
.description(COUNTER_WITH_TAG_DESCRIPTION)
.tag(TAG_NAME_1, "sib")
.tag(TAG_NAME_2, "111")
.tag(TAG_RN, RN)
.tag(TAG_UNIMON_UD, UNIMON_ID)
.register(meterRegistry);
simpleCounterWithTags2 = Counter.builder(COUNTER_WITH_TAG_NAME)
.description(COUNTER_WITH_TAG_DESCRIPTION)
.tag(TAG_NAME_1, "msk")
.tag(TAG_NAME_2, "111")
.tag(TAG_RN, RN)
.tag(TAG_UNIMON_UD, UNIMON_ID)
.register(meterRegistry);
}
/**
* Увеличение счетчика на 1
*/
@GetMapping("/counter")
@ResponseBody
public String incrementCounter() {
simpleCounter.increment();
return String.format("Counter has been increases\nCurrent value: %s", simpleCounter.count());
}
/**
* Увеличение на 1 счетчика с разрезом фиксации "terbank" и значением "sib"
*/
@GetMapping("/counterWithTags/terbank/sib")
@ResponseBody
public String incrementCounterTags1() {
simpleCounterWithTags.increment(1);
return String.format("Counter has been increases\nCurrent value: %s", simpleCounterWithTags.count());
}
/**
* Увеличение на 2 счетчика с разрезом фиксации "terbank" и значением "msk"
*/
@GetMapping("/counterWithTags/terbank/msk")
@ResponseBody
public String incrementCounterTags2() {
simpleCounterWithTags2.increment(2);
return String.format("Counter has been increases\nCurrent value: %s", simpleCounterWithTags2.count());
}
@GetMapping("/counterWithUserTagValue")
@ResponseBody
public String incrementCounterWithUserTag(@RequestParam String val) {
simpleCounterWithUserTag = Counter.builder(COUNTER_WITH_USER_TAG_NAME)
.tag(TAG_USER, val)
.tag(TAG_UNIMON_UD, UNIMON_ID)
.register(meterRegistry);
simpleCounterWithUserTag.increment(1);
return String.format("Counter has been increases\nCurrent value: %s", simpleCounterWithUserTag.count());
}
}
Пример метрики DistributionSummary
@RestController
public class DistributionSummaryController {
@Autowired
MeterRegistry meterRegistry;
private DistributionSummary simpleSummary;
private static final String TAG_RN = "rn";
private static final String RN = "unimon-sandbox-micrometer";
private static final String TAG_UNIMON_UD = "unimonId";
private static final String UNIMON_ID = "unimon-sandbox-micrometer";
/**
* Инициальзация метрик типа DistributionSummary
*/
@PostConstruct
public void init() {
simpleSummary = DistributionSummary.builder("simple.distributionSummary")
.description("Just a simple distributionSummary")
.publishPercentiles(0.5, 0.75, 0.9)
.tag(TAG_RN, RN)
.tag(TAG_UNIMON_UD, UNIMON_ID)
.register(meterRegistry);
}
/**
* Добавление случайного значения в множество
*/
@GetMapping("/distributionSummary")
@ResponseBody
public String addRecord() {
simpleSummary.record(Math.random());
return String.format("value added to the distributionSummary \n Current count: %s", simpleSummary.count());
}
}
Пример метрики Gauge
@RestController
public class GaugeController {
private static final String GAUGE_NAME = "simple.gauge";
private static final String GAUGE_DESCRIPTION = "Just a simple gauge";
private static final String GAUGE_WITH_TAG_NAME = "simple.gaugeWithTags";
private static final String GAUGE_WITH_TAG_DESCRIPTION = "Just a simple gauge with tags";
private static final String TAG_NAME_1 = "listName";
private static final String TAG_NAME_2 = "returnValue";
private static final String TAG_RN = "rn";
private static final String RN = "unimon-sandbox-micrometer";
private static final String TAG_UNIMON_UD = "unimonId";
private static final String UNIMON_ID = "unimon-sandbox-micrometer";
@Autowired
MeterRegistry meterRegistry;
private Gauge simpleGauge;
private Gauge simpleGaugeWithTags;
private Gauge simpleGaugeWithTags2;
private ServiceAvailabilityState availabilityState = new ServiceAvailabilityState();
private List<Integer> list1 = new ArrayList<>(4);
private List<Integer> list2 = new ArrayList<>(4);
/**
* Инициальзация метрик типа Gauge
*/
@PostConstruct
public void init() {
simpleGauge = Gauge.builder(GAUGE_NAME, availabilityState, ServiceAvailabilityState::getStateFlag)
.description(GAUGE_DESCRIPTION)
.tag(TAG_RN, RN)
.tag(TAG_UNIMON_UD, UNIMON_ID)
.register(meterRegistry);
simpleGaugeWithTags = Gauge.builder(GAUGE_WITH_TAG_NAME, list1, List::size)
.description(GAUGE_WITH_TAG_DESCRIPTION)
.tag(TAG_NAME_1, "list1")
.tag(TAG_NAME_2, "size")
.tag(TAG_RN, RN)
.tag(TAG_UNIMON_UD, UNIMON_ID)
.register(meterRegistry);
simpleGaugeWithTags2 = Gauge.builder(GAUGE_WITH_TAG_NAME, list2, List::size)
.description(GAUGE_WITH_TAG_DESCRIPTION)
.tag(TAG_NAME_1, "list2")
.tag(TAG_NAME_2, "size")
.tag(TAG_RN, RN)
.tag(TAG_UNIMON_UD, UNIMON_ID)
.register(meterRegistry);
}
/**
* Установка значения метрики
* @param val - значение метрики
*/
@GetMapping("/gauge")
@ResponseBody
public String setGauge(@RequestParam String val) {
try {
availabilityState.setStateFlag(Integer.valueOf(val));
} catch (NumberFormatException e) {
return "Integer value should be provided to set this Gauge";
}
return String.format("Gauge has been set\nCurrent value: %s", simpleGauge.value());
}
/**
* Добавляет к list1 значение, при этом изменяется значение метрики (размер списка) с разрезом ListName = list1
*/
@GetMapping("/gaugeWithTags/listName/list1")
@ResponseBody
public String addElementList1() {
list1.add(1);
return String.format("Gauge has been updated\nCurrent value: %s", simpleGaugeWithTags.value());
}
/**
* Добавляет к list2 значение, при этом изменяется значение метрики (размер списка) с разрезом ListName = list2
*/
@GetMapping("/gaugeWithTags/listName/list2")
@ResponseBody
public String addElementList2() {
list2.add(1);
return String.format("Gauge has been updated\nCurrent value: %s", simpleGaugeWithTags2.value());
}
}
class ServiceAvailabilityState {
Integer stateFlag = 0;
public Integer getStateFlag() {
return stateFlag;
}
public void setStateFlag(Integer stateFlag) {
this.stateFlag = stateFlag;
}
}
Пример метрики Timer
@RestController
public class TimerController {
@Autowired
MeterRegistry meterRegistry;
private Timer simpleTimer;
private Timer simpleTimerWithPercentile;
private static final String TAG_RN = "rn";
private static final String RN = "unimon-sandbox-micrometer";
private static final String TAG_UNIMON_UD = "unimonId";
private static final String UNIMON_ID = "unimon-sandbox-micrometer";
/**
* Инициальзация метрик типа Timer
*/
@PostConstruct
public void init() {
simpleTimer = Timer.builder("simple.timer")
.description("Just a simple timer")
.tag(TAG_RN, RN)
.tag(TAG_UNIMON_UD, UNIMON_ID)
.register(meterRegistry);
simpleTimerWithPercentile = Timer.builder("simple.timerWithPercentile")
.description("Just a simple timer with percentile")
.tag(TAG_RN, RN)
.tag(TAG_UNIMON_UD, UNIMON_ID)
.publishPercentiles(0.5, 0.9, 0.99)
.register(meterRegistry);
}
/**
* Добавляет в метрику типа Timer значение длительности вызова sleep()
*/
@GetMapping("/timer")
@ResponseBody
public String runTimer() {
simpleTimer.record(3000, TimeUnit.MILLISECONDS);
return String.format("Timer has been run\nCurrent value: %s \nCurrentCount: %s",
simpleTimer.totalTime(TimeUnit.MILLISECONDS), simpleTimer.count());
}
/**
* Добавляет в метрику типа Timer с включенной публикацией процентилей, значение длительности вызова sleep()
*/
@GetMapping("/timerWithPercentile")
@ResponseBody
public String runTimerWithPercentile() {
simpleTimerWithPercentile.record(() -> {
try {
TimeUnit.SECONDS.sleep((int) (Math.random() * 4));
} catch (InterruptedException ignored) { }
});
return String.format("Timer has been run\nCurrent value: %s \nCurrentCount: %s",
simpleTimerWithPercentile.totalTime(TimeUnit.MILLISECONDS), simpleTimerWithPercentile.count());
}
}
4. Настройки для подключения Agent#
Выполнить настройки из пункта «Подключение и конфигурирование». Просмотреть метрики, выставленные приложением в формате Prometheus можно по ссылке: http://localhost:8081/actuator/prometheus.