Skip to content

Commit

Permalink
Close camunda-community-hub#83: provide camunda metrics
Browse files Browse the repository at this point in the history
  • Loading branch information
arolfes committed Mar 12, 2021
1 parent 9f33f42 commit 5d4bd5a
Show file tree
Hide file tree
Showing 13 changed files with 735 additions and 0 deletions.
7 changes: 7 additions & 0 deletions micronaut-camunda-bpm-example/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ micronaut {

dependencies {
implementation(project(":micronaut-camunda-bpm-feature"))

// imported to enable http endpoint for metrics
implementation("io.micronaut:micronaut-management")

// imported to activate metrics
implementation("io.micronaut.micrometer:micronaut-micrometer-core")

runtimeOnly("com.h2database:h2")
runtimeOnly("ch.qos.logback:logback-classic")

Expand Down
16 changes: 16 additions & 0 deletions micronaut-camunda-bpm-example/src/main/resources/application.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
micronaut:
application:
name: micronaut-camunda-example
metrics:
export:
logging:
enabled: true
binders:
camunda:
enabled: true
bpmnExecution:
enabled: true
dmnExecution:
enabled: true
jobExecutor:
enabled: true
historyCleanUp:
enabled: true

camunda:
admin-user:
id: admin
Expand Down
1 change: 1 addition & 0 deletions micronaut-camunda-bpm-feature/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ dependencies {
implementation("io.micronaut:micronaut-runtime")
api("org.camunda.bpm:camunda-engine:$camundaVersion")

compileOnly("io.micronaut.micrometer:micronaut-micrometer-core")
compileOnly("io.micronaut.servlet:micronaut-servlet-engine")
compileOnly("io.micronaut:micronaut-http-server-netty")
compileOnly("io.micronaut.servlet:micronaut-http-server-jetty")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright 2021 original authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package info.novatec.micronaut.camunda.bpm.feature.metrics;

import io.micronaut.configuration.metrics.annotation.RequiresMetrics;
import io.micronaut.context.BeanProvider;
import io.micronaut.context.annotation.Requires;
import io.micronaut.core.util.StringUtils;
import org.camunda.bpm.engine.ManagementService;
import org.camunda.bpm.engine.management.Metrics;

import javax.inject.Singleton;

import static io.micronaut.configuration.metrics.micrometer.MeterRegistryFactory.MICRONAUT_METRICS_BINDERS;

/**
* Holder class to count bpmn execution metrics.
* <p>
* It is not possible to execute the following code directly in BpmnExectionMetricsBinder
* <code>
* Gauge.builder("MyMetricName", providerManagementService.get().createMetricsQuery().name(Metrics.ROOT_PROCESS_INSTANCE_START), MetricsQuery::sum).register(...)
* </code>
* therefore we have this HolderClass.
*/
@Singleton
@RequiresMetrics
@Requires(property = MICRONAUT_METRICS_BINDERS + ".camunda.enabled", value = StringUtils.TRUE, defaultValue = StringUtils.FALSE)
@Requires(property = MICRONAUT_METRICS_BINDERS + ".camunda.bpmnExecution.enabled", value = StringUtils.TRUE, defaultValue = StringUtils.FALSE)
public class BpmnExecutionMetrics extends CamundaMetrics {

public BpmnExecutionMetrics(BeanProvider<ManagementService> providerManagementService) {
super(providerManagementService);
}

public long countRootProcessInstanceStart() {
return sumMetric(Metrics.ROOT_PROCESS_INSTANCE_START);
}

public long countActivityInstanceStart() {
return sumMetric(Metrics.ACTIVTY_INSTANCE_START);
}

public long countActivityInstanceEnd() {
return sumMetric(Metrics.ACTIVTY_INSTANCE_END);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* Copyright 2021 original authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package info.novatec.micronaut.camunda.bpm.feature.metrics;

import io.micrometer.core.instrument.Gauge;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tags;
import io.micrometer.core.instrument.binder.MeterBinder;
import io.micrometer.core.lang.NonNull;
import io.micronaut.configuration.metrics.annotation.RequiresMetrics;
import io.micronaut.context.annotation.Requires;
import io.micronaut.core.util.StringUtils;
import org.camunda.bpm.engine.management.Metrics;

import javax.inject.Singleton;

import static info.novatec.micronaut.camunda.bpm.feature.metrics.CamundaMetricsBinderTags.BPMN_EXECUTION_DEFAULT_TAGS;
import static info.novatec.micronaut.camunda.bpm.feature.metrics.CamundaMetricsBinderTags.TAG_KEY_NAME;
import static io.micronaut.configuration.metrics.micrometer.MeterRegistryFactory.MICRONAUT_METRICS_BINDERS;

/**
* Provides Metrics about BPMN Execution.
*/
@Singleton
@RequiresMetrics
@Requires(property = MICRONAUT_METRICS_BINDERS + ".camunda.enabled", value = StringUtils.TRUE, defaultValue = StringUtils.FALSE)
@Requires(property = MICRONAUT_METRICS_BINDERS + ".camunda.bpmnExecution.enabled", value = StringUtils.TRUE, defaultValue = StringUtils.FALSE)
public class BpmnExecutionMetricsBinder implements MeterBinder {

protected final BpmnExecutionMetrics bpmnExecutionMetrics;

public BpmnExecutionMetricsBinder(BpmnExecutionMetrics bpmnExecutionMetrics) {
this.bpmnExecutionMetrics = bpmnExecutionMetrics;
}

@Override
public void bindTo(@NonNull MeterRegistry registry) {

/*
This Code
Gauge.builder("camunda.bpmn." + Metrics.ROOT_PROCESS_INSTANCE_START.replace('-', '.'),
providerManagementService.get().createMetricsQuery().name(Metrics.ROOT_PROCESS_INSTANCE_START),
MetricsQuery::sum
).description("The number of root process instance executions started. This is also known as Root Process Instances (RPI). A root process instance has no parent process instance, i.e. it is a top-level execution.")
.tags(Tags.concat(BPMN_EXECUTION_DEFAULT_TAGS, TAG_KEY_NAME, Metrics.ROOT_PROCESS_INSTANCE_START))
.register(registry);
is executed to early and the bean for providerManagementService is not instanciated
so that we would run into a boot loop and then the application crashes.
To avoid this, we implemented some "holder" objects. In this case info.novatec.micronaut.camunda.bpm.feature.metrics.BpmnExecutionMetrics
*/
Gauge.builder(
createMetricName(Metrics.ROOT_PROCESS_INSTANCE_START),
bpmnExecutionMetrics,
BpmnExecutionMetrics::countRootProcessInstanceStart
)
.description("The number of root process instance executions started. This is also known as Root Process Instances (RPI). A root process instance has no parent process instance, i.e. it is a top-level execution.")
.tags(Tags.concat(BPMN_EXECUTION_DEFAULT_TAGS, TAG_KEY_NAME, Metrics.ROOT_PROCESS_INSTANCE_START))
.register(registry);

Gauge.builder(
createMetricName(Metrics.ACTIVTY_INSTANCE_START),
bpmnExecutionMetrics,
BpmnExecutionMetrics::countActivityInstanceStart
)
.description("The number of activity instances started. This is also known as flow node instances (FNI).")
.tags(Tags.concat(BPMN_EXECUTION_DEFAULT_TAGS, TAG_KEY_NAME, Metrics.ACTIVTY_INSTANCE_START))
.register(registry);

Gauge.builder(
createMetricName(Metrics.ACTIVTY_INSTANCE_END),
bpmnExecutionMetrics,
BpmnExecutionMetrics::countActivityInstanceEnd
)
.description("The number of activity instances ended.")
.tags(Tags.concat(BPMN_EXECUTION_DEFAULT_TAGS, TAG_KEY_NAME, Metrics.ACTIVTY_INSTANCE_END))
.register(registry);
}

private String createMetricName(String camundaName) {
return "camunda.bpmn." + camundaName.replace('-', '.');
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright 2021 original authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package info.novatec.micronaut.camunda.bpm.feature.metrics;

import io.micronaut.context.BeanProvider;
import io.micronaut.context.exceptions.BeanInstantiationException;
import org.camunda.bpm.engine.ManagementService;
import org.camunda.bpm.engine.management.MetricsQuery;

import java.util.HashMap;
import java.util.Map;

/**
* Base class to use inbuild camunda metrics.
* see also https://docs.camunda.org/manual/latest/user-guide/process-engine/metrics/
*/
abstract public class CamundaMetrics {

protected final BeanProvider<ManagementService> providerManagementService;

protected final Map<String, MetricsQuery> metricsQueryMap = new HashMap<>();

protected CamundaMetrics(BeanProvider<ManagementService> providerManagementService) {
this.providerManagementService = providerManagementService;
}

protected long sumMetric(String metricName) {
MetricsQuery metricsQuery = getMetricsQuery(metricName);
if (metricsQuery != null) {
return metricsQuery.sum();
}
throw new BeanInstantiationException("Camunda MetricsQuery is null");
}

protected MetricsQuery getMetricsQuery(String metricName) {
if (metricsQueryMap.containsKey(metricName)) {
return metricsQueryMap.get(metricName);
}
MetricsQuery metricsQuery = providerManagementService.get().createMetricsQuery().name(metricName);
metricsQueryMap.put(metricName, metricsQuery); // does not return the initialized metrics query
return metricsQuery;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright 2021 original authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package info.novatec.micronaut.camunda.bpm.feature.metrics;

import io.micrometer.core.instrument.Tag;

import java.util.Arrays;

/**
* Constants for our MetricsBinders.
*/
public final class CamundaMetricsBinderTags {

static final String TAG_KEY_NAME = "name";

static final String TAG_KEY_CATEGORY = "category";

static final Tag TAG_CLASS_CAMUNDA = Tag.of("class", "camunda");

static final Iterable<Tag> JOB_EXECUTOR_DEFAULT_TAGS = Arrays.asList(TAG_CLASS_CAMUNDA, Tag.of(TAG_KEY_CATEGORY, "job-executor"));

static final Iterable<Tag> DMN_EXECUTION_DEFAULT_TAGS = Arrays.asList(TAG_CLASS_CAMUNDA, Tag.of(TAG_KEY_CATEGORY, "dmn-execution"));

static final Iterable<Tag> BPMN_EXECUTION_DEFAULT_TAGS = Arrays.asList(TAG_CLASS_CAMUNDA, Tag.of(TAG_KEY_CATEGORY, "bpmn-execution"));

static final Iterable<Tag> HISTORY_CLEAN_UP_DEFAULT_TAGS = Arrays.asList(TAG_CLASS_CAMUNDA, Tag.of(TAG_KEY_CATEGORY, "history-clean-up"));

private CamundaMetricsBinderTags() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright 2021 original authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package info.novatec.micronaut.camunda.bpm.feature.metrics;

import io.micronaut.configuration.metrics.annotation.RequiresMetrics;
import io.micronaut.context.BeanProvider;
import io.micronaut.context.annotation.Requires;
import io.micronaut.core.util.StringUtils;
import org.camunda.bpm.engine.ManagementService;
import org.camunda.bpm.engine.management.Metrics;

import javax.inject.Singleton;

import static io.micronaut.configuration.metrics.micrometer.MeterRegistryFactory.MICRONAUT_METRICS_BINDERS;

/**
* Holder class to count dmn execution metrics.
*/
@Singleton
@RequiresMetrics
@Requires(property = MICRONAUT_METRICS_BINDERS + ".camunda.enabled", value = StringUtils.TRUE, defaultValue = StringUtils.FALSE)
@Requires(property = MICRONAUT_METRICS_BINDERS + ".camunda.dmnExecution.enabled", value = StringUtils.TRUE, defaultValue = StringUtils.FALSE)
public class DmnExecutionMetrics extends CamundaMetrics {

public DmnExecutionMetrics(BeanProvider<ManagementService> providerManagementService) {
super(providerManagementService);
}

public long countExecutedDecisionInstances() {
return sumMetric(Metrics.EXECUTED_DECISION_INSTANCES);
}

public long countExecutedDecisionElements() {
return sumMetric(Metrics.EXECUTED_DECISION_ELEMENTS);
}

}
Loading

0 comments on commit 5d4bd5a

Please sign in to comment.