No-Cost RHEL Developer Subscription now available

Since version 7.4, Red Hat Process Automation Manager (RHPAM) and Red Hat Decision Manager (RHDM) provide support for monitoring various metrics through Prometheus. The platform's Execution Server (also known as KIE-Server) provides a Prometheus extension which, if enabled, exposes metrics like the number of containers deployed, the response time of rule and decision evaluations, etc.

In this example, we will use the RHDM 7 Insurance Pricing Demo as our base project. This demo contains a simple Decision Model and Notation (DMN) model that calculates the insurance price based on the age of the customer and whether the customer had previous incidents. The GitHub repository for this demo can be found here and contains detailed instructions on how to set up and run the demo.

The KIE-Server Prometheus extension

By default, the KIE-Server Prometheus extension is disabled. This extension can be enabled by setting the Java System Property org.kie.prometheus.server.ext.disabled to false. This task can, for example, be done when starting the Red Hat JBoss Enterprise Application Platform (JBoss EAP) server:

$ ./standalone.sh -Dorg.kie.prometheus.server.ext.disabled=false

Or, the extension can be configured in the <system-properties> section of the JBoss EAP server configuration file (e.g. standalone.xml):

<property name="org.kie.prometheus.server.ext.disabled" value="false"/>

When the extension is enabled, it provides a number of out-of-the-box metrics for Drools (both rules and DMN decisions), OptaPlanner, and jBPM deployments. The metrics are available at the URL: http://<host>:<port>/kie-server/services/rest/metrics.

This image shows the metrics in a vanilla Red Hat Decision Manager 7.5 Execution Server, with a single DMN project (KJAR) deployed, after a single DMN evaluation:

Example metrics output

We can see that KIE-Server provides out-of-the-box metrics for, for example, the response time of the DMN evaluations. Depending on the type of deployment (e.g., DMN, Drools, jBPM), the Prometheus extension enables different metrics.

Custom metrics: The KIE-Server PrometheusMetricsProvider

The Prometheus extension of KIE-Server has a pluggable architecture, so users of the platform to write their own extensions. This allows us to write extensions that collect domain-specific metrics of our decision models, rules, and processes. For example, we can write an extension that provides metrics about the actual decisions that a DMN decision node takes, such as the price of insurance. Say that the price of insurance is between $1,000 and $3,000. We can define a Prometheus KIE-Server Extension that creates a Prometheus histogram that allows us to keep track of the distribution of insurance prices.

To implement custom Prometheus metrics, the extension provides the PrometheusMetricsProvider interface. This interface defines methods that allow you to return various Drools, DMN, OptaPlanner, and jBPM EventListeners. When the custom PrometheusMetricsProvider is registered, the extension registers the EventListeners dynamically with the various runtimes.

Your custom PrometheusMetricsProvider is registered via the Java ServiceLoader mechanism. For example, a file with the name org.kie.server.services.prometheus.PrometheusMetricsProvider must be placed in your project's /META-INF/services folder. The content of this file is the fully qualified class name of your PrometheusMetricsProvider implementation. When you place your PrometheusMetricsProvider's JAR file in the WEB-INF/lib folder of the KIE-Server, the provider is automatically discovered and registered.

Your first KIE-Server PrometheusMetricsProvider

Let's write our first provider, which offers simple metrics for our Red Hat Decision Manager Insurance Pricing DMN demo that monitors the distribution of insurance prices determined by the DMN model. First, we create a simple Java Maven project. We use the Maven archetype approach to scaffold a new project:

$ mvn archetype:generate -DgroupId=com.redhat.demos.dm -DartifactId=insurance-pricing-dmn-prometheus-provider -Dversion=1.0.0 -DarchetypeArtifactId=maven-archetype-quickstart -DinteractiveMode=false

To the pom.xml file, we add a single dependency (the version to be used depends on the version of your Red Hat Decision Manager or Red Hat Process Automation Manager installation):

<dependency>
  <groupId>org.kie.server</groupId>
  <artifactId>kie-server-services-prometheus</artifactId>
  <version>7.27.0.Final</version>
  <scope>provided</scope>
</dependency>

We also need to add a build section to our pom.xml file to set the Java source and target version:

<build>
  <plugins>
    <plugin>
      <artifactId>maven-compiler-plugin</artifactId>
      <version>3.1</version>
      <configuration>
        <source>1.8</source>
        <target>1.8</target>
      </configuration>
    </plugin>
  </plugins>
</build>

In your project's src/main/java directory, create a Java package with the name com.redhat.demos.dm.prometheus. In this package, create a class with the name InsurancePricingPrometheusMetricsProvider.java. This class must implement the PrometheusMetricsProvider interface.

Provide a default implementation for the methods:

public class InsurancePricingPrometheusMetricsProvider implements PrometheusMetricsProvider {

  public AgendaEventListener createAgendaEventListener(String arg0, KieContainerInstance arg1) {
    return null;
  }

  public AsynchronousJobListener createAsynchronousJobListener() {
    return null;
  }

  public DMNRuntimeEventListener createDMNRuntimeEventListener(KieContainerInstance arg0) {
    return null;
  }

  public DeploymentEventListener createDeploymentEventListener() {
    return null;
  }

  public PhaseLifecycleListener createPhaseLifecycleListener(String arg0) {
    return null;
  }
}

We now define our DMNListener, which will react to events in our DMN engine and collect the Prometheus metrics. In the same package, create a Java class with the name InsurancePricingDMNListener.java. We'll make this class implement the DMNRuntimeEventListener interface:

public class InsurancePricingDMNListener implements DMNRuntimeEventListener {
}

In our InsurancePricingPrometheusMetricsProvider, we can now implement the createDMNRuntimeEventListener method and have it return the DMNRuntimeEventListener implementation we've just created:

public DMNRuntimeEventListener createDMNRuntimeEventListener(KieContainerInstance kci) {
  return new InsurancePricingDMNListener();
}

In our InsurancePricingDMNListener, we define a Histogram in which we will collect our insurance pricings:

private static final Histogram insurancePricing = Histogram.build()
                                                           .name("insurance_pricing")
                                                           .help("Insurance Pricing")
                                                           .labelNames("decision_namespace", "decision_name")
                                                           .buckets(1000, 2000, 3000)
                                                           .register();

Note that in this example, we define our Histogram as a static class member. The reason for this is that the Prometheus Java client only allows you to register a single collector under a given name. Another option would have been to register the collector with a dynamic name. For example, one that depends on the Maven groupId, artifactId, and version of the KieContainerInstance for which we create the collector.

With our Histogram defined, we can now implement the afterEvaluateDecision of the DMNRuntimeEventListener, in which we can retrieve the outcome of the decision and add it to our Prometheus collector:

public void afterEvaluateDecision(AfterEvaluateDecisionEvent event) {
  DecisionNode decisionNode = event.getDecision();
  String decisionNodeName = decisionNode.getName();
  DMNDecisionResult result = event.getResult().getDecisionResultByName(decisionNodeName);
 
  switch (decisionNodeName) {
    case "Insurance Total Price":
      double resultAsDouble = 0.0;
      Object resultAsObject = result.getResult();
      //We only store if we find and Integer.
      if (resultAsObject instanceof Integer) {
        resultAsDouble = ((Integer) resultAsObject).doubleValue();
        insurancePricing.labels(decisionNode.getModelName(), decisionNode.getModelNamespace()).observe(resultAsDouble);
      }
      break;
    default:
      //Not the decision we want to monitor. Discarding.
      break;
  }
}

What we do in this code is determine the name of the decision that has been taken, and if the name is equal to "Insurance Price Total," add the result of the decision (the insurance price) to the Prometheus histogram.

The finished project can be found here.

Package and deploy the PrometheusMetricsProvider

To deploy our PrometheusMetricsProvider, we first need to package it in a JAR file. We use Maven to do this:

$ mvn clean install

This command will create the insurance-pricing-dmn-prometheus-provider-1.0.0.jar file in the project's target directory. We can now copy this JAR file into the WEB-INF/lib directory of the RHPAM/RHDM Execution Server/KIE-Server.

With our PrometheusMetricsProvider deployed, we can now start the platform with the Prometheus extension enabled:

$ ./target/jboss-eap-7.2/bin/standalone.sh -Dorg.kie.prometheus.server.ext.disabled=false

Run the demo

With your PrometheusMetricsProvider installed and Red Hat Decision Manager running, we can now test our metrics. Consult the demo guide in the Insurance Pricing DMN Demo README to deploy the DMN model to the execution server and execute a request.

When you now consult the Prometheus metrics page in KIE-Server, you can see the following metrics, which record the distribution of the insurance prices determined by the DMN model:

The resulting metrics.

Conclusion

We've now seen how Red Hat Decision Manager and Red Hat Process Automation Manager provide support for Prometheus through the new KIE-Server extension. The extension uses a pluggable architecture, which allows us to plug in custom PrometheusMetricsProviders. These metric providers can augment the out-of-the-box Prometheus metrics with custom, domain-specific, metrics.

 

Last updated: January 22, 2024