Skip to content

Commit d8bbc10

Browse files
committed
fixed some react update issues
extracted common logic to logUtils improved codeBlock highlight adjusted CLICKHOUSE LOGS values
1 parent 443f2a1 commit d8bbc10

File tree

9 files changed

+121
-200
lines changed

9 files changed

+121
-200
lines changed

apps/webapp/app/components/code/CodeBlock.tsx

Lines changed: 4 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { Highlight, Prism } from "prism-react-renderer";
55
import { forwardRef, ReactNode, useCallback, useEffect, useState } from "react";
66
import { TextWrapIcon } from "~/assets/icons/TextWrapIcon";
77
import { cn } from "~/utils/cn";
8+
import { highlightSearchText } from "~/utils/logUtils";
89
import { Button } from "../primitives/Buttons";
910
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "../primitives/Dialog";
1011
import { Paragraph } from "../primitives/Paragraph";
@@ -72,12 +73,6 @@ type CodeBlockProps = {
7273
const dimAmount = 0.5;
7374
const extraLinesWhenClipping = 0.35;
7475

75-
const SEARCH_HIGHLIGHT_STYLES = {
76-
backgroundColor: "#facc15",
77-
color: "#000000",
78-
fontWeight: "500",
79-
} as const;
80-
8176
const defaultTheme: PrismTheme = {
8277
plain: {
8378
color: "#9C9AF2",
@@ -371,7 +366,7 @@ export const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(
371366
)}
372367
dir="ltr"
373368
>
374-
{highlightSearchInText(code, searchTerm)}
369+
{highlightSearchText(code, searchTerm)}
375370
</pre>
376371
</div>
377372
)}
@@ -413,7 +408,7 @@ export const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(
413408
className="overflow-auto px-3 py-3 scrollbar-thin scrollbar-track-transparent scrollbar-thumb-charcoal-600"
414409
>
415410
<pre className="relative mr-2 p-2 font-mono text-base leading-relaxed" dir="ltr">
416-
{highlightSearchInText(code, searchTerm)}
411+
{highlightSearchText(code, searchTerm)}
417412
</pre>
418413
</div>
419414
)}
@@ -426,42 +421,6 @@ export const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(
426421

427422
CodeBlock.displayName = "CodeBlock";
428423

429-
/**
430-
* Highlights search term matches in plain text
431-
*/
432-
function highlightSearchInText(text: string, searchTerm: string | undefined): React.ReactNode {
433-
if (!searchTerm || searchTerm.trim() === "") {
434-
return text;
435-
}
436-
437-
const escapedSearch = searchTerm.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
438-
const regex = new RegExp(escapedSearch, "gi");
439-
440-
const parts: React.ReactNode[] = [];
441-
let lastIndex = 0;
442-
let match;
443-
let matchCount = 0;
444-
445-
while ((match = regex.exec(text)) !== null) {
446-
if (match.index > lastIndex) {
447-
parts.push(text.substring(lastIndex, match.index));
448-
}
449-
parts.push(
450-
<span key={`match-${matchCount}`} style={SEARCH_HIGHLIGHT_STYLES}>
451-
{match[0]}
452-
</span>
453-
);
454-
lastIndex = regex.lastIndex;
455-
matchCount++;
456-
}
457-
458-
if (lastIndex < text.length) {
459-
parts.push(text.substring(lastIndex));
460-
}
461-
462-
return parts.length > 0 ? parts : text;
463-
}
464-
465424
function Chrome({ title }: { title?: string }) {
466425
return (
467426
<div className="grid h-7 grid-cols-[100px_auto_100px] border-b border-charcoal-800 bg-charcoal-900">
@@ -607,40 +566,7 @@ function HighlightCode({
607566
const tokenProps = getTokenProps({ token, key });
608567

609568
// Highlight search term matches in token
610-
let content: React.ReactNode = token.content;
611-
if (searchTerm && searchTerm.trim() !== "" && token.content) {
612-
const escapedSearch = searchTerm.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
613-
const regex = new RegExp(escapedSearch, "gi");
614-
615-
const parts: React.ReactNode[] = [];
616-
let lastIndex = 0;
617-
let match;
618-
let matchCount = 0;
619-
620-
while ((match = regex.exec(token.content)) !== null) {
621-
if (match.index > lastIndex) {
622-
parts.push(token.content.substring(lastIndex, match.index));
623-
}
624-
parts.push(
625-
<span
626-
key={`match-${matchCount}`}
627-
style={SEARCH_HIGHLIGHT_STYLES}
628-
>
629-
{match[0]}
630-
</span>
631-
);
632-
lastIndex = regex.lastIndex;
633-
matchCount++;
634-
}
635-
636-
if (lastIndex < token.content.length) {
637-
parts.push(token.content.substring(lastIndex));
638-
}
639-
640-
if (parts.length > 0) {
641-
content = parts;
642-
}
643-
}
569+
const content = highlightSearchText(token.content, searchTerm);
644570

645571
return (
646572
<span

apps/webapp/app/components/logs/LogDetailView.tsx

Lines changed: 0 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -94,53 +94,6 @@ function formatStringJSON(str: string): string {
9494
.replace(/\\t/g, "\t"); // Converts literal "\t" to tab
9595
}
9696

97-
// Highlight search term in JSON string - returns React nodes with highlights
98-
function highlightJsonWithSearch(json: string, searchTerm: string | undefined): ReactNode {
99-
if (!searchTerm || searchTerm.trim() === "") {
100-
return json;
101-
}
102-
103-
// Escape special regex characters in the search term
104-
const escapedSearch = searchTerm.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
105-
const regex = new RegExp(escapedSearch, "gi");
106-
107-
const parts: ReactNode[] = [];
108-
let lastIndex = 0;
109-
let match;
110-
let matchCount = 0;
111-
112-
while ((match = regex.exec(json)) !== null) {
113-
// Add text before match
114-
if (match.index > lastIndex) {
115-
parts.push(json.substring(lastIndex, match.index));
116-
}
117-
// Add highlighted match with inline styles
118-
parts.push(
119-
<span
120-
key={`match-${matchCount}`}
121-
style={{
122-
backgroundColor: "#facc15",
123-
color: "#000000",
124-
fontWeight: "500",
125-
borderRadius: "0.25rem",
126-
padding: "0 0.125rem",
127-
}}
128-
>
129-
{match[0]}
130-
</span>
131-
);
132-
lastIndex = regex.lastIndex;
133-
matchCount++;
134-
}
135-
136-
// Add remaining text
137-
if (lastIndex < json.length) {
138-
parts.push(json.substring(lastIndex));
139-
}
140-
141-
return parts.length > 0 ? parts : json;
142-
}
143-
14497

14598
export function LogDetailView({ logId, initialLog, onClose, searchTerm }: LogDetailViewProps) {
14699
const organization = useOrganization();

apps/webapp/app/components/logs/LogsTable.tsx

Lines changed: 4 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { useEnvironment } from "~/hooks/useEnvironment";
77
import { useOrganization } from "~/hooks/useOrganizations";
88
import { useProject } from "~/hooks/useProject";
99
import type { LogEntry, LogsListAppliedFilters } from "~/presenters/v3/LogsListPresenter.server";
10-
import { getLevelColor } from "~/utils/logUtils";
10+
import { getLevelColor, highlightSearchText } from "~/utils/logUtils";
1111
import { v3RunSpanPath } from "~/utils/pathBuilder";
1212
import { DateTime } from "../primitives/DateTime";
1313
import { Paragraph } from "../primitives/Paragraph";
@@ -58,30 +58,6 @@ function getLevelBorderColor(level: LogEntry["level"]): string {
5858
}
5959
}
6060

61-
// Case-insensitive text highlighting
62-
function highlightText(text: string, searchTerm: string | undefined): ReactNode {
63-
if (!searchTerm || searchTerm.trim() === "") {
64-
return text;
65-
}
66-
67-
const lowerText = text.toLowerCase();
68-
const lowerSearch = searchTerm.toLowerCase();
69-
const index = lowerText.indexOf(lowerSearch);
70-
71-
if (index === -1) {
72-
return text;
73-
}
74-
75-
return (
76-
<>
77-
{text.slice(0, index)}
78-
<mark className="rounded px-0.5 bg-yellow-400 text-black font-medium">
79-
{text.slice(index, index + searchTerm.length)}
80-
</mark>
81-
{text.slice(index + searchTerm.length)}
82-
</>
83-
);
84-
}
8561

8662
export function LogsTable({
8763
logs,
@@ -206,7 +182,7 @@ export function LogsTable({
206182
</TableCell>
207183
<TableCell className="max-w-0 truncate" onClick={handleRowClick} hasAction>
208184
<span className="block truncate font-mono text-xs" title={log.message}>
209-
{highlightText(log.message, searchTerm)}
185+
{highlightSearchText(log.message, searchTerm)}
210186
</span>
211187
</TableCell>
212188
<TableCellMenu
@@ -249,10 +225,10 @@ function NoLogs({ title }: { title: string }) {
249225
}
250226

251227
function BlankState({ isLoading }: { isLoading?: boolean }) {
252-
if (isLoading) return <TableBlankRow colSpan={7}></TableBlankRow>;
228+
if (isLoading) return <TableBlankRow colSpan={6}></TableBlankRow>;
253229

254230
return (
255-
<TableBlankRow colSpan={7}>
231+
<TableBlankRow colSpan={6}>
256232
<div className="flex flex-col items-center justify-center gap-6">
257233
<Paragraph className="w-auto" variant="base/bright">
258234
No logs match your filters. Try refreshing or modifying your filters.

apps/webapp/app/env.server.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1176,16 +1176,16 @@ const EnvironmentSchema = z
11761176
CLICKHOUSE_COMPRESSION_REQUEST: z.string().default("1"),
11771177

11781178
// Logs List Query Settings (for paginated log views)
1179-
CLICKHOUSE_LOGS_LIST_MAX_MEMORY_USAGE: z.coerce.number().int().default(2_000_000_000),
1180-
CLICKHOUSE_LOGS_LIST_MAX_BYTES_BEFORE_EXTERNAL_SORT: z.coerce.number().int().default(1_000_000_000),
1181-
CLICKHOUSE_LOGS_LIST_MAX_THREADS: z.coerce.number().int().default(4),
1182-
CLICKHOUSE_LOGS_LIST_MAX_ROWS_TO_READ: z.coerce.number().int().optional(),
1183-
CLICKHOUSE_LOGS_LIST_MAX_EXECUTION_TIME: z.coerce.number().int().optional(),
1179+
CLICKHOUSE_LOGS_LIST_MAX_MEMORY_USAGE: z.coerce.number().int().default(256_000_000),
1180+
CLICKHOUSE_LOGS_LIST_MAX_BYTES_BEFORE_EXTERNAL_SORT: z.coerce.number().int().default(256_000_000),
1181+
CLICKHOUSE_LOGS_LIST_MAX_THREADS: z.coerce.number().int().default(2),
1182+
CLICKHOUSE_LOGS_LIST_MAX_ROWS_TO_READ: z.coerce.number().int().default(10_000_000),
1183+
CLICKHOUSE_LOGS_LIST_MAX_EXECUTION_TIME: z.coerce.number().int().default(120),
11841184

11851185
// Logs Detail Query Settings (for single log views)
1186-
CLICKHOUSE_LOGS_DETAIL_MAX_MEMORY_USAGE: z.coerce.number().int().default(500_000_000),
1186+
CLICKHOUSE_LOGS_DETAIL_MAX_MEMORY_USAGE: z.coerce.number().int().default(64_000_000),
11871187
CLICKHOUSE_LOGS_DETAIL_MAX_THREADS: z.coerce.number().int().default(2),
1188-
CLICKHOUSE_LOGS_DETAIL_MAX_EXECUTION_TIME: z.coerce.number().int().optional(),
1188+
CLICKHOUSE_LOGS_DETAIL_MAX_EXECUTION_TIME: z.coerce.number().int().default(60),
11891189

11901190
EVENTS_CLICKHOUSE_URL: z
11911191
.string()

apps/webapp/app/presenters/v3/LogsListPresenter.server.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@ import { kindToLevel, type LogLevel } from "~/utils/logUtils";
2121

2222
export type { LogLevel };
2323

24+
type ErrorAttributes = {
25+
error?: {
26+
message?: unknown;
27+
};
28+
[key: string]: unknown;
29+
};
30+
2431
export type LogsListOptions = {
2532
userId?: string;
2633
projectId: string;
@@ -237,7 +244,7 @@ export class LogsListPresenter {
237244
if (hasRunLevelFilters) {
238245
const runsRepository = new RunsRepository({
239246
clickhouse: this.clickhouse,
240-
prisma: this.replica as PrismaClient,
247+
prisma: this.replica,
241248
});
242249

243250
function clampToNow(date: Date): Date {
@@ -460,14 +467,14 @@ export class LogsListPresenter {
460467
// For error logs with status ERROR, try to extract error message from attributes
461468
if (log.status === "ERROR" && log.attributes) {
462469
try {
463-
let attributes = log.attributes as Record<string, any>;
470+
let attributes = log.attributes as ErrorAttributes;
464471

465472
if (attributes?.error?.message && typeof attributes.error.message === 'string') {
466-
displayMessage = attributes.error.message;
467-
}
468-
} catch {
469-
// If attributes parsing fails, use the regular message
473+
displayMessage = attributes.error.message;
470474
}
475+
} catch {
476+
// If attributes parsing fails, use the regular message
477+
}
471478
}
472479

473480
return {

0 commit comments

Comments
 (0)