Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for viewing search results in context for text logs (clp-text). #489

Merged
merged 31 commits into from
Jul 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
0007fb4
Add initial code.
junhaoliao Jul 22, 2024
c6d89d9
Reformat code.
junhaoliao Jul 22, 2024
44c15eb
Reformat code.
junhaoliao Jul 22, 2024
17bcd11
Add loading state UI to log-viewer-webui-client.
junhaoliao Jul 23, 2024
420126a
Display Axios error when any.
junhaoliao Jul 23, 2024
a6bf9d2
Set logEventIdx cursor position.
junhaoliao Jul 23, 2024
c1f6e19
Add LogViewerWebuiClientUrl setting override in `start-clp.py`.
junhaoliao Jul 23, 2024
18b9411
Reformat code.
junhaoliao Jul 23, 2024
f684c09
Avoid CSS-in-JS by moving styles to a separate stylesheet.
junhaoliao Jul 23, 2024
2b6d83a
Merge branch 'main' into pr-489
kirkrodrigues Jul 24, 2024
ba61aa6
Rename `LogViewerWebuiClientUrl` -> `LogViewerWebuiUrl`.
junhaoliao Jul 25, 2024
302ac3a
Break HTML tag into two lines.
junhaoliao Jul 25, 2024
7eed866
Refactor theme key to use constants.
junhaoliao Jul 25, 2024
d8c6b42
Apply docs / prompts suggestions from code review.
junhaoliao Jul 25, 2024
8c860e5
Rename `stepNumber` -> `stepIndicatorText`.
junhaoliao Jul 25, 2024
c81aef8
Rename `ix` -> `idx`; `msg_ix` -> `log_event_idx`; use camelCase wher…
junhaoliao Jul 25, 2024
d6a5e9b
Refactor Axios error reporting code for clearness.
junhaoliao Jul 25, 2024
fb6c7b6
Log axios error object together with the error message.
junhaoliao Jul 25, 2024
ae526eb
Move `QUERY_STATE_DESCRIPTIONS` into api/query.js and use enum `QUERY…
junhaoliao Jul 25, 2024
6568b5a
Update components/log-viewer-webui/client/src/ui/Loading.jsx
junhaoliao Jul 25, 2024
3f6d40f
Rename `Query`/`QueryState` -> `QueryStatus`.
junhaoliao Jul 25, 2024
50b9f70
Add extra button for opening up Log Viewer, instead of the previous h…
junhaoliao Jul 25, 2024
46d8a7a
Add href to <a/> and remove onclick handler.
junhaoliao Jul 26, 2024
db770f7
Move React state setter usages from api submission functions back int…
junhaoliao Jul 26, 2024
ec84389
Add space before `!important`.
junhaoliao Jul 26, 2024
af59797
Hide horizontal scrollbar in search result content if it does not ove…
junhaoliao Jul 26, 2024
8bb0e84
Hide "Go to the log context" button for clp-s where "Extract IR" is n…
junhaoliao Jul 26, 2024
2b52fd5
Reformat code - apply suggestions from code review.
junhaoliao Jul 27, 2024
a48588f
Add early return statement in erroneous URL parameter case - Apply su…
junhaoliao Jul 27, 2024
4cf54ad
Docs / Prompts improvement - Apply suggestions from code review
junhaoliao Jul 27, 2024
a270ff4
Rename QUERY_LOAD_STATE -> QUERY_LOADING_STATES.
junhaoliao Jul 27, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -786,6 +786,9 @@ def start_webui(instance_id: str, clp_config: CLPConfig, mounts: CLPDockerMounts
},
"public": {
"ClpStorageEngine": clp_config.package.storage_engine,
"LogViewerWebuiUrl": (
f"http://{clp_config.log_viewer_webui.host}:{clp_config.log_viewer_webui.port}",
),
},
}
meteor_settings = read_and_update_settings_json(settings_json_path, meteor_settings_updates)
Expand Down
668 changes: 598 additions & 70 deletions components/log-viewer-webui/client/package-lock.json

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions components/log-viewer-webui/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@
]
},
"dependencies": {
"@emotion/react": "^11.13.0",
"@emotion/styled": "^11.13.0",
kirkrodrigues marked this conversation as resolved.
Show resolved Hide resolved
"@mui/joy": "^5.0.0-beta.48",
"@types/react": "^18.3.3",
kirkrodrigues marked this conversation as resolved.
Show resolved Hide resolved
"axios": "^1.7.2",
"react": "^18.3.1",
"react-dom": "^18.3.1"
}
Expand Down
5 changes: 5 additions & 0 deletions components/log-viewer-webui/client/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
<meta charset="utf-8"/>
<meta name="description" content="YScope Log Viewer">
<meta name="viewport" content="initial-scale=1, maximum-scale=1">
<link rel="preconnect" href="https://fonts.googleapis.com"/>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin/>
<link rel="stylesheet"
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap"
/>
</head>
<body>
<div id="root"></div>
Expand Down
10 changes: 9 additions & 1 deletion components/log-viewer-webui/client/src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
import {CssVarsProvider} from "@mui/joy/styles/CssVarsProvider";

import LOCAL_STORAGE_KEY from "./typings/LOCAL_STORAGE_KEY.js";
import QueryStatus from "./ui/QueryStatus.jsx";


/**
* Renders the main application.
*
* @return {JSX.Element}
*/
const App = () => {
return (
<h1>Hello world!</h1>
<CssVarsProvider modeStorageKey={LOCAL_STORAGE_KEY.UI_THEME}>
<QueryStatus/>
</CssVarsProvider>
);
};

Expand Down
32 changes: 32 additions & 0 deletions components/log-viewer-webui/client/src/api/query.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import axios from "axios";


/**
* @typedef {object} ExtractIrResp
* @property {number} begin_msg_ix
* @property {number} end_msg_ix
* @property {string} file_split_id
* @property {boolean} is_last_ir_chunk
* @property {string} orig_file_id
* @property {string} path
* @property {string} _id
*/

/**
* Submits a job to extract the split of an original file that contains a given log event. The file
* is extracted as a CLP IR file.
*
* @param {number|string} origFileId The ID of the original file
* @param {number} logEventIdx The index of the log event
* @param {Function} onUploadProgress Callback to handle upload progress events.
* @return {Promise<axios.AxiosResponse<ExtractIrResp>>}
*/
const submitExtractIrJob = async (origFileId, logEventIdx, onUploadProgress) => {
return await axios.post(
"/query/extract-ir",
{logEventIdx, origFileId},
{onUploadProgress}
);
};

export {submitExtractIrJob};
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* Enum of `window.localStorage` keys.
*/
const LOCAL_STORAGE_KEY = Object.freeze({
UI_THEME: "uiTheme",
});

export default LOCAL_STORAGE_KEY;
37 changes: 37 additions & 0 deletions components/log-viewer-webui/client/src/typings/query.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* @typedef {number} QueryLoadingState
*/
let enumQueryLoadingState;
/**
* Enum of query loading state.
*
* @enum {QueryLoadingState}
*/
const QUERY_LOADING_STATES = Object.freeze({
SUBMITTING: (enumQueryLoadingState = 0),
WAITING: ++enumQueryLoadingState,
LOADING: ++enumQueryLoadingState,
});

/**
* Descriptions for query loading states.
*/
const QUERY_LOADING_STATE_DESCRIPTIONS = Object.freeze({
[QUERY_LOADING_STATES.SUBMITTING]: {
label: "Submitting query Job",
description: "Parsing arguments and submitting job to the server.",
},
[QUERY_LOADING_STATES.WAITING]: {
label: "Waiting for job to finish",
description: "The job is running. Waiting for the job to finish.",
},
[QUERY_LOADING_STATES.LOADING]: {
label: "Loading Log Viewer",
description: "The query has been completed and the results are being loaded.",
},
});

export {
QUERY_LOADING_STATE_DESCRIPTIONS,
QUERY_LOADING_STATES,
};
24 changes: 24 additions & 0 deletions components/log-viewer-webui/client/src/ui/Loading.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
.loading-sheet {
height: 100%;

display: flex;
flex-direction: column;

align-items: center;
justify-content: center;
}

.loading-progress-container {
width: 100%;
}

.loading-stepper-container {
display: flex;
flex-grow: 1;

align-items: center;
}

.loading-stepper {
--Stepper-verticalGap: 2rem !important;
}
130 changes: 130 additions & 0 deletions components/log-viewer-webui/client/src/ui/Loading.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import {
Box,
LinearProgress,
Sheet,
Step,
StepIndicator,
Stepper,
Typography,
} from "@mui/joy";

import {
QUERY_LOADING_STATE_DESCRIPTIONS,
QUERY_LOADING_STATES,
} from "../typings/query.js";

import "./Loading.css";


/**
* Renders a step with a label and description.
*
* @param {object} props
* @param {string} props.description
* @param {boolean} props.isActive
* @param {boolean} props.isError
* @param {string} props.label
* @param {number | string} props.stepIndicatorText
* @return {React.ReactElement}
*/
const LoadingStep = ({
description,
isActive,
isError,
label,
stepIndicatorText,
}) => {
let color = isActive ?
"primary" :
"neutral";

if (isError) {
color = "danger";
}

return (
<Step
indicator={
<StepIndicator
color={color}
variant={isActive ?
"solid" :
"outlined"}
>
{stepIndicatorText}
</StepIndicator>
}
>
<Typography
color={color}
level={"title-lg"}
>
{label}
</Typography>
<Typography level={"body-sm"}>
{description}
</Typography>
</Step>
);
};

/**
* Displays status of a pending query job.
*
* @param {object} props
* @param {QueryLoadState} props.currentState
* @param {string} props.errorMsg
* @return {React.ReactElement}
*/
const Loading = ({currentState, errorMsg}) => {
const steps = [];
Object.values(QUERY_LOADING_STATES).forEach((state) => {
const isActive = (currentState === state);
const stateDescription = QUERY_LOADING_STATE_DESCRIPTIONS[state];
steps.push(
<LoadingStep
description={stateDescription.description}
isActive={isActive}
isError={false}
key={state}
label={stateDescription.label}
stepIndicatorText={state + 1}/>
);
if (isActive && null !== errorMsg) {
steps.push(
<LoadingStep
description={errorMsg}
isActive={isActive}
isError={true}
key={`${state}-error`}
label={"Error"}
stepIndicatorText={"X"}/>
);
}
});

return (
<>
<Sheet className={"loading-sheet"}>
<Box className={"loading-progress-container"}>
<LinearProgress
determinate={null !== errorMsg}
color={null === errorMsg ?
"primary" :
"danger"}/>
</Box>
<Box className={"loading-stepper-container"}>
<Stepper
className={"loading-stepper"}
orientation={"vertical"}
size={"lg"}
>
{steps}
</Stepper>
</Box>
</Sheet>
</>
);
};

export default Loading;
80 changes: 80 additions & 0 deletions components/log-viewer-webui/client/src/ui/QueryStatus.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import {
useEffect,
useRef,
useState,
} from "react";

import {AxiosError} from "axios";

import {submitExtractIrJob} from "../api/query.js";
import {QUERY_LOADING_STATES} from "../typings/query.js";
import Loading from "./Loading.jsx";


/**
* Submits queries and renders the query states.
*
* @return {React.ReactElement}
*/
const QueryStatus = () => {
const [queryState, setQueryState] = useState(QUERY_LOADING_STATES.SUBMITTING);
const [errorMsg, setErrorMsg] = useState(null);
const isFirstRun = useRef(true);

useEffect(() => {
if (false === isFirstRun.current) {
return;
}
isFirstRun.current = false;

const searchParams = new URLSearchParams(window.location.search);
const origFileId = searchParams.get("origFileId");
const logEventIdx = searchParams.get("logEventIdx");
if (null === origFileId || null === logEventIdx) {
const error = "Either `origFileId` or `logEventIdx` are missing from the URL " +
"parameters. Note that non-IR-extraction queries are not supported at the moment.";

console.error(error);
setErrorMsg(error);
junhaoliao marked this conversation as resolved.
Show resolved Hide resolved
return;
}

submitExtractIrJob(
origFileId,
Number(logEventIdx),
() => {
setQueryState(QUERY_LOADING_STATES.WAITING);
}
)
.then(({data}) => {
setQueryState(QUERY_LOADING_STATES.LOADING);

const innerLogEventNum = logEventIdx - data.begin_msg_ix + 1;
window.location = `/log-viewer/index.html?filePath=/ir/${data.path}` +
`#logEventIdx=${innerLogEventNum}`;
})
.catch((e) => {
let msg = "Unknown error.";
if (e instanceof AxiosError) {
msg = e.message;
if ("undefined" !== typeof e.response) {
if ("undefined" !== typeof e.response.data.message) {
msg = e.response.data.message;
} else {
msg = e.response.statusText;
}
}
}
console.error(msg, e);
setErrorMsg(msg);
});
}, []);

return (
<Loading
currentState={queryState}
errorMsg={errorMsg}/>
);
};

export default QueryStatus;
8 changes: 8 additions & 0 deletions components/log-viewer-webui/client/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ const plugins = [
];

const config = {
devServer: {
proxy: [
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Proxy added to avoid CORS issues during development.

{
context: ["/"],
target: "http://localhost:3000",
kirkrodrigues marked this conversation as resolved.
Show resolved Hide resolved
},
],
},
devtool: isProduction ?
"source-map" :
"eval-source-map",
Expand Down
Loading