mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
Merge pull request #2 from drudge/log-test
feat(logs): improvements based on feedback
This commit is contained in:
@@ -1,9 +1,6 @@
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import { getLogType, LogLine, parseLogs } from "./utils";
|
||||
import { TerminalLine } from "./terminal-line";
|
||||
import { Download as DownloadIcon } from "lucide-react";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -11,8 +8,11 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { api } from "@/utils/api";
|
||||
import { Download as DownloadIcon } from "lucide-react";
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import { TerminalLine } from "./terminal-line";
|
||||
import { type LogLine, getLogType, parseLogs } from "./utils";
|
||||
|
||||
interface Props {
|
||||
containerId: string;
|
||||
@@ -26,11 +26,11 @@ export const DockerLogsId: React.FC<Props> = ({ containerId, serverId }) => {
|
||||
const { data } = api.docker.getConfig.useQuery(
|
||||
{
|
||||
containerId,
|
||||
serverId,
|
||||
serverId: serverId ?? undefined,
|
||||
},
|
||||
{
|
||||
enabled: !!containerId,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
const [rawLogs, setRawLogs] = React.useState("");
|
||||
@@ -132,7 +132,7 @@ export const DockerLogsId: React.FC<Props> = ({ containerId, serverId }) => {
|
||||
const logContent = filteredLogs
|
||||
.map(
|
||||
({ timestamp, message }: { timestamp: Date | null; message: string }) =>
|
||||
`${timestamp?.toISOString() || "No timestamp"} ${message}`
|
||||
`${timestamp?.toISOString() || "No timestamp"} ${message}`,
|
||||
)
|
||||
.join("\n");
|
||||
|
||||
@@ -190,19 +190,13 @@ export const DockerLogsId: React.FC<Props> = ({ containerId, serverId }) => {
|
||||
onChange={handleLines}
|
||||
className="inline-flex h-9 text-sm text-white placeholder-gray-400 w-full sm:w-auto"
|
||||
/>
|
||||
<Input
|
||||
type="search"
|
||||
placeholder="Search logs..."
|
||||
value={search}
|
||||
onChange={handleSearch}
|
||||
className="inline-flex h-9 text-sm text-white placeholder-gray-400 w-full sm:w-auto"
|
||||
/>
|
||||
|
||||
<Select value={since} onValueChange={handleSince}>
|
||||
<SelectTrigger className="w-[180px] h-9">
|
||||
<SelectValue placeholder="Time filter" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="1h">Last 1 hour</SelectItem>
|
||||
<SelectItem value="1h">Last hour</SelectItem>
|
||||
<SelectItem value="6h">Last 6 hours</SelectItem>
|
||||
<SelectItem value="24h">Last 24 hours</SelectItem>
|
||||
<SelectItem value="168h">Last 7 days</SelectItem>
|
||||
@@ -233,6 +227,14 @@ export const DockerLogsId: React.FC<Props> = ({ containerId, serverId }) => {
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
<Input
|
||||
type="search"
|
||||
placeholder="Search logs..."
|
||||
value={search}
|
||||
onChange={handleSearch}
|
||||
className="inline-flex h-9 text-sm text-white placeholder-gray-400 w-full sm:w-auto"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
|
||||
@@ -46,10 +46,7 @@ export const ShowDockerModalLogs = ({
|
||||
<DialogDescription>View the logs for {containerId}</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="flex flex-col gap-4 pt-2.5">
|
||||
<DockerLogsId
|
||||
containerId={containerId || ""}
|
||||
serverId={serverId}
|
||||
/>
|
||||
<DockerLogsId containerId={containerId || ""} serverId={serverId} />
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from "@/components/ui/tooltip";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { getLogType, type LogLine } from "./utils";
|
||||
import { escapeRegExp } from "lodash";
|
||||
import React from "react";
|
||||
import { escapeRegExp } from 'lodash';
|
||||
import { type LogLine, getLogType } from "./utils";
|
||||
|
||||
interface LogLineProps {
|
||||
log: LogLine;
|
||||
@@ -10,18 +16,17 @@ interface LogLineProps {
|
||||
}
|
||||
|
||||
export function TerminalLine({ log, searchTerm }: LogLineProps) {
|
||||
const { timestamp, message } = log;
|
||||
const { timestamp, message, rawTimestamp } = log;
|
||||
const { type, variant, color } = getLogType(message);
|
||||
|
||||
const formattedTime = timestamp
|
||||
? timestamp.toLocaleString("en-GB", {
|
||||
? timestamp.toLocaleString([], {
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
month: "short",
|
||||
year: "numeric",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
year: "2-digit",
|
||||
second: "2-digit",
|
||||
hour12: false,
|
||||
})
|
||||
: "--- No time found ---";
|
||||
|
||||
@@ -36,7 +41,26 @@ export function TerminalLine({ log, searchTerm }: LogLineProps) {
|
||||
</span>
|
||||
) : (
|
||||
part
|
||||
)
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
const tooltip = (color: string, timestamp: string) => {
|
||||
return (
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div
|
||||
className={cn("w-2 h-full flex-shrink-0 rounded-[3px]", color)}
|
||||
/>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p className="text text-xs text-muted-foreground break-all max-w-md">
|
||||
{timestamp}
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -48,14 +72,14 @@ export function TerminalLine({ log, searchTerm }: LogLineProps) {
|
||||
? "bg-red-500/10 hover:bg-red-500/15"
|
||||
: type === "warning"
|
||||
? "bg-yellow-500/10 hover:bg-yellow-500/15"
|
||||
: "hover:bg-gray-200/50 dark:hover:bg-gray-800/50"
|
||||
: "hover:bg-gray-200/50 dark:hover:bg-gray-800/50",
|
||||
)}
|
||||
>
|
||||
{" "}
|
||||
<div className="flex items-start gap-x-2">
|
||||
{/* Icon to expand the log item maybe implement a colapsible later */}
|
||||
{/* <Square className="size-4 text-muted-foreground opacity-0 group-hover/logitem:opacity-100 transition-opacity" /> */}
|
||||
<div className={cn("w-2 h-full flex-shrink-0 rounded-[3px]", color)} />
|
||||
{rawTimestamp && tooltip(color, rawTimestamp)}
|
||||
<span className="select-none pl-2 text-muted-foreground w-full sm:w-40 flex-shrink-0">
|
||||
{formattedTime}
|
||||
</span>
|
||||
|
||||
@@ -2,6 +2,7 @@ export type LogType = "error" | "warning" | "success" | "info";
|
||||
export type LogVariant = "red" | "yellow" | "green" | "blue";
|
||||
|
||||
export interface LogLine {
|
||||
rawTimestamp: string | null;
|
||||
timestamp: Date | null;
|
||||
message: string;
|
||||
}
|
||||
@@ -61,11 +62,12 @@ export function parseLogs(logString: string): LogLine[] {
|
||||
const cleanedMessage = message
|
||||
?.replace(
|
||||
/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z|\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3} UTC/g,
|
||||
""
|
||||
"",
|
||||
)
|
||||
.trim();
|
||||
|
||||
return {
|
||||
rawTimestamp: timestamp,
|
||||
timestamp: timestamp ? new Date(timestamp.replace(" UTC", "Z")) : null,
|
||||
message: cleanedMessage,
|
||||
};
|
||||
@@ -107,7 +109,7 @@ export const getLogType = (message: string): LogStyle => {
|
||||
|
||||
if (
|
||||
/(?:successfully|complete[d]?)\s+(?:initialized|started|completed|done)/i.test(
|
||||
lowerMessage
|
||||
lowerMessage,
|
||||
) ||
|
||||
/\[(?:success|ok|done)\]/i.test(lowerMessage) ||
|
||||
/(?:listening|running)\s+(?:on|at)\s+(?:port\s+)?\d+/i.test(lowerMessage) ||
|
||||
|
||||
@@ -91,10 +91,7 @@ export const ShowModalLogs = ({ appName, children, serverId }: Props) => {
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<DockerLogsId
|
||||
containerId={containerId || ""}
|
||||
serverId={serverId}
|
||||
/>
|
||||
<DockerLogsId containerId={containerId || ""} serverId={serverId} />
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
@@ -14,16 +14,14 @@ const badgeVariants = cva(
|
||||
"border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||
destructive:
|
||||
"border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
|
||||
red:
|
||||
"border-transparent select-none items-center whitespace-nowrap font-medium bg-red-500/15 text-destructive text-xs h-4 px-1 py-1 rounded-md",
|
||||
red: "border-transparent select-none items-center whitespace-nowrap font-medium bg-red-500/15 text-destructive text-xs h-4 px-1 py-1 rounded-md",
|
||||
yellow:
|
||||
"border-transparent select-none items-center whitespace-nowrap font-medium bg-yellow-500/15 text-yellow-500 text-xs h-4 px-1 py-1 rounded-md",
|
||||
orange:
|
||||
"border-transparent select-none items-center whitespace-nowrap font-medium bg-orange-500/15 text-orange-500 text-xs h-4 px-1 py-1 rounded-md",
|
||||
green:
|
||||
"border-transparent select-none items-center whitespace-nowrap font-medium bg-emerald-500/15 text-emerald-500 text-xs h-4 px-1 py-1 rounded-md",
|
||||
blue:
|
||||
"border-transparent select-none items-center whitespace-nowrap font-medium bg-blue-500/15 text-blue-500 text-xs h-4 px-1 py-1 rounded-md",
|
||||
blue: "border-transparent select-none items-center whitespace-nowrap font-medium bg-blue-500/15 text-blue-500 text-xs h-4 px-1 py-1 rounded-md",
|
||||
blank:
|
||||
"border-transparent select-none items-center whitespace-nowrap font-medium dark:bg-white/15 bg-black/15 text-foreground text-xs h-4 px-1 py-1 rounded-md",
|
||||
outline: "text-foreground",
|
||||
|
||||
Reference in New Issue
Block a user