-
Notifications
You must be signed in to change notification settings - Fork 1.7k
feat(engine): Micrometer metrics #5337
Changes from all commits
ed4d581
94a58c3
ea6dad2
4612b6b
dad0fde
a3c5911
65a4900
e0135e3
0811997
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| package org.camunda.bpm.spring.boot.starter.actuator; | ||
|
|
||
| import jakarta.annotation.PostConstruct; | ||
| import io.micrometer.core.instrument.MeterRegistry; | ||
| import java.util.Timer; | ||
| import org.camunda.bpm.engine.ProcessEngine; | ||
| import org.camunda.bpm.spring.boot.starter.property.CamundaBpmProperties; | ||
| import org.springframework.beans.factory.annotation.Autowired; | ||
|
|
||
| public class MicrometerMetricsReporter { | ||
|
|
||
| @Autowired | ||
| MeterRegistry registry; | ||
|
|
||
| @Autowired | ||
| ProcessEngine processEngine; | ||
|
|
||
| @Autowired | ||
| CamundaBpmProperties camundaBpmProperties; | ||
|
|
||
| private MicrometerMetricsReportingTask micrometerMetricsReportingTask; | ||
|
|
||
| @PostConstruct | ||
| private void init() { | ||
| startReportingCamundaActuatorMetrics(); | ||
| } | ||
|
|
||
| private void startReportingCamundaActuatorMetrics() { | ||
| Timer timer = new Timer("Micrometer Camunda Metrics Reporter", true); | ||
| long reportingIntervalInMillis = camundaBpmProperties.getMetrics().getActuator().getInterval() * 1000L; | ||
| micrometerMetricsReportingTask = new MicrometerMetricsReportingTask(registry, processEngine); | ||
| timer.scheduleAtFixedRate(micrometerMetricsReportingTask, | ||
| reportingIntervalInMillis, | ||
| reportingIntervalInMillis); | ||
| } | ||
|
|
||
| public void reportNow() { | ||
| if(micrometerMetricsReportingTask != null) { | ||
| micrometerMetricsReportingTask.run(); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,41 @@ | ||||||
| package org.camunda.bpm.spring.boot.starter.actuator; | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing license header |
||||||
|
|
||||||
| import io.micrometer.core.instrument.Gauge; | ||||||
| import io.micrometer.core.instrument.MeterRegistry; | ||||||
| import org.camunda.bpm.engine.ProcessEngine; | ||||||
| import org.camunda.bpm.engine.management.MetricIntervalValue; | ||||||
| import java.util.HashMap; | ||||||
| import java.util.List; | ||||||
| import java.util.Map; | ||||||
| import java.util.TimerTask; | ||||||
|
|
||||||
|
|
||||||
|
Comment on lines
+11
to
+12
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| public class MicrometerMetricsReportingTask extends TimerTask { | ||||||
|
|
||||||
| private final MeterRegistry meterRegistry; | ||||||
| private final ProcessEngine processEngine; | ||||||
| Map<String, Long> metricsMap; | ||||||
|
|
||||||
| public MicrometerMetricsReportingTask(MeterRegistry registry, ProcessEngine processEngine) { | ||||||
| this.meterRegistry = registry; | ||||||
| this.processEngine = processEngine; | ||||||
| metricsMap = new HashMap<>(); | ||||||
| } | ||||||
|
|
||||||
| public void run() { | ||||||
| List<MetricIntervalValue> metricsList = processEngine.getManagementService() | ||||||
| .createMetricsQuery().interval(1); | ||||||
| if(metricsList.isEmpty()){ | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| return; | ||||||
| } | ||||||
| metricsList.forEach(metric ->{ | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| if(!metricsMap.containsKey(metric.getName())) { | ||||||
| Gauge.builder("camunda." + metric.getName(),metricsMap, map -> map.get(metric.getName())) | ||||||
| .register(meterRegistry); | ||||||
| } | ||||||
| metricsMap.put(metric.getName(), metric.getValue()); | ||||||
| }); | ||||||
| } | ||||||
|
|
||||||
| } | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| package org.camunda.bpm.spring.boot.starter.property; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing license header |
||
|
|
||
| import org.camunda.bpm.spring.boot.starter.util.SpringBootStarterException; | ||
|
|
||
| public class ActuatorProperty { | ||
|
|
||
| private Integer interval = -1; // Default value | ||
|
|
||
| public Integer getInterval() { | ||
| return interval; | ||
| } | ||
|
|
||
| public void setInterval(Integer interval) { | ||
| if (interval == null || interval < -1 || interval == 0) { | ||
| throw new SpringBootStarterException("Invalid value for camunda.bpm.metrics.actuator.interval: " + interval + | ||
| ". Value must be -1 or greater than 0."); | ||
| } | ||
| this.interval = interval; | ||
| } | ||
|
|
||
| @Override | ||
| public String toString() { | ||
| return "ActuatorProperty[interval=" + interval + "]"; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| package org.camunda.bpm.spring.boot.starter.actuator.micrometer.metrics; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing license header |
||
|
|
||
| import org.camunda.bpm.spring.boot.starter.test.nonpa.TestApplication; | ||
| import org.junit.Assert; | ||
| import org.junit.Test; | ||
| import org.junit.runner.RunWith; | ||
| import org.springframework.beans.factory.annotation.Autowired; | ||
| import org.springframework.boot.test.context.SpringBootTest; | ||
| import org.springframework.context.ApplicationContext; | ||
| import org.springframework.test.context.junit4.SpringRunner; | ||
|
|
||
| @RunWith(SpringRunner.class) | ||
| @SpringBootTest( | ||
| classes = {TestApplication.class}, | ||
| webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT | ||
| ) | ||
| public class ActuatorMetricsPropertyAbsentIT { | ||
|
|
||
| @Autowired | ||
| private ApplicationContext applicationContext; | ||
|
|
||
| @Test | ||
| public void actuatorMetricsNotCreated() { | ||
| //MicrometerMetricsReporter not created when camunda.bpm.metrics.actuator.interval is absent | ||
| Assert.assertFalse(applicationContext.containsBean("micrometerMetricsReporter")); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| package org.camunda.bpm.spring.boot.starter.actuator.micrometer.metrics; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing license header |
||
|
|
||
| import org.camunda.bpm.spring.boot.starter.test.nonpa.TestApplication; | ||
| import org.junit.Assert; | ||
| import org.junit.Test; | ||
| import org.junit.runner.RunWith; | ||
| import org.springframework.beans.factory.annotation.Autowired; | ||
| import org.springframework.boot.test.context.SpringBootTest; | ||
| import org.springframework.context.ApplicationContext; | ||
| import org.springframework.test.context.junit4.SpringRunner; | ||
|
|
||
| @RunWith(SpringRunner.class) | ||
| @SpringBootTest( | ||
| classes = {TestApplication.class}, | ||
| webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, | ||
| properties = { "camunda.bpm.metrics.actuator.interval=-1"} | ||
| ) | ||
| public class ActuatorMetricsPropertyDefaultIT { | ||
|
|
||
| @Autowired | ||
| private ApplicationContext applicationContext; | ||
|
|
||
| @Test | ||
| public void actuatorMetricsNotCreated() { | ||
| //MicrometerMetricsReporter not created when camunda.bpm.metrics.actuator.interval is -1. | ||
| Assert.assertFalse(applicationContext.containsBean("micrometerMetricsReporter")); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| package org.camunda.bpm.spring.boot.starter.actuator.micrometer.metrics; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing license header |
||
|
|
||
| import org.camunda.bpm.spring.boot.starter.test.nonpa.TestApplication; | ||
| import org.camunda.bpm.spring.boot.starter.util.SpringBootStarterException; | ||
| import org.junit.Assert; | ||
| import org.junit.Test; | ||
| import org.springframework.boot.SpringApplication; | ||
| import java.util.Map; | ||
|
|
||
| public class ActuatorMetricsPropertyInvalidIT { | ||
|
|
||
| @Test | ||
| public void shouldThrowSpringBootStarterExceptionWhenIntervalLessThanNegative1() { | ||
| startApplicationWithInvalidIntervalProperty(-2); | ||
| } | ||
|
|
||
| @Test | ||
| public void shouldThrowSpringBootStarterExceptionWhenIntervalIsZero() { | ||
| startApplicationWithInvalidIntervalProperty(0); | ||
| } | ||
|
|
||
| private void startApplicationWithInvalidIntervalProperty(int interval){ | ||
| SpringApplication app = new SpringApplication(TestApplication.class); | ||
| app.setDefaultProperties(Map.of("camunda.bpm.metrics.actuator.interval", interval)); | ||
| Exception exception = Assert.assertThrows(Exception.class, app::run); | ||
| Throwable rootCause = findRootCause(exception); | ||
| Assert.assertTrue(rootCause instanceof SpringBootStarterException); | ||
| Assert.assertEquals("Invalid value for camunda.bpm.metrics.actuator.interval: " + interval + ". Value must be -1 or greater than 0.", | ||
| rootCause.getMessage()); | ||
| } | ||
| private Throwable findRootCause(Throwable e) { | ||
| while (e.getCause() != null) { | ||
| e = e.getCause(); | ||
| } | ||
| return e; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| package org.camunda.bpm.spring.boot.starter.actuator.micrometer.metrics; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing license header |
||
|
|
||
| import org.camunda.bpm.engine.management.MetricIntervalValue; | ||
| import org.camunda.bpm.spring.boot.starter.AbstractCamundaAutoConfigurationIT; | ||
| import org.camunda.bpm.spring.boot.starter.actuator.MicrometerMetricsReporter; | ||
| import org.camunda.bpm.spring.boot.starter.test.nonpa.TestApplication; | ||
| import org.junit.Assert; | ||
| import org.junit.Test; | ||
| import org.junit.runner.RunWith; | ||
| import org.springframework.beans.factory.annotation.Autowired; | ||
| import org.springframework.boot.test.context.SpringBootTest; | ||
| import org.springframework.boot.test.web.client.TestRestTemplate; | ||
| import org.springframework.context.ApplicationContext; | ||
| import org.springframework.test.context.junit4.SpringRunner; | ||
| import java.util.List; | ||
| import java.util.Map; | ||
|
|
||
| @RunWith(SpringRunner.class) | ||
| @SpringBootTest( | ||
| classes = {TestApplication.class}, | ||
| webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, | ||
| properties = { "camunda.bpm.metrics.actuator.interval=1", "management.endpoints.web.exposure.include=metrics"} | ||
| ) | ||
| public class ActuatorMetricsPropertyValidIT extends AbstractCamundaAutoConfigurationIT { | ||
|
|
||
| @Autowired | ||
| private TestRestTemplate testRestTemplate; | ||
|
|
||
| @Autowired | ||
| MicrometerMetricsReporter micrometerMetricsReporter; | ||
|
|
||
| @Autowired | ||
| private ApplicationContext applicationContext; | ||
|
|
||
| @Test | ||
| public void actuatorMetricsCreated() { | ||
| //MicrometerMetricsReporter bean is created | ||
| Assert.assertTrue(applicationContext.containsBean("micrometerMetricsReporter")); | ||
|
|
||
| processEngine.getManagementService().reportDbMetricsNow(); | ||
| micrometerMetricsReporter.reportNow(); | ||
|
|
||
| Map<String, List<String>> response = testRestTemplate.getForEntity("/actuator/metrics", Map.class).getBody(); | ||
| List<String> metricNames = response.get("names"); | ||
| List<MetricIntervalValue> dbMetrics = processEngine.getManagementService().createMetricsQuery().interval(); | ||
| long actuatorMetricsLength = metricNames.stream().filter(metric->metric.startsWith("camunda.")).count(); | ||
|
|
||
| //Number of metrics from DB equals to metrics coming from actuator | ||
| Assert.assertEquals(actuatorMetricsLength, dbMetrics.size()); | ||
|
|
||
| for(MetricIntervalValue metric : dbMetrics) { | ||
| Map<String, Object> actuatorMetricResponse = testRestTemplate | ||
| .getForEntity("/actuator/metrics/camunda." + metric.getName(), Map.class) | ||
| .getBody(); | ||
|
|
||
| List<Map<String, Object>> measurements = (List<Map<String, Object>>) actuatorMetricResponse.get("measurements"); | ||
| Double actuatorValue = (Double) measurements.get(0).get("value"); | ||
|
|
||
| //Checking metric value | ||
| Assert.assertEquals(metric.getValue(), actuatorValue.longValue()); | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing license header