Skip to content

Commit a6a911e

Browse files
authored
Merge pull request #18 from moda20/Feature/adding-system-event-log-page
2 parents f39ae9f + c26221b commit a6a911e

10 files changed

Lines changed: 146 additions & 102 deletions

File tree

package-lock.json

Lines changed: 15 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
"@reduxjs/toolkit": "^2.0.1",
4343
"@shadcn/ui": "^0.0.4",
4444
"@tanstack/react-table": "^8.20.5",
45+
"ansi-colors": "^4.1.3",
4546
"axios": "^1.10.0",
4647
"class-variance-authority": "^0.7.0",
4748
"clsx": "^2.1.1",

src/components/custom/DrawerLatestRuns.tsx

Lines changed: 40 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
} from "lucide-react"
1111
import SheetActionDialog from "@/components/sheet-action-dialog"
1212
import type { jobsTableData } from "@/features/jobsTable/interfaces"
13-
import type { ReactNode } from "react"
13+
import { ReactNode, useMemo } from "react"
1414
import jobsService from "@/services/JobsService"
1515
import { useCallback, useState } from "react"
1616
import moment from "moment"
@@ -24,6 +24,9 @@ import ScrollableList from "@/components/custom/general/ScrollableList"
2424
import { getLokiLogs } from "@/services/components/logsService"
2525
import { Label } from "@/components/ui/label"
2626
import { Loader } from "lucide"
27+
import { useSocketLogs } from "@/lib/socketUtils"
28+
import { MiscNotificationTopics } from "@/models/network"
29+
import { LiveLogViewer } from "@/components/custom/general/LogViewer"
2730

2831
export interface DrawerLatestRunsProps {
2932
JobDetails: jobsTableData
@@ -35,6 +38,9 @@ export default function DrawerLatestRuns({
3538
trigger,
3639
}: DrawerLatestRunsProps) {
3740
const [LogItems, setLogItems] = useState<JobRunLog[]>([])
41+
const [selectedLogItem, setSelectedLogItem] = useState<any | undefined>(
42+
undefined,
43+
)
3844
const [itemsTotal, setItemstotal] = useState(0)
3945
const [inputSchema, setInputSchema] = useState<JobRunsQuerySchema>({
4046
jobId: JobDetails.id,
@@ -43,7 +49,21 @@ export default function DrawerLatestRuns({
4349
})
4450
const [loading, setLoading] = useState(false)
4551
const [showLogs, setShowLogs] = useState(false)
46-
const [logLines, setLogLines] = useState<any[]>([])
52+
53+
const { logs } = useSocketLogs({
54+
actions: [],
55+
format: (stream: any) => {
56+
stream.values.forEach((log: any) => {
57+
log.fullMessage = `${log.timestamp} | ${log.type?.toUpperCase()} | ${log.message}`
58+
})
59+
return stream
60+
},
61+
initialLogsFilter: selectedLogItem?.filterRange,
62+
logInterval: undefined,
63+
mergeOutputStreams: false,
64+
setEndToMidnight: false,
65+
logQuery: `{logId="${selectedLogItem?.log_id}"}`,
66+
})
4767

4868
const resetDrawer = useCallback(() => {
4969
setLogItems([])
@@ -54,7 +74,6 @@ export default function DrawerLatestRuns({
5474
})
5575
setLoading(false)
5676
setShowLogs(false)
57-
setLogLines([])
5877
}, [inputSchema])
5978

6079
const getLatestRuns = useCallback(() => {
@@ -72,6 +91,13 @@ export default function DrawerLatestRuns({
7291
? moment(log.start_time).format("YYYY-MM-DD HH-mm")
7392
: moment(log.start_time).fromNow()
7493
log.start_time_title = `Start time : ${moment(log.start_time).format("YYYY-MM-DD HH-mm-ss")}`
94+
log.offsettedStartedTime = log.offsettedEndTime = new Date(
95+
log.end_time.getTime(),
96+
)
97+
log.filterRange = {
98+
from: new Date(log.start_time.getTime()),
99+
to: new Date(log.end_time.getTime()),
100+
}
75101
})
76102
return data
77103
})
@@ -95,16 +121,8 @@ export default function DrawerLatestRuns({
95121

96122
const openAndFetchLogs = useCallback((LogItem: JobRunLog) => {
97123
if (LogItem && LogItem.end_time) {
98-
const offsetStartTime = new Date(LogItem.start_time.getTime())
99-
const offsetEndTime = new Date(LogItem.end_time.getTime())
124+
setSelectedLogItem(LogItem)
100125
setShowLogs(true)
101-
return getLokiLogs(
102-
`{logId="${LogItem.log_id}"}`,
103-
offsetStartTime,
104-
offsetEndTime,
105-
).then(data => {
106-
setLogLines(data)
107-
})
108126
}
109127
}, [])
110128

@@ -139,12 +157,10 @@ export default function DrawerLatestRuns({
139157
>
140158
<ScrollableList
141159
originalList={LogItems}
142-
loadMore={(inputSchema.offset ?? 0) <= itemsTotal}
160+
loadMore={!loading && (inputSchema.offset ?? 0) <= itemsTotal}
143161
loadMoreAction={getMoreRuns}
144162
className="px-[1px] py-[2px] min-w-[280px]"
145-
onItemClick={(item: JobRunLog, index: number) => {
146-
openAndFetchLogs(item)
147-
}}
163+
onItemClick={openAndFetchLogs}
148164
itemClassName={(item: any) => {
149165
return cn(
150166
"focus:rounded-lg outline-none focus:ring-2 focus:ring-opacity-50 focus-visible:ring-2 focus-visible:ring-offset-0 focus-visible:ring-opacity-50 hover:ring-2 hover:ring-opacity-50 hover:rounded-lg focus:ring-blue-500 focus-visible:ring-blue-500 hover:ring-blue-500",
@@ -234,25 +250,15 @@ export default function DrawerLatestRuns({
234250
icon={LoaderPinwheelIcon}
235251
className="h-full"
236252
>
237-
{logLines?.[0] && (
238-
<ScrollArea className="pb-2 flex flex-col gap-0.5 h-full">
239-
{logLines[0]?.values.map((log: any, index: number) => {
240-
return (
241-
<div
242-
key={index}
243-
className={"flex flex-row items-center gap-2 mb-2"}
244-
>
245-
<Label className="min-w-[170px] whitespace-nowrap">
246-
{log.timestamp}
247-
</Label>
248-
<Badge variant={"defaultTeal"}>{log.type}</Badge>
249-
<Label className="leading-5">{log.message}</Label>
250-
</div>
251-
)
252-
})}
253-
</ScrollArea>
253+
{logs?.[0] && (
254+
<LiveLogViewer
255+
initialLogs={logs[0]?.values.map((e: any) => e.fullMessage)}
256+
wrapLines={false}
257+
scrollToLine={1}
258+
extraLines={4}
259+
/>
254260
)}
255-
{!logLines?.[0] && !loading && (
261+
{!logs?.[0] && !loading && (
256262
<div className="w-full h-full flex flex-col gap-4 items-center justify-center">
257263
<LogsIcon className="w-16 h-16 text-destructive" />
258264
<div className="text-muted-foreground text-md">

src/components/custom/DrawerLokiLogs.tsx

Lines changed: 27 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Label } from "@/components/ui/label"
22
import SheetActionDialog from "@/components/sheet-action-dialog"
33
import type { ReactNode } from "react"
4+
import { useEffect } from "react"
45
import { useState } from "react"
56
import { Badge } from "@/components/ui/badge"
67
import { ScrollArea } from "@/components/ui/scroll-area"
@@ -14,6 +15,9 @@ import { LoaderPinwheelIcon, LogsIcon } from "lucide-react"
1415
import { getLokiLogs } from "@/services/components/logsService"
1516
import TabCarousel from "@/components/custom/general/TabCarousel"
1617
import Spinner from "@/components/ui/spinner"
18+
import { useSocketLogs } from "@/lib/socketUtils"
19+
import { MiscNotificationTopics } from "@/models/network"
20+
import { LiveLogViewer } from "@/components/custom/general/LogViewer"
1721

1822
export interface DrawerLokiLogsProps {
1923
start?: Date
@@ -23,7 +27,6 @@ export interface DrawerLokiLogsProps {
2327
}
2428

2529
export default function DrawerLokiLogs(props: DrawerLokiLogsProps) {
26-
const [logs, setLogs] = useState([])
2730
const [watch, setWatch] = useState<CheckedState>(true)
2831
const [dialogOpen, setDialogOpen] = useState<CheckedState>(false)
2932
const [period, setPeriod] = useState<DateRange | undefined>({
@@ -33,41 +36,32 @@ export default function DrawerLokiLogs(props: DrawerLokiLogsProps) {
3336
const [activeTab, setActiveTab] = useState<string>("")
3437
const [loading, setLoading] = useState(false)
3538

36-
useInterval(
37-
() => {
38-
getLogs()
39+
const { logs } = useSocketLogs({
40+
actions: [],
41+
format: (stream: any) => {
42+
stream.values.forEach((log: any) => {
43+
log.fullMessage = `${log.timestamp} | ${log.type?.toUpperCase()} | ${log.message}`
44+
})
45+
return stream
3946
},
40-
dialogOpen && watch ? 10000 : null,
41-
)
42-
43-
const getLogs = () => {
44-
return getLokiLogs(`{job="${props.jobName}"}`, period?.from, period?.to, {
45-
setEndToMidnight: true,
46-
}).then(parsedLogs => {
47-
setLogs(parsedLogs ?? [])
48-
if (!activeTab) setActiveTab(parsedLogs[0]?.uniqueId)
49-
return
50-
})
51-
}
47+
initialLogsFilter: period,
48+
logInterval: dialogOpen && watch ? 10000 : undefined,
49+
mergeOutputStreams: false,
50+
logQuery: `{job="${props.jobName}"}`,
51+
})
5252

5353
const fetchLogs = (open?: boolean) => {
5454
setDialogOpen(!!open)
55-
if (open) {
56-
setLoading(true)
57-
getLogs().then(() => {
58-
setLoading(false)
59-
})
60-
}
6155
}
6256

6357
const updatePeriod = (period: DateRange | undefined) => {
6458
setPeriod(period)
65-
setLoading(true)
66-
getLogs().then(() => {
67-
setLoading(false)
68-
})
6959
}
7060

61+
useEffect(() => {
62+
if (!activeTab) setActiveTab(logs[0]?.uniqueId)
63+
}, [logs])
64+
7165
return (
7266
<SheetActionDialog
7367
side={"right"}
@@ -79,6 +73,7 @@ export default function DrawerLokiLogs(props: DrawerLokiLogsProps) {
7973
}
8074
onOpenChange={open => fetchLogs(open)}
8175
modal={true}
76+
innerContainerClassName="pb-2"
8277
>
8378
<div className={"flex flex-row items-center gap-2 my-2 h-100"}>
8479
<div className="flex flex-row items-center gap-2 border rounded-md p-2 bg-sidebar">
@@ -141,28 +136,12 @@ export default function DrawerLokiLogs(props: DrawerLokiLogsProps) {
141136
className="h-[inherit]"
142137
title={stream.title}
143138
>
144-
<ScrollArea className="pb-2 flex flex-col gap-0.5 h-full">
145-
<div
146-
className={
147-
"rounded-md h-full p-1 overflow-clip bg-sidebar"
148-
}
149-
>
150-
{stream.values.map((log: any, index: number) => {
151-
return (
152-
<div
153-
key={index}
154-
className={"flex flex-row items-center gap-2 mb-2"}
155-
>
156-
<Label className="min-w-[170px] whitespace-nowrap">
157-
{log.timestamp}
158-
</Label>
159-
<Badge variant={"defaultTeal"}>{log.type}</Badge>
160-
<Label className="leading-5">{log.message}</Label>
161-
</div>
162-
)
163-
})}
164-
</div>
165-
</ScrollArea>
139+
<LiveLogViewer
140+
initialLogs={stream.values.map((e: any) => e.fullMessage)}
141+
wrapLines={false}
142+
scrollToLine={1}
143+
extraLines={4}
144+
/>
166145
</TabsContent>
167146
)
168147
})}

src/components/custom/general/LogViewer.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
import type React from "react"
22
import { useEffect, useState } from "react"
3+
import type { LazyLogProps } from "@melloware/react-logviewer"
34
import { LazyLog, ScrollFollow } from "@melloware/react-logviewer"
5+
import { colorLog } from "@/utils/generalUtils"
46

5-
interface LiveLogViewerProps {
7+
interface LiveLogViewerProps extends LazyLogProps {
68
initialLogs: string[]
79
height?: number | string
810
width?: number | string
911
follow?: boolean
1012
newLogs?: string[]
13+
coloring?: boolean
1114
}
1215

1316
export const LiveLogViewer: React.FC<LiveLogViewerProps> = ({
@@ -16,20 +19,21 @@ export const LiveLogViewer: React.FC<LiveLogViewerProps> = ({
1619
width = "100%",
1720
follow = true,
1821
newLogs,
22+
coloring,
1923
...lazyProps
2024
}) => {
2125
const [buffer, setBuffer] = useState<string[]>(() => [...initialLogs])
2226

2327
useEffect(() => {
2428
if (initialLogs.length) {
25-
setBuffer(prev => [...initialLogs, ...(prev ?? [])])
29+
setBuffer(prev => colorLog(initialLogs))
2630
}
2731
}, [initialLogs])
2832

2933
useEffect(() => {
3034
if (newLogs && newLogs.length > 0) {
3135
setBuffer(prev => {
32-
return [...prev, ...newLogs]
36+
return [...prev, ...colorLog(newLogs)]
3337
})
3438
}
3539
}, [newLogs])

src/features/system/eventLog.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export function EventLog() {
1616
fullMessage: `${data.logs.timestamp} | ${data.logs.level?.toUpperCase()} | ${data.logs.message}`,
1717
}),
1818
initialLogsFilter: period,
19+
mergeOutputStreams: true,
1920
})
2021

2122
const onPeriodFilterChange = useCallback((period?: DateRange) => {

0 commit comments

Comments
 (0)