davsclaus commented on code in PR #29384:
URL: https://github.com/apache/camel/pull/29384#discussion_r4503213023


##########
dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/TimelineLog.java:
##########
@@ -0,0 +1,121 @@
+/*
+ * 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.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeParseException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * The WARN and ERROR lines of the log of each integration per second, for the 
Timeline (CAMEL-26691): a cause when
+ * something went wrong without an exception in an exchange. Read from the log 
file the TUI already shows, as it grows:
+ * the last part of it at first, then what is added at each refresh. A line 
without a time of its own (a stack trace) is
+ * part of the line before and not counted.
+ */
+final class TimelineLog {
+
+    // how much of a log is read the first time: the lines of the last 
minutes, not the whole file
+    static final int FIRST_READ = 256 * 1024;
+    // at most this much is read at a refresh, so a log that grows fast does 
not hold it up
+    private static final int MAX_READ = 1024 * 1024;
+    private static final Pattern LINE = Pattern.compile(
+            "^(\\d{4}-\\d{2}-\\d{2})[ 
T](\\d{2}:\\d{2}:\\d{2})\\S*\\s+(WARN|ERROR)\\b");
+    private static final Pattern ANSI = Pattern.compile("\u001B\\[[;\\d]*m");
+    private static final DateTimeFormatter TIME = 
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+
+    // where the log of each integration was read to
+    private final Map<String, Long> offsets = new HashMap<>();
+    private final ZoneId zone;
+
+    TimelineLog() {
+        this(ZoneId.systemDefault());
+    }
+
+    TimelineLog(ZoneId zone) {
+        this.zone = zone;
+    }
+
+    /** Reads what was added to the log of the integration since the last 
time, into the timeline. */
+    void read(String pid, Path file, Timeline timeline) {
+        if (pid == null || file == null || !Files.isRegularFile(file)) {
+            return;
+        }
+        try (RandomAccessFile raf = new RandomAccessFile(file.toFile(), "r")) {
+            long size = raf.length();
+            Long known = offsets.get(pid);
+            // the first time from the last part; a log that got shorter was 
rolled or cleared: from its start
+            long from = known == null ? Math.max(0, size - FIRST_READ) : known 
> size ? 0 : known;
+            long to = Math.min(size, from + MAX_READ);
+            if (to <= from) {
+                offsets.put(pid, from);
+                return;
+            }
+            byte[] bytes = new byte[(int) (to - from)];
+            raf.seek(from);
+            raf.readFully(bytes);
+            // only whole lines: the rest is read the next time
+            int end = bytes.length;
+            while (end > 0 && bytes[end - 1] != '\n') {
+                end--;
+            }
+            if (end == 0 && to < size) {
+                // a line longer than the read: skip it
+                end = bytes.length;
+            }
+            String text = new String(bytes, 0, end, StandardCharsets.UTF_8);
+            // read from the middle of the file: the first line is a part of 
one
+            int start = known == null && from > 0 ? text.indexOf('\n') + 1 : 0;
+            for (String line : text.substring(Math.max(0, start)).split("\n")) 
{
+                count(pid, line, timeline);
+            }
+            offsets.put(pid, from + end);
+        } catch (IOException e) {

Review Comment:
   Fixed in bf4c73d759fb: the catch now logs at DEBUG with the pid and the 
message, so the TUI stays quiet.
   
   _Claude Code on behalf of Claus Ibsen_



##########
dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/Timeline.java:
##########
@@ -432,6 +495,16 @@ private static long[] filled(int seconds) {
      * The causes in the window: the errors per second by label, then the 
faults of each chaos rule. Each series is
      * oldest first.
      */
+    /** A WARN or ERROR line in the log of the integration, at the second of 
its time. */

Review Comment:
   Fixed in bf4c73d759fb: `logLine()` and its Javadoc now sit above the 
`causes()` Javadoc, which is back on its method.
   
   _Claude Code on behalf of Claus Ibsen_



##########
catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/dev-consoles/timeline.json:
##########
@@ -218,6 +218,58 @@
         },
         "description": "The faults per second of each camel-chaos rule that 
fired in the seconds (only present when one did)"
       },
+      "routeEvents": {
+        "type": "array",
+        "items": {
+          "type": "object",
+          "properties": {
+            "routeId": {
+              "type": "string",
+              "description": "The route ID"
+            },
+            "event": {
+              "type": "string",
+              "description": "What happened to the route: started, stopped, 
reloaded, removed or restartFailed"
+            },
+            "second": {
+              "type": "integer",
+              "description": "The epoch second it happened"
+            }
+          },
+          "required": [

Review Comment:
   Leaving this as is: the dev console schema generator marks only primitive 
record components as required, and that holds for every console in the catalog, 
not only the timeline. Changing it would be a generator-wide change, not one 
for this PR.
   
   _Claude Code on behalf of Claus Ibsen_



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to