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

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/main by this push:
     new 8955af9d7a18 CAMEL-23385: camel-diagram - Show live counters (#23094)
8955af9d7a18 is described below

commit 8955af9d7a1854f52c0553815702e8f5b894ebc9
Author: Claus Ibsen <[email protected]>
AuthorDate: Sun May 10 20:00:16 2026 +0200

    CAMEL-23385: camel-diagram - Show live counters (#23094)
    
    * CAMEL-23385: camel-diagram - Show live counters
---
 .../camel/diagram/DefaultRouteDiagramDumper.java   | 15 ++--
 .../apache/camel/diagram/DiagramDevConsole.java    | 11 ++-
 .../camel/diagram/RouteDiagramLayoutEngine.java    | 12 ++--
 .../apache/camel/diagram/RouteDiagramRenderer.java | 71 +++++++++++++++++--
 .../org/apache/camel/diagram/RouteDiagramTest.java |  3 +-
 .../org/apache/camel/spi/RouteDiagramDumper.java   | 26 ++++++-
 .../camel-jbang-cmd-route-diagram.adoc             |  2 +-
 .../META-INF/camel-jbang-commands-metadata.json    |  2 +-
 .../commands/action/CamelRouteDiagramAction.java   | 79 ++++++++++++----------
 9 files changed, 158 insertions(+), 63 deletions(-)

diff --git 
a/components/camel-diagram/src/main/java/org/apache/camel/diagram/DefaultRouteDiagramDumper.java
 
b/components/camel-diagram/src/main/java/org/apache/camel/diagram/DefaultRouteDiagramDumper.java
index 97a78d434494..364d579cf7dd 100644
--- 
a/components/camel-diagram/src/main/java/org/apache/camel/diagram/DefaultRouteDiagramDumper.java
+++ 
b/components/camel-diagram/src/main/java/org/apache/camel/diagram/DefaultRouteDiagramDumper.java
@@ -96,7 +96,7 @@ public class DefaultRouteDiagramDumper extends ServiceSupport 
implements CamelCo
             root = (JsonObject) dc.call(DevConsole.MediaType.JSON, 
Map.of("filter", filter));
             var routes = RouteDiagramHelper.parseRoutes(root);
 
-            BufferedImage image = renderImage(routes, theme.name(), 12, 180, 
"CODE");
+            BufferedImage image = renderImage(routes, theme.name(), 12, 180, 
"CODE", false);
             folder.mkdirs();
             String name = RouteDiagramHelper.extractSourceName(group);
             File f = new File(folder, name + ".png");
@@ -105,13 +105,15 @@ public class DefaultRouteDiagramDumper extends 
ServiceSupport implements CamelCo
     }
 
     @Override
-    public BufferedImage dumpRoutesAsImage(String filter, Theme theme, 
NodeLabelMode nodeLabel, int nodeWidth, int fontSize) {
+    public BufferedImage dumpRoutesAsImage(
+            String filter, Theme theme, boolean metrics, NodeLabelMode 
nodeLabel, int nodeWidth, int fontSize) {
         // use dev-console to render the route structure in json format which 
the diagram render expects
         DevConsole dc = 
getCamelContext().getCamelContextExtension().getContextPlugin(DevConsoleRegistry.class)
                 .resolveById("route-structure");
-        JsonObject root = (JsonObject) dc.call(DevConsole.MediaType.JSON, 
Map.of("filter", filter));
+        JsonObject root
+                = (JsonObject) dc.call(DevConsole.MediaType.JSON, 
Map.of("filter", filter, "metric", String.valueOf(metrics)));
         var routes = RouteDiagramHelper.parseRoutes(root);
-        return renderImage(routes, theme.name(), fontSize, nodeWidth, 
nodeLabel.name());
+        return renderImage(routes, theme.name(), fontSize, nodeWidth, 
nodeLabel.name(), metrics);
     }
 
     @Override
@@ -128,9 +130,10 @@ public class DefaultRouteDiagramDumper extends 
ServiceSupport implements CamelCo
     }
 
     private static BufferedImage renderImage(
-            List<RouteDiagramLayoutEngine.RouteInfo> routes, String theme, int 
fontSize, int nodeWidth, String nodeLabel) {
+            List<RouteDiagramLayoutEngine.RouteInfo> routes, String theme, int 
fontSize, int nodeWidth,
+            String nodeLabel, boolean metrics) {
         RouteDiagramRenderer renderer = new RouteDiagramRenderer(
-                nodeWidth * RouteDiagramLayoutEngine.SCALE, fontSize * 
RouteDiagramLayoutEngine.SCALE);
+                nodeWidth * RouteDiagramLayoutEngine.SCALE, fontSize * 
RouteDiagramLayoutEngine.SCALE, metrics);
         RouteDiagramLayoutEngine engine = new RouteDiagramLayoutEngine(
                 nodeWidth, fontSize, 
RouteDiagramLayoutEngine.NodeLabelMode.valueOf(nodeLabel.toUpperCase()));
 
diff --git 
a/components/camel-diagram/src/main/java/org/apache/camel/diagram/DiagramDevConsole.java
 
b/components/camel-diagram/src/main/java/org/apache/camel/diagram/DiagramDevConsole.java
index a4ad608e8722..261ae7e50646 100644
--- 
a/components/camel-diagram/src/main/java/org/apache/camel/diagram/DiagramDevConsole.java
+++ 
b/components/camel-diagram/src/main/java/org/apache/camel/diagram/DiagramDevConsole.java
@@ -55,6 +55,11 @@ public class DiagramDevConsole extends AbstractDevConsole {
      */
     public static final String NODE_LABEL = "nodeLabel";
 
+    /**
+     * Whether to include live metric counters. Is default true.
+     */
+    public static final String METRIC = "metric";
+
     public DiagramDevConsole() {
         super("camel", "route-diagram", "Route Diagram", "Visual route 
diagrams");
     }
@@ -70,6 +75,7 @@ public class DiagramDevConsole extends AbstractDevConsole {
         int nodeWidth = Integer
                 .parseInt(options.getOrDefault(NODE_WIDTH, "" + 
RouteDiagramLayoutEngine.DEFAULT_BOX_WIDTH).toString());
         String nodeLabel = (String) options.getOrDefault(NODE_LABEL, 
RouteDiagramDumper.NodeLabelMode.CODE.name());
+        boolean metric = "true".equalsIgnoreCase((String) 
options.getOrDefault(METRIC, "true"));
 
         // special for text
         if ("text".equalsIgnoreCase(theme)) {
@@ -78,7 +84,7 @@ public class DiagramDevConsole extends AbstractDevConsole {
             try {
                 RouteDiagramDumper dumper = 
PluginHelper.getRouteDiagramDumper(getCamelContext());
                 BufferedImage image = dumper.dumpRoutesAsImage(filter, 
RouteDiagramDumper.Theme.valueOf(theme.toUpperCase()),
-                        
RouteDiagramDumper.NodeLabelMode.valueOf(nodeLabel.toUpperCase()), nodeWidth, 
fontSize);
+                        metric, 
RouteDiagramDumper.NodeLabelMode.valueOf(nodeLabel.toUpperCase()), nodeWidth, 
fontSize);
                 String base64 = dumper.imageToBase64(image);
                 // For HTML embedding:
                 String html = String.format(
@@ -102,12 +108,13 @@ public class DiagramDevConsole extends AbstractDevConsole 
{
         int nodeWidth = Integer
                 .parseInt(options.getOrDefault(NODE_WIDTH, "" + 
RouteDiagramLayoutEngine.DEFAULT_BOX_WIDTH).toString());
         String nodeLabel = (String) options.getOrDefault(NODE_LABEL, 
RouteDiagramDumper.NodeLabelMode.CODE.name());
+        boolean metric = "true".equalsIgnoreCase((String) 
options.getOrDefault(METRIC, "true"));
 
         JsonObject root = new JsonObject();
         try {
             RouteDiagramDumper dumper = 
PluginHelper.getRouteDiagramDumper(getCamelContext());
             BufferedImage image = dumper.dumpRoutesAsImage(filter, 
RouteDiagramDumper.Theme.valueOf(theme.toUpperCase()),
-                    
RouteDiagramDumper.NodeLabelMode.valueOf(nodeLabel.toUpperCase()), nodeWidth, 
fontSize);
+                    metric, 
RouteDiagramDumper.NodeLabelMode.valueOf(nodeLabel.toUpperCase()), nodeWidth, 
fontSize);
             String base64 = dumper.imageToBase64(image);
             root.put("image", base64);
         } catch (Exception e) {
diff --git 
a/components/camel-diagram/src/main/java/org/apache/camel/diagram/RouteDiagramLayoutEngine.java
 
b/components/camel-diagram/src/main/java/org/apache/camel/diagram/RouteDiagramLayoutEngine.java
index 9ca815c03cca..f052e68126c8 100644
--- 
a/components/camel-diagram/src/main/java/org/apache/camel/diagram/RouteDiagramLayoutEngine.java
+++ 
b/components/camel-diagram/src/main/java/org/apache/camel/diagram/RouteDiagramLayoutEngine.java
@@ -54,13 +54,13 @@ public class RouteDiagramLayoutEngine {
     private final FontMetrics fontMetrics;
     private final NodeLabelMode nodeLabelMode;
 
-    private static final Set<String> BRANCHING_EIPS = Set.of(
+    static final Set<String> BRANCHING_EIPS = Set.of(
             "choice", "multicast", "doTry", "loadBalance", "recipientList", 
"circuitBreaker");
 
-    private static final Set<String> BRANCH_CHILD_TYPES = Set.of(
+    static final Set<String> BRANCH_CHILD_TYPES = Set.of(
             "when", "otherwise", "doCatch", "doFinally", "onFallback");
 
-    private static final Set<String> STRUCTURAL_TYPES = Set.of(
+    static final Set<String> STRUCTURAL_TYPES = Set.of(
             "route", "from");
 
     static class Bounds {
@@ -162,13 +162,13 @@ public class RouteDiagramLayoutEngine {
         public String routeId;
         public String source;
         public RouteStatInfo stat;
-        public List<NodeInfo> nodes = new ArrayList<>();
+        public final List<NodeInfo> nodes = new ArrayList<>();
     }
 
     public static class TreeNode {
         public final NodeInfo info;
         public TreeNode parent;
-        public List<TreeNode> children = new ArrayList<>();
+        public final List<TreeNode> children = new ArrayList<>();
         public int subtreeWidth;
         public LayoutNode layoutNode;
 
@@ -196,7 +196,7 @@ public class RouteDiagramLayoutEngine {
         public int labelY;
         public int maxX;
         public int maxY;
-        public List<LayoutNode> nodes = new ArrayList<>();
+        public final List<LayoutNode> nodes = new ArrayList<>();
     }
 
     public static TreeNode buildTree(List<NodeInfo> nodes) {
diff --git 
a/components/camel-diagram/src/main/java/org/apache/camel/diagram/RouteDiagramRenderer.java
 
b/components/camel-diagram/src/main/java/org/apache/camel/diagram/RouteDiagramRenderer.java
index a77ff9722ef4..d261ffbbd135 100644
--- 
a/components/camel-diagram/src/main/java/org/apache/camel/diagram/RouteDiagramRenderer.java
+++ 
b/components/camel-diagram/src/main/java/org/apache/camel/diagram/RouteDiagramRenderer.java
@@ -30,6 +30,7 @@ import 
org.apache.camel.diagram.RouteDiagramLayoutEngine.RouteInfo;
 import org.apache.camel.diagram.RouteDiagramLayoutEngine.TreeNode;
 import org.jline.utils.Colors;
 
+import static 
org.apache.camel.diagram.RouteDiagramLayoutEngine.BRANCH_CHILD_TYPES;
 import static org.apache.camel.diagram.RouteDiagramLayoutEngine.PADDING;
 import static org.apache.camel.diagram.RouteDiagramLayoutEngine.SCALE;
 import static org.apache.camel.diagram.RouteDiagramLayoutEngine.SCOPE_BOX_PAD;
@@ -61,6 +62,7 @@ public class RouteDiagramRenderer {
     private final int fontSizeNode;
     private final int fontSizeLabel;
     private final int nodeTextPadding;
+    private final boolean metrics;
 
     private static final String DARK_COLORS
             = 
"bg=#0d1117:text=#f0f6fc:arrow=#656c76:label=#d1d7e0:from=#238636:to=#1f6feb:eip=#8957e5"
@@ -80,25 +82,30 @@ public class RouteDiagramRenderer {
     public RouteDiagramRenderer() {
         this(RouteDiagramLayoutEngine.DEFAULT_BOX_WIDTH * SCALE,
              RouteDiagramLayoutEngine.DEFAULT_FONT_SIZE * SCALE,
-             new RouteDiagramLayoutEngine().getNodeTextPadding());
+             new RouteDiagramLayoutEngine().getNodeTextPadding(),
+             false);
     }
 
-    public RouteDiagramRenderer(int nodeWidth, int fontSizeScaled) {
+    public RouteDiagramRenderer(int nodeWidth, int fontSizeScaled, boolean 
metrics) {
         this(nodeWidth, fontSizeScaled, new RouteDiagramLayoutEngine(
-                nodeWidth / SCALE, fontSizeScaled / 
SCALE).getNodeTextPadding());
+                nodeWidth / SCALE, fontSizeScaled / 
SCALE).getNodeTextPadding(),
+             metrics);
     }
 
-    public RouteDiagramRenderer(int nodeWidth, int fontSizeScaled, int 
nodeTextPadding) {
+    public RouteDiagramRenderer(int nodeWidth, int fontSizeScaled, int 
nodeTextPadding, boolean metrics) {
         this.nodeWidth = nodeWidth;
         this.fontSizeNode = fontSizeScaled;
         this.fontSizeLabel = fontSizeScaled + 1 * SCALE;
         this.nodeTextPadding = nodeTextPadding;
+        this.metrics = metrics;
     }
 
     public static class DiagramColors {
         private Color bg;
         private Color text;
         private Color arrow;
+        private Color counter;
+        private Color counterFail;
         private Color routeLabel;
         private Color nodeFrom;
         private Color nodeTo;
@@ -127,6 +134,8 @@ public class RouteDiagramRenderer {
             c.bg = parseColor(map.get("bg"));
             c.text = parseColor(map.getOrDefault("text", "#ffffff"));
             c.arrow = parseColor(map.getOrDefault("arrow", "#b4b4b4"));
+            c.counter = parseColor(map.getOrDefault("counter", "#2e7d32"));
+            c.counterFail = parseColor(map.getOrDefault("counter", "#ff0000"));
             c.routeLabel = parseColor(map.getOrDefault("label", "#c8c8c8"));
             c.nodeFrom = parseColor(map.getOrDefault("from", "#2e7d32"));
             c.nodeTo = parseColor(map.getOrDefault("to", "#1565c0"));
@@ -164,6 +173,14 @@ public class RouteDiagramRenderer {
             return text;
         }
 
+        public Color getCounter() {
+            return counter;
+        }
+
+        public Color getCounterFail() {
+            return counterFail;
+        }
+
         public Color getArrow() {
             return arrow;
         }
@@ -292,8 +309,17 @@ public class RouteDiagramRenderer {
     }
 
     private void drawArrowFromMerge(Graphics2D g, LayoutNode to, DiagramColors 
colors) {
+        var stat = to.treeNode.info.stat;
+        long total = stat != null ? stat.exchangesTotal : 0;
+        long failed = stat != null ? stat.exchangesFailed : 0;
+        long ok = total - failed;
+
         g.setColor(colors.getArrow());
-        g.setStroke(new BasicStroke(STROKE_WIDTH));
+        if (!metrics || total > 0) {
+            g.setStroke(new BasicStroke(STROKE_WIDTH));
+        } else {
+            g.setStroke(DASHED_STROKE);
+        }
 
         int toCx = to.x + nodeWidth / 2;
         int toTy = getTopY(to);
@@ -309,6 +335,16 @@ public class RouteDiagramRenderer {
             g.drawLine(toCx, midY, toCx, toTy - ARROW_SIZE / 2);
         }
         drawArrowHead(g, toCx, toTy);
+
+        if (ok > 0) {
+            g.setColor(colors.getCounter());
+            g.drawString("" + ok, toCx + 2 + fontSizeNode, toTy - 2 - 
fontSizeNode);
+        }
+        if (failed > 0) {
+            g.setColor(colors.getCounterFail());
+            int width = g.getFontMetrics().stringWidth("" + failed);
+            g.drawString("" + failed, toCx - 2 - fontSizeNode - width, toTy - 
2 - fontSizeNode);
+        }
     }
 
     private void drawNode(Graphics2D g, LayoutNode node, DiagramColors colors) 
{
@@ -345,8 +381,21 @@ public class RouteDiagramRenderer {
     }
 
     private void drawArrow(Graphics2D g, LayoutNode from, LayoutNode to, 
DiagramColors colors) {
+        var stat = metrics ? to.treeNode.info.stat : null;
+        if (metrics && BRANCH_CHILD_TYPES.contains(to.type) && 
!to.treeNode.children.isEmpty()) {
+            // grab stat from first child (for example choice to have counters 
for when/otherwise)
+            stat = to.treeNode.children.get(0).info.stat;
+        }
+        long total = stat != null ? stat.exchangesTotal : 0;
+        long failed = stat != null ? stat.exchangesFailed : 0;
+        long ok = total - failed;
+
         g.setColor(colors.getArrow());
-        g.setStroke(new BasicStroke(STROKE_WIDTH));
+        if (!metrics || total > 0) {
+            g.setStroke(new BasicStroke(STROKE_WIDTH));
+        } else {
+            g.setStroke(DASHED_STROKE);
+        }
 
         int fromCx = from.x + nodeWidth / 2;
         int fromBy = from.y + from.height;
@@ -362,6 +411,16 @@ public class RouteDiagramRenderer {
             g.drawLine(toCx, midY, toCx, toTy - ARROW_SIZE / 2);
         }
         drawArrowHead(g, toCx, toTy);
+
+        if (ok > 0) {
+            g.setColor(colors.getCounter());
+            g.drawString("" + ok, toCx + 2 + fontSizeNode, toTy - 2 - 
fontSizeNode);
+        }
+        if (failed > 0) {
+            g.setColor(colors.getCounterFail());
+            int width = g.getFontMetrics().stringWidth("" + failed);
+            g.drawString("" + failed, toCx - 2 - fontSizeNode - width, toTy - 
2 - fontSizeNode);
+        }
     }
 
     private void drawArrowHead(Graphics2D g, int x, int y) {
diff --git 
a/components/camel-diagram/src/test/java/org/apache/camel/diagram/RouteDiagramTest.java
 
b/components/camel-diagram/src/test/java/org/apache/camel/diagram/RouteDiagramTest.java
index 602fdbeb8d55..80e8f2359541 100644
--- 
a/components/camel-diagram/src/test/java/org/apache/camel/diagram/RouteDiagramTest.java
+++ 
b/components/camel-diagram/src/test/java/org/apache/camel/diagram/RouteDiagramTest.java
@@ -584,7 +584,8 @@ class RouteDiagramTest {
         RouteDiagramLayoutEngine engine = new RouteDiagramLayoutEngine(250, 
16);
         LayoutRoute lr = engine.layoutRoute(route, 
RouteDiagramLayoutEngine.PADDING);
 
-        RouteDiagramRenderer renderer = new 
RouteDiagramRenderer(engine.getNodeWidth(), 16 * 
RouteDiagramLayoutEngine.SCALE);
+        RouteDiagramRenderer renderer
+                = new RouteDiagramRenderer(engine.getNodeWidth(), 16 * 
RouteDiagramLayoutEngine.SCALE, false);
         DiagramColors colors = DiagramColors.parse("dark");
         BufferedImage image = renderer.renderDiagram(List.of(lr), lr.maxY + 
RouteDiagramLayoutEngine.V_GAP, colors);
 
diff --git 
a/core/camel-api/src/main/java/org/apache/camel/spi/RouteDiagramDumper.java 
b/core/camel-api/src/main/java/org/apache/camel/spi/RouteDiagramDumper.java
index be0e9d95f4a7..7060cc5d0da2 100644
--- a/core/camel-api/src/main/java/org/apache/camel/spi/RouteDiagramDumper.java
+++ b/core/camel-api/src/main/java/org/apache/camel/spi/RouteDiagramDumper.java
@@ -43,26 +43,46 @@ public interface RouteDiagramDumper {
     String FACTORY = "route-diagram-dumper";
 
     /**
-     * Dumps the routes into a single combined PNG file
+     * Dumps the routes as PNG files in the given file
+     *
+     * @param filter to filter routes
+     * @param theme  the coloring theme
+     * @param file   the name of the file to store
      */
     void dumpRoutesToFile(String filter, Theme theme, File file) throws 
IOException;
 
     /**
      * Dumps the routes as PNG files in the given folder
+     *
+     * @param filter to filter routes
+     * @param theme  the coloring theme
+     * @param folder the folder to store the files
      */
     void dumpRoutesToFolder(String filter, Theme theme, File folder) throws 
IOException;
 
     /**
      * Dumps the routes as a PNG image
+     *
+     * @param filter to filter routes
+     * @param theme  the coloring theme
      */
     default BufferedImage dumpRoutesAsImage(String filter, Theme theme) {
-        return dumpRoutesAsImage(filter, theme, NodeLabelMode.CODE, 180, 12);
+        return dumpRoutesAsImage(filter, theme, false, NodeLabelMode.CODE, 
180, 12);
     }
 
     /**
      * Dumps the routes as a PNG image
+     *
+     * @param filter    to filter routes
+     * @param theme     the coloring theme
+     * @param metrics   whether to include live metric counters
+     * @param nodeLabel what information to display in the nodes
+     * @param nodeWidth the width in pixels of the node boxes
+     * @param fontSize  the font size
      */
-    BufferedImage dumpRoutesAsImage(String filter, Theme theme, NodeLabelMode 
nodeLabel, int nodeWidth, int fontSize);
+    BufferedImage dumpRoutesAsImage(
+            String filter, Theme theme, boolean metrics,
+            NodeLabelMode nodeLabel, int nodeWidth, int fontSize);
 
     /**
      * Converts the image to base64
diff --git 
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-cmd-route-diagram.adoc
 
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-cmd-route-diagram.adoc
index 4a900c5a1a56..be9889a6c1cd 100644
--- 
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-cmd-route-diagram.adoc
+++ 
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-cmd-route-diagram.adoc
@@ -23,7 +23,7 @@ camel cmd route-diagram [options]
 | `--filter` | Filter route by filename or route id |  | String
 | `--font-size` | Font size in logical pixels for node text | 12 | int
 | `--ignore-loading-error` | Whether to ignore route loading and compilation 
errors (use this with care!) | false | boolean
-| `--metric` | Whether to include live metrics (only possible for running 
Camel application) | false | boolean
+| `--metric` | Whether to include live metrics (only possible for running 
Camel application) | true | boolean
 | `--node-label` | What text to display in diagram nodes: code, description, 
or both (default) | both | String
 | `--output` | Save diagram to a PNG file instead of displaying in terminal |  
| String
 | `--theme,--colors` | Color theme preset (dark, light, transparent, text) or 
custom colors (e.g. bg=#1e1e1e:from=#2e7d32:to=#1565c0). Values can be #hex or 
ANSI color names (e.g. from=seagreen:to=steelblue). Use bg= for transparent. 
Can also be set via DIAGRAM_COLORS env var. | dark | String
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json
 
b/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json
index 0553d5478fa5..9e3f577faf42 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/generated/resources/META-INF/camel-jbang-commands-metadata.json
@@ -2,7 +2,7 @@
   "commands": [
     { "name": "bind", "fullName": "bind", "description": "DEPRECATED: Bind 
source and sink Kamelets as a new Camel integration", "deprecated": true, 
"sourceClass": "org.apache.camel.dsl.jbang.core.commands.bind.Bind", "options": 
[ { "names": "--error-handler", "description": "Add error handler 
(none|log|sink:<endpoint>). Sink endpoints are expected in the format 
[[apigroup\/]version:]kind:[namespace\/]name, plain Camel URIs or Kamelet 
name.", "javaType": "java.lang.String", "type": "stri [...]
     { "name": "catalog", "fullName": "catalog", "description": "List artifacts 
from Camel Catalog", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.catalog.CatalogCommand", "options": [ 
{ "names": "-h,--help", "description": "Display the help and sub-commands", 
"javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": 
"component", "fullName": "catalog component", "description": "List components 
from the Camel Catalog", "sourceClass": "org.apache.camel.dsl.jbang.co [...]
-    { "name": "cmd", "fullName": "cmd", "description": "Performs commands in 
the running Camel integrations, such as start\/stop route, or change logging 
levels.", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.action.CamelAction", "options": [ { 
"names": "-h,--help", "description": "Display the help and sub-commands", 
"javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": 
"browse", "fullName": "cmd browse", "description": "Browse pending messages on 
endpoints [...]
+    { "name": "cmd", "fullName": "cmd", "description": "Performs commands in 
the running Camel integrations, such as start\/stop route, or change logging 
levels.", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.action.CamelAction", "options": [ { 
"names": "-h,--help", "description": "Display the help and sub-commands", 
"javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": 
"browse", "fullName": "cmd browse", "description": "Browse pending messages on 
endpoints [...]
     { "name": "completion", "fullName": "completion", "description": "Generate 
completion script for bash\/zsh", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.Complete", "options": [ { "names": 
"-h,--help", "description": "Display the help and sub-commands", "javaType": 
"boolean", "type": "boolean" } ] },
     { "name": "config", "fullName": "config", "description": "Get and set user 
configuration values", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.config.ConfigCommand", "options": [ { 
"names": "-h,--help", "description": "Display the help and sub-commands", 
"javaType": "boolean", "type": "boolean" } ], "subcommands": [ { "name": "get", 
"fullName": "config get", "description": "Display user configuration value", 
"sourceClass": "org.apache.camel.dsl.jbang.core.commands.config. [...]
     { "name": "debug", "fullName": "debug", "description": "Debug local Camel 
integration", "sourceClass": "org.apache.camel.dsl.jbang.core.commands.Debug", 
"options": [ { "names": "--ago", "description": "Use ago instead of yyyy-MM-dd 
HH:mm:ss in timestamp.", "javaType": "boolean", "type": "boolean" }, { "names": 
"--background", "description": "Run in the background", "defaultValue": 
"false", "javaType": "boolean", "type": "boolean" }, { "names": 
"--background-wait", "description": "To  [...]
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelRouteDiagramAction.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelRouteDiagramAction.java
index 225861b01897..c4989bfc8485 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelRouteDiagramAction.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelRouteDiagramAction.java
@@ -89,7 +89,7 @@ public class CamelRouteDiagramAction extends 
ActionBaseCommand {
                         defaultValue = "both")
     String nodeLabel;
 
-    @CommandLine.Option(names = { "--metric" }, defaultValue = "false",
+    @CommandLine.Option(names = { "--metric" }, defaultValue = "true",
                         description = "Whether to include live metrics (only 
possible for running Camel application)")
     boolean metric;
 
@@ -103,41 +103,6 @@ public class CamelRouteDiagramAction extends 
ActionBaseCommand {
         super(main);
     }
 
-    /**
-     * Renders the routes contained in the given source file as a PNG diagram 
saved to {@code outputFile}. Convenience
-     * entry point for programmatic invocation (e.g. from MCP tools) that 
always targets a non-running source file and
-     * skips the running-PID lookup.
-     *
-     * @param  sourceFile         path to the route source file (YAML, XML, 
Java, ...)
-     * @param  outputFile         path to the PNG file to write
-     * @param  theme              color theme spec (e.g. "dark", "light", 
"transparent" or custom)
-     * @param  filter             optional route filter (route id or filename 
pattern)
-     * @param  width              image width in pixels (0 = auto)
-     * @param  ignoreLoadingError whether to ignore route loading and 
compilation errors
-     * @return                    exit code; 0 on success, non-zero otherwise
-     * @throws Exception          if the source cannot be read or the diagram 
cannot be rendered
-     */
-    public Integer renderSourceToFile(
-            String sourceFile, String outputFile, String theme, String filter,
-            int width, boolean ignoreLoadingError,
-            int fontSize, int boxWidth, String nodeLabel)
-            throws Exception {
-        this.name = sourceFile;
-        this.output = outputFile;
-        if (theme != null && !theme.isBlank()) {
-            this.theme = theme;
-        }
-        this.filter = filter;
-        this.width = width;
-        this.ignoreLoadingError = ignoreLoadingError;
-        this.fontSize = fontSize;
-        this.boxWidth = boxWidth;
-        if (nodeLabel != null && !nodeLabel.isBlank()) {
-            this.nodeLabel = nodeLabel;
-        }
-        return doCall();
-    }
-
     @Override
     public Integer doCall() throws Exception {
         System.setProperty("java.awt.headless", "true");
@@ -196,7 +161,8 @@ public class CamelRouteDiagramAction extends 
ActionBaseCommand {
             NodeLabelMode labelMode = parseNodeLabelMode(nodeLabel);
             RouteDiagramLayoutEngine engine = new 
RouteDiagramLayoutEngine(boxWidth, fontSize, labelMode);
             RouteDiagramRenderer renderer = new RouteDiagramRenderer(
-                    engine.getNodeWidth(), fontSize * 
RouteDiagramLayoutEngine.SCALE, engine.getNodeTextPadding());
+                    engine.getNodeWidth(), fontSize * 
RouteDiagramLayoutEngine.SCALE, engine.getNodeTextPadding(),
+                    pid > 0 && metric);
 
             if ("text".equals(theme)) {
                 for (String line : renderer.printTextDiagram(routes, 
labelMode)) {
@@ -327,4 +293,43 @@ public class CamelRouteDiagramAction extends 
ActionBaseCommand {
         }
         return NodeLabelMode.CODE;
     }
+
+    /**
+     * Used BY MCP tools
+     *
+     * Renders the routes contained in the given source file as a PNG diagram 
saved to {@code outputFile}. Convenience
+     * entry point for programmatic invocation (e.g. from MCP tools) that 
always targets a non-running source file and
+     * skips the running-PID lookup.
+     *
+     * @param  sourceFile         path to the route source file (YAML, XML, 
Java, ...)
+     * @param  outputFile         path to the PNG file to write
+     * @param  theme              color theme spec (e.g. "dark", "light", 
"transparent" or custom)
+     * @param  filter             optional route filter (route id or filename 
pattern)
+     * @param  width              image width in pixels (0 = auto)
+     * @param  ignoreLoadingError whether to ignore route loading and 
compilation errors
+     * @return                    exit code; 0 on success, non-zero otherwise
+     * @throws Exception          if the source cannot be read or the diagram 
cannot be rendered
+     */
+    public Integer renderSourceToFile(
+            String sourceFile, String outputFile, String theme, String filter,
+            int width, boolean ignoreLoadingError,
+            int fontSize, int boxWidth, String nodeLabel)
+            throws Exception {
+        this.name = sourceFile;
+        this.output = outputFile;
+        if (theme != null && !theme.isBlank()) {
+            this.theme = theme;
+        }
+        this.filter = filter;
+        this.width = width;
+        this.ignoreLoadingError = ignoreLoadingError;
+        this.fontSize = fontSize;
+        this.boxWidth = boxWidth;
+        if (nodeLabel != null && !nodeLabel.isBlank()) {
+            this.nodeLabel = nodeLabel;
+        }
+        this.metric = false;
+        return doCall();
+    }
+
 }

Reply via email to