Skip to content

Commit

Permalink
refactor logs
Browse files Browse the repository at this point in the history
  • Loading branch information
breeg554 committed Dec 13, 2024
1 parent cf20a19 commit 87f6890
Show file tree
Hide file tree
Showing 4 changed files with 390 additions and 192 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,24 @@ import { useFetcher } from '@remix-run/react';
import { ClientOnly } from 'remix-utils/client-only';
import { useLocalStorage } from 'usehooks-ts';

import { EmptyMessage } from '~/components/list/ItemList';
import { EmptyMessage, ItemList } from '~/components/list/ItemList';
import { PinButton } from '~/components/pages/pipelines/build/BuilderSidebar/BuilderSidebar';
import { RunLogs } from '~/components/pages/pipelines/components/RunLogs';
import {
RunLogs,
Log,
LogBlockName,
LogDate,
LogMessage,
LogsLoadMoreWrapper,
LogsWrapper,
LogTopic,
LogTypes,
RunLogsFilter,
} from '~/components/pages/pipelines/components/RunLogs';
} from '~/components/pages/pipelines/components/RunLogs.components';
import { IExtendedPipeline } from '~/components/pages/pipelines/pipeline.types';
import { loader as logsLoader } from '~/components/pages/pipelines/runLogs/loader.server';
import { useRunPipeline } from '~/components/pages/pipelines/RunPipelineProvider';
import { LoadMoreButton } from '~/components/pagination/LoadMoreButton';
import { useBreakpoints } from '~/hooks/useBreakpoints';
import { useOrganizationId } from '~/hooks/useOrganizationId';
import { cn } from '~/utils/cn';
Expand Down Expand Up @@ -213,15 +222,44 @@ function SidebarLogs({
return (
<RunLogs
key={key}
defaultLogs={fetcher.data?.logs ?? []}
defaultAfter={fetcher.data?.pagination.after}
blockName={blockName}
runId={Number(runId)}
pipelineId={pipeline.id}
blockName={blockName}
organizationId={pipeline.organization_id}
className="w-full max-h-[calc(100%_-_45px)] px-2"
variant="light"
size="sm"
defaultLogs={fetcher.data?.logs ?? []}
defaultAfter={fetcher.data?.pagination.after}
renderLogs={(logs, { status, fetchNext, fetchNextRef, after }) => (
<LogsWrapper
size="sm"
variant="light"
className="w-full max-h-[calc(100%_-_45px)] px-2"
>
<ItemList
items={logs}
renderItem={(log) => (
<Log log={log} className={cn('py-1')}>
<LogDate className="mr-2">{log.created_at}</LogDate>
<LogTopic className="mr-2">{log.context}</LogTopic>
<LogBlockName className="mr-2">{log.block_name}</LogBlockName>
<LogMessage className="mr-2" log={log}>
{log.message}
</LogMessage>
<LogTypes>{log.message_types?.join(' -> ')}</LogTypes>
</Log>
)}
/>

<LogsLoadMoreWrapper ref={fetchNextRef}>
<LoadMoreButton
className="text-xs"
isFetching={status !== 'idle'}
disabled={status !== 'idle'}
hasNextPage={!!after}
onClick={fetchNext}
/>
</LogsLoadMoreWrapper>
</LogsWrapper>
)}
/>
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
import type { ComponentPropsWithRef } from 'react';
import React from 'react';

import type { IPipelineRunLog } from '~/api/pipeline/pipeline.contracts';
import { SelectInput } from '~/components/form/inputs/select/select.input';
import type { SelectInputProps } from '~/components/form/inputs/select/select.input-impl.client';
import { BasicLink } from '~/components/link/BasicLink';
import { EmptyMessage } from '~/components/list/ItemList';
import { cn } from '~/utils/cn';
import { routes } from '~/utils/routes.utils';

export type LogsVariant = 'dark' | 'light';
export type LogsSize = 'sm' | 'md';

interface LogsFilterProps {
value: string | null | undefined;
onSelect: (blockName: string) => void;
onClear: () => void;
options: { value: string; label: string }[];
}

export function RunLogsFilter({
value,
onClear,
onSelect,
options,
...rest
}: LogsFilterProps & Partial<SelectInputProps>) {
return (
<SelectInput
allowClear
onClear={onClear}
placeholder="Filter by block..."
value={value}
onSelect={onSelect}
options={options}
{...rest}
/>
);
}

export type LogProps = {
variant?: LogsVariant;
size?: LogsSize;
};

const LogContext = React.createContext<Required<LogProps> | undefined>(
undefined,
);

const useLogContext = () => {
const ctx = React.use(LogContext);
if (!ctx) {
throw new Error('useLogContext must be used within a LogContext');
}

return ctx;
};

export function Log({
className,
children,
...rest
}: React.HTMLAttributes<HTMLParagraphElement> & { log: IPipelineRunLog }) {
const { variant, size } = useLogContext();
return (
<p
className={cn(
{
'hover:bg-gray-700': variant === 'dark',
'hover:bg-muted': variant === 'light',
},

getLogSize(size),
className,
)}
{...rest}
>
{children}
</p>
);
}

export function LogDate({
className,
children,
...rest
}: React.HTMLAttributes<HTMLSpanElement>) {
return (
<span
className={cn('text-cyan-400 whitespace-nowrap', className)}
{...rest}
>
{children}
</span>
);
}

export function LogTopic({
className,
children,
...rest
}: React.HTMLAttributes<HTMLSpanElement>) {
return (
<span className={cn('text-yellow-500 ', className)} {...rest}>
{children}
</span>
);
}

export function LogBlockName({
className,
children,
...rest
}: React.HTMLAttributes<HTMLSpanElement>) {
return (
<span className={cn('text-purple-500', className)} {...rest}>
{children}
</span>
);
}

export function LogMessage({
className,
children,
log,
...rest
}: React.HTMLAttributes<HTMLSpanElement> & { log: IPipelineRunLog }) {
const { variant } = useLogContext();
const isError = log?.message_types?.includes('error');
return (
<span
className={cn(
{
'text-red-500': isError,
'text-gray-300': !isError && variant === 'dark',
'text-muted-foreground': !isError && variant === 'light',
},
className,
)}
{...rest}
>
{children}
</span>
);
}

export function LogTypes({
className,
children,
...rest
}: React.HTMLAttributes<HTMLSpanElement>) {
return (
<span className={cn('text-green-400', className)} {...rest}>
{children}
</span>
);
}

export function LogsLoadMoreWrapper({
className,
children,
...rest
}: ComponentPropsWithRef<'div'>) {
return (
<div className={cn('flex justify-center', className)} {...rest}>
{children}
</div>
);
}

export function LogsWrapper({
className,
children,
variant = 'dark',
size = 'md',
...rest
}: ComponentPropsWithRef<'div'> & LogProps) {
return (
<LogContext value={{ variant, size }}>
<div
className={cn(
'p-4 h-[65vh] max-h-[450px] overflow-y-auto rounded-lg flex flex-col-reverse',
{
'bg-gray-800 text-gray-400': variant === 'dark',
'bg-[#fbfbfb] text-foreground': variant === 'light',
},
className,
)}
{...rest}
>
{children}
</div>
</LogContext>
);
}

function getLogSize(size: LogsSize = 'md') {
switch (size) {
case 'sm':
return 'text-xs';
case 'md':
return 'text-base';
}
}

interface LogsEmptyMessageProps {
organizationId: number;
pipelineId: number;
className?: string;
}
export function LogsEmptyMessage({
organizationId,
className,
pipelineId,
}: LogsEmptyMessageProps) {
return (
<EmptyMessage
className={cn('block mx-auto text-center w-fit max-w-[350px]', className)}
>
No logs found for this run. You can enable logs in the workflow{' '}
<BasicLink
target="_blank"
className="font-semibold text-foreground hover:underline"
to={routes.pipelineSettings(organizationId, pipelineId)}
>
settings
</BasicLink>
.
</EmptyMessage>
);
}
Loading

0 comments on commit 87f6890

Please sign in to comment.