This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch fix/camel-tui-load-avg
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 327985259ca5a7217697780143ac386fd8abae88
Author: Claus Ibsen <[email protected]>
AuthorDate: Sun May 17 19:34:54 2026 +0200

    TUI: add CPU and inflight EWMA load averages to info panel
    
    Displays 1m/5m/15m exponentially weighted moving averages for both
    process CPU% (sampled via ProcessHandle.totalCpuDuration delta) and
    inflight exchange concurrency in the overview info panel, mirroring
    the Unix uptime load average convention used by Camel's own LoadTriplet.
    
    Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
---
 .../dsl/jbang/core/commands/tui/CamelMonitor.java  | 73 ++++++++++++++++++++++
 1 file changed, 73 insertions(+)

diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelMonitor.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelMonitor.java
index ab3a259ca40d..5cc05f6bf56b 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelMonitor.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelMonitor.java
@@ -21,6 +21,7 @@ import java.io.RandomAccessFile;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
+import java.time.Duration;
 import java.time.Instant;
 import java.time.ZoneId;
 import java.util.ArrayList;
@@ -31,6 +32,7 @@ import java.util.LinkedHashSet;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -182,6 +184,11 @@ public class CamelMonitor extends CamelCommand {
     private final Map<String, LinkedList<long[]>> endpointRemoteSamples = new 
ConcurrentHashMap<>();
     private final Map<String, Long> previousEndpointRemoteTime = new 
ConcurrentHashMap<>();
 
+    // Load averages (EWMA) — CPU% and inflight exchanges, per PID
+    private final Map<String, LoadAvg> cpuLoadAvg = new ConcurrentHashMap<>();
+    private final Map<String, LoadAvg> inflightLoadAvg = new 
ConcurrentHashMap<>();
+    private final Map<String, long[]> prevCpuSample = new 
ConcurrentHashMap<>();
+
     // Overview sort state
     private String overviewSort = "name";
     private int overviewSortIndex = 1;
@@ -1456,6 +1463,22 @@ public class CamelMonitor extends CamelCommand {
                         Span.styled("Thds: ", dim),
                         Span.raw(sel.threadCount + " / " + 
sel.peakThreadCount)));
             }
+            LoadAvg cpu = cpuLoadAvg.get(sel.pid);
+            LoadAvg infl = inflightLoadAvg.get(sel.pid);
+            if (cpu != null || infl != null) {
+                lines.add(Line.from(Span.raw("")));
+                lines.add(Line.from(Span.styled("Load (1m/5m/15m):", dim)));
+                if (cpu != null) {
+                    lines.add(Line.from(
+                            Span.styled("CPU:  ", dim),
+                            Span.raw(cpu.format("%.1f / %.1f / %.1f %%"))));
+                }
+                if (infl != null) {
+                    lines.add(Line.from(
+                            Span.styled("Infl: ", dim),
+                            Span.raw(infl.format("%.1f / %.1f / %.1f"))));
+                }
+            }
         } else {
             lines.add(Line.from(Span.raw("-")));
         }
@@ -4110,6 +4133,7 @@ public class CamelMonitor extends CamelCommand {
                                 infos.add(info);
                                 updateThroughputHistory(info);
                                 updateEndpointHistory(info);
+                                updateLoadMetrics(ph, info);
                             }
                         }
                     });
@@ -4140,6 +4164,9 @@ public class CamelMonitor extends CamelCommand {
                     endpointRemoteOutHistory.remove(entry.getKey());
                     endpointRemoteSamples.remove(entry.getKey());
                     previousEndpointRemoteTime.remove(entry.getKey());
+                    cpuLoadAvg.remove(entry.getKey());
+                    inflightLoadAvg.remove(entry.getKey());
+                    prevCpuSample.remove(entry.getKey());
                 } else if (!livePids.contains(entry.getKey())) {
                     IntegrationInfo ghost = entry.getValue().info;
                     ghost.vanishing = true;
@@ -4313,6 +4340,30 @@ public class CamelMonitor extends CamelCommand {
         traces.set(allTraces);
     }
 
+    private void updateLoadMetrics(ProcessHandle ph, IntegrationInfo info) {
+        String pid = info.pid;
+
+        // Inflight EWMA — feed current inflight count directly
+        inflightLoadAvg.computeIfAbsent(pid, k -> new 
LoadAvg()).update(info.inflight);
+
+        // CPU EWMA — compute % from ProcessHandle CPU duration delta
+        Optional<Duration> durOpt = ph.info().totalCpuDuration();
+        if (durOpt.isPresent()) {
+            long cpuNanos = durOpt.get().toNanos();
+            long wallMs = System.currentTimeMillis();
+            long[] prev = prevCpuSample.get(pid);
+            if (prev != null) {
+                long deltaCpuNanos = cpuNanos - prev[0];
+                long deltaWallNanos = (wallMs - prev[1]) * 1_000_000L;
+                if (deltaWallNanos > 0) {
+                    double cpuPct = (double) deltaCpuNanos / deltaWallNanos * 
100.0;
+                    cpuLoadAvg.computeIfAbsent(pid, k -> new 
LoadAvg()).update(Math.max(0, cpuPct));
+                }
+            }
+            prevCpuSample.put(pid, new long[] { cpuNanos, wallMs });
+        }
+    }
+
     @SuppressWarnings("unchecked")
     private void readTraceFile(String pid, List<TraceEntry> allTraces) {
         Path traceFile = CommandLineHelper.getCamelDir().resolve(pid + 
"-trace.json");
@@ -5070,6 +5121,28 @@ public class CamelMonitor extends CamelCommand {
         return TuiHelper.objToLong(o);
     }
 
+    // ---- Load Average ----
+
+    private static class LoadAvg {
+        private static final double EXP_1 = Math.exp(-1 / 60.0);
+        private static final double EXP_5 = Math.exp(-1 / (60.0 * 5.0));
+        private static final double EXP_15 = Math.exp(-1 / (60.0 * 15.0));
+
+        private double load1 = Double.NaN;
+        private double load5 = Double.NaN;
+        private double load15 = Double.NaN;
+
+        synchronized void update(double value) {
+            load1 = Double.isNaN(load1) ? value : value + EXP_1 * (load1 - 
value);
+            load5 = Double.isNaN(load5) ? value : value + EXP_5 * (load5 - 
value);
+            load15 = Double.isNaN(load15) ? value : value + EXP_15 * (load15 - 
value);
+        }
+
+        synchronized String format(String fmt) {
+            return Double.isNaN(load1) ? "-" : String.format(fmt, load1, 
load5, load15);
+        }
+    }
+
     // ---- Data Classes ----
 
     static class IntegrationInfo {

Reply via email to