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 d147f486b953 CAMEL-23514: TUI UX improvements - log buffer, scroll
fix, sort, badges, JBang detection (#23265)
d147f486b953 is described below
commit d147f486b953ef6f0eb9b84f922aa12ac6600079
Author: Claus Ibsen <[email protected]>
AuthorDate: Sun May 17 13:47:58 2026 +0200
CAMEL-23514: TUI UX improvements - log buffer, scroll fix, sort, badges,
JBang detection (#23265)
* CAMEL-23514: TUI log tab - reduce buffer to 3000 lines, show chunk range,
fix scroll drift
- Reduce MAX_LOG_LINES from 5000 to 3000 to reduce memory pressure
- Show line range in Log block title when buffer is full (e.g. "Log
level:INFO #42001-45000")
- Fix log view drifting/scrolling when follow mode is off and buffer is at
capacity:
when new lines evict old ones from the front of the ring buffer,
compensate logScroll
by the eviction delta so the pinned view stays on the same content
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
* CAMEL-23514: TUI improvements - dev mode, suspend support, sort
enhancements
- ContextDevConsole: emit devMode flag (true when ResourceReloadStrategy
active)
and Dev Mode line in text output
- RouteDevConsole: emit supportsSuspension per route in JSON and text output
- TUI Overview: show [dev] badge next to name for integrations running with
--dev
- TUI Overview: add VERSION to sort cycle
- TUI Info tab: show Profile and Reload count on same line (Reload only if
> 0);
fix reload parsing from nested statistics.reload.reloaded
- TUI Routes tab: gate P key and footer hint on route.supportsSuspension;
add FROM to sort cycle; rename consumers tab ID column to ROUTE
- TUI all sort tabs: add hidden S key to reverse sort order (arrow flips
▼/▲);
pressing s to cycle columns resets reverse to false
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
* CAMEL-23514: TUI UX polish - Log (*) badge, Last tab, JBang platform
detection
- Show Log (*) in yellow in tab header when log lines are present
- Rename History tab to Last
- Detect JBang platform from process command line; show as 'JBang v0.x.y'
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
---------
Co-authored-by: Claude Sonnet 4.6 <[email protected]>
---
.../camel/impl/console/ContextDevConsole.java | 4 +
.../apache/camel/impl/console/RouteDevConsole.java | 4 +
.../dsl/jbang/core/commands/tui/CamelMonitor.java | 228 ++++++++++++++++-----
3 files changed, 185 insertions(+), 51 deletions(-)
diff --git
a/core/camel-console/src/main/java/org/apache/camel/impl/console/ContextDevConsole.java
b/core/camel-console/src/main/java/org/apache/camel/impl/console/ContextDevConsole.java
index c5ec722d11cc..9e590550b410 100644
---
a/core/camel-console/src/main/java/org/apache/camel/impl/console/ContextDevConsole.java
+++
b/core/camel-console/src/main/java/org/apache/camel/impl/console/ContextDevConsole.java
@@ -25,6 +25,7 @@ import java.util.Set;
import org.apache.camel.api.management.ManagedCamelContext;
import org.apache.camel.api.management.mbean.ManagedCamelContextMBean;
import org.apache.camel.spi.ReloadStrategy;
+import org.apache.camel.spi.ResourceReloadStrategy;
import org.apache.camel.spi.annotations.DevConsole;
import org.apache.camel.support.CamelContextHelper;
import org.apache.camel.support.ExceptionHelper;
@@ -90,6 +91,8 @@ public class ContextDevConsole extends AbstractDevConsole {
sb.append(String.format("%n Idle Since: %s", ""));
}
sb.append(String.format("%n Reloaded: %s/%s", reloaded,
reloadedFailed));
+ boolean devMode =
getCamelContext().hasService(ResourceReloadStrategy.class) != null;
+ sb.append(String.format("%n Dev Mode: %s", devMode));
sb.append(String.format("%n Mean Time: %s",
TimeUtils.printDuration(mb.getMeanProcessingTime(), true)));
sb.append(String.format("%n Max Time: %s",
TimeUtils.printDuration(mb.getMaxProcessingTime(), true)));
sb.append(String.format("%n Min Time: %s",
TimeUtils.printDuration(mb.getMinProcessingTime(), true)));
@@ -133,6 +136,7 @@ public class ContextDevConsole extends AbstractDevConsole {
root.put("state", getCamelContext().getStatus().name());
root.put("phase",
getCamelContext().getCamelContextExtension().getStatusPhase());
root.put("uptime", getCamelContext().getUptime().toMillis());
+ root.put("devMode",
getCamelContext().hasService(ResourceReloadStrategy.class) != null);
ManagedCamelContext mcc =
getCamelContext().getCamelContextExtension().getContextPlugin(ManagedCamelContext.class);
if (mcc != null) {
diff --git
a/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteDevConsole.java
b/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteDevConsole.java
index 898d930e8380..f5df4eb4e91f 100644
---
a/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteDevConsole.java
+++
b/core/camel-console/src/main/java/org/apache/camel/impl/console/RouteDevConsole.java
@@ -113,6 +113,8 @@ public class RouteDevConsole extends AbstractDevConsole {
sb.append(String.format("%n Source: %s",
mrb.getSourceLocation()));
}
sb.append(String.format("%n State: %s", mrb.getState()));
+ Route r = getCamelContext().getRoute(mrb.getRouteId());
+ sb.append(String.format("%n Supports Suspension: %s", r != null
&& r.supportsSuspension()));
if (mrb.getLastError() != null) {
String phase =
StringHelper.capitalize(mrb.getLastError().getPhase().name().toLowerCase());
String ago =
TimeUtils.printSince(mrb.getLastError().getDate().getTime());
@@ -243,6 +245,8 @@ public class RouteDevConsole extends AbstractDevConsole {
jo.put("source", mrb.getSourceLocation());
}
jo.put("state", mrb.getState());
+ Route r = getCamelContext().getRoute(mrb.getRouteId());
+ jo.put("supportsSuspension", r != null && r.supportsSuspension());
jo.put("uptime", mrb.getUptime());
if (mrb.getLastError() != null) {
String phase =
StringHelper.capitalize(mrb.getLastError().getPhase().name().toLowerCase());
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelMonitor.java
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelMonitor.java
index 8984a3c2ea76..c45e08ebd104 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelMonitor.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/CamelMonitor.java
@@ -108,7 +108,7 @@ public class CamelMonitor extends CamelCommand {
private static final long VANISH_DURATION_MS = 6000;
private static final long DEFAULT_REFRESH_MS = 100;
private static final int MAX_SPARKLINE_POINTS = 60;
- private static final int MAX_LOG_LINES = 5000;
+ private static final int MAX_LOG_LINES = 3000;
private static final int MAX_TRACES = 200;
private static final int NUM_TABS = 9;
@@ -124,10 +124,10 @@ public class CamelMonitor extends CamelCommand {
private static final int TAB_TRACE = 8;
// Overview sort columns
- private static final String[] OVERVIEW_SORT_COLUMNS = { "pid", "name",
"status", "total", "fail" };
+ private static final String[] OVERVIEW_SORT_COLUMNS = { "pid", "name",
"version", "status", "total", "fail" };
// Route sort columns
- private static final String[] ROUTE_SORT_COLUMNS = { "name", "group",
"status", "total", "failed" };
+ private static final String[] ROUTE_SORT_COLUMNS = { "name", "group",
"from", "status", "total", "failed" };
// Consumer sort columns (order matches table column order)
private static final String[] CONSUMER_SORT_COLUMNS = { "id", "status",
"type", "inflight", "total", "uri" };
@@ -171,18 +171,22 @@ public class CamelMonitor extends CamelCommand {
// Overview sort state
private String overviewSort = "name";
private int overviewSortIndex = 1;
+ private boolean overviewSortReversed;
// Route sort state
private String routeSort = "name";
private int routeSortIndex = 0;
+ private boolean routeSortReversed;
// Consumer sort state (default: id = index 0)
private String consumerSort = "id";
private int consumerSortIndex = 0;
+ private boolean consumerSortReversed;
// Endpoint sort state (default: route = index 1)
private String endpointSort = "route";
private int endpointSortIndex = 1;
+ private boolean endpointSortReversed;
// Endpoint filter state
private boolean showOnlyRemote;
@@ -190,6 +194,7 @@ public class CamelMonitor extends CamelCommand {
// Circuit breaker sort state (default: route = index 0)
private String cbSort = "route";
private int cbSortIndex = 0;
+ private boolean cbSortReversed;
// Health filter state
private boolean showOnlyDown;
@@ -198,6 +203,7 @@ public class CamelMonitor extends CamelCommand {
private volatile List<LogEntry> filteredLogEntries = new ArrayList<>();
// Incremental log tail state — persisted across refresh cycles
private long logFilePos = -1;
+ private long logTotalLinesRead;
private String logFilePid;
private final StringBuilder logLineBuffer = new StringBuilder();
private final List<LogEntry> mutableFilteredEntries = new ArrayList<>();
@@ -207,6 +213,7 @@ public class CamelMonitor extends CamelCommand {
private int cachedLogMaxWidth;
private List<Line> cachedLogLines = Collections.emptyList();
private int logScroll;
+ private long logEvictedSeen;
private final ScrollbarState logScrollState = new ScrollbarState();
private boolean logFollowMode = true;
private boolean logWordWrap = true;
@@ -220,6 +227,7 @@ public class CamelMonitor extends CamelCommand {
private static final String[] TRACE_SORT_COLUMNS = { "time", "route",
"elapsed", "exchange" };
private String traceSort = "time";
private int traceSortIndex;
+ private boolean traceSortReversed;
private boolean traceDetailView;
private volatile List<String> traceSortedExchangeIds =
Collections.emptyList();
private String traceSelectedExchangeId;
@@ -494,9 +502,14 @@ public class CamelMonitor extends CamelCommand {
}
// Overview tab: sort
- if (tab == TAB_OVERVIEW && ke.isCharIgnoreCase('s')) {
+ if (tab == TAB_OVERVIEW && ke.isChar('s')) {
overviewSortIndex = (overviewSortIndex + 1) %
OVERVIEW_SORT_COLUMNS.length;
overviewSort = OVERVIEW_SORT_COLUMNS[overviewSortIndex];
+ overviewSortReversed = false;
+ return true;
+ }
+ if (tab == TAB_OVERVIEW && ke.isChar('S')) {
+ overviewSortReversed = !overviewSortReversed;
return true;
}
// Overview tab: toggle chart between all integrations and
selected only
@@ -515,23 +528,38 @@ public class CamelMonitor extends CamelCommand {
}
// Consumers tab: sort
- if (tab == TAB_CONSUMERS && ke.isCharIgnoreCase('s')) {
+ if (tab == TAB_CONSUMERS && ke.isChar('s')) {
consumerSortIndex = (consumerSortIndex + 1) %
CONSUMER_SORT_COLUMNS.length;
consumerSort = CONSUMER_SORT_COLUMNS[consumerSortIndex];
+ consumerSortReversed = false;
+ return true;
+ }
+ if (tab == TAB_CONSUMERS && ke.isChar('S')) {
+ consumerSortReversed = !consumerSortReversed;
return true;
}
// Circuit breaker tab: sort
- if (tab == TAB_CIRCUIT_BREAKER && ke.isCharIgnoreCase('s')) {
+ if (tab == TAB_CIRCUIT_BREAKER && ke.isChar('s')) {
cbSortIndex = (cbSortIndex + 1) % CB_SORT_COLUMNS.length;
cbSort = CB_SORT_COLUMNS[cbSortIndex];
+ cbSortReversed = false;
+ return true;
+ }
+ if (tab == TAB_CIRCUIT_BREAKER && ke.isChar('S')) {
+ cbSortReversed = !cbSortReversed;
return true;
}
// Endpoints tab: sort and filter
- if (tab == TAB_ENDPOINTS && ke.isCharIgnoreCase('s')) {
+ if (tab == TAB_ENDPOINTS && ke.isChar('s')) {
endpointSortIndex = (endpointSortIndex + 1) %
ENDPOINT_SORT_COLUMNS.length;
endpointSort = ENDPOINT_SORT_COLUMNS[endpointSortIndex];
+ endpointSortReversed = false;
+ return true;
+ }
+ if (tab == TAB_ENDPOINTS && ke.isChar('S')) {
+ endpointSortReversed = !endpointSortReversed;
return true;
}
if (tab == TAB_ENDPOINTS && ke.isCharIgnoreCase('r')) {
@@ -540,9 +568,14 @@ public class CamelMonitor extends CamelCommand {
}
// Routes tab: sort and diagram
- if (tab == TAB_ROUTES && ke.isCharIgnoreCase('s')) {
+ if (tab == TAB_ROUTES && ke.isChar('s')) {
routeSortIndex = (routeSortIndex + 1) %
ROUTE_SORT_COLUMNS.length;
routeSort = ROUTE_SORT_COLUMNS[routeSortIndex];
+ routeSortReversed = false;
+ return true;
+ }
+ if (tab == TAB_ROUTES && ke.isChar('S')) {
+ routeSortReversed = !routeSortReversed;
return true;
}
if (tab == TAB_ROUTES && ke.isChar('d')) {
@@ -580,7 +613,7 @@ public class CamelMonitor extends CamelCommand {
toggleRouteStartStop();
return true;
}
- if (tab == TAB_ROUTES && !showSource && !showDiagram &&
ke.isChar('P')) {
+ if (tab == TAB_ROUTES && !showSource && !showDiagram &&
ke.isChar('P') && selectedRouteSupportsSuspension()) {
toggleRouteSuspendResume();
return true;
}
@@ -710,9 +743,14 @@ public class CamelMonitor extends CamelCommand {
return true;
}
} else {
- if (ke.isCharIgnoreCase('s')) {
+ if (ke.isChar('s')) {
traceSortIndex = (traceSortIndex + 1) %
TRACE_SORT_COLUMNS.length;
traceSort = TRACE_SORT_COLUMNS[traceSortIndex];
+ traceSortReversed = false;
+ return true;
+ }
+ if (ke.isChar('S')) {
+ traceSortReversed = !traceSortReversed;
return true;
}
if (ke.isConfirm()) {
@@ -875,6 +913,8 @@ public class CamelMonitor extends CamelCommand {
filteredLogEntries = Collections.emptyList();
cachedLogEntries = null;
logFilePos = -1;
+ logTotalLinesRead = 0;
+ logEvictedSeen = 0;
logLineBuffer.setLength(0);
// Trace (TAB_TRACE)
traceDetailView = false;
@@ -1029,13 +1069,16 @@ public class CamelMonitor extends CamelCommand {
Tabs tabs = Tabs.builder()
.titles(
badge(" 1 Overview ", activeCount),
- Line.from(" 2 Log "),
+ filteredLogEntries.isEmpty()
+ ? Line.from(" 2 Log ")
+ : Line.from(Span.raw(" 2 Log "),
Span.styled("(*)", Style.EMPTY.fg(Color.YELLOW).bold()),
+ Span.raw(" ")),
badge(" 3 Routes ", routeCount),
badge(" 4 Consumers ", consumerCount),
badge(" 5 Endpoints ", endpointCount),
badgeCb(" 6 Circuit Breaker ", cbCount, cbOpenCount),
badgeHealth(" 7 Health ", healthCount,
healthDownCount),
- badge(" 8 History ", historyCount),
+ badge(" 8 Last ", historyCount),
hasTraces
? Line.from(Span.raw(" 9 Trace "),
Span.styled("(*)", Style.EMPTY.fg(Color.YELLOW).bold()),
Span.raw(" "))
@@ -1115,9 +1158,14 @@ public class CamelMonitor extends CamelCommand {
String sinceLastDisplay = formatSinceLast(info);
+ Line nameLine = info.devMode
+ ? Line.from(
+ Span.styled(info.name != null ? info.name :
"", Style.EMPTY.fg(Color.CYAN)),
+ Span.styled(" [dev]",
Style.EMPTY.fg(Color.YELLOW).dim()))
+ : Line.from(Span.styled(info.name != null ? info.name
: "", Style.EMPTY.fg(Color.CYAN)));
rows.add(Row.from(
Cell.from(info.pid),
- Cell.from(Span.styled(info.name != null ? info.name :
"", Style.EMPTY.fg(Color.CYAN))),
+ Cell.from(nameLine),
Cell.from(info.camelVersion != null ?
info.camelVersion : ""),
centerCell(info.ready != null ? info.ready : "", 5),
Cell.from(Span.styled(extractState(info.state),
statusStyle)),
@@ -1134,7 +1182,7 @@ public class CamelMonitor extends CamelCommand {
Row header = Row.from(
Cell.from(Span.styled(overviewSortLabel("PID", "pid"),
overviewSortStyle("pid"))),
Cell.from(Span.styled(overviewSortLabel("NAME", "name"),
overviewSortStyle("name"))),
- Cell.from(Span.styled("VERSION", Style.EMPTY.bold())),
+ Cell.from(Span.styled(overviewSortLabel("VERSION", "version"),
overviewSortStyle("version"))),
centerCell("READY", 5, Style.EMPTY.bold()),
Cell.from(Span.styled(overviewSortLabel("STATUS", "status"),
overviewSortStyle("status"))),
Cell.from(Span.styled("AGE", Style.EMPTY.bold())),
@@ -1334,7 +1382,7 @@ public class CamelMonitor extends CamelCommand {
// Identity
if (sel.platform != null) {
String plat = sel.platformVersion != null
- ? sel.platform + "/" + sel.platformVersion
+ ? sel.platform + " v" + sel.platformVersion
: sel.platform;
lines.add(Line.from(
Span.styled("Runtime: ", dim),
@@ -1345,6 +1393,21 @@ public class CamelMonitor extends CamelCommand {
Span.styled("Version: ", dim),
Span.raw(TuiHelper.truncate(sel.camelVersion,
inner.width() - 9))));
}
+ if (sel.profile != null || sel.reloaded > 0) {
+ List<Span> profileSpans = new ArrayList<>();
+ if (sel.profile != null) {
+ profileSpans.add(Span.styled("Profile: ", dim));
+ profileSpans.add(Span.raw(sel.profile));
+ }
+ if (sel.reloaded > 0) {
+ if (!profileSpans.isEmpty()) {
+ profileSpans.add(Span.raw(" "));
+ }
+ profileSpans.add(Span.styled("Reload: ", dim));
+ profileSpans.add(Span.raw(String.valueOf(sel.reloaded)));
+ }
+ lines.add(Line.from(profileSpans));
+ }
lines.add(Line.from(Span.raw("")));
// Resources
if (sel.javaVersion != null) {
@@ -1462,7 +1525,7 @@ public class CamelMonitor extends CamelCommand {
.header(Row.from(
Cell.from(Span.styled(routeSortLabel("ROUTE", "name"),
routeSortStyle("name"))),
Cell.from(Span.styled(routeSortLabel("GROUP",
"group"), routeSortStyle("group"))),
- Cell.from(Span.styled("FROM", Style.EMPTY.bold())),
+ Cell.from(Span.styled(routeSortLabel("FROM", "from"),
routeSortStyle("from"))),
Cell.from(Span.styled(routeSortLabel("STATUS",
"status"), routeSortStyle("status"))),
Cell.from(Span.styled("AGE", Style.EMPTY.bold())),
rightCell("COVER", 6, Style.EMPTY.bold()),
@@ -1518,7 +1581,7 @@ public class CamelMonitor extends CamelCommand {
if (a.vanishing != b.vanishing) {
return a.vanishing ? 1 : -1;
}
- return switch (overviewSort) {
+ int result = switch (overviewSort) {
case "pid" -> {
String pa = a.pid != null ? a.pid : "";
String pb = b.pid != null ? b.pid : "";
@@ -1529,15 +1592,21 @@ public class CamelMonitor extends CamelCommand {
String nb = b.name != null ? b.name : "";
yield na.compareToIgnoreCase(nb);
}
+ case "version" -> {
+ String va = a.camelVersion != null ? a.camelVersion : "";
+ String vb = b.camelVersion != null ? b.camelVersion : "";
+ yield va.compareToIgnoreCase(vb);
+ }
case "status" -> Integer.compare(a.state, b.state);
case "total" -> Long.compare(b.exchangesTotal, a.exchangesTotal);
case "fail" -> Long.compare(b.failed, a.failed);
default -> 0;
};
+ return overviewSortReversed ? -result : result;
}
private String overviewSortLabel(String label, String column) {
- return sortLabel(label, column, overviewSort);
+ return sortLabel(label, column, overviewSort, overviewSortReversed);
}
private Style overviewSortStyle(String column) {
@@ -1545,7 +1614,7 @@ public class CamelMonitor extends CamelCommand {
}
private int sortRoute(RouteInfo a, RouteInfo b) {
- return switch (routeSort) {
+ int result = switch (routeSort) {
case "total" -> Long.compare(b.total, a.total);
case "failed" -> Long.compare(b.failed, a.failed);
case "name" -> {
@@ -1563,12 +1632,18 @@ public class CamelMonitor extends CamelCommand {
String gb = b.group != null ? b.group : "";
yield ga.compareToIgnoreCase(gb);
}
+ case "from" -> {
+ String fa = a.from != null ? a.from : "";
+ String fb = b.from != null ? b.from : "";
+ yield fa.compareToIgnoreCase(fb);
+ }
default -> 0;
};
+ return routeSortReversed ? -result : result;
}
private String traceSortLabel(String label, String column) {
- return sortLabel(label, column, traceSort);
+ return sortLabel(label, column, traceSort, traceSortReversed);
}
private Style traceSortStyle(String column) {
@@ -1576,7 +1651,7 @@ public class CamelMonitor extends CamelCommand {
}
private String routeSortLabel(String label, String column) {
- return sortLabel(label, column, routeSort);
+ return sortLabel(label, column, routeSort, routeSortReversed);
}
private Style routeSortStyle(String column) {
@@ -1584,7 +1659,7 @@ public class CamelMonitor extends CamelCommand {
}
private String consumerSortLabel(String label, String column) {
- return sortLabel(label, column, consumerSort);
+ return sortLabel(label, column, consumerSort, consumerSortReversed);
}
private Style consumerSortStyle(String column) {
@@ -1592,7 +1667,7 @@ public class CamelMonitor extends CamelCommand {
}
private int sortConsumer(ConsumerInfo a, ConsumerInfo b) {
- return switch (consumerSort) {
+ int result = switch (consumerSort) {
case "status" -> {
String sa = consumerStatus(a);
String sb = consumerStatus(b);
@@ -1620,6 +1695,7 @@ public class CamelMonitor extends CamelCommand {
yield ia.compareToIgnoreCase(ib);
}
};
+ return consumerSortReversed ? -result : result;
}
private static String consumerStatus(ConsumerInfo ci) {
@@ -1719,7 +1795,7 @@ public class CamelMonitor extends CamelCommand {
Table table = Table.builder()
.rows(rows)
.header(Row.from(
- Cell.from(Span.styled(consumerSortLabel("ID", "id"),
consumerSortStyle("id"))),
+ Cell.from(Span.styled(consumerSortLabel("ROUTE",
"id"), consumerSortStyle("id"))),
Cell.from(Span.styled(consumerSortLabel("STATUS",
"status"), consumerSortStyle("status"))),
Cell.from(Span.styled(consumerSortLabel("TYPE",
"type"), consumerSortStyle("type"))),
rightCell(consumerSortLabel("INFLIGHT", "inflight"),
8, consumerSortStyle("inflight")),
@@ -1746,7 +1822,7 @@ public class CamelMonitor extends CamelCommand {
}
private String endpointSortLabel(String label, String column) {
- return sortLabel(label, column, endpointSort);
+ return sortLabel(label, column, endpointSort, endpointSortReversed);
}
private Style endpointSortStyle(String column) {
@@ -1754,7 +1830,7 @@ public class CamelMonitor extends CamelCommand {
}
private int sortEndpoint(EndpointInfo a, EndpointInfo b) {
- return switch (endpointSort) {
+ int result = switch (endpointSort) {
case "component" -> {
String ca = a.component != null ? a.component : "";
String cb = b.component != null ? b.component : "";
@@ -1777,10 +1853,11 @@ public class CamelMonitor extends CamelCommand {
yield ra.compareToIgnoreCase(rb);
}
};
+ return endpointSortReversed ? -result : result;
}
- private static String sortLabel(String label, String column, String
currentSort) {
- return currentSort.equals(column) ? label + "▼" : label;
+ private static String sortLabel(String label, String column, String
currentSort, boolean reversed) {
+ return currentSort.equals(column) ? label + (reversed ? "▲" : "▼") :
label;
}
private static Style sortStyle(String column, String currentSort) {
@@ -2319,6 +2396,19 @@ public class CamelMonitor extends CamelCommand {
return route.state;
}
+ private boolean selectedRouteSupportsSuspension() {
+ IntegrationInfo info = findSelectedIntegration();
+ if (info == null || info.routes.isEmpty()) {
+ return false;
+ }
+ List<RouteInfo> sortedRoutes = new ArrayList<>(info.routes);
+ sortedRoutes.sort(this::sortRoute);
+ Integer sel = routeTableState.selected();
+ RouteInfo route = (sel != null && sel >= 0 && sel <
sortedRoutes.size())
+ ? sortedRoutes.get(sel) : sortedRoutes.get(0);
+ return route.supportsSuspension;
+ }
+
private void loadSourceInBackground(String pid, String routeId) {
Path outputFile = getOutputFile(pid);
PathUtils.deleteFile(outputFile);
@@ -2650,20 +2740,21 @@ public class CamelMonitor extends CamelCommand {
}
private String cbSortLabel(String label, String column) {
- return cbSort.equals(column) ? label + " ▴" : label;
+ return sortLabel(label, column, cbSort, cbSortReversed);
}
private Style cbSortStyle(String column) {
- return cbSort.equals(column) ? Style.EMPTY.fg(Color.YELLOW).bold() :
Style.EMPTY.bold();
+ return sortStyle(column, cbSort);
}
private int sortCircuitBreaker(CircuitBreakerInfo a, CircuitBreakerInfo b)
{
- return switch (cbSort) {
+ int result = switch (cbSort) {
case "id" -> compareStr(a.id, b.id);
case "component" -> compareStr(a.component, b.component);
case "state" -> compareStr(a.state, b.state);
default -> compareStr(a.routeId, b.routeId); // "route"
};
+ return cbSortReversed ? -result : result;
}
private static int compareStr(String a, String b) {
@@ -2890,9 +2981,13 @@ public class CamelMonitor extends CamelCommand {
List<LogEntry> entries = filteredLogEntries;
int contentHeight = entries.size();
+ long totalRead = logTotalLinesRead;
+ String chunkSuffix = totalRead > entries.size()
+ ? " #" + (totalRead - entries.size() + 1) + "-" + totalRead
+ : "";
String logTitle = info.rootLogLevel != null
- ? " Log level:" + info.rootLogLevel + " "
- : " Log ";
+ ? " Log level:" + info.rootLogLevel + chunkSuffix + " "
+ : " Log" + chunkSuffix + " ";
Block block = Block.builder()
.borderType(BorderType.ROUNDED)
.title(logTitle)
@@ -2902,6 +2997,15 @@ public class CamelMonitor extends CamelCommand {
Rect inner = block.inner(area);
int visibleHeight = Math.max(1, inner.height());
+ // Compensate for buffer evictions: when the ring buffer is full and
new lines
+ // push old ones off the front, logScroll (an absolute index) would
drift forward
+ // without this adjustment, making the view scroll even with follow
mode off.
+ long evictedNow = Math.max(0L, logTotalLinesRead - entries.size());
+ if (!logFollowMode && evictedNow > logEvictedSeen) {
+ logScroll = (int) Math.max(0, logScroll - (evictedNow -
logEvictedSeen));
+ }
+ logEvictedSeen = evictedNow;
+
if (logFollowMode) {
logScroll = Math.max(0, contentHeight - visibleHeight);
}
@@ -3141,18 +3245,19 @@ public class CamelMonitor extends CamelCommand {
}
// Sort
- summaries.sort((a, b) -> switch (traceSort) {
- case "time" -> Long.compare(b.epochMs, a.epochMs);
- case "route" -> {
- String ra = a.routeId != null ? a.routeId : "";
- String rb = b.routeId != null ? b.routeId : "";
- yield ra.compareToIgnoreCase(rb);
- }
- case "elapsed" -> Long.compare(b.elapsed, a.elapsed);
- case "exchange" -> {
- yield a.exchangeId.compareTo(b.exchangeId);
- }
- default -> 0;
+ summaries.sort((a, b) -> {
+ int result = switch (traceSort) {
+ case "time" -> Long.compare(b.epochMs, a.epochMs);
+ case "route" -> {
+ String ra = a.routeId != null ? a.routeId : "";
+ String rb = b.routeId != null ? b.routeId : "";
+ yield ra.compareToIgnoreCase(rb);
+ }
+ case "elapsed" -> Long.compare(b.elapsed, a.elapsed);
+ case "exchange" -> a.exchangeId.compareTo(b.exchangeId);
+ default -> 0;
+ };
+ return traceSortReversed ? -result : result;
});
traceSortedExchangeIds =
summaries.stream().map(ExchangeSummary::exchangeId).toList();
@@ -3686,12 +3791,17 @@ public class CamelMonitor extends CamelCommand {
hint(spans, "d", "diagram");
hint(spans, "D", "text diagram");
String routeState = selectedRouteState();
+ boolean supSus = selectedRouteSupportsSuspension();
if ("Started".equals(routeState)) {
hint(spans, "p", "stop");
- hint(spans, "P", "suspend");
+ if (supSus) {
+ hint(spans, "P", "suspend");
+ }
} else if ("Suspended".equals(routeState)) {
hint(spans, "p", "start");
- hint(spans, "P", "resume");
+ if (supSus) {
+ hint(spans, "P", "resume");
+ }
} else if (routeState != null) {
hint(spans, "p", "start");
}
@@ -3916,11 +4026,14 @@ public class CamelMonitor extends CamelCommand {
// Integration changed: reset all incremental log state
mutableFilteredEntries.clear();
logFilePos = -1;
+ logTotalLinesRead = 0;
+ logEvictedSeen = 0;
logLineBuffer.setLength(0);
}
List<String> newRawLines = new ArrayList<>();
readNewLogLines(selected.pid, newRawLines);
if (!newRawLines.isEmpty()) {
+ logTotalLinesRead += newRawLines.size();
for (String line : newRawLines) {
mutableFilteredEntries.add(parseLogLine(line));
}
@@ -4414,10 +4527,20 @@ public class CamelMonitor extends CamelCommand {
info.state = context.getIntegerOrDefault("phase", 0);
info.camelVersion = context.getString("version");
info.profile = context.getString("profile");
+ info.devMode = context.getBooleanOrDefault("devMode", false);
JsonObject runtime = (JsonObject) root.get("runtime");
info.platform = runtime != null ? runtime.getString("platform") : null;
info.platformVersion = runtime != null ?
runtime.getString("platformVersion") : null;
+ if ("Camel".equals(info.platform)) {
+ String cl = ph.info().commandLine().orElse("");
+ if (cl.contains("main.CamelJBang run")) {
+ info.platform = "JBang";
+ if (info.platformVersion == null) {
+ info.platformVersion = VersionHelper.getJBangVersion();
+ }
+ }
+ }
info.javaVersion = runtime != null ? runtime.getString("javaVersion")
: null;
info.javaVendor = runtime != null ? runtime.getString("javaVendor") :
null;
info.javaVmName = runtime != null ? runtime.getString("javaVmName") :
null;
@@ -4445,9 +4568,9 @@ public class CamelMonitor extends CamelCommand {
if (tsFailed > 0) {
info.sinceLastFailed = TimeUtils.printSince(tsFailed);
}
- Object reloaded = stats.get("reloaded");
- if (reloaded != null) {
- info.reloaded = reloaded.toString();
+ Map<String, ?> reloadStats = (Map<String, ?>) stats.get("reload");
+ if (reloadStats != null) {
+ info.reloaded = (int) objToLong(reloadStats.get("reloaded"));
}
}
@@ -4482,6 +4605,7 @@ public class CamelMonitor extends CamelCommand {
ri.group = rj.getString("group");
ri.from = rj.getString("from");
ri.state = rj.getString("state");
+ ri.supportsSuspension =
rj.getBooleanOrDefault("supportsSuspension", false);
ri.uptime = rj.getString("uptime");
Map<String, ?> rs = rj.getMap("statistics");
@@ -4770,6 +4894,7 @@ public class CamelMonitor extends CamelCommand {
String javaVendor;
String javaVmName;
String profile;
+ boolean devMode;
String ready;
int state;
long uptime;
@@ -4783,7 +4908,7 @@ public class CamelMonitor extends CamelCommand {
String sinceLastStarted;
String sinceLastCompleted;
String sinceLastFailed;
- String reloaded;
+ int reloaded;
String rootLogLevel;
int routeStarted;
int routeTotal;
@@ -4806,6 +4931,7 @@ public class CamelMonitor extends CamelCommand {
String group;
String from;
String state;
+ boolean supportsSuspension;
String uptime;
String throughput;
String coverage;