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

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

commit fca75e237f10eeaf4615cb2b7ddb0f944a4ee4fb
Author: Claus Ibsen <[email protected]>
AuthorDate: Sat Nov 7 16:28:45 2026 +0100

    CAMEL-26700: About - the seconds a circuit breaker of a route was open, as 
on the Timeline
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../dsl/jbang/core/commands/tui/AboutView.java      | 14 +++++++++++---
 .../dsl/jbang/core/commands/tui/AboutViewTest.java  | 21 +++++++++++++++++++++
 2 files changed, 32 insertions(+), 3 deletions(-)

diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/AboutView.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/AboutView.java
index cd90522c73bd..77376ba71cc5 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/AboutView.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/AboutView.java
@@ -139,7 +139,7 @@ final class AboutView {
 
     /**
      * The keys of What happened: + zooms in on the last trouble in the window 
(a failure, a handled error, a
-     * redelivery, a route down), - zooms out, and ← → move a zoomed window.
+     * redelivery, a route down, a circuit breaker open), - zooms out, and ← → 
move a zoomed window.
      */
     boolean handleKeyEvent(KeyEvent ke, IntegrationInfo info) {
         if (info == null) {
@@ -226,7 +226,7 @@ final class AboutView {
         for (Timeline.RouteSeries r : ctx.timeline.routes(info.pid, 
routeOrder(info), windowEnd, seconds)) {
             for (int i = seconds - 1; i >= 0; i--) {
                 if (r.failed()[i] > 0 || r.handled()[i] > 0 || 
r.recovered()[i] > 0 || r.attempts()[i] > 0
-                        || r.down()[i] > 0) {
+                        || r.down()[i] > 0 || r.open()[i] > 0) {
                     last = Math.max(last, windowEnd - seconds + 1 + i);
                     break;
                 }
@@ -350,7 +350,7 @@ final class AboutView {
             long max = 1;
             // completed, recovered, handled, failed, down, redelivery 
attempts: the height is the exchanges done, the
             // attempts only colour the column (an exchange retried many times 
is still one exchange)
-            long[][] col = new long[columns][6];
+            long[][] col = new long[columns][7];
             for (int i = 0; i < seconds; i++) {
                 long[] c = col[i / per];
                 c[0] += Math.max(0, r.completed()[i]);
@@ -359,6 +359,7 @@ final class AboutView {
                 c[3] += Math.max(0, r.failed()[i]);
                 c[4] += Math.max(0, r.down()[i]);
                 c[5] += Math.max(0, r.attempts()[i]);
+                c[6] = Math.max(c[6], r.open()[i]);
             }
             for (long[] c : col) {
                 max = Math.max(max, c[0] + c[1] + c[2] + c[3]);
@@ -367,6 +368,9 @@ final class AboutView {
                 long t = c[0] + c[1] + c[2] + c[3];
                 if (c[4] > 0) {
                     spans.add(Span.styled("░", Theme.error()));
+                } else if (c[6] > 0) {
+                    // a circuit breaker of the route was open (half open 
dim), as on the Timeline
+                    spans.add(Span.styled("▒", c[6] >= 2 ? Theme.notice() : 
Theme.muted()));
                 } else if (t == 0 && c[5] == 0) {
                     spans.add(Span.styled("·", Theme.muted()));
                 } else {
@@ -379,6 +383,10 @@ final class AboutView {
             }
             long done = r.total(r.completed()) + r.total(r.recovered()) + 
r.total(r.handled()) + r.total(r.failed());
             spans.add(Span.styled("  " + done, Theme.success()));
+            if (r.openSeconds() > 0) {
+                spans.add(Span.styled("  ⚡ open " + r.openSeconds() + "s " + 
ago(last - lastAt(r.open(), end, seconds)),
+                        Theme.notice()));
+            }
             // the worst that happened, when it last did, and for a recovery 
the failure it recovered from (a failed or
             // handled one has its cause on the Last 5m line and on the 
Timeline)
             long[] worst = r.total(r.failed()) > 0 ? r.failed() : 
r.total(r.handled()) > 0 ? r.handled()
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/AboutViewTest.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/AboutViewTest.java
index 4701e5c185fc..adb4bdb54da3 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/AboutViewTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/AboutViewTest.java
@@ -144,6 +144,27 @@ class AboutViewTest {
         assertThat(render(about, info)).contains("1 recovered 2m ago · 
Connection refused");
     }
 
+    @Test
+    void theSecondsACircuitBreakerOfARouteWasOpen() {
+        IntegrationInfo info = new IntegrationInfo();
+        info.pid = "1234";
+        RouteInfo r = new RouteInfo();
+        r.routeId = "orders";
+        info.routes.add(r);
+        MonitorContext ctx = new MonitorContext(new 
AtomicReference<>(List.of(info)), new AtomicReference<>(List.of()));
+        TimelineJson t = new TimelineJson(T0 / 1000 + 299, 300);
+        long[] states = new long[300];
+        for (int i = 0; i < 300; i++) {
+            t.route("orders", "completed", i, 1);
+            states[i] = i >= 230 && i < 240 ? 2 : 0;
+        }
+        ctx.timeline.merge("1234", t.breaker("orders", "cb1", states).json(), 
true);
+        AboutView about = new AboutView(ctx);
+        // the bolt is two columns wide: the screen has a blank cell after it
+        String screen = render(about, info).replace("⚡ ", "⚡");
+        assertThat(screen).contains("⚡ open 10s 1m ago").contains("▒");
+    }
+
     @Test
     void theSendsOfARouteToAnEndpoint() {
         IntegrationInfo info = new IntegrationInfo();

Reply via email to