Skip to content

Commit

Permalink
Merge pull request middlewarehq#571 from vishalmishraa/system-logs-ui
Browse files Browse the repository at this point in the history
  • Loading branch information
jayantbh authored Oct 19, 2024
2 parents 4fa53bf + 9ec9da3 commit b524c08
Show file tree
Hide file tree
Showing 10 changed files with 585 additions and 28 deletions.
52 changes: 52 additions & 0 deletions web-server/src/components/Service/SystemLog/FormattedLog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { useTheme } from '@mui/material';
import { useCallback } from 'react';

import { Line } from '@/components/Text';
import { ParsedLog } from '@/types/resources';

export const FormattedLog = ({ log }: { log: ParsedLog; index: number }) => {
const theme = useTheme();
const getLevelColor = useCallback(
(level: string) => {
const colors: { [key: string]: string } = {
INFO: theme.colors.success.main,
MAIN_INFO: theme.colors.success.main,
CHILD_INFO: theme.colors.success.main,
SENTINEL_INFO: theme.colors.success.main,
WARN: theme.colors.warning.main,
WARNING: theme.colors.warning.main,
NOTICE: theme.colors.warning.dark,
ERROR: theme.colors.error.main,
FATAL: theme.colors.error.main,
PANIC: theme.colors.error.main,
DEBUG: theme.colors.info.light,
MAIN_SYSTEM: theme.colors.primary.main,
CHILD_SYSTEM: theme.colors.primary.main,
SENTINEL_SYSTEM: theme.colors.primary.main,
LOG: theme.colors.info.main,
CRITICAL: theme.colors.error.main
};

return colors[level.toUpperCase()] || theme.colors.info.main;
},
[theme]
);

const { timestamp, ip, logLevel, message } = log;
return (
<Line mono marginBottom={1}>
<Line component="span" color="info">
{timestamp}
</Line>{' '}
{ip && (
<Line component="span" color="primary">
{ip}{' '}
</Line>
)}
<Line component="span" color={getLevelColor(logLevel)}>
[{logLevel}]
</Line>{' '}
{message}
</Line>
);
};
9 changes: 9 additions & 0 deletions web-server/src/components/Service/SystemLog/PlainLog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Line } from '@/components/Text';

export const PlainLog = ({ log }: { log: string; index: number }) => {
return (
<Line mono marginBottom={1}>
{log}
</Line>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { CopyAll } from '@mui/icons-material';
import { Button, Typography } from '@mui/material';
import { useSnackbar } from 'notistack';
import CopyToClipboard from 'react-copy-to-clipboard';

import { FlexBox } from '@/components/FlexBox';
import { Line } from '@/components/Text';

export const SystemLogErrorMessage = ({ errorBody }: { errorBody: any }) => {
const { enqueueSnackbar } = useSnackbar();
return (
<FlexBox alignCenter gap={1}>
<Line tiny color="warning.main" fontWeight="bold">
<Typography variant="h6">
Something went wrong displaying the logs.
</Typography>
An error occurred while processing the logs. Please report this issue.
</Line>
<CopyToClipboard
text={JSON.stringify(errorBody, null, ' ')}
onCopy={() => {
enqueueSnackbar(`Error logs copied to clipboard`, {
variant: 'success'
});
}}
>
<Button
size="small"
variant="contained"
startIcon={<CopyAll />}
sx={{ fontWeight: 'bold' }}
>
Copy Logs
</Button>
</CopyToClipboard>
<Button
variant="contained"
size="small"
href="https://github.com/middlewarehq/middleware/issues/new/choose"
target="_blank"
rel="noopener noreferrer"
>
Report Issue
</Button>
</FlexBox>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { CircularProgress } from '@mui/material';
import { useEffect, useMemo, useRef } from 'react';

import { FlexBox } from '@/components/FlexBox';
import { PlainLog } from '@/components/Service/SystemLog/PlainLog';
import { SystemLogErrorMessage } from '@/components/Service/SystemLog/SystemLogErrorMessage';
import { Line } from '@/components/Text';
import { track } from '@/constants/events';
import { ServiceNames } from '@/constants/service';
import { useSystemLogs } from '@/hooks/useSystemLogs';

export const SystemLogsErrorFallback = ({
error,
serviceName
}: {
error: Error;
serviceName: ServiceNames;
}) => {
const { services, loading, logs } = useSystemLogs({ serviceName });

const containerRef = useRef<HTMLDivElement>(null);
const errorBody = useMemo(
() => ({
message: error?.message?.replace('\\n', '\n') || '',
stack: error?.stack?.replace('\\n', '\n') || ''
}),
[error]
);

useEffect(() => {
if (error) {
track('ERR_FALLBACK_SHOWN', { err: errorBody });
console.error(error);
}
}, [errorBody, error]);

useEffect(() => {
if (containerRef.current) {
containerRef.current.scrollIntoView({ behavior: 'smooth' });
}
}, [logs]);

return (
<FlexBox col>
{loading ? (
<FlexBox alignCenter gap2>
<CircularProgress size="20px" />
<Line>Loading...</Line>
</FlexBox>
) : (
services &&
logs.map((log, index) => {
return <PlainLog log={log} index={index} key={index} />;
})
)}
<FlexBox ref={containerRef} />
<SystemLogErrorMessage errorBody={errorBody} />
</FlexBox>
);
};
9 changes: 9 additions & 0 deletions web-server/src/constants/log-formatter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const generalLogRegex =
/^\[(.*?)\] \[(\d+)\] \[(INFO|ERROR|WARN|DEBUG|WARNING|CRITICAL)\] (.+)$/;
export const httpLogRegex =
/^(\S+) (\S+) (\S+) \[([^\]]+)\] "([^"]*)" (\d+) (\d+) "([^"]*)" "([^"]*)"$/;
export const redisLogRegex =
/^(?<role>\d+:[XCMS]) (?<timestamp>\d{2} \w{3} \d{4} \d{2}:\d{2}:\d{2}\.\d{3}) (?<loglevel>[\.\-\#\*]) (?<message>.*)$/;
export const postgresLogRegex =
/^(?<timestamp>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3} UTC) \[\d+\] (?<loglevel>[A-Z]+):\s*(?<message>(.|\n)+?)$/;
export const dataSyncLogRegex = /\[(\w+)\]\s(.+?)\sfor\s(\w+)\s(.+)/;
60 changes: 32 additions & 28 deletions web-server/src/content/Service/SystemLogs.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import CircularProgress from '@mui/material/CircularProgress';
import { useEffect, useRef, useMemo } from 'react';
import { CircularProgress } from '@mui/material';
import { useEffect, useRef } from 'react';
import { ErrorBoundary } from 'react-error-boundary';

import { FlexBox } from '@/components/FlexBox';
import { FormattedLog } from '@/components/Service/SystemLog/FormattedLog';
import { PlainLog } from '@/components/Service/SystemLog/PlainLog';
import { SystemLogsErrorFallback } from '@/components/Service/SystemLog/SystemLogsErrorFllback';
import { Line } from '@/components/Text';
import { ServiceNames } from '@/constants/service';
import { useSelector } from '@/store';
import { useSystemLogs } from '@/hooks/useSystemLogs';
import { parseLogLine } from '@/utils/logFormatter';

export const SystemLogs = ({ serviceName }: { serviceName: ServiceNames }) => {
const services = useSelector((state) => state.service.services);
const loading = useSelector((state) => state.service.loading);
const logs = useMemo(() => {
return services[serviceName].logs || [];
}, [serviceName, services]);
const { services, loading, logs } = useSystemLogs({ serviceName });

const containerRef = useRef<HTMLDivElement>(null);

Expand All @@ -22,26 +23,29 @@ export const SystemLogs = ({ serviceName }: { serviceName: ServiceNames }) => {
}, [logs]);

return (
<FlexBox col>
{loading ? (
<FlexBox alignCenter gap2>
<CircularProgress size="20px" />
<Line>Loading...</Line>
</FlexBox>
) : (
services &&
logs.map((log, index) => (
<Line
key={index}
marginBottom={'8px'}
fontSize={'14px'}
fontFamily={'monospace'}
>
{log}
</Line>
))
<ErrorBoundary
FallbackComponent={({ error }) => (
<SystemLogsErrorFallback error={error} serviceName={serviceName} />
)}
<FlexBox ref={containerRef} />
</FlexBox>
>
<FlexBox col>
{loading ? (
<FlexBox alignCenter gap2>
<CircularProgress size="20px" />
<Line>Loading...</Line>
</FlexBox>
) : (
services &&
logs.map((log, index) => {
const parsedLog = parseLogLine(log);
if (!parsedLog) {
return <PlainLog log={log} index={index} key={index} />;
}
return <FormattedLog log={parsedLog} index={index} key={index} />;
})
)}
<FlexBox ref={containerRef} />
</FlexBox>
</ErrorBoundary>
);
};
22 changes: 22 additions & 0 deletions web-server/src/hooks/useSystemLogs.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { useMemo } from 'react';

import { ServiceNames } from '@/constants/service';
import { useSelector } from '@/store';
export const useSystemLogs = ({
serviceName
}: {
serviceName: ServiceNames;
}) => {
const services = useSelector((state) => state.service.services);
const loading = useSelector((state) => state.service.loading);
const logs = useMemo(
() => services[serviceName]?.logs || [],
[serviceName, services]
);

return {
services,
loading,
logs
};
};
21 changes: 21 additions & 0 deletions web-server/src/types/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1039,3 +1039,24 @@ export type DB_OrgRepo = {
deployment_type: 'PR_MERGE' | 'WORKFLOW';
repo_workflows: RepoWorkflow[];
};

export enum LogLevel {
'DEBUG' = 'DEBUG',
'INFO' = 'INFO',
'NOTICE' = 'NOTICE',
'WARNING' = 'WARNING',
'ERROR' = 'ERROR',
'LOG' = 'LOG',
'FATAL' = 'FATAL',
'PANIC' = 'PANIC',
'STATEMENT' = 'STATEMENT',
'DETAIL' = 'DETAIL'
}

export interface ParsedLog {
timestamp: string;
logLevel: string;
message: string;
role?: string;
ip?: string;
}
Loading

0 comments on commit b524c08

Please sign in to comment.