فهرست منبع

chore: event list logs improvement

Soham Parekh 3 سال پیش
والد
کامیت
3be52f28d7

+ 191 - 58
dashboard/src/main/home/cluster-dashboard/expanded-chart/events/EventList.tsx

@@ -16,21 +16,50 @@ import Modal from "main/home/modals/Modal";
 import time from "assets/time.svg";
 import { Context } from "shared/Context";
 import { InitLogData } from "../logs-section/LogsSection";
-import { setServers } from "dns";
+import { Direction, Log, parseLogs } from "../logs-section/useAgentLogs";
+import dayjs from "dayjs";
+import Anser from "anser";
 
 type Props = {
+  namespace: string;
   filters: any;
   setLogData?: (logData: InitLogData) => void;
 };
 
-const EventList: React.FC<Props> = ({ filters, setLogData }) => {
+const EventList: React.FC<Props> = ({ filters, namespace, setLogData }) => {
   const { currentProject, currentCluster } = useContext(Context);
   const [events, setEvents] = useState([]);
+  const [logs, setLogs] = useState<Log[]>([]);
   const [expandedEvent, setExpandedEvent] = useState(null);
   const [expandedIncidentEvents, setExpandedIncidentEvents] = useState(null);
   const [isLoading, setIsLoading] = useState(true);
   const [refresh, setRefresh] = useState(true);
 
+  const redirectToLogs = (incident: any) => {
+    api
+      .getIncidentEvents(
+        "<token>",
+        {
+          incident_id: incident.id,
+        },
+        {
+          project_id: currentProject.id,
+          cluster_id: currentCluster.id,
+        }
+      )
+      .then((res) => {
+        const podName = res.data?.events[0]?.pod_name;
+        const timestamp = res.data?.events[0]?.last_seen;
+        const revision = res.data?.events[0]?.revision;
+
+        setLogData({
+          podName,
+          timestamp,
+          revision,
+        });
+      });
+  };
+
   useEffect(() => {
     if (!refresh) {
       return;
@@ -78,34 +107,41 @@ const EventList: React.FC<Props> = ({ filters, setLogData }) => {
         }
       )
       .then((res) => {
-        setExpandedIncidentEvents(res.data.events);
-      });
-  }, [expandedEvent]);
-
-  const redirectToLogs = (incident: any) => {
-    api
-      .getIncidentEvents(
-        "<token>",
-        {
-          incident_id: incident.id,
-        },
-        {
-          project_id: currentProject.id,
-          cluster_id: currentCluster.id,
+        if (!expandedEvent.should_view_logs) {
+          return null;
         }
-      )
-      .then((res) => {
-        const podName = res.data?.events[0]?.pod_name;
-        const timestamp = res.data?.events[0]?.last_seen;
-        const revision = res.data?.events[0]?.revision;
 
-        setLogData({
-          podName,
-          timestamp,
-          revision,
-        });
+        const events = res.data?.events ?? [];
+
+        api
+          .getLogs(
+            "<token>",
+            {
+              pod_selector: events[0]?.pod_name,
+              namespace,
+              revision: events[0]?.revision,
+              start_range: dayjs(events[0]?.last_seen)
+                .subtract(14, "day")
+                .toISOString(),
+              end_range: dayjs(events[0]?.last_seen).toISOString(),
+              limit: 100,
+              direction: Direction.backward,
+            },
+            {
+              cluster_id: currentCluster.id,
+              project_id: currentProject.id,
+            }
+          )
+          .then((res) => {
+            const logs = parseLogs(
+              res.data.logs?.filter(Boolean).map((logLine: any) => logLine.line)
+            ).reverse();
+            setLogs(logs);
+          });
+
+        setExpandedIncidentEvents(res.data.events);
       });
-  };
+  }, [expandedEvent]);
 
   const renderExpandedEventMessage = () => {
     if (!expandedIncidentEvents) {
@@ -113,10 +149,44 @@ const EventList: React.FC<Props> = ({ filters, setLogData }) => {
     }
 
     return (
-      <Message>
-        <img src={document} />
-        {expandedIncidentEvents[0].detail}
-      </Message>
+      <>
+        <Message>
+          <img src={document} />
+          {expandedIncidentEvents[0].detail}
+        </Message>
+        {logs.length ? (
+          <LogsSectionWrapper>
+            <StyledLogsSection>
+              {logs?.map((log, i) => {
+                return (
+                  <LogSpan key={[log.lineNumber, i].join(".")}>
+                    <span className="line-number">{log.lineNumber}.</span>
+                    <span className="line-timestamp">
+                      {dayjs(log.timestamp).format("MMM D, YYYY HH:mm:ss")}
+                    </span>
+                    <LogOuter key={[log.lineNumber, i].join(".")}>
+                      {log.line?.map((ansi, j) => {
+                        if (ansi.clearLine) {
+                          return null;
+                        }
+
+                        return (
+                          <LogInnerSpan
+                            key={[log.lineNumber, i, j].join(".")}
+                            ansi={ansi}
+                          >
+                            {ansi.content.replace(/ /g, "\u00a0")}
+                          </LogInnerSpan>
+                        );
+                      })}
+                    </LogOuter>
+                  </LogSpan>
+                );
+              })}
+            </StyledLogsSection>
+          </LogsSectionWrapper>
+        ) : null}
+      </>
     );
   };
 
@@ -213,32 +283,6 @@ const EventList: React.FC<Props> = ({ filters, setLogData }) => {
               return null;
             },
           },
-          {
-            id: "logs",
-            accessor: "",
-            width: 30,
-            Cell: ({ row }: CellProps<any>) => {
-              if (row.original.type != "incident") {
-                return null;
-              }
-
-              if (!row.original.data.should_view_logs) {
-                return null;
-              }
-
-              return (
-                <TableButton
-                  width="102px"
-                  onClick={() => {
-                    redirectToLogs(row.original.data);
-                  }}
-                >
-                  <Icon src={document} />
-                  View logs
-                </TableButton>
-              );
-            },
-          },
         ],
       },
     ],
@@ -248,7 +292,13 @@ const EventList: React.FC<Props> = ({ filters, setLogData }) => {
   return (
     <>
       {expandedEvent && (
-        <Modal onRequestClose={() => setExpandedEvent(null)} height="auto">
+        <Modal
+          onRequestClose={() => {
+            setExpandedEvent(null);
+            setLogs([]);
+          }}
+          height="auto"
+        >
           <TitleSection icon={danger}>
             <Text>{expandedEvent.release_name}</Text>
           </TitleSection>
@@ -505,3 +555,86 @@ const DocsLink = styled.a`
     font-size: 12px;
   }
 `;
+
+const LogsSectionWrapper = styled.div`
+  position: relative;
+`;
+
+const StyledLogsSection = styled.div`
+  margin-top: 20px;
+  width: 100%;
+  height: 200px;
+  display: flex;
+  flex-direction: column;
+  position: relative;
+  font-size: 13px;
+  border-radius: 8px;
+  border: 1px solid #ffffff33;
+  border-top: none;
+  background: #101420;
+  animation: floatIn 0.3s;
+  animation-timing-function: ease-out;
+  animation-fill-mode: forwards;
+  overflow-y: auto;
+  overflow-wrap: break-word;
+  position: relative;
+  @keyframes floatIn {
+    from {
+      opacity: 0;
+      transform: translateY(10px);
+    }
+    to {
+      opacity: 1;
+      transform: translateY(0px);
+    }
+  }
+`;
+
+const LogSpan = styled.div`
+  font-family: monospace;
+  user-select: text;
+  display: flex;
+  align-items: flex-end;
+  gap: 8px;
+  width: 100%;
+  & > * {
+    padding-block: 5px;
+  }
+  & > .line-timestamp {
+    height: 100%;
+    color: #949effff;
+    opacity: 0.5;
+    font-family: monospace;
+    min-width: fit-content;
+    padding-inline-end: 5px;
+  }
+  & > .line-number {
+    height: 100%;
+    background: #202538;
+    display: inline-block;
+    text-align: right;
+    min-width: 45px;
+    padding-inline-end: 5px;
+    opacity: 0.3;
+    font-family: monospace;
+  }
+`;
+
+const LogOuter = styled.div`
+  display: inline-block;
+  word-wrap: anywhere;
+  flex-grow: 1;
+  font-family: monospace, sans-serif;
+  font-size: 12px;
+`;
+
+const LogInnerSpan = styled.span`
+  font-family: monospace, sans-serif;
+  font-size: 12px;
+  font-weight: ${(props: { ansi: Anser.AnserJsonEntry }) =>
+    props.ansi?.decoration && props.ansi?.decoration == "bold" ? "700" : "400"};
+  color: ${(props: { ansi: Anser.AnserJsonEntry }) =>
+    props.ansi?.fg ? `rgb(${props.ansi?.fg})` : "white"};
+  background-color: ${(props: { ansi: Anser.AnserJsonEntry }) =>
+    props.ansi?.bg ? `rgb(${props.ansi?.bg})` : "transparent"};
+`;

+ 5 - 1
dashboard/src/main/home/cluster-dashboard/expanded-chart/events/EventsTab.tsx

@@ -99,7 +99,11 @@ const EventsTab: React.FC<Props> = ({
 
   return (
     <EventsPageWrapper>
-      <EventList setLogData={setLogData} filters={getFilters()} />
+      <EventList
+        namespace={currentChart.namespace}
+        setLogData={setLogData}
+        filters={getFilters()}
+      />
     </EventsPageWrapper>
   );
 };

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

@@ -17,7 +17,7 @@ export enum Direction {
   backward = "backward",
 }
 
-interface Log {
+export interface Log {
   line: AnserJsonEntry[];
   lineNumber: number;
   timestamp: string;
@@ -29,7 +29,7 @@ interface LogLine {
   time: string;
 }
 
-const parseLogs = (logs: string[] = []): Log[] => {
+export const parseLogs = (logs: string[] = []): Log[] => {
   return logs
     .filter(Boolean)
     .filter(isJSON)