// This file was generated by Mendix Studio Pro.
//
// WARNING: Only the following code will be retained when actions are regenerated:
// - the import list
// - the code between BEGIN USER CODE and END USER CODE
// - the code between BEGIN EXTRA CODE and END EXTRA CODE
// Other code you write will be lost the next time you deploy the project.
// Special characters, e.g., é, ö, à, etc. are supported in comments.
package sitappmetrics.actions;
import com.mendix.systemwideinterfaces.core.IContext;
import com.mendix.systemwideinterfaces.core.UserAction;
import com.mendix.core.Core;
import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.lang.management.ManagementFactory;
import java.util.Set;
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
import com.mendix.systemwideinterfaces.core.IMendixObject;
public class JA_MetricLIst_Create extends UserAction<java.util.List<IMendixObject>>
{
public JA_MetricLIst_Create(IContext context)
{
super(context);
}
@java.lang.Override
public java.util.List<IMendixObject> executeAction() throws Exception
{
// BEGIN USER CODE
List<IMendixObject> returnList = new ArrayList<IMendixObject>();
try {
// 1. SETUP LOGGING
// We log to the Mendix Console so we can see when the action starts.
Core.getLogger("DB_METRICS").info("--- SNAPSHOT WITH UNITS ---");
// 2. CONNECT TO JAVA INTERNALS (JMX)
// We get the MBeanServer, which is like a dashboard for the Java Virtual
// Machine.
// It holds all the live statistics (memory, CPU, database connections).
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
// 3. QUERY MENDIX METRICS
// We ask for all metrics that start with "Mendix:".
// "Mendix:*" is a wildcard query.
Set<ObjectName> unsortedNames = mbs.queryNames(new ObjectName("Mendix:*"), null);
// Convert Set to List so we can sort them alphabetically (easier to read in
// logs).
List<ObjectName> sortedNames = new ArrayList<>(unsortedNames);
Collections.sort(sortedNames, (n1, n2) -> n1.toString().compareTo(n2.toString()));
// 4. ITERATE THROUGH EVERY METRIC FOUND
for (ObjectName name : sortedNames) {
Object value = "Unknown";
String nameStr = name.toString();
// 5. EXTRACT THE VALUE
// JMX metrics are not consistent. Some use the attribute "Value", others use
// "Count".
// We try "Value" first; if that fails, we try "Count".
try {
value = mbs.getAttribute(name, "Value");
} catch (javax.management.AttributeNotFoundException e1) {
try {
value = mbs.getAttribute(name, "Count");
} catch (javax.management.AttributeNotFoundException e2) {
// If neither exists, it might be a complex object we can't read easily.
value = "[Complex]";
}
}
// 6. GUESS THE UNIT (HEURISTICS)
// The raw data doesn't tell us if it is Bytes, Seconds, or a Count.
// We look at the name of the metric to guess the unit.
String unit = "";
String lowerName = nameStr.toLowerCase();
if (lowerName.contains("byte") || lowerName.contains("memory")) {
unit = "[Bytes]";
} else if (lowerName.contains("time")) {
// Mendix/Micrometer usually reports time in Nanoseconds
unit = "[Nanoseconds]";
} else if (lowerName.contains("usage") || lowerName.contains("load")) {
// Usage is usually a ratio between 0.0 and 1.0 (e.g. 0.5 = 50%)
unit = "[0.0 - 1.0 Ratio]";
} else if (lowerName.contains("threads")) {
unit = "[Threads]";
} else if (lowerName.contains("connections") || lowerName.contains("numactive")) {
unit = "[Connections]";
} else if (lowerName.contains("requests") || lowerName.contains("selects")
|| lowerName.contains("inserts")) {
unit = "[Count]";
}
// 7. PREPARE DATA FOR MENDIX
// Clean up the name string for readability
String shortName = nameStr.replace("Mendix:name=", "");
// Log it to the console
Core.getLogger("DB_METRICS").info(shortName + " = " + value + " " + unit);
// 8. CALL MICROFLOW TO SAVE DATA
// We call a Mendix Microflow ("SUB_Metric_Create") to actually save this data
// into an Entity.
// We pass the Name, the Value, and the Unit we determined above.
IMendixObject metricObject = Core.microflowCall("SITAppMetrics.SUB_Metric_Create")
.withParam("FullName", shortName)
.withParam("Value", value.toString()) // Ensure we pass a String if the MF expects a String
.withParam("TypeName", unit)
.execute(this.getContext()); // FIXED: Added parentheses ()
// Add the result to our list to return at the end
returnList.add(metricObject);
}
Core.getLogger("DB_METRICS").info("--- END SNAPSHOT ---");
} catch (Exception e) {
// If anything crashes, log the error so the app doesn't break silently.
Core.getLogger("DB_METRICS").error("Error generating snapshot", e);
}
return returnList;
// END USER CODE
}
/**
* Returns a string representation of this action
* @return a string representation of this action
*/
@java.lang.Override
public java.lang.String toString()
{
return "JA_MetricLIst_Create";
}
// BEGIN EXTRA CODE
// END EXTRA CODE
}
You may want to try this java action to create an object for each metric. This uses MBeanServer, which is like a dashboard for the Java Virtual Machine.
Hi Lim,
You can also look into Micrometer-based metrics, which Mendix supports out of the box. Mendix can expose runtime metrics (for example Jetty metrics and database connection pool metrics) via Micrometer. When the Prometheus registry is enabled, these metrics are exposed on the /prometheus endpoint of the Mendix admin port.
This is the recommended and supported way to access technical metrics such as active threads, request handling, and DB pool usage in non-Mendix Cloud setups. Typically, these metrics are consumed by external monitoring tools like Prometheus and Grafana, but they can also be queried programmatically if needed.