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

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

commit 2d23b9ba0a288ccb46671325ee3c82d2fd647f02
Author: Claus Ibsen <[email protected]>
AuthorDate: Tue Jun 30 15:01:12 2026 +0200

    CAMEL-23862: Add SQL Trace dev console, TUI tab, and CLI command
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../apache/camel/catalog/dev-consoles.properties   |   1 +
 .../camel/catalog/dev-consoles/sql-trace.json      |  15 +
 .../impl/console/SqlTraceDevConsoleConfigurer.java |  67 ++++
 .../org/apache/camel/dev-console/sql-trace.json    |  15 +
 ...rg.apache.camel.impl.console.SqlTraceDevConsole |   2 +
 .../org/apache/camel/dev-console/sql-trace         |   2 +
 .../org/apache/camel/dev-consoles.properties       |   2 +-
 .../camel/impl/console/SqlTraceDevConsole.java     | 284 +++++++++++++++++
 .../jbang-commands/camel-jbang-get-sql-trace.adoc  |  28 ++
 .../ROOT/pages/jbang-commands/camel-jbang-get.adoc |   1 +
 .../camel/cli/connector/LocalCliConnector.java     |   7 +
 .../META-INF/camel-jbang-commands-metadata.json    |   2 +-
 .../dsl/jbang/core/commands/CamelJBangMain.java    |   1 +
 .../jbang/core/commands/process/ListSqlTrace.java  | 211 +++++++++++++
 .../jbang/core/commands/tui/IntegrationInfo.java   |   6 +
 .../dsl/jbang/core/commands/tui/McpFacade.java     |   4 +-
 .../dsl/jbang/core/commands/tui/PopupManager.java  |  16 +-
 .../dsl/jbang/core/commands/tui/SqlTraceInfo.java  |  30 ++
 .../dsl/jbang/core/commands/tui/SqlTraceTab.java   | 336 +++++++++++++++++++++
 .../dsl/jbang/core/commands/tui/StatusParser.java  |  31 ++
 .../dsl/jbang/core/commands/tui/TabRegistry.java   |  12 +-
 .../core/commands/tui/SqlTraceTabRenderTest.java   | 196 ++++++++++++
 22 files changed, 1255 insertions(+), 14 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles.properties
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles.properties
index 217aa1876741..aff1673d5525 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles.properties
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles.properties
@@ -57,6 +57,7 @@ sftp
 simple-language
 source
 sql-query
+sql-trace
 startup-recorder
 stub
 system-properties
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/sql-trace.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/sql-trace.json
new file mode 100644
index 000000000000..55335065a456
--- /dev/null
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/sql-trace.json
@@ -0,0 +1,15 @@
+{
+  "console": {
+    "kind": "console",
+    "group": "camel",
+    "name": "sql-trace",
+    "title": "SQL Trace",
+    "description": "Trace SQL query executions",
+    "deprecated": false,
+    "javaType": "org.apache.camel.impl.console.SqlTraceDevConsole",
+    "groupId": "org.apache.camel",
+    "artifactId": "camel-console",
+    "version": "4.21.0-SNAPSHOT"
+  }
+}
+
diff --git 
a/core/camel-console/src/generated/java/org/apache/camel/impl/console/SqlTraceDevConsoleConfigurer.java
 
b/core/camel-console/src/generated/java/org/apache/camel/impl/console/SqlTraceDevConsoleConfigurer.java
new file mode 100644
index 000000000000..0948021b78e5
--- /dev/null
+++ 
b/core/camel-console/src/generated/java/org/apache/camel/impl/console/SqlTraceDevConsoleConfigurer.java
@@ -0,0 +1,67 @@
+/* Generated by camel build tools - do NOT edit this file! */
+package org.apache.camel.impl.console;
+
+import javax.annotation.processing.Generated;
+import java.util.Map;
+
+import org.apache.camel.CamelContext;
+import org.apache.camel.spi.ExtendedPropertyConfigurerGetter;
+import org.apache.camel.spi.PropertyConfigurerGetter;
+import org.apache.camel.spi.ConfigurerStrategy;
+import org.apache.camel.spi.GeneratedPropertyConfigurer;
+import org.apache.camel.util.CaseInsensitiveMap;
+import org.apache.camel.impl.console.SqlTraceDevConsole;
+
+/**
+ * Generated by camel build tools - do NOT edit this file!
+ */
+@Generated("org.apache.camel.maven.packaging.GenerateConfigurerMojo")
+@SuppressWarnings("unchecked")
+public class SqlTraceDevConsoleConfigurer extends 
org.apache.camel.support.component.PropertyConfigurerSupport implements 
GeneratedPropertyConfigurer, ExtendedPropertyConfigurerGetter {
+
+    private static final Map<String, Object> ALL_OPTIONS;
+    static {
+        Map<String, Object> map = new CaseInsensitiveMap();
+        map.put("CamelContext", org.apache.camel.CamelContext.class);
+        map.put("Capacity", int.class);
+        ALL_OPTIONS = map;
+    }
+
+    @Override
+    public boolean configure(CamelContext camelContext, Object obj, String 
name, Object value, boolean ignoreCase) {
+        org.apache.camel.impl.console.SqlTraceDevConsole target = 
(org.apache.camel.impl.console.SqlTraceDevConsole) obj;
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "camelcontext":
+        case "camelContext": target.setCamelContext(property(camelContext, 
org.apache.camel.CamelContext.class, value)); return true;
+        case "capacity": target.setCapacity(property(camelContext, int.class, 
value)); return true;
+        default: return false;
+        }
+    }
+
+    @Override
+    public Map<String, Object> getAllOptions(Object target) {
+        return ALL_OPTIONS;
+    }
+
+    @Override
+    public Class<?> getOptionType(String name, boolean ignoreCase) {
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "camelcontext":
+        case "camelContext": return org.apache.camel.CamelContext.class;
+        case "capacity": return int.class;
+        default: return null;
+        }
+    }
+
+    @Override
+    public Object getOptionValue(Object obj, String name, boolean ignoreCase) {
+        org.apache.camel.impl.console.SqlTraceDevConsole target = 
(org.apache.camel.impl.console.SqlTraceDevConsole) obj;
+        switch (ignoreCase ? name.toLowerCase() : name) {
+        case "camelcontext":
+        case "camelContext": return target.getCamelContext();
+        case "capacity": return target.getCapacity();
+        default: return null;
+        }
+    }
+}
+
diff --git 
a/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/sql-trace.json
 
b/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/sql-trace.json
new file mode 100644
index 000000000000..55335065a456
--- /dev/null
+++ 
b/core/camel-console/src/generated/resources/META-INF/org/apache/camel/dev-console/sql-trace.json
@@ -0,0 +1,15 @@
+{
+  "console": {
+    "kind": "console",
+    "group": "camel",
+    "name": "sql-trace",
+    "title": "SQL Trace",
+    "description": "Trace SQL query executions",
+    "deprecated": false,
+    "javaType": "org.apache.camel.impl.console.SqlTraceDevConsole",
+    "groupId": "org.apache.camel",
+    "artifactId": "camel-console",
+    "version": "4.21.0-SNAPSHOT"
+  }
+}
+
diff --git 
a/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.impl.console.SqlTraceDevConsole
 
b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.impl.console.SqlTraceDevConsole
new file mode 100644
index 000000000000..41852b612ce6
--- /dev/null
+++ 
b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/configurer/org.apache.camel.impl.console.SqlTraceDevConsole
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.impl.console.SqlTraceDevConsoleConfigurer
diff --git 
a/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/sql-trace
 
b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/sql-trace
new file mode 100644
index 000000000000..4d0f5e4e974b
--- /dev/null
+++ 
b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-console/sql-trace
@@ -0,0 +1,2 @@
+# Generated by camel build tools - do NOT edit this file!
+class=org.apache.camel.impl.console.SqlTraceDevConsole
diff --git 
a/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-consoles.properties
 
b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-consoles.properties
index 40b26b866b52..fe5d76fe5112 100644
--- 
a/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-consoles.properties
+++ 
b/core/camel-console/src/generated/resources/META-INF/services/org/apache/camel/dev-consoles.properties
@@ -1,5 +1,5 @@
 # Generated by camel build tools - do NOT edit this file!
-dev-consoles=bean blocked browse circuit-breaker consumer context datasource 
debug endpoint errors eval-language event gc health inflight internal-tasks 
java-security jvm log memory message-history processor producer properties 
receive reload rest rest-spec route route-controller route-dump route-group 
route-structure route-topology send service simple-language source sql-query 
startup-recorder system-properties thread top trace transformers 
type-converters variables
+dev-consoles=bean blocked browse circuit-breaker consumer context datasource 
debug endpoint errors eval-language event gc health inflight internal-tasks 
java-security jvm log memory message-history processor producer properties 
receive reload rest rest-spec route route-controller route-dump route-group 
route-structure route-topology send service simple-language source sql-query 
sql-trace startup-recorder system-properties thread top trace transformers 
type-converters variables
 groupId=org.apache.camel
 artifactId=camel-console
 version=4.21.0-SNAPSHOT
diff --git 
a/core/camel-console/src/main/java/org/apache/camel/impl/console/SqlTraceDevConsole.java
 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/SqlTraceDevConsole.java
new file mode 100644
index 000000000000..cd5841af616a
--- /dev/null
+++ 
b/core/camel-console/src/main/java/org/apache/camel/impl/console/SqlTraceDevConsole.java
@@ -0,0 +1,284 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.impl.console;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.camel.Exchange;
+import org.apache.camel.NonManagedService;
+import org.apache.camel.spi.CamelEvent;
+import org.apache.camel.spi.Configurer;
+import org.apache.camel.spi.Metadata;
+import org.apache.camel.spi.annotations.DevConsole;
+import org.apache.camel.support.EventNotifierSupport;
+import org.apache.camel.support.console.AbstractDevConsole;
+import org.apache.camel.util.StringHelper;
+import org.apache.camel.util.json.JsonArray;
+import org.apache.camel.util.json.JsonObject;
+
+@DevConsole(name = "sql-trace", displayName = "SQL Trace", description = 
"Trace SQL query executions")
+@Configurer(extended = true)
+public class SqlTraceDevConsole extends AbstractDevConsole {
+
+    @Metadata(defaultValue = "200",
+              description = "Maximum capacity of traced SQL statements 
(capacity must be between 25 and 1000)")
+    private int capacity = 200;
+
+    private JsonObject[] events;
+    private final AtomicInteger pos = new AtomicInteger();
+    private final ConsoleEventNotifier listener = new ConsoleEventNotifier();
+
+    public SqlTraceDevConsole() {
+        super("camel", "sql-trace", "SQL Trace", "Trace SQL query executions");
+    }
+
+    public int getCapacity() {
+        return capacity;
+    }
+
+    public void setCapacity(int capacity) {
+        this.capacity = capacity;
+    }
+
+    @Override
+    protected void doInit() throws Exception {
+        if (capacity > 1000 || capacity < 25) {
+            throw new IllegalArgumentException("Capacity must be between 25 
and 1000");
+        }
+        this.events = new JsonObject[capacity];
+    }
+
+    @Override
+    protected void doStart() throws Exception {
+        getCamelContext().getManagementStrategy().addEventNotifier(listener);
+    }
+
+    @Override
+    protected void doStop() throws Exception {
+        
getCamelContext().getManagementStrategy().removeEventNotifier(listener);
+    }
+
+    @Override
+    protected String doCallText(Map<String, Object> options) {
+        StringBuilder sb = new StringBuilder();
+
+        List<JsonObject> list = collectEvents();
+        for (JsonObject jo : list) {
+            sb.append(String.format("    %s %s %s (%d ms) route:%s%n",
+                    jo.getString("category"),
+                    jo.getString("query"),
+                    jo.getBooleanOrDefault("failed", false) ? "FAILED" : "OK",
+                    jo.getLongOrDefault("duration", 0),
+                    jo.getString("routeId")));
+        }
+        if (!list.isEmpty()) {
+            sb.insert(0, String.format("Last %d SQL Statements:%n", 
list.size()));
+        }
+
+        return sb.toString();
+    }
+
+    @Override
+    protected JsonObject doCallJson(Map<String, Object> options) {
+        JsonObject root = new JsonObject();
+
+        List<JsonObject> list = collectEvents();
+        if (!list.isEmpty()) {
+            JsonArray arr = new JsonArray();
+            arr.addAll(list);
+            root.put("statements", arr);
+
+            // compute summary
+            JsonObject summary = new JsonObject();
+            long total = list.size();
+            long totalTime = 0;
+            long slowest = 0;
+            long slowCount = 0;
+            long failedCount = 0;
+            long selectCount = 0;
+            long insertCount = 0;
+            long updateCount = 0;
+            long deleteCount = 0;
+
+            for (JsonObject jo : list) {
+                long duration = jo.getLongOrDefault("duration", 0);
+                totalTime += duration;
+                if (duration > slowest) {
+                    slowest = duration;
+                }
+                if (duration >= 100) {
+                    slowCount++;
+                }
+                if (jo.getBooleanOrDefault("failed", false)) {
+                    failedCount++;
+                }
+                String cat = jo.getStringOrDefault("category", "");
+                switch (cat) {
+                    case "SELECT":
+                        selectCount++;
+                        break;
+                    case "INSERT":
+                        insertCount++;
+                        break;
+                    case "UPDATE":
+                        updateCount++;
+                        break;
+                    case "DELETE":
+                        deleteCount++;
+                        break;
+                    default:
+                        break;
+                }
+            }
+
+            summary.put("totalQueries", total);
+            summary.put("avgTime", total > 0 ? totalTime / total : 0);
+            summary.put("slowestTime", slowest);
+            summary.put("slowCount", slowCount);
+            summary.put("failedCount", failedCount);
+            summary.put("selectCount", selectCount);
+            summary.put("insertCount", insertCount);
+            summary.put("updateCount", updateCount);
+            summary.put("deleteCount", deleteCount);
+            root.put("summary", summary);
+        }
+
+        return root;
+    }
+
+    private List<JsonObject> collectEvents() {
+        List<JsonObject> list = new ArrayList<>();
+        int cursor = pos.get();
+        // cursor points to the NEXT write slot, so walk backward from cursor-1
+        for (int i = 0; i < capacity; i++) {
+            cursor = (cursor - 1 + capacity) % capacity;
+            JsonObject event = events[cursor];
+            if (event != null) {
+                list.add(event);
+            }
+        }
+        return list;
+    }
+
+    private static String extractQuery(String endpointUri) {
+        if (endpointUri.startsWith("sql:")) {
+            String query = StringHelper.after(endpointUri, "sql:");
+            if (query != null) {
+                // remove query parameters
+                int idx = query.indexOf('?');
+                if (idx > 0) {
+                    query = query.substring(0, idx);
+                }
+                return query;
+            }
+        } else if (endpointUri.startsWith("jdbc:")) {
+            // for jdbc component, the URI path is the datasource name, not 
the SQL query
+            return null;
+        }
+        return null;
+    }
+
+    private static String detectCategory(String query) {
+        if (query != null && !query.isEmpty()) {
+            String upper = query.stripLeading().toUpperCase(Locale.ENGLISH);
+            if (upper.startsWith("SELECT")) {
+                return "SELECT";
+            } else if (upper.startsWith("INSERT")) {
+                return "INSERT";
+            } else if (upper.startsWith("UPDATE")) {
+                return "UPDATE";
+            } else if (upper.startsWith("DELETE")) {
+                return "DELETE";
+            } else if (upper.startsWith("CALL") || upper.startsWith("EXEC")) {
+                return "CALL";
+            }
+        }
+        return "OTHER";
+    }
+
+    private class ConsoleEventNotifier extends EventNotifierSupport implements 
NonManagedService {
+
+        ConsoleEventNotifier() {
+            setIgnoreCamelContextEvents(true);
+            setIgnoreRouteEvents(true);
+            setIgnoreServiceEvents(true);
+            setIgnoreExchangeCreatedEvent(true);
+            setIgnoreExchangeCompletedEvent(true);
+            setIgnoreExchangeFailedEvents(true);
+            setIgnoreExchangeRedeliveryEvents(true);
+            setIgnoreExchangeSendingEvents(true);
+            setIgnoreStepEvents(true);
+        }
+
+        @Override
+        public void notify(CamelEvent event) throws Exception {
+            if (event instanceof CamelEvent.ExchangeSentEvent ese) {
+                String uri = ese.getEndpoint().getEndpointUri();
+                if (uri.startsWith("sql:") || uri.startsWith("jdbc:")) {
+                    Exchange exchange = ese.getExchange();
+
+                    String query = extractQuery(uri);
+                    // for jdbc component, try to get query from exchange 
header
+                    if (query == null) {
+                        Object headerQuery = 
exchange.getMessage().getHeader("CamelSqlQuery");
+                        if (headerQuery != null) {
+                            query = headerQuery.toString();
+                        }
+                    }
+
+                    JsonObject jo = new JsonObject();
+                    jo.put("timestamp", event.getTimestamp());
+                    jo.put("exchangeId", exchange.getExchangeId());
+                    jo.put("routeId", exchange.getFromRouteId());
+                    jo.put("endpoint", uri);
+                    if (query != null) {
+                        jo.put("query", query);
+                        jo.put("category", detectCategory(query));
+                    } else {
+                        jo.put("query", uri);
+                        jo.put("category", "OTHER");
+                    }
+                    jo.put("duration", ese.getTimeTaken());
+                    jo.put("failed", exchange.isFailed());
+
+                    // row/update counts from sql and jdbc component headers
+                    Object rc = 
exchange.getMessage().getHeader("CamelSqlRowCount");
+                    if (rc == null) {
+                        rc = 
exchange.getMessage().getHeader("CamelJdbcRowCount");
+                    }
+                    if (rc instanceof Number) {
+                        jo.put("rowCount", ((Number) rc).intValue());
+                    }
+                    Object uc = 
exchange.getMessage().getHeader("CamelSqlUpdateCount");
+                    if (uc == null) {
+                        uc = 
exchange.getMessage().getHeader("CamelJdbcUpdateCount");
+                    }
+                    if (uc instanceof Number) {
+                        jo.put("updateCount", ((Number) uc).intValue());
+                    }
+
+                    int p = pos.getAndUpdate(operand -> ++operand % capacity);
+                    events[p] = jo;
+                }
+            }
+        }
+    }
+}
diff --git 
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-get-sql-trace.adoc
 
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-get-sql-trace.adoc
new file mode 100644
index 000000000000..65255889b756
--- /dev/null
+++ 
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-get-sql-trace.adoc
@@ -0,0 +1,28 @@
+
+// AUTO-GENERATED by camel-package-maven-plugin - DO NOT EDIT THIS FILE
+= camel get sql-trace
+
+Get SQL query trace data
+
+
+== Usage
+
+[source,bash]
+----
+camel get sql-trace [options]
+----
+
+
+
+== Options
+
+[cols="2,5,1,2",options="header"]
+|===
+| Option | Description | Default | Type
+| `--json` | Output in JSON Format |  | boolean
+| `--sort` | Sort by pid, name or age | pid | String
+| `--watch` | Execute periodically and showing output fullscreen |  | boolean
+| `-h,--help` | Display the help and sub-commands |  | boolean
+|===
+
+
diff --git 
a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-get.adoc 
b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-get.adoc
index 8d2eb511b28e..5365803b1101 100644
--- a/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-get.adoc
+++ b/docs/user-manual/modules/ROOT/pages/jbang-commands/camel-jbang-get.adoc
@@ -45,6 +45,7 @@ camel get [options]
 | xref:jbang-commands/camel-jbang-get-route-controller.adoc[route-controller] 
| List status of route controller
 | xref:jbang-commands/camel-jbang-get-service.adoc[service] | Get services of 
Camel integrations
 | xref:jbang-commands/camel-jbang-get-source.adoc[source] | Display Camel 
route source code
+| xref:jbang-commands/camel-jbang-get-sql-trace.adoc[sql-trace] | Get SQL 
query trace data
 | xref:jbang-commands/camel-jbang-get-startup-recorder.adoc[startup-recorder] 
| Display startup recording
 | xref:jbang-commands/camel-jbang-get-transformer.adoc[transformer] | Get list 
of data type transformers
 | xref:jbang-commands/camel-jbang-get-variable.adoc[variable] | List variables 
of Camel integrations
diff --git 
a/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java
 
b/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java
index fe943b169bb7..d8d562fb77a9 100644
--- 
a/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java
+++ 
b/dsl/camel-cli-connector/src/main/java/org/apache/camel/cli/connector/LocalCliConnector.java
@@ -1504,6 +1504,13 @@ public class LocalCliConnector extends ServiceSupport 
implements CliConnector, C
                         root.put("dataSources", json);
                     }
                 }
+                DevConsole dc28 = dcr.resolveById("sql-trace");
+                if (dc28 != null) {
+                    JsonObject json = (JsonObject) 
dc28.call(DevConsole.MediaType.JSON);
+                    if (json != null && !json.isEmpty()) {
+                        root.put("sqlTrace", json);
+                    }
+                }
             }
             // various details
             JsonObject mem = collectMemory();
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 6ee1954980ad..93ef87cda9f2 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, or LLM_API_KEY env vars", "javaType": 
"java.lang.String", "type": "string" }, { "names": "--api-type", "description": 
"API type: 'ollama', 'openai' (OpenAI-compatible), or 'anthropic' (A [...]
     { "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": "bean", 
"fullName": "get  [...]
+    { "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": "bean", 
"fullName": "get  [...]
     { "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/CamelJBangMain.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
index da28f9ad87bb..86f19a1abd67 100644
--- 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/CamelJBangMain.java
@@ -180,6 +180,7 @@ public class CamelJBangMain implements Callable<Integer> {
                         .addSubcommand("route-controller", new CommandLine(new 
RouteControllerAction(this)))
                         .addSubcommand("service", new CommandLine(new 
ListService(this)))
                         .addSubcommand("source", new CommandLine(new 
CamelSourceAction(this)))
+                        .addSubcommand("sql-trace", new CommandLine(new 
ListSqlTrace(this)))
                         .addSubcommand("startup-recorder", new CommandLine(new 
CamelStartupRecorderAction(this)))
                         .addSubcommand("transformer", new CommandLine(new 
ListTransformer(this)))
                         .addSubcommand("variable", new CommandLine(new 
ListVariable(this)))
diff --git 
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListSqlTrace.java
 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListSqlTrace.java
new file mode 100644
index 000000000000..275fe015b6b2
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/process/ListSqlTrace.java
@@ -0,0 +1,211 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.jbang.core.commands.process;
+
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import com.github.freva.asciitable.AsciiTable;
+import com.github.freva.asciitable.Column;
+import com.github.freva.asciitable.HorizontalAlign;
+import com.github.freva.asciitable.OverflowBehaviour;
+import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
+import org.apache.camel.dsl.jbang.core.common.PidNameAgeCompletionCandidates;
+import org.apache.camel.dsl.jbang.core.common.ProcessHelper;
+import org.apache.camel.util.TimeUtils;
+import org.apache.camel.util.json.JsonArray;
+import org.apache.camel.util.json.JsonObject;
+import org.apache.camel.util.json.Jsoner;
+import picocli.CommandLine;
+import picocli.CommandLine.Command;
+
+@Command(name = "sql-trace", description = "Get SQL query trace data", 
sortOptions = false,
+         showDefaultValues = true,
+         footer = {
+                 "%nExamples:",
+                 "  camel get sql-trace",
+                 "  camel get sql-trace --watch" })
+public class ListSqlTrace extends ProcessWatchCommand {
+
+    private static final DateTimeFormatter TIME_FMT = 
DateTimeFormatter.ofPattern("HH:mm:ss.SSS");
+
+    @CommandLine.Parameters(description = "Name or pid of running Camel 
integration", arity = "0..1")
+    String name = "*";
+
+    @CommandLine.Option(names = { "--sort" }, completionCandidates = 
PidNameAgeCompletionCandidates.class,
+                        description = "Sort by pid, name or age", defaultValue 
= "pid")
+    String sort;
+
+    public ListSqlTrace(CamelJBangMain main) {
+        super(main);
+    }
+
+    @Override
+    public Integer doProcessWatchCall() throws Exception {
+        List<Row> rows = new ArrayList<>();
+
+        List<Long> pids = findPids(name);
+        ProcessHandle.allProcesses()
+                .filter(ph -> pids.contains(ph.pid()))
+                .forEach(ph -> {
+                    JsonObject root = loadStatus(ph.pid());
+                    if (root != null) {
+                        JsonObject context = (JsonObject) root.get("context");
+                        JsonObject sqlTrace = (JsonObject) 
root.get("sqlTrace");
+                        if (context != null && sqlTrace != null) {
+                            JsonArray array = (JsonArray) 
sqlTrace.get("statements");
+                            if (array != null) {
+                                for (int i = 0; i < array.size(); i++) {
+                                    JsonObject o = (JsonObject) array.get(i);
+                                    Row row = new Row();
+                                    row.name = context.getString("name");
+                                    if ("CamelJBang".equals(row.name)) {
+                                        row.name = 
ProcessHelper.extractName(root, ph);
+                                    }
+                                    row.pid = Long.toString(ph.pid());
+                                    row.uptime = extractSince(ph);
+                                    row.age = TimeUtils.printSince(row.uptime);
+                                    row.timestamp = 
o.getLongOrDefault("timestamp", 0);
+                                    row.exchangeId = o.getString("exchangeId");
+                                    row.routeId = o.getString("routeId");
+                                    row.query = o.getString("query");
+                                    row.category = o.getString("category");
+                                    row.endpoint = o.getString("endpoint");
+                                    row.duration = 
o.getLongOrDefault("duration", 0);
+                                    row.rowCount = 
o.getIntegerOrDefault("rowCount", 0);
+                                    row.updateCount = 
o.getIntegerOrDefault("updateCount", 0);
+                                    row.failed = 
o.getBooleanOrDefault("failed", false);
+                                    rows.add(row);
+                                }
+                            }
+                        }
+                    }
+                });
+
+        // sort rows
+        rows.sort(this::sortRow);
+
+        if (!rows.isEmpty()) {
+            printTable(rows);
+        }
+
+        return 0;
+    }
+
+    protected void printTable(List<Row> rows) {
+        if (jsonOutput) {
+            printer().println(Jsoner.serialize(rows.stream().map(r -> {
+                JsonObject jo = new JsonObject();
+                jo.put("pid", r.pid);
+                jo.put("name", r.name);
+                jo.put("age", r.age);
+                jo.put("timestamp", r.timestamp);
+                jo.put("exchangeId", r.exchangeId);
+                jo.put("routeId", r.routeId);
+                jo.put("query", r.query);
+                jo.put("category", r.category);
+                jo.put("endpoint", r.endpoint);
+                jo.put("duration", r.duration);
+                jo.put("rowCount", r.rowCount);
+                jo.put("updateCount", r.updateCount);
+                jo.put("failed", r.failed);
+                return jo;
+            }).collect(Collectors.toList())));
+            return;
+        }
+        int tw = terminalWidth();
+        int sqlW = Math.max(20, Math.min(80, tw - 100));
+        printer().println(AsciiTable.getTable(AsciiTable.NO_BORDERS, rows, 
Arrays.asList(
+                new 
Column().header("PID").headerAlign(HorizontalAlign.CENTER).with(r -> r.pid),
+                new 
Column().header("NAME").dataAlign(HorizontalAlign.LEFT).maxWidth(30, 
OverflowBehaviour.ELLIPSIS_RIGHT)
+                        .with(r -> r.name),
+                new 
Column().header("AGE").headerAlign(HorizontalAlign.CENTER).with(r -> r.age),
+                new 
Column().header("TIME").headerAlign(HorizontalAlign.CENTER).with(this::formatTime),
+                new 
Column().header("CAT").dataAlign(HorizontalAlign.LEFT).with(r -> r.category != 
null ? r.category : ""),
+                new Column().header("SQL").dataAlign(HorizontalAlign.LEFT)
+                        .maxWidth(sqlW, OverflowBehaviour.ELLIPSIS_RIGHT)
+                        .with(r -> r.query != null ? r.query : ""),
+                new Column().header("ROUTE").dataAlign(HorizontalAlign.LEFT)
+                        .maxWidth(20, OverflowBehaviour.ELLIPSIS_RIGHT)
+                        .with(r -> r.routeId != null ? r.routeId : ""),
+                new 
Column().header("DURATION").headerAlign(HorizontalAlign.RIGHT).dataAlign(HorizontalAlign.RIGHT)
+                        .with(r -> r.duration + " ms"),
+                new 
Column().header("ROWS").headerAlign(HorizontalAlign.RIGHT).dataAlign(HorizontalAlign.RIGHT)
+                        .with(this::formatRows),
+                new Column().header("STATUS").dataAlign(HorizontalAlign.CENTER)
+                        .with(r -> r.failed ? "FAIL" : "OK"))));
+    }
+
+    private String formatTime(Row r) {
+        if (r.timestamp > 0) {
+            return LocalDateTime.ofInstant(Instant.ofEpochMilli(r.timestamp), 
ZoneId.systemDefault())
+                    .format(TIME_FMT);
+        }
+        return "";
+    }
+
+    private String formatRows(Row r) {
+        if (r.rowCount > 0) {
+            return Integer.toString(r.rowCount);
+        } else if (r.updateCount > 0) {
+            return Integer.toString(r.updateCount);
+        }
+        return "";
+    }
+
+    protected int sortRow(Row o1, Row o2) {
+        String s = sort;
+        int negate = 1;
+        if (s.startsWith("-")) {
+            s = s.substring(1);
+            negate = -1;
+        }
+        switch (s) {
+            case "pid":
+                return Long.compare(Long.parseLong(o1.pid), 
Long.parseLong(o2.pid)) * negate;
+            case "name":
+                return o1.name.compareToIgnoreCase(o2.name) * negate;
+            case "age":
+                return Long.compare(o1.uptime, o2.uptime) * negate;
+            default:
+                return 0;
+        }
+    }
+
+    static class Row {
+        String pid;
+        String name;
+        long uptime;
+        String age;
+        long timestamp;
+        String exchangeId;
+        String routeId;
+        String query;
+        String category;
+        String endpoint;
+        long duration;
+        int rowCount;
+        int updateCount;
+        boolean failed;
+    }
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/IntegrationInfo.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/IntegrationInfo.java
index a4009ab251c4..2820a1956d93 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/IntegrationInfo.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/IntegrationInfo.java
@@ -83,6 +83,12 @@ class IntegrationInfo {
     final List<HttpEndpointInfo> httpEndpoints = new ArrayList<>();
     final List<ConfigurationTab.ConfigProperty> configProperties = new 
ArrayList<>();
     final List<DataSourceInfo> dataSources = new ArrayList<>();
+    final List<SqlTraceInfo> sqlTraceStatements = new ArrayList<>();
+    long sqlTraceTotal;
+    long sqlTraceAvgTime;
+    long sqlTraceSlowestTime;
+    long sqlTraceSlowCount;
+    long sqlTraceFailedCount;
     String httpServer;
     String readmeFiles;
 }
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/McpFacade.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/McpFacade.java
index 99efb93f2856..d8fa65a41ec7 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/McpFacade.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/McpFacade.java
@@ -89,8 +89,8 @@ class McpFacade {
 
     static final String[] MORE_TAB_NAMES = {
             "Beans", "Browse", "Circuit Breaker", "Classpath", "Configuration",
-            "Consumers", "DataSource", "Inflight", "Memory", "Metrics", "SQL 
Query", "Spans", "Process", "Startup",
-            "Threads"
+            "Consumers", "DataSource", "Inflight", "Memory", "Metrics", "SQL 
Query", "SQL Trace", "Spans", "Process",
+            "Startup", "Threads"
     };
 
     private final MonitorContext ctx;
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/PopupManager.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/PopupManager.java
index 6a615d808037..ba1c87e859ed 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/PopupManager.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/PopupManager.java
@@ -176,7 +176,7 @@ class PopupManager {
             return true;
         }
         if (ke.isDown()) {
-            morePopupState.selectNext(15);
+            morePopupState.selectNext(16);
             return true;
         }
         int shortcutSel = morePopupShortcut(ke);
@@ -239,7 +239,7 @@ class PopupManager {
 
     void renderMorePopup(Frame frame, Rect area) {
         int popupW = 22;
-        int popupH = 17;
+        int popupH = 18;
         // Position just below the "0 More▾" tab label
         int dividerW = CharWidth.of(" | ");
         int tabBarX = 0;
@@ -272,6 +272,7 @@ class PopupManager {
                 ListItem.from(Line.from(Span.raw("  "), Span.styled("M", 
keyStyle), Span.raw("emory"))),
                 ListItem.from(Line.from(Span.raw("  M"), Span.styled("e", 
keyStyle), Span.raw("trics"))),
                 ListItem.from(Line.from(Span.raw("  S"), Span.styled("Q", 
keyStyle), Span.raw("L Query"))),
+                ListItem.from(Line.from(Span.raw("  SQL T"), Span.styled("r", 
keyStyle), Span.raw("ace"))),
                 ListItem.from(Line.from(Span.raw("  "), Span.styled("O", 
keyStyle), Span.raw("Tel Spans"))),
                 ListItem.from(Line.from(Span.raw("  "), Span.styled("P", 
keyStyle), Span.raw("rocess"))),
                 ListItem.from(Line.from(Span.raw("  "), Span.styled("S", 
keyStyle), Span.raw("tartup"))),
@@ -408,18 +409,21 @@ class PopupManager {
         if (ke.isChar('q')) {
             return 10;
         }
-        if (ke.isChar('o')) {
+        if (ke.isChar('r')) {
             return 11;
         }
-        if (ke.isChar('p')) {
+        if (ke.isChar('o')) {
             return 12;
         }
-        if (ke.isChar('s')) {
+        if (ke.isChar('p')) {
             return 13;
         }
-        if (ke.isChar('t')) {
+        if (ke.isChar('s')) {
             return 14;
         }
+        if (ke.isChar('t')) {
+            return 15;
+        }
         return -1;
     }
 
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SqlTraceInfo.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SqlTraceInfo.java
new file mode 100644
index 000000000000..cdd6168607c9
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SqlTraceInfo.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.jbang.core.commands.tui;
+
+class SqlTraceInfo {
+    String exchangeId;
+    String routeId;
+    String query;
+    String category;
+    String endpoint;
+    long timestamp;
+    long duration;
+    int rowCount;
+    int updateCount;
+    boolean failed;
+}
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SqlTraceTab.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SqlTraceTab.java
new file mode 100644
index 000000000000..37fe1c062a63
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/SqlTraceTab.java
@@ -0,0 +1,336 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.jbang.core.commands.tui;
+
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.List;
+
+import dev.tamboui.layout.Constraint;
+import dev.tamboui.layout.Layout;
+import dev.tamboui.layout.Rect;
+import dev.tamboui.style.Color;
+import dev.tamboui.style.Style;
+import dev.tamboui.terminal.Frame;
+import dev.tamboui.text.Line;
+import dev.tamboui.text.Span;
+import dev.tamboui.text.Text;
+import dev.tamboui.tui.event.KeyEvent;
+import dev.tamboui.widgets.block.Block;
+import dev.tamboui.widgets.block.BorderType;
+import dev.tamboui.widgets.paragraph.Paragraph;
+import dev.tamboui.widgets.table.Cell;
+import dev.tamboui.widgets.table.Row;
+import dev.tamboui.widgets.table.Table;
+import dev.tamboui.widgets.table.TableState;
+import org.apache.camel.util.json.JsonArray;
+import org.apache.camel.util.json.JsonObject;
+
+import static org.apache.camel.dsl.jbang.core.commands.tui.MonitorContext.*;
+
+class SqlTraceTab implements MonitorTab {
+
+    private static final String[] SORT_COLUMNS = { "time", "category", "sql", 
"route", "duration", "rows" };
+    private static final DateTimeFormatter TIME_FMT = 
DateTimeFormatter.ofPattern("HH:mm:ss.SSS");
+
+    private final MonitorContext ctx;
+    private final TableState tableState = new TableState();
+    private String sort = "time";
+    private int sortIndex;
+    private boolean sortReversed;
+
+    SqlTraceTab(MonitorContext ctx) {
+        this.ctx = ctx;
+    }
+
+    @Override
+    public boolean handleKeyEvent(KeyEvent ke) {
+        if (ke.isChar('s')) {
+            sortIndex = (sortIndex + 1) % SORT_COLUMNS.length;
+            sort = SORT_COLUMNS[sortIndex];
+            sortReversed = false;
+            return true;
+        }
+        if (ke.isChar('S')) {
+            sortReversed = !sortReversed;
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    public boolean handleEscape() {
+        return false;
+    }
+
+    @Override
+    public void navigateUp() {
+    }
+
+    @Override
+    public void navigateDown() {
+    }
+
+    @Override
+    public void render(Frame frame, Rect area) {
+        IntegrationInfo info = ctx.findSelectedIntegration();
+        if (info == null) {
+            renderNoSelection(frame, area);
+            return;
+        }
+
+        List<Rect> layout = Layout.vertical()
+                .constraints(Constraint.length(3), Constraint.fill())
+                .split(area);
+
+        renderKpiStrip(frame, layout.get(0), info);
+        renderTable(frame, layout.get(1), info);
+    }
+
+    private void renderKpiStrip(Frame frame, Rect area, IntegrationInfo info) {
+        Style labelStyle = Style.EMPTY.dim();
+        Style valueStyle = Style.EMPTY.fg(Color.CYAN).bold();
+        Style warnStyle = Style.EMPTY.fg(Color.YELLOW).bold();
+        Style errorStyle = Style.EMPTY.fg(Color.LIGHT_RED).bold();
+
+        List<Span> spans = new ArrayList<>();
+        spans.add(Span.styled("  Total: ", labelStyle));
+        spans.add(Span.styled(String.valueOf(info.sqlTraceTotal), valueStyle));
+        spans.add(Span.styled("  Avg: ", labelStyle));
+        spans.add(Span.styled(info.sqlTraceAvgTime + " ms", valueStyle));
+        spans.add(Span.styled("  Slowest: ", labelStyle));
+        Style slowestStyle = info.sqlTraceSlowestTime >= 100 ? warnStyle : 
valueStyle;
+        spans.add(Span.styled(info.sqlTraceSlowestTime + " ms", slowestStyle));
+        spans.add(Span.styled("  Slow(>=100ms): ", labelStyle));
+        Style slowStyle = info.sqlTraceSlowCount > 0 ? warnStyle : valueStyle;
+        spans.add(Span.styled(String.valueOf(info.sqlTraceSlowCount), 
slowStyle));
+        spans.add(Span.styled("  Failed: ", labelStyle));
+        Style failStyle = info.sqlTraceFailedCount > 0 ? errorStyle : 
valueStyle;
+        spans.add(Span.styled(String.valueOf(info.sqlTraceFailedCount), 
failStyle));
+
+        Paragraph kpi = Paragraph.builder()
+                .text(Text.from(Line.from(spans)))
+                .block(Block.builder().borderType(BorderType.ROUNDED)
+                        .title(" SQL Trace ").build())
+                .build();
+        frame.renderWidget(kpi, area);
+    }
+
+    private void renderTable(Frame frame, Rect area, IntegrationInfo info) {
+        List<SqlTraceInfo> sorted = new ArrayList<>(info.sqlTraceStatements);
+        sorted.sort(this::sortTrace);
+
+        List<Row> rows = new ArrayList<>();
+        for (SqlTraceInfo si : sorted) {
+            Style durStyle = si.duration >= 100 ? Style.EMPTY.fg(Color.YELLOW) 
: Style.EMPTY;
+            Style statusStyle = si.failed ? Style.EMPTY.fg(Color.LIGHT_RED) : 
Style.EMPTY.fg(Color.GREEN);
+            String status = si.failed ? "FAIL" : "OK";
+
+            String time = "";
+            if (si.timestamp > 0) {
+                time = 
LocalDateTime.ofInstant(Instant.ofEpochMilli(si.timestamp), 
ZoneId.systemDefault())
+                        .format(TIME_FMT);
+            }
+
+            String rowsStr = "";
+            if (si.rowCount > 0) {
+                rowsStr = String.valueOf(si.rowCount);
+            } else if (si.updateCount > 0) {
+                rowsStr = String.valueOf(si.updateCount);
+            }
+
+            rows.add(Row.from(
+                    Cell.from(Span.styled(time, Style.EMPTY.dim())),
+                    Cell.from(Span.styled(si.category != null ? si.category : 
"", categoryStyle(si.category))),
+                    Cell.from(si.query != null ? si.query : ""),
+                    Cell.from(Span.styled(si.routeId != null ? si.routeId : 
"", Style.EMPTY.fg(Color.CYAN))),
+                    rightCell(String.valueOf(si.duration), 10, durStyle),
+                    rightCell(rowsStr, 8),
+                    Cell.from(Span.styled(status, statusStyle))));
+        }
+
+        if (rows.isEmpty()) {
+            rows.add(Row.from(
+                    Cell.from(""), Cell.from(""),
+                    Cell.from(Span.styled("No SQL statements traced", 
Style.EMPTY.dim())),
+                    Cell.from(""), Cell.from(""), Cell.from(""), 
Cell.from("")));
+        }
+
+        Table table = Table.builder()
+                .rows(rows)
+                .header(Row.from(
+                        Cell.from(Span.styled(sortLabel("TIME", "time"), 
sortStyle("time"))),
+                        Cell.from(Span.styled(sortLabel("CAT", "category"), 
sortStyle("category"))),
+                        Cell.from(Span.styled(sortLabel("SQL", "sql"), 
sortStyle("sql"))),
+                        Cell.from(Span.styled(sortLabel("ROUTE", "route"), 
sortStyle("route"))),
+                        rightCell(sortLabel("DURATION", "duration"), 10, 
sortStyle("duration")),
+                        rightCell(sortLabel("ROWS", "rows"), 8, 
sortStyle("rows")),
+                        Cell.from(Span.styled("STATUS", Style.EMPTY.bold()))))
+                .widths(
+                        Constraint.length(14),
+                        Constraint.length(8),
+                        Constraint.fill(),
+                        Constraint.length(20),
+                        Constraint.length(10),
+                        Constraint.length(8),
+                        Constraint.length(8))
+                .block(Block.builder().borderType(BorderType.ROUNDED)
+                        .title(" Statements sort:" + sort + " ").build())
+                .build();
+
+        frame.renderStatefulWidget(table, area, tableState);
+    }
+
+    private static Style categoryStyle(String category) {
+        if (category == null) {
+            return Style.EMPTY;
+        }
+        return switch (category) {
+            case "SELECT" -> Style.EMPTY.fg(Color.CYAN);
+            case "INSERT" -> Style.EMPTY.fg(Color.GREEN);
+            case "UPDATE" -> Style.EMPTY.fg(Color.YELLOW);
+            case "DELETE" -> Style.EMPTY.fg(Color.LIGHT_RED);
+            default -> Style.EMPTY;
+        };
+    }
+
+    @Override
+    public void renderFooter(List<Span> spans) {
+        hint(spans, "Esc", "back");
+        hint(spans, "s", "sort");
+    }
+
+    private String sortLabel(String label, String column) {
+        return MonitorContext.sortLabel(label, column, sort, sortReversed);
+    }
+
+    private Style sortStyle(String column) {
+        return MonitorContext.sortStyle(column, sort);
+    }
+
+    private int sortTrace(SqlTraceInfo a, SqlTraceInfo b) {
+        int result = switch (sort) {
+            case "category" -> {
+                String ca = a.category != null ? a.category : "";
+                String cb = b.category != null ? b.category : "";
+                yield ca.compareToIgnoreCase(cb);
+            }
+            case "sql" -> {
+                String qa = a.query != null ? a.query : "";
+                String qb = b.query != null ? b.query : "";
+                yield qa.compareToIgnoreCase(qb);
+            }
+            case "route" -> {
+                String ra = a.routeId != null ? a.routeId : "";
+                String rb = b.routeId != null ? b.routeId : "";
+                yield ra.compareToIgnoreCase(rb);
+            }
+            case "duration" -> Long.compare(b.duration, a.duration);
+            case "rows" -> {
+                int ra = a.rowCount > 0 ? a.rowCount : a.updateCount;
+                int rb = b.rowCount > 0 ? b.rowCount : b.updateCount;
+                yield Integer.compare(rb, ra);
+            }
+            default -> Long.compare(b.timestamp, a.timestamp); // "time" — 
newest first
+        };
+        return sortReversed ? -result : result;
+    }
+
+    @Override
+    public SelectionContext getSelectionContext() {
+        IntegrationInfo info = ctx.findSelectedIntegration();
+        if (info == null || info.sqlTraceStatements.isEmpty()) {
+            return null;
+        }
+        List<SqlTraceInfo> sorted = new ArrayList<>(info.sqlTraceStatements);
+        sorted.sort(this::sortTrace);
+        List<String> items = sorted.stream()
+                .map(s -> s.query != null ? s.query : s.endpoint)
+                .toList();
+        Integer sel = tableState.selected();
+        return new SelectionContext("table", items, sel != null ? sel : -1, 
items.size(), "SQL Trace");
+    }
+
+    @Override
+    public String getHelpText() {
+        return """
+                # SQL Trace
+
+                Traces SQL query executions flowing through `camel-sql` and 
`camel-jdbc`
+                components. Captures individual executions with timing, row 
counts,
+                and failure status.
+
+                ## KPI Strip
+
+                The top bar shows aggregate statistics:
+                - **Total** — Total number of SQL statements traced
+                - **Avg** — Average execution time in milliseconds
+                - **Slowest** — Longest single execution (yellow when >= 100ms)
+                - **Slow(>=100ms)** — Count of slow queries (yellow when > 0)
+                - **Failed** — Count of failed executions (red when > 0)
+
+                ## Table Columns
+
+                - **TIME** — Timestamp of the execution
+                - **CAT** — SQL category: SELECT, INSERT, UPDATE, DELETE, 
CALL, or OTHER
+                - **SQL** — The SQL query text
+                - **ROUTE** — The Camel route ID that executed the query
+                - **DURATION** — Execution time in ms (yellow when >= 100ms)
+                - **ROWS** — Row count (for SELECT) or update count (for 
INSERT/UPDATE/DELETE)
+                - **STATUS** — OK (green) or FAIL (red)
+
+                ## Keys
+
+                - `Up/Down` — select statement
+                - `s` — cycle sort column
+                - `S` — reverse sort order
+                """;
+    }
+
+    @Override
+    public JsonObject getTableDataAsJson() {
+        IntegrationInfo info = ctx.findSelectedIntegration();
+        if (info == null) {
+            return null;
+        }
+        JsonObject result = new JsonObject();
+        result.put("tab", "SQL Trace");
+        JsonArray rows = new JsonArray();
+        for (SqlTraceInfo si : info.sqlTraceStatements) {
+            JsonObject row = new JsonObject();
+            row.put("timestamp", si.timestamp);
+            row.put("exchangeId", si.exchangeId);
+            row.put("routeId", si.routeId);
+            row.put("query", si.query);
+            row.put("category", si.category);
+            row.put("endpoint", si.endpoint);
+            row.put("duration", si.duration);
+            row.put("rowCount", si.rowCount);
+            row.put("updateCount", si.updateCount);
+            row.put("failed", si.failed);
+            rows.add(row);
+        }
+        result.put("rows", rows);
+        result.put("totalRows", info.sqlTraceStatements.size());
+        Integer sel = tableState.selected();
+        result.put("selectedIndex", sel != null ? sel : -1);
+        return result;
+    }
+}
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 515c080554fb..54ef3c317adf 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
@@ -522,6 +522,37 @@ final class StatusParser {
             }
         }
 
+        // Parse sqlTrace
+        JsonObject sqlTraceObj = (JsonObject) root.get("sqlTrace");
+        if (sqlTraceObj != null) {
+            JsonObject summary = (JsonObject) sqlTraceObj.get("summary");
+            if (summary != null) {
+                info.sqlTraceTotal = summary.getLongOrDefault("totalQueries", 
0);
+                info.sqlTraceAvgTime = summary.getLongOrDefault("avgTime", 0);
+                info.sqlTraceSlowestTime = 
summary.getLongOrDefault("slowestTime", 0);
+                info.sqlTraceSlowCount = summary.getLongOrDefault("slowCount", 
0);
+                info.sqlTraceFailedCount = 
summary.getLongOrDefault("failedCount", 0);
+            }
+            JsonArray stmts = (JsonArray) sqlTraceObj.get("statements");
+            if (stmts != null) {
+                for (Object s : stmts) {
+                    JsonObject sj = (JsonObject) s;
+                    SqlTraceInfo si = new SqlTraceInfo();
+                    si.exchangeId = sj.getString("exchangeId");
+                    si.routeId = sj.getString("routeId");
+                    si.query = sj.getString("query");
+                    si.category = sj.getString("category");
+                    si.endpoint = sj.getString("endpoint");
+                    si.timestamp = sj.getLongOrDefault("timestamp", 0);
+                    si.duration = sj.getLongOrDefault("duration", 0);
+                    si.rowCount = sj.getIntegerOrDefault("rowCount", 0);
+                    si.updateCount = sj.getIntegerOrDefault("updateCount", 0);
+                    si.failed = sj.getBooleanOrDefault("failed", false);
+                    info.sqlTraceStatements.add(si);
+                }
+            }
+        }
+
         return info;
     }
 
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TabRegistry.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TabRegistry.java
index 68a948ed51e2..c0a9051f2125 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TabRegistry.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TabRegistry.java
@@ -87,6 +87,7 @@ class TabRegistry {
     private OverviewTab overviewTab;
     private DataSourceTab dataSourceTab;
     private SqlQueryTab sqlQueryTab;
+    private SqlTraceTab sqlTraceTab;
 
     private MonitorTab activeMoreTab;
 
@@ -105,6 +106,7 @@ class TabRegistry {
         consumersTab = new ConsumersTab(ctx);
         dataSourceTab = new DataSourceTab(ctx);
         sqlQueryTab = new SqlQueryTab(ctx);
+        sqlTraceTab = new SqlTraceTab(ctx);
         endpointsTab = new EndpointsTab(ctx, dataService.metrics());
         httpTab = new HttpTab(ctx);
         healthTab = new HealthTab(ctx);
@@ -213,10 +215,11 @@ class TabRegistry {
             case 8 -> memoryTab;
             case 9 -> metricsTab;
             case 10 -> sqlQueryTab;
-            case 11 -> spansTab;
-            case 12 -> processTab;
-            case 13 -> startupTab;
-            case 14 -> threadsTab;
+            case 11 -> sqlTraceTab;
+            case 12 -> spansTab;
+            case 13 -> processTab;
+            case 14 -> startupTab;
+            case 15 -> threadsTab;
             default -> null;
         };
         if (activeMoreTab != null) {
@@ -240,6 +243,7 @@ class TabRegistry {
         consumersTab.onIntegrationChanged();
         dataSourceTab.onIntegrationChanged();
         sqlQueryTab.onIntegrationChanged();
+        sqlTraceTab.onIntegrationChanged();
         circuitBreakerTab.onIntegrationChanged();
         inflightTab.onIntegrationChanged();
         spansTab.onIntegrationChanged();
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SqlTraceTabRenderTest.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SqlTraceTabRenderTest.java
new file mode 100644
index 000000000000..0b66e45a1490
--- /dev/null
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/SqlTraceTabRenderTest.java
@@ -0,0 +1,196 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.jbang.core.commands.tui;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicReference;
+
+import dev.tamboui.buffer.Buffer;
+import dev.tamboui.layout.Rect;
+import dev.tamboui.style.Color;
+import dev.tamboui.terminal.Frame;
+import dev.tamboui.text.Span;
+import dev.tamboui.tui.event.KeyEvent;
+import dev.tamboui.tui.event.KeyModifiers;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+class SqlTraceTabRenderTest {
+
+    private MonitorContext ctx;
+    private IntegrationInfo info;
+
+    @BeforeEach
+    void setUp() {
+        info = new IntegrationInfo();
+        info.pid = "1234";
+        info.name = "test-app";
+
+        AtomicReference<List<IntegrationInfo>> data = new 
AtomicReference<>(List.of(info));
+        AtomicReference<List<InfraInfo>> infraData = new 
AtomicReference<>(List.of());
+        ctx = new MonitorContext(data, infraData);
+        ctx.selectedPid = "1234";
+    }
+
+    @Test
+    void renderShowsKpiStrip() {
+        info.sqlTraceTotal = 42;
+        info.sqlTraceAvgTime = 15;
+        info.sqlTraceSlowestTime = 120;
+        info.sqlTraceSlowCount = 3;
+        info.sqlTraceFailedCount = 1;
+
+        SqlTraceTab tab = new SqlTraceTab(ctx);
+        String rendered = TuiTestHelper.renderToString(tab, 140, 25);
+
+        assertTrue(rendered.contains("SQL Trace"), "Should show SQL Trace 
title");
+        assertTrue(rendered.contains("Total:"), "Should show Total KPI");
+        assertTrue(rendered.contains("42"), "Should show total count");
+        assertTrue(rendered.contains("Avg:"), "Should show Avg KPI");
+    }
+
+    @Test
+    void renderShowsTableHeaders() {
+        addSqlTrace("SELECT * FROM users", "SELECT", "route1", 25, 10, false);
+
+        SqlTraceTab tab = new SqlTraceTab(ctx);
+        String rendered = TuiTestHelper.renderToString(tab, 140, 25);
+
+        assertTrue(rendered.contains("TIME"), "Should show TIME header");
+        assertTrue(rendered.contains("CAT"), "Should show CAT header");
+        assertTrue(rendered.contains("SQL"), "Should show SQL header");
+        assertTrue(rendered.contains("ROUTE"), "Should show ROUTE header");
+        assertTrue(rendered.contains("DURATION"), "Should show DURATION 
header");
+    }
+
+    @Test
+    void renderShowsSqlData() {
+        addSqlTrace("SELECT * FROM orders", "SELECT", "orderRoute", 15, 5, 
false);
+
+        SqlTraceTab tab = new SqlTraceTab(ctx);
+        String rendered = TuiTestHelper.renderToString(tab, 140, 25);
+
+        assertTrue(rendered.contains("SELECT * FROM orders"), "Should render 
SQL query text");
+        assertTrue(rendered.contains("orderRoute"), "Should render route ID");
+    }
+
+    @Test
+    void renderSlowQueryHighlighted() {
+        addSqlTrace("SELECT * FROM big_table", "SELECT", "route1", 150, 1000, 
false);
+
+        SqlTraceTab tab = new SqlTraceTab(ctx);
+
+        Rect area = new Rect(0, 0, 140, 25);
+        Buffer buffer = Buffer.empty(area);
+        Frame frame = Frame.forTesting(buffer);
+        tab.render(frame, area);
+
+        boolean foundYellow = TuiTestHelper.findCellWithColor(buffer, "1", 
Color.YELLOW);
+        assertTrue(foundYellow, "Slow query duration should be highlighted in 
YELLOW");
+    }
+
+    @Test
+    void renderFailedQueryInRed() {
+        addSqlTrace("INSERT INTO bad_table", "INSERT", "route1", 5, 0, true);
+
+        SqlTraceTab tab = new SqlTraceTab(ctx);
+
+        Rect area = new Rect(0, 0, 140, 25);
+        Buffer buffer = Buffer.empty(area);
+        Frame frame = Frame.forTesting(buffer);
+        tab.render(frame, area);
+
+        boolean foundRed = TuiTestHelper.findCellWithColor(buffer, "F", 
Color.LIGHT_RED);
+        assertTrue(foundRed, "Failed status should be rendered in LIGHT_RED");
+    }
+
+    @Test
+    void renderEmptyShowsPlaceholder() {
+        SqlTraceTab tab = new SqlTraceTab(ctx);
+        String rendered = TuiTestHelper.renderToString(tab, 140, 25);
+
+        assertTrue(rendered.contains("No SQL statements"), "Should show 
placeholder when no traces exist");
+    }
+
+    @Test
+    void renderNoSelectionShowsPrompt() {
+        ctx.selectedPid = null;
+
+        SqlTraceTab tab = new SqlTraceTab(ctx);
+        String rendered = TuiTestHelper.renderToString(tab, 100, 25);
+
+        assertTrue(rendered.contains("No integration selected") || 
rendered.contains("Select an integration"),
+                "Should show selection prompt when no integration selected");
+    }
+
+    @Test
+    void sortCycleChangesSortIndicator() {
+        addSqlTrace("SELECT 1", "SELECT", "route1", 10, 1, false);
+
+        SqlTraceTab tab = new SqlTraceTab(ctx);
+
+        tab.handleKeyEvent(KeyEvent.ofChar('s', KeyModifiers.NONE));
+        String rendered = TuiTestHelper.renderToString(tab, 140, 25);
+
+        assertTrue(rendered.contains("sort:category"), "Sort should cycle to 
'category' after pressing 's'");
+    }
+
+    @Test
+    void renderFooterHints() {
+        SqlTraceTab tab = new SqlTraceTab(ctx);
+        List<Span> footerSpans = new ArrayList<>();
+        tab.renderFooter(footerSpans);
+
+        String footer = footerSpans.stream()
+                .map(Span::content)
+                .reduce("", String::concat);
+
+        assertTrue(footer.contains("Esc"), "Footer should contain Esc hint");
+        assertTrue(footer.contains("sort"), "Footer should contain sort hint");
+    }
+
+    @Test
+    void helpTextIsAvailable() {
+        SqlTraceTab tab = new SqlTraceTab(ctx);
+        String help = tab.getHelpText();
+        assertNotNull(help, "Help text should not be null");
+        assertTrue(help.contains("SQL Trace"), "Help text should mention SQL 
Trace");
+        assertTrue(help.contains("KPI Strip"), "Help text should describe KPI 
strip");
+    }
+
+    // ---- Helper methods ----
+
+    private void addSqlTrace(
+            String query, String category, String routeId, long duration, int 
rowCount,
+            boolean failed) {
+        SqlTraceInfo si = new SqlTraceInfo();
+        si.query = query;
+        si.category = category;
+        si.routeId = routeId;
+        si.duration = duration;
+        si.rowCount = rowCount;
+        si.failed = failed;
+        si.exchangeId = "ID-test-1234";
+        si.endpoint = "sql:" + query;
+        si.timestamp = System.currentTimeMillis();
+        info.sqlTraceStatements.add(si);
+    }
+}

Reply via email to