Przeglądaj źródła

chore: scroll to bottom

Soham Parekh 3 lat temu
rodzic
commit
80932117aa

+ 57 - 97
dashboard/package-lock.json

@@ -366,11 +366,11 @@
       }
     },
     "node_modules/@babel/helper-module-imports": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz",
-      "integrity": "sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz",
+      "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==",
       "dependencies": {
-        "@babel/types": "^7.16.0"
+        "@babel/types": "^7.18.6"
       },
       "engines": {
         "node": ">=6.9.0"
@@ -408,9 +408,9 @@
       }
     },
     "node_modules/@babel/helper-plugin-utils": {
-      "version": "7.14.5",
-      "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz",
-      "integrity": "sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ==",
+      "version": "7.19.0",
+      "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz",
+      "integrity": "sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw==",
       "dev": true,
       "engines": {
         "node": ">=6.9.0"
@@ -480,10 +480,18 @@
         "node": ">=6.9.0"
       }
     },
+    "node_modules/@babel/helper-string-parser": {
+      "version": "7.19.4",
+      "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz",
+      "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==",
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
     "node_modules/@babel/helper-validator-identifier": {
-      "version": "7.15.7",
-      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz",
-      "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==",
+      "version": "7.19.1",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
+      "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==",
       "engines": {
         "node": ">=6.9.0"
       }
@@ -906,12 +914,12 @@
       }
     },
     "node_modules/@babel/plugin-syntax-jsx": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.0.tgz",
-      "integrity": "sha512-8zv2+xiPHwly31RK4RmnEYY5zziuF3O7W2kIDW+07ewWDh6Oi0dRq8kwvulRkFgt6DB97RlKs5c1y068iPlCUg==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz",
+      "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==",
       "dev": true,
       "dependencies": {
-        "@babel/helper-plugin-utils": "^7.14.5"
+        "@babel/helper-plugin-utils": "^7.18.6"
       },
       "engines": {
         "node": ">=6.9.0"
@@ -1774,9 +1782,9 @@
       }
     },
     "node_modules/@babel/runtime": {
-      "version": "7.16.3",
-      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.3.tgz",
-      "integrity": "sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ==",
+      "version": "7.19.4",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.19.4.tgz",
+      "integrity": "sha512-EXpLCrk55f+cYqmHsSR+yD/0gAIMxxA9QK9lnQWzhMCvt+YmoBN7Zx94s++Kv0+unHk39vxNO8t+CMA2WSS3wA==",
       "dependencies": {
         "regenerator-runtime": "^0.13.4"
       },
@@ -1830,11 +1838,12 @@
       }
     },
     "node_modules/@babel/types": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz",
-      "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==",
+      "version": "7.19.4",
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.4.tgz",
+      "integrity": "sha512-M5LK7nAeS6+9j7hAq+b3fQs+pNfUtTGq+yFFfHnauFA8zQtLRfmuipmsKDKKLuyG+wC8ABW43A153YNawNTEtw==",
       "dependencies": {
-        "@babel/helper-validator-identifier": "^7.15.7",
+        "@babel/helper-string-parser": "^7.19.4",
+        "@babel/helper-validator-identifier": "^7.19.1",
         "to-fast-properties": "^2.0.0"
       },
       "engines": {
@@ -4050,37 +4059,6 @@
         "styled-components": ">= 2"
       }
     },
-    "node_modules/babel-plugin-styled-components/node_modules/@babel/helper-module-imports": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz",
-      "integrity": "sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg==",
-      "dependencies": {
-        "@babel/types": "^7.16.0"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/babel-plugin-styled-components/node_modules/@babel/helper-validator-identifier": {
-      "version": "7.15.7",
-      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz",
-      "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==",
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
-    "node_modules/babel-plugin-styled-components/node_modules/@babel/types": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz",
-      "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==",
-      "dependencies": {
-        "@babel/helper-validator-identifier": "^7.15.7",
-        "to-fast-properties": "^2.0.0"
-      },
-      "engines": {
-        "node": ">=6.9.0"
-      }
-    },
     "node_modules/babel-plugin-syntax-jsx": {
       "version": "6.18.0",
       "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz",
@@ -14757,11 +14735,11 @@
       }
     },
     "@babel/helper-module-imports": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz",
-      "integrity": "sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz",
+      "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==",
       "requires": {
-        "@babel/types": "^7.16.0"
+        "@babel/types": "^7.18.6"
       }
     },
     "@babel/helper-module-transforms": {
@@ -14790,9 +14768,9 @@
       }
     },
     "@babel/helper-plugin-utils": {
-      "version": "7.14.5",
-      "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.14.5.tgz",
-      "integrity": "sha512-/37qQCE3K0vvZKwoK4XU/irIJQdIfCJuhU5eKnNxpFDsOkgFaUAwbv+RYw6eYgsC0E4hS7r5KqGULUogqui0fQ==",
+      "version": "7.19.0",
+      "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz",
+      "integrity": "sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw==",
       "dev": true
     },
     "@babel/helper-remap-async-to-generator": {
@@ -14844,10 +14822,15 @@
         "@babel/types": "^7.16.0"
       }
     },
+    "@babel/helper-string-parser": {
+      "version": "7.19.4",
+      "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz",
+      "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw=="
+    },
     "@babel/helper-validator-identifier": {
-      "version": "7.15.7",
-      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz",
-      "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w=="
+      "version": "7.19.1",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
+      "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w=="
     },
     "@babel/helper-validator-option": {
       "version": "7.14.5",
@@ -15126,12 +15109,12 @@
       }
     },
     "@babel/plugin-syntax-jsx": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.16.0.tgz",
-      "integrity": "sha512-8zv2+xiPHwly31RK4RmnEYY5zziuF3O7W2kIDW+07ewWDh6Oi0dRq8kwvulRkFgt6DB97RlKs5c1y068iPlCUg==",
+      "version": "7.18.6",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz",
+      "integrity": "sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.14.5"
+        "@babel/helper-plugin-utils": "^7.18.6"
       }
     },
     "@babel/plugin-syntax-logical-assignment-operators": {
@@ -15708,9 +15691,9 @@
       }
     },
     "@babel/runtime": {
-      "version": "7.16.3",
-      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.3.tgz",
-      "integrity": "sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ==",
+      "version": "7.19.4",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.19.4.tgz",
+      "integrity": "sha512-EXpLCrk55f+cYqmHsSR+yD/0gAIMxxA9QK9lnQWzhMCvt+YmoBN7Zx94s++Kv0+unHk39vxNO8t+CMA2WSS3wA==",
       "requires": {
         "regenerator-runtime": "^0.13.4"
       }
@@ -15752,11 +15735,12 @@
       }
     },
     "@babel/types": {
-      "version": "7.16.0",
-      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz",
-      "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==",
+      "version": "7.19.4",
+      "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.4.tgz",
+      "integrity": "sha512-M5LK7nAeS6+9j7hAq+b3fQs+pNfUtTGq+yFFfHnauFA8zQtLRfmuipmsKDKKLuyG+wC8ABW43A153YNawNTEtw==",
       "requires": {
-        "@babel/helper-validator-identifier": "^7.15.7",
+        "@babel/helper-string-parser": "^7.19.4",
+        "@babel/helper-validator-identifier": "^7.19.1",
         "to-fast-properties": "^2.0.0"
       }
     },
@@ -17603,30 +17587,6 @@
         "@babel/helper-module-imports": "^7.15.4",
         "babel-plugin-syntax-jsx": "^6.18.0",
         "lodash": "^4.17.11"
-      },
-      "dependencies": {
-        "@babel/helper-module-imports": {
-          "version": "7.16.0",
-          "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.16.0.tgz",
-          "integrity": "sha512-kkH7sWzKPq0xt3H1n+ghb4xEMP8k0U7XV3kkB+ZGy69kDk2ySFW1qPi06sjKzFY3t1j6XbJSqr4mF9L7CYVyhg==",
-          "requires": {
-            "@babel/types": "^7.16.0"
-          }
-        },
-        "@babel/helper-validator-identifier": {
-          "version": "7.15.7",
-          "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz",
-          "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w=="
-        },
-        "@babel/types": {
-          "version": "7.16.0",
-          "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.16.0.tgz",
-          "integrity": "sha512-PJgg/k3SdLsGb3hhisFvtLOw5ts113klrpLuIPtCJIU+BB24fqq6lf8RWqKJEjzqXR9AEH1rIb5XTqwBHB+kQg==",
-          "requires": {
-            "@babel/helper-validator-identifier": "^7.15.7",
-            "to-fast-properties": "^2.0.0"
-          }
-        }
       }
     },
     "babel-plugin-syntax-jsx": {

+ 34 - 14
dashboard/src/main/home/cluster-dashboard/expanded-chart/logs-section/LogsSection.tsx

@@ -1,16 +1,19 @@
-import React, { useContext, useEffect, useState } from "react";
+import React, {
+  useCallback,
+  useContext,
+  useEffect,
+  useRef,
+  useState,
+} from "react";
 
 import styled from "styled-components";
 import RadioFilter from "components/RadioFilter";
 
 import filterOutline from "assets/filter-outline.svg";
-import downArrow from "assets/down-arrow.svg";
 import { Context } from "shared/Context";
 import api from "shared/api";
 import { useLogs } from "./useAgentLogs";
 import Anser from "anser";
-import { flatMap } from "lodash";
-import time from "assets/time.svg";
 import DateTimePicker from "components/date-time-picker/DateTimePicker";
 
 type Props = {
@@ -28,10 +31,11 @@ const LogsSection: React.FC<Props> = ({
   isFullscreen,
   setIsFullscreen,
 }) => {
+  const scrollToBottomRef = useRef<HTMLDivElement | undefined>(undefined);
   const { currentProject, currentCluster } = useContext(Context);
   const [podFilter, setPodFilter] = useState();
   const [podFilterOpts, setPodFilterOpts] = useState<string[]>();
-  const [scrollToBottom, setScrollToBottom] = useState(true);
+  const [scrollToBottomEnabled, setScrollToBottomEnabled] = useState(true);
   const [searchText, setSearchText] = useState("");
   const [enteredSearchText, setEnteredSearchText] = useState("");
   const [selectedDate, setSelectedDate] = useState<Date | undefined>(undefined);
@@ -40,7 +44,7 @@ const LogsSection: React.FC<Props> = ({
     podFilter,
     currentChart.namespace,
     enteredSearchText,
-    selectedDate
+    selectedDate,
   );
 
   useEffect(() => {
@@ -56,25 +60,32 @@ const LogsSection: React.FC<Props> = ({
         }
       )
       .then((res) => {
-        console.log(res.data);
         setPodFilterOpts(res.data);
         setPodFilter(res.data[0]);
       });
-
-    console.log(currentChart);
   }, []);
 
+  useEffect(() => {
+    if (scrollToBottomRef.current && scrollToBottomEnabled) {
+      scrollToBottomRef.current.scrollIntoView({
+        behavior: 'smooth',
+        block: 'end',
+      });
+    }
+  }, [logs, scrollToBottomRef, scrollToBottomEnabled]);
+
   const renderLogs = () => {
     return logs?.map((log, i) => {
       return (
-        <Log key={i}>
-          {log.map((ansi, j) => {
+        <Log key={[log.lineNumber, i].join(".")}>
+          <span className="line-number">{log.lineNumber}</span>
+          {log.line.map((ansi, j) => {
             if (ansi.clearLine) {
               return null;
             }
 
             return (
-              <LogSpan key={i + "." + j} ansi={ansi}>
+              <LogSpan key={[log.lineNumber, i, j].join(".")} ansi={ansi}>
                 {ansi.content.replace(/ /g, "\u00a0")}
               </LogSpan>
             );
@@ -124,8 +135,8 @@ const LogsSection: React.FC<Props> = ({
             />
           </Flex>
           <Flex>
-            <Button onClick={() => setScrollToBottom(!scrollToBottom)}>
-              <Checkbox checked={scrollToBottom}>
+            <Button onClick={() => setScrollToBottomEnabled((s) => !s)}>
+              <Checkbox checked={scrollToBottomEnabled}>
                 <i className="material-icons">done</i>
               </Checkbox>
               Scroll to bottom
@@ -155,6 +166,7 @@ const LogsSection: React.FC<Props> = ({
               Refresh
             </Highlight>
           </Message> */}
+          <div ref={scrollToBottomRef} />
         </StyledLogsSection>
       </>
     );
@@ -404,6 +416,14 @@ const StyledLogsSection = styled.div<{ isFullscreen: boolean }>`
 const Log = styled.div`
   font-family: monospace;
   user-select: text;
+  & > .line-number {
+    display: inline-block;
+    text-align: right;
+    min-width: 35px;
+    margin-right: 8px;
+    opacity: 0.3;
+    font-family: monospace;
+  }
 `;
 
 const LogSpan = styled.span`

+ 37 - 14
dashboard/src/main/home/cluster-dashboard/expanded-chart/logs-section/useAgentLogs.ts

@@ -20,12 +20,22 @@ interface Log {
   timestamp: string;
 }
 
-const parseLogs = (logs: string[] = []): AnserJsonEntry[][] => {
-  return logs.filter(Boolean).map((logLine: string) => {
-    const parsedLine = JSON.parse(logLine);
+interface LogLine {
+  log: string;
+  stream: string;
+  time: string;
+}
+
+const parseLogs = (logs: string[] = []): Log[] => {
+  return logs.filter(Boolean).map((logLine: string, idx) => {
+    const parsedLine: LogLine = JSON.parse(logLine);
     // TODO Move log parsing to the render method
     const ansiLog = Anser.ansiToJson(parsedLine.log);
-    return ansiLog;
+    return {
+      line: ansiLog,
+      lineNumber: idx + 1,
+      timestamp: parsedLine.time,
+    };
   });
 };
 
@@ -39,9 +49,9 @@ export const useLogs = (
   const d = dayjs().subtract(14, "days");
 
   const isLive = !setDate;
-  const logsBufferRef = useRef<AnserJsonEntry[][]>([]);
+  const logsBufferRef = useRef<Log[]>([]);
   const { currentCluster, currentProject } = useContext(Context);
-  const [logs, setLogs] = useState<AnserJsonEntry[][]>([]);
+  const [logs, setLogs] = useState<Log[]>([]);
   const [startDate, setStartDate] = useState<dayjs.Dayjs>(d);
   const [endDate, setEndDate] = useState<dayjs.Dayjs>(dayjs(setDate));
 
@@ -63,17 +73,30 @@ export const useLogs = (
     closeAllWebsockets,
   } = useWebsockets();
 
-  const updateLogs = (newLogs: AnserJsonEntry[][]) => {
+  const updateLogs = (newLogs: Log[]) => {
+    // Nothing to update here
+    if (!newLogs.length) {
+      return;
+    }
+
     setLogs((logs) => {
-      let currentLogs = logs;
-      currentLogs.push(...newLogs);
-      if (logs.length > MAX_LOGS) {
+      let updatedLogs = _.cloneDeep(logs);
+      const lastLineNumber = updatedLogs.at(-1)?.lineNumber ?? 0;
+
+      updatedLogs.push(
+        ...newLogs.map((log) => ({
+          ...log,
+          lineNumber: lastLineNumber + log.lineNumber,
+        }))
+      );
+
+      if (updatedLogs.length > MAX_LOGS) {
         const logsToBeRemoved =
           newLogs.length < MAX_BUFFER_LOGS ? newLogs.length : MAX_BUFFER_LOGS;
-        currentLogs = currentLogs.slice(logsToBeRemoved);
+        updatedLogs = updatedLogs.slice(logsToBeRemoved);
       }
 
-      return logs;
+      return updatedLogs;
     });
   };
 
@@ -90,7 +113,7 @@ export const useLogs = (
     logsBufferRef.current = [];
   };
 
-  const pushLogs = (newLogs: AnserJsonEntry[][]) => {
+  const pushLogs = (newLogs: Log[]) => {
     logsBufferRef.current.push(...newLogs);
 
     if (logsBufferRef.current.length > MAX_BUFFER_LOGS) {
@@ -148,7 +171,7 @@ export const useLogs = (
         );
 
         pushLogs(
-          direction === Direction.backward ? newLogs.reverse() : newLogs
+          direction === Direction.backward ? newLogs : newLogs.reverse()
         );
         cb && cb();
       });