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 81def551326e CAMEL-24844: the message dump carries the body's size, 
and the camel-jbang history views and tool show type and size per step
81def551326e is described below

commit 81def551326ee77dd96ca4df79d8d9d6c479108a
Author: Claus Ibsen <[email protected]>
AuthorDate: Sun Sep 20 15:13:26 2026 +0200

    CAMEL-24844: the message dump carries the body's size, and the camel-jbang 
history views and tool show type and size per step
    
    The runtime side of CAMEL-24844 on a live app: what the body is at every
    step, answered where people and agents look.
    
    The message dump (MessageHelper.dumpAsJSon, carried by every backlog tracer
    event) says "type": "null" when the body is null, where it said nothing
    before, and carries a size for text and byte bodies from the
    MessageSizeStrategy when it is enabled (the dev profile does): lengths only,
    nothing read or converted.
    
    The camel-jbang history and error views parse the size and show it next to
    the type of each step's body. The CLI's message table says what a size
    counts, "size: 3 elements" for a list and "size: 11 bytes" for text, and
    shows the dumped value's length only when it differs; camel history gets
    --json, named as the other commands name it, printing the recorded history.
    
    The camel-jbang-mcp history tool (get_history) gets summary=true: one entry
    per step with route, node, elapsed, and the body's type and size as it
    reached the step, without bodies, headers and properties, so a small model
    sees where the body turned from text into a Map or bytes without paying for
    the full dump.
    
    Closes #26627
    
    Co-Authored-By: Claude Fable 5.1 <[email protected]>
    Claude-Session: https://claude.ai/code/session_01Bp3538HRBPMQkb5ta9xRaj
---
 .../org/apache/camel/util/MessageHelperTest.java   | 20 ++++++++
 .../org/apache/camel/support/MessageHelper.java    | 15 +++++-
 .../jbang-commands/camel-jbang-get-history.adoc    |  1 +
 .../META-INF/camel-jbang-commands-metadata.json    |  2 +-
 .../core/commands/action/CamelHistoryAction.java   | 18 ++++++++
 .../core/commands/action/MessageTableHelper.java   | 20 ++++++--
 .../dsl/jbang/core/commands/ai/ToolRegistry.java   | 53 +++++++++++++++++++++-
 .../commands/action/CamelHistoryActionTest.java    | 17 +++++++
 .../jbang/core/commands/ai/ToolRegistryTest.java   | 35 ++++++++++++++
 .../dsl/jbang/core/commands/tui/ErrorInfo.java     |  1 +
 .../dsl/jbang/core/commands/tui/ErrorsTab.java     |  2 +-
 .../dsl/jbang/core/commands/tui/HistoryEntry.java  |  1 +
 .../dsl/jbang/core/commands/tui/HistoryTab.java    | 19 ++++++--
 .../dsl/jbang/core/commands/tui/StatusParser.java  | 12 ++++-
 .../dsl/jbang/core/commands/tui/TraceEntry.java    |  1 +
 .../jbang/core/commands/tui/StatusParserTest.java  | 19 ++++++++
 16 files changed, 223 insertions(+), 13 deletions(-)

diff --git 
a/core/camel-core/src/test/java/org/apache/camel/util/MessageHelperTest.java 
b/core/camel-core/src/test/java/org/apache/camel/util/MessageHelperTest.java
index 2f047a915594..83e1d0f4286d 100644
--- a/core/camel-core/src/test/java/org/apache/camel/util/MessageHelperTest.java
+++ b/core/camel-core/src/test/java/org/apache/camel/util/MessageHelperTest.java
@@ -300,4 +300,24 @@ public class MessageHelperTest {
         assertTrue(out.contains("Hello World"));
     }
 
+    @Test
+    public void testDumpAsJSonBodySizeAndNull() {
+        // CAMEL-24844: the size from the message size strategy when it is 
enabled, and a null body says so
+        camelContext.getMessageSizeStrategy().setEnabled(true);
+        Exchange exchange = new DefaultExchange(camelContext);
+        Message message = exchange.getIn();
+        message.setBody("Hello World");
+        String out = MessageHelper.dumpAsJSon(message, true);
+        assertTrue(out.contains("\"type\": \"java.lang.String\""), out);
+        assertTrue(out.contains("\"size\": 11"), out);
+
+        message.setBody(null);
+        out = MessageHelper.dumpAsJSon(message, true);
+        assertTrue(out.contains("\"type\": \"null\""), out);
+        assertTrue(!out.contains("\"size\""), "no body, no size: " + out);
+
+        message.setBody("");
+        out = MessageHelper.dumpAsJSon(message, true);
+        assertTrue(out.contains("\"size\": 0"), "an empty text has a size of 
0: " + out);
+    }
 }
diff --git 
a/core/camel-support/src/main/java/org/apache/camel/support/MessageHelper.java 
b/core/camel-support/src/main/java/org/apache/camel/support/MessageHelper.java
index 3371aa18fb2a..1e86d51d244e 100644
--- 
a/core/camel-support/src/main/java/org/apache/camel/support/MessageHelper.java
+++ 
b/core/camel-support/src/main/java/org/apache/camel/support/MessageHelper.java
@@ -39,6 +39,7 @@ import org.apache.camel.WrappedFile;
 import org.apache.camel.spi.DataTypeAware;
 import org.apache.camel.spi.ExchangeFormatter;
 import org.apache.camel.spi.HeaderFilterStrategy;
+import org.apache.camel.spi.MessageSizeStrategy;
 import org.apache.camel.trait.message.MessageTrait;
 import org.apache.camel.util.ImportantHeaderUtils;
 import org.apache.camel.util.ObjectHelper;
@@ -1090,7 +1091,7 @@ public final class MessageHelper {
             JsonObject jb = new JsonObject();
             jo.put("body", jb);
             Object body = message.getBody();
-            String type = ObjectHelper.classCanonicalName(body);
+            String type = body != null ? ObjectHelper.classCanonicalName(body) 
: "null";
             if (type != null) {
                 jb.put("type", type);
             }
@@ -1121,6 +1122,18 @@ public final class MessageHelper {
                     jb.put("size", size);
                 }
             }
+            if (body != null && !jb.containsKey("size") && 
message.getExchange() != null
+                    && message.getExchange().getContext() != null) {
+                // the size of a text or byte body, from the message size 
strategy when it is enabled (the dev
+                // profile does): lengths only, nothing is read or converted 
(CAMEL-24844)
+                MessageSizeStrategy sizeStrategy = 
message.getExchange().getContext().getMessageSizeStrategy();
+                if (sizeStrategy != null && sizeStrategy.isEnabled()) {
+                    long size = sizeStrategy.computeBodySize(message);
+                    if (size >= 0) {
+                        jb.put("size", size);
+                    }
+                }
+            }
             String data = extractBodyForLogging(message, null, 
allowCachedStreams, allowStreams, allowFiles, maxChars);
             if (data != null) {
                 if ("[Body is null]".equals(data)) {
diff --git 
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-get-history.adoc
 
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-get-history.adoc
index a3114809ca3d..009697986176 100644
--- 
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-get-history.adoc
+++ 
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-get-history.adoc
@@ -23,6 +23,7 @@ camel get history [options]
 | `--depth` | Depth of tracing. 0=Created Completed. 1=All events on 1st 
route, 2=All events on 1st 2nd depth, and so on. 9 = all events on every depth. 
| 9 | int
 | `--diagram` | Display a route diagram with the message path highlighted |  | 
boolean
 | `--it` | Interactive mode for enhanced history information |  | boolean
+| `--json` | Output in JSON Format |  | boolean
 | `--limit-split` | Limit Split to a maximum number of entries to be displayed 
|  | int
 | `--logging-color` | Use colored logging | true | boolean
 | `--mask` | Whether to mask endpoint URIs to avoid printing sensitive 
information such as password or access keys |  | boolean
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 3118cacfdb31..9bc284eca34b 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
@@ -15,7 +15,7 @@
     { "name": "eval", "fullName": "eval", "description": "Evaluate Camel 
expressions and scripts", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.EvalCommand", "options": [ { "names": 
"-h,--help", "description": "Display the help and sub-commands", "javaType": 
"boolean", "type": "boolean" } ], "subcommands": [ { "name": "expression", 
"fullName": "eval expression", "description": "Evaluates Camel expression", 
"sourceClass": "org.apache.camel.dsl.jbang.core.commands.action.EvalEx [...]
     { "name": "explain", "fullName": "explain", "description": "Explain what a 
Camel route does using AI\/LLM", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.Explain", "options": [ { "names": 
"--api-key", "description": "API key for authentication. Also reads 
ANTHROPIC_API_KEY, OPENAI_API_KEY, WATSONX_APIKEY, or LLM_API_KEY env vars", 
"javaType": "java.lang.String", "type": "string" }, { "names": "--api-type", 
"description": "API type: 'ollama', 'openai' (OpenAI-compatible), ' [...]
     { "name": "export", "fullName": "export", "description": "Export to other 
runtimes (Camel Main, Spring Boot, or Quarkus)", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.Export", "options": [ { "names": 
"--build-property", "description": "Maven build properties, ex. 
--build-property=prop1=foo", "javaType": "java.util.List", "type": "array" }, { 
"names": "--camel-spring-boot-version", "description": "Camel version to use 
with Spring Boot", "javaType": "java.lang.String", "ty [...]
-    { "name": "get", "fullName": "get", "description": "Get status of Camel 
integrations", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.process.CamelStatus", "options": [ { 
"names": "--watch", "description": "Execute periodically and showing output 
fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": 
"-h,--help", "description": "Display the help and sub-commands", "javaType": 
"boolean", "type": "boolean" } ], "subcommands": [ { "name": "activity", 
"fullName": " [...]
+    { "name": "get", "fullName": "get", "description": "Get status of Camel 
integrations", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.process.CamelStatus", "options": [ { 
"names": "--watch", "description": "Execute periodically and showing output 
fullscreen", "javaType": "boolean", "type": "boolean" }, { "names": 
"-h,--help", "description": "Display the help and sub-commands", "javaType": 
"boolean", "type": "boolean" } ], "subcommands": [ { "name": "activity", 
"fullName": " [...]
     { "name": "harden", "fullName": "harden", "description": "Suggest security 
hardening for Camel routes using AI\/LLM", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.Harden", "options": [ { "names": 
"--api-key", "description": "API key for authentication. Also reads 
OPENAI_API_KEY or LLM_API_KEY env vars", "javaType": "java.lang.String", 
"type": "string" }, { "names": "--api-type", "description": "API type: 'ollama' 
or 'openai' (OpenAI-compatible)", "defaultValue": "ollama", [...]
     { "name": "hawtio", "fullName": "hawtio", "description": "Launch Hawtio 
web console", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.process.Hawtio", "options": [ { 
"names": "--host", "description": "Hostname to bind the Hawtio web console to", 
"defaultValue": "127.0.0.1", "javaType": "java.lang.String", "type": "string" 
}, { "names": "--openUrl", "description": "To automatic open Hawtio web console 
in the web browser", "defaultValue": "true", "javaType": "boolean", "type": 
[...]
     { "name": "infra", "fullName": "infra", "description": "List and Run 
external services for testing and prototyping", "sourceClass": 
"org.apache.camel.dsl.jbang.core.commands.infra.InfraCommand", "options": [ { 
"names": "--json", "description": "Output in JSON Format", "javaType": 
"boolean", "type": "boolean" }, { "names": "-h,--help", "description": "Display 
the help and sub-commands", "javaType": "boolean", "type": "boolean" } ], 
"subcommands": [ { "name": "get", "fullName": "infra  [...]
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelHistoryAction.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelHistoryAction.java
index a60fa2444734..2f30984e858e 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelHistoryAction.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/CamelHistoryAction.java
@@ -134,6 +134,10 @@ public class CamelHistoryAction extends ActionWatchCommand 
{
                         description = "Pretty print message body when using 
JSon or XML format")
     boolean pretty;
 
+    @CommandLine.Option(names = { "--json" },
+                        description = "Output in JSON Format")
+    boolean jsonOutput;
+
     @CommandLine.Option(names = { "--logging-color" }, defaultValue = "true", 
description = "Use colored logging")
     boolean loggingColor = true;
 
@@ -167,6 +171,20 @@ public class CamelHistoryAction extends ActionWatchCommand 
{
             name = "*";
         }
 
+        if (jsonOutput) {
+            for (long pid : findPids(name)) {
+                Path p = getMessageHistoryFile(Long.toString(pid));
+                if (Files.exists(p)) {
+                    for (String line : Files.readAllLines(p)) {
+                        if (!line.isBlank()) {
+                            printer().println(line);
+                        }
+                    }
+                }
+            }
+            return 0;
+        }
+
         List<List<Row>> pids = loadRows();
 
         if (!pids.isEmpty()) {
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/MessageTableHelper.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/MessageTableHelper.java
index 1e9ae066031a..315c793fa6c2 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/MessageTableHelper.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/action/MessageTableHelper.java
@@ -467,6 +467,18 @@ public class MessageTableHelper {
             return s;
         }
 
+        /** Whether the dump's size counts elements (a collection, a map or an 
array other than bytes). */
+        static boolean sizeIsCount(String type) {
+            if (type == null) {
+                return false;
+            }
+            if (type.endsWith("[]")) {
+                return !type.equals("byte[]");
+            }
+            return type.startsWith("java.util.") && (type.contains("List") || 
type.contains("Set") || type.contains("Map")
+                    || type.contains("Collection") || type.contains("Queue") 
|| type.contains("Deque"));
+        }
+
         String typeAndLengthAsString() {
             String s;
             if (type == null) {
@@ -493,13 +505,15 @@ public class MessageTableHelper {
             long p = position != null ? position : -1;
             StringBuilder sb = new StringBuilder();
             if (sz != -1) {
-                sb.append(" size: ").append(sz);
+                // the dump's size is a count for a collection or an array, 
bytes for text, bytes, a stream or a file
+                sb.append(" size: ").append(sz).append(sizeIsCount(type) ? " 
elements" : " bytes");
             }
             if (p != -1) {
                 sb.append(" pos: ").append(p);
             }
-            if (l != -1) {
-                sb.append(" bytes: ").append(l);
+            if (l != -1 && l != sz) {
+                // the length of the value as dumped (it may be cut), when it 
is not the size already shown
+                sb.append(" shown: ").append(l);
             }
             if (!sb.isEmpty()) {
                 s = s + " (" + sb.toString().trim() + ")";
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/ToolRegistry.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/ToolRegistry.java
index 6dba0839df9b..b4674d69cdae 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/ToolRegistry.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/ToolRegistry.java
@@ -213,10 +213,20 @@ public final class ToolRegistry {
                 }));
 
         register(tool("get_history",
-                "Get the message history trace of the last completed 
exchange.")
+                "Get the message history trace of the last completed exchange: 
every step with the message as it was"
+                                     + " there. With summary=true only the 
steps, each with route, node, elapsed, and the"
+                                     + " body's type and size as it reached 
the step (what to read to see where the body"
+                                     + " changed from text into a Map or 
bytes).")
+                .param("summary", "boolean", "Only the steps with route, node, 
elapsed, body type and size", false)
                 .executor((ctx, args) -> {
                     JsonObject history = ctx.readHistoryFile();
-                    return history != null ? history.toJson() : "No message 
history available.";
+                    if (history == null) {
+                        return "No message history available.";
+                    }
+                    if ("true".equalsIgnoreCase(args.get("summary"))) {
+                        return historySummary(history).toJson();
+                    }
+                    return history.toJson();
                 }));
 
         register(tool("get_route_source",
@@ -1176,4 +1186,43 @@ public final class ToolRegistry {
                 || (title != null && title.toLowerCase().contains(lf))
                 || (description != null && 
description.toLowerCase().contains(lf));
     }
+
+    /**
+     * The steps of the last completed exchange, one line each: route, node, 
elapsed, and the body's type and size as it
+     * reached the step (from the message dump of the trace event), without 
the bodies, headers and properties.
+     */
+    static JsonObject historySummary(JsonObject history) {
+        JsonObject answer = new JsonObject();
+        if (history.get("name") != null) {
+            answer.put("name", history.get("name"));
+        }
+        JsonArray steps = new JsonArray();
+        Object traces = history.get("traces");
+        if (traces instanceof java.util.List<?> list) {
+            for (Object o : list) {
+                if (!(o instanceof JsonObject t)) {
+                    continue;
+                }
+                JsonObject step = new JsonObject();
+                for (String key : new String[] {
+                        "routeId", "nodeId", "nodeShortName", "nodeLabel", 
"location", "elapsed",
+                        "first", "last", "failed" }) {
+                    if (t.get(key) != null) {
+                        step.put(key, t.get(key));
+                    }
+                }
+                if (t.get("message") instanceof JsonObject m && m.get("body") 
instanceof JsonObject b) {
+                    if (b.get("type") != null) {
+                        step.put("bodyType", b.get("type"));
+                    }
+                    if (b.get("size") != null) {
+                        step.put("bodySize", b.get("size"));
+                    }
+                }
+                steps.add(step);
+            }
+        }
+        answer.put("steps", steps);
+        return answer;
+    }
 }
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/CamelHistoryActionTest.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/CamelHistoryActionTest.java
index a137f72ff99d..7ef13aff324e 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/CamelHistoryActionTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/action/CamelHistoryActionTest.java
@@ -55,6 +55,23 @@ class CamelHistoryActionTest extends 
ActionCommandTestSupport {
         assertTrue(out.contains("ABCDEFGH-0001"), "should print the exchange 
id, was: " + out);
     }
 
+    @Test
+    void testJsonOutputIsTheRecordedHistory() throws Exception {
+        writeStatusFile(TEST_PID, "myApp");
+        writeMessageHistoryFile(TEST_PID, singleTraceLine());
+        CamelHistoryAction command = new CamelHistoryAction(new 
CamelJBangMain().withPrinter(printer));
+        command.name = "myApp";
+        command.depth = 9;
+        command.loggingColor = false;
+        command.jsonOutput = true;
+        int exit = callWithSingleProcess(command);
+        assertEquals(0, exit);
+        String out = printer.getOutput();
+        assertTrue(out.contains("\"traces\""), "the recorded history as JSON, 
was: " + out);
+        assertTrue(out.contains("ABCDEFGH-0001"), "the exchange id in the 
JSON, was: " + out);
+        assertTrue(!out.contains("Message History of last completed"), "no 
table in JSON mode, was: " + out);
+    }
+
     @Test
     void testRendersNothingWhenNameDoesNotMatch() throws Exception {
         writeStatusFile(TEST_PID, "myApp");
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/ToolRegistryTest.java
 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/ToolRegistryTest.java
index f08d209c07d3..671a88db8d40 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/ToolRegistryTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/ToolRegistryTest.java
@@ -21,6 +21,8 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
+import org.apache.camel.util.json.JsonArray;
+import org.apache.camel.util.json.JsonObject;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -190,4 +192,37 @@ class ToolRegistryTest {
         assertThrows(ToolExecutionException.class,
                 () -> ToolRegistry.execute("detect_config_drift", ctx, 
Map.of()));
     }
+
+    @Test
+    void historySummaryKeepsTheStepsAndTheBodyTypeAndSize() {
+        // CAMEL-24844: the compact form of get_history a small model can read
+        JsonObject body = new JsonObject();
+        body.put("type", "java.util.LinkedHashMap");
+        body.put("size", 3);
+        body.put("value", "{orderId=ORD-1001}");
+        JsonObject message = new JsonObject();
+        message.put("body", body);
+        message.put("headers", new JsonArray());
+        JsonObject trace = new JsonObject();
+        trace.put("routeId", "route1");
+        trace.put("nodeId", "unmarshal1");
+        trace.put("nodeShortName", "unmarshal");
+        trace.put("elapsed", 2);
+        trace.put("message", message);
+        JsonArray traces = new JsonArray();
+        traces.add(trace);
+        JsonObject history = new JsonObject();
+        history.put("name", "shop");
+        history.put("traces", traces);
+
+        JsonObject summary = ToolRegistry.historySummary(history);
+        assertEquals("shop", summary.get("name"));
+        JsonArray steps = summary.getCollection("steps");
+        assertEquals(1, steps.size());
+        JsonObject step = (JsonObject) steps.get(0);
+        assertEquals("unmarshal1", step.get("nodeId"));
+        assertEquals("java.util.LinkedHashMap", step.get("bodyType"));
+        assertEquals(3, step.get("bodySize"));
+        assertNull(step.get("message"), "no bodies, headers or properties in 
the summary");
+    }
 }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ErrorInfo.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ErrorInfo.java
index d01b5026412e..ad94f33fe897 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ErrorInfo.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ErrorInfo.java
@@ -36,6 +36,7 @@ class ErrorInfo {
     String[] messageHistory;
     String body;
     String bodyType;
+    long bodySize = -1;
     final Map<String, Object> headers = new LinkedHashMap<>();
     final Map<String, String> headerTypes = new LinkedHashMap<>();
     final Map<String, Object> properties = new LinkedHashMap<>();
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ErrorsTab.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ErrorsTab.java
index 129c4ac47d5d..bc39693b6517 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ErrorsTab.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/ErrorsTab.java
@@ -532,7 +532,7 @@ class ErrorsTab extends AbstractTableTab {
             HistoryTab.addKvLines(lines, " Headers:", ei.headers, 
ei.headerTypes, false, null);
         }
         if (showBody) {
-            HistoryTab.addBodyLines(lines, ei.body, ei.bodyType, false);
+            HistoryTab.addBodyLines(lines, ei.body, ei.bodyType, ei.bodySize, 
false);
         }
 
         int[] scroll = { detailScroll };
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HistoryEntry.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HistoryEntry.java
index 06339e4a7d1a..07f2394ccaad 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HistoryEntry.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HistoryEntry.java
@@ -41,6 +41,7 @@ class HistoryEntry {
     long epochMs;
     String body;
     String bodyType;
+    long bodySize = -1;
     String exception;
     Map<String, Object> headers;
     Map<String, String> headerTypes;
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HistoryTab.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HistoryTab.java
index 8a3b5d0fcbf3..1d36ec4b600c 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HistoryTab.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/HistoryTab.java
@@ -906,6 +906,7 @@ class HistoryTab extends AbstractTab {
         boolean failed = false;
         String body = null;
         String bodyType = null;
+        long bodySize = -1;
         String exception = null;
         Map<String, Object> headers = null;
         Map<String, Object> properties = null;
@@ -927,6 +928,7 @@ class HistoryTab extends AbstractTab {
             failed = e.failed;
             body = e.body;
             bodyType = e.bodyType;
+            bodySize = e.bodySize;
             exception = e.exception;
             headers = e.headers;
             properties = e.exchangeProperties;
@@ -950,6 +952,7 @@ class HistoryTab extends AbstractTab {
             failed = e.failed;
             body = e.body;
             bodyType = e.bodyType;
+            bodySize = e.bodySize;
             exception = e.exception;
             headers = e.headers;
             properties = e.exchangeProperties;
@@ -1050,9 +1053,11 @@ class HistoryTab extends AbstractTab {
         if (showBody && body != null) {
             Style headerStyle = bodyChanged ? Theme.change().bold() : 
Theme.muted();
             lines.add(Line.from(Span.raw("")));
+            String detail = bodyType != null && bodySize >= 0
+                    ? bodyType + ", " + HeapHistogramTab.formatBytes(bodySize) 
: bodyType;
             lines.add(Line.from(
                     Span.styled(" Body", headerStyle),
-                    bodyType != null ? Span.styled(" (" + bodyType + ")", 
Style.EMPTY.dim()) : Span.raw("")));
+                    detail != null ? Span.styled(" (" + detail + ")", 
Style.EMPTY.dim()) : Span.raw("")));
             for (String line : body.split("\n")) {
                 lines.add(Line.from(Span.raw(" " + line)));
             }
@@ -1388,7 +1393,7 @@ class HistoryTab extends AbstractTab {
                     headersChanged, prev != null ? prev.headers : null);
         }
         if (showTraceBody) {
-            addBodyLines(lines, entry.body, entry.bodyType, bodyChanged);
+            addBodyLines(lines, entry.body, entry.bodyType, entry.bodySize, 
bodyChanged);
         }
         addExceptionLines(lines, entry.exception);
 
@@ -1673,7 +1678,7 @@ class HistoryTab extends AbstractTab {
                     headersChanged, prev != null ? prev.headers : null);
         }
         if (showHistoryBody) {
-            addBodyLines(lines, entry.body, entry.bodyType, bodyChanged);
+            addBodyLines(lines, entry.body, entry.bodyType, entry.bodySize, 
bodyChanged);
         }
         addExceptionLines(lines, entry.exception);
 
@@ -2196,12 +2201,18 @@ class HistoryTab extends AbstractTab {
     }
 
     static void addBodyLines(List<Line> lines, String body, String bodyType, 
boolean changed) {
+        addBodyLines(lines, body, bodyType, -1, changed);
+    }
+
+    /** The body's type and, when the running app measured it, its size next 
to the body lines (CAMEL-24844). */
+    static void addBodyLines(List<Line> lines, String body, String bodyType, 
long bodySize, boolean changed) {
         Style headerStyle = changed ? Theme.change().bold() : Theme.muted();
         if (body != null) {
             if (bodyType != null) {
+                String detail = bodySize >= 0 ? bodyType + ", " + 
HeapHistogramTab.formatBytes(bodySize) : bodyType;
                 lines.add(Line.from(
                         Span.styled(" Body: ", headerStyle),
-                        Span.styled("(" + bodyType + ")", Style.EMPTY.dim())));
+                        Span.styled("(" + detail + ")", Style.EMPTY.dim())));
             } else {
                 lines.add(Line.from(Span.styled(" Body:", headerStyle)));
             }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/StatusParser.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/StatusParser.java
index da3e27a8eab8..38a6a42692c0 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/StatusParser.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/StatusParser.java
@@ -44,6 +44,7 @@ final class StatusParser {
             Map<String, String> headerTypes,
             String body,
             String bodyType,
+            long bodySize,
             Map<String, Object> exchangeProperties,
             Map<String, String> exchangePropertyTypes,
             Map<String, Object> exchangeVariables,
@@ -968,6 +969,7 @@ final class StatusParser {
             entry.headerTypes = md.headerTypes();
             entry.body = md.body();
             entry.bodyType = md.bodyType();
+            entry.bodySize = md.bodySize();
             if (entry.body != null) {
                 entry.bodyPreview = entry.body.replace("\n", " 
").replace("\r", "");
             }
@@ -1076,6 +1078,7 @@ final class StatusParser {
             entry.headerTypes = md.headerTypes();
             entry.body = md.body();
             entry.bodyType = md.bodyType();
+            entry.bodySize = md.bodySize();
             entry.exchangeProperties = md.exchangeProperties();
             entry.exchangePropertyTypes = md.exchangePropertyTypes();
             entry.exchangeVariables = md.exchangeVariables();
@@ -1102,6 +1105,7 @@ final class StatusParser {
         Map<String, String> headerTypes = null;
         String body = null;
         String bodyType = null;
+        long bodySize = -1;
         Map<String, Object> exchangeProperties = null;
         Map<String, String> exchangePropertyTypes = null;
         Map<String, Object> exchangeVariables = null;
@@ -1130,6 +1134,9 @@ final class StatusParser {
             Object val = bodyJson.get("value");
             body = val != null ? val.toString() : null;
             bodyType = TuiHelper.shortTypeName(bodyJson.getString("type"));
+            if (bodyJson.get("size") instanceof Number n) {
+                bodySize = n.longValue();
+            }
         } else if (bodyObj != null) {
             body = bodyObj.toString();
         }
@@ -1171,7 +1178,7 @@ final class StatusParser {
         }
 
         return new MessageData(
-                headers, headerTypes, body, bodyType,
+                headers, headerTypes, body, bodyType, bodySize,
                 exchangeProperties, exchangePropertyTypes, exchangeVariables, 
exchangeVariableTypes);
     }
 
@@ -1439,6 +1446,9 @@ final class StatusParser {
                 if (bodyObj instanceof JsonObject bodyJson) {
                     ei.body = bodyJson.getString("value");
                     ei.bodyType = 
TuiHelper.shortTypeName(bodyJson.getString("type"));
+                    if (bodyJson.get("size") instanceof Number n) {
+                        ei.bodySize = n.longValue();
+                    }
                 } else if (bodyObj != null) {
                     ei.body = bodyObj.toString();
                 }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TraceEntry.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TraceEntry.java
index a566178d6b44..a23466c745dc 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TraceEntry.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TraceEntry.java
@@ -42,6 +42,7 @@ class TraceEntry {
     long epochMs;
     String body;
     String bodyType;
+    long bodySize = -1;
     String bodyPreview;
     Map<String, Object> headers;
     Map<String, String> headerTypes;
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/StatusParserTest.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/StatusParserTest.java
index ad690a17c6e0..4d4195b58e66 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/StatusParserTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/StatusParserTest.java
@@ -403,6 +403,25 @@ class StatusParserTest {
         assertEquals("String", md.headerTypes().get("Content-Type"));
     }
 
+    @Test
+    void parseMessageBodyTypeAndSize() {
+        // CAMEL-24844: the body's type and the size the running app measured
+        JsonObject body = new JsonObject();
+        body.put("type", "java.lang.String");
+        body.put("size", 11);
+        body.put("value", "Hello World");
+        JsonObject message = new JsonObject();
+        message.put("body", body);
+
+        StatusParser.MessageData md = StatusParser.parseMessage(message);
+        assertEquals("Hello World", md.body());
+        assertEquals("String", md.bodyType());
+        assertEquals(11, md.bodySize());
+
+        body.remove("size");
+        assertEquals(-1, StatusParser.parseMessage(message).bodySize());
+    }
+
     @Test
     void parseMessageWithHeadersAsMap() {
         JsonObject message = new JsonObject();

Reply via email to