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 6 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
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
4 changes: 4 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,10 @@
<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"/>
junhaoliao marked this conversation as resolved.
Show resolved Hide resolved
</head>
<body>
<div id="root"></div>
Expand Down
9 changes: 8 additions & 1 deletion components/log-viewer-webui/client/src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import {CssVarsProvider} from "@mui/joy/styles/CssVarsProvider";

import Query from "./ui/QueryState.jsx";


/**
* Renders the main application.
*
* @return {JSX.Element}
*/
const App = () => {
return (
<h1>Hello world!</h1>
<CssVarsProvider modeStorageKey={"uiTheme"}>
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

create a const for string "uiTheme"

<Query/>
</CssVarsProvider>
);
};

Expand Down
55 changes: 55 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,55 @@
import axios, {AxiosError} from "axios";


/**
* @typedef {number} QueryLoadState
*/
let enumQueryLoadState;
/**
* Enum of query loading state.
*
* @enum {QueryLoadState}
*/
const QUERY_LOAD_STATE = Object.freeze({
SUBMITTING: (enumQueryLoadState = 0),
WAITING: ++enumQueryLoadState,
Copy link
Member

Choose a reason for hiding this comment

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

Unused?

LOADING: ++enumQueryLoadState,
});

/**
* Submits a job to extract a segment of the original file, which contains a given log event index,
* into CLP's IR format for viewing in the Log Viewer.
junhaoliao marked this conversation as resolved.
Show resolved Hide resolved
*
* @param {number|string} origFileId The ID of the original file to extract IR from
junhaoliao marked this conversation as resolved.
Show resolved Hide resolved
* @param {number} logEventIx The index of the log event
Copy link
Member

Choose a reason for hiding this comment

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

Let's use logEventIdx. logEventIx is a mistake that's prevalent in clp, but we should avoid propagating it if we can.

* @param {Function} onQueryStateChange Callback to set query state.
* @param {Function} onErrorMsg Callback to set error message.
*/
const submitExtractIrJob = async (origFileId, logEventIx, onQueryStateChange, onErrorMsg) => {
Copy link
Member

Choose a reason for hiding this comment

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

This method has side-effects (setting query state, changing the URL) that (1) aren't obvious from its name and (2) don't really seem specific to the api "module". Perhaps its better to simplify this method to just submitting the POST request and do the rest in the then and catch parts of the caller's promise?

try {
const {data} = await axios.post("/query/extract-ir", {
msg_ix: logEventIx,
orig_file_id: origFileId,
Copy link
Member

Choose a reason for hiding this comment

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

I missed this in the previous PR, but why are we using snake_case for these? I think the only place we actually need to use snake_case is when inserting the row into database, right?

Copy link
Member

Choose a reason for hiding this comment

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

In a similar vein, msg_ix should really be logEventIdx until we have to insert it into the database in the server.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

why are we using snake_case for these? I think the only place we actually need to use snake_case is when inserting the row into database, right?

Right, I was using the webui code as an example but forgot the search query args are directly insert into the DB. Will update accordingly.

});

onQueryStateChange(QUERY_LOAD_STATE.LOADING);

const innerLogEventIx = logEventIx - data.begin_msg_ix + 1;
Copy link
Member

Choose a reason for hiding this comment

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

Should this be innerLogEventNum following our naming rules for the new log viewer?

window.location = `/log-viewer/index.html?filePath=/ir/${data.path}` +
`#logEventIdx=${innerLogEventIx}`;
} catch (e) {
let errorMsg = "Unknown error.";
if (e instanceof AxiosError) {
errorMsg = e.message;
if ("undefined" !== typeof e.response) {
errorMsg = e.response.data.message ?? e.response.statusText;
}
Copy link
Member

Choose a reason for hiding this comment

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

This reads as if we're going to append to errorMsg, but really we're implementing an if-else. Can we rewrite it as an if-else or did you mean to append to errorMsg?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

really we're implementing an if-else

Right, depending on whether the request was sent successfully and whether the server returns a valid response, we pick the error message (from one of the 3 places) that best represents the error.

Let's continue the discussion with these as references:

  1. If the caught error is caused by Axios, the error is of type AxiosError in all cases.
  2. message , name , and code seem always defined even when the request doesn't reach the server.
    1. message is a high-level description of what happened
    2. name is name of the extended Error class, usually AxiosError .
    3. code is specific to the error. When the system call fails it could be a errno code; when it is a request error it’s a code provided by Axios. Good for debugging?
  3. config.url is good information
  4. When reponse is available:
    1. response.status contains the HTTP status code: 404, …
    2. response.statusText contains the HTTP status text: 404
    3. response.data contains the HTTP response body

Can we rewrite it as an if-else

Sure

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

If we're obsessed enough with syntactic sugar, we can write it as

    errorMsg = e.response?.data?.message ?? e.response?.statusText ?? e.message;

(only if it doesn't give us more "?" when reading the code; pun intended lol

Copy link
Member

Choose a reason for hiding this comment

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

😛 I think in this case, fewer "?" is better, lol.

}
onErrorMsg(errorMsg);
junhaoliao marked this conversation as resolved.
Show resolved Hide resolved
}
};

export {
QUERY_LOAD_STATE,
submitExtractIrJob,
};
148 changes: 148 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,148 @@
import {
Box,
LinearProgress,
Sheet,
Step,
StepIndicator,
Stepper,
Typography,
} from "@mui/joy";


/**
* Descriptions for query states.
*/
const QUERY_STATE_DESCRIPTIONS = Object.freeze([
Copy link
Member

Choose a reason for hiding this comment

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

Can we move this into the query.js and use the enums as indices (so that they don't go out of sync)?

{
label: "Submitting query Job",
description: "Parsing arguments and submitting job to the server.",
},
{
label: "Waiting for job To finish",
junhaoliao marked this conversation as resolved.
Show resolved Hide resolved
description: "The job is running. Waiting for the job to finish.",
},
{
label: "Loading up Log Viewer",
junhaoliao marked this conversation as resolved.
Show resolved Hide resolved
description: "The query has been completed and the results are being loaded.",
},
]);


/**
* Renders a step with a label and a description text.
junhaoliao marked this conversation as resolved.
Show resolved Hide resolved
*
* @param {object} props
* @param {string} props.description
* @param {boolean} props.isActive
* @param {boolean} props.isError whether an error message should be shown instead of a step.
junhaoliao marked this conversation as resolved.
Show resolved Hide resolved
* @param {string} props.label
* @param {number} props.stepNumber
junhaoliao marked this conversation as resolved.
Show resolved Hide resolved
* @return {React.ReactElement}
*/
const LoadingStep = ({
description,
isActive,
isError,
label,
stepNumber,
}) => {
let color = "danger";
if (false === isError) {
color = isActive ?
"primary" :
"neutral";
}
junhaoliao marked this conversation as resolved.
Show resolved Hide resolved

return (
<Step
indicator={
<StepIndicator
color={color}
variant={isActive ?
"solid" :
"outlined"}
>
{stepNumber}
</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 = [];
QUERY_STATE_DESCRIPTIONS.forEach((state, index) => {
const isActive = (currentState === index);
steps.push(
<LoadingStep
description={state.description}
isActive={isActive}
isError={false}
key={index}
label={state.label}
stepNumber={index + 1}/>
);
if (isActive && null !== errorMsg) {
steps.push(
<LoadingStep
description={errorMsg}
isActive={isActive}
isError={true}
key={`${index}-error`}
label={"Error"}
stepNumber={"X"}/>
);
}
});

return (
<>
<Sheet
style={{
alignItems: "center",
display: "flex",
flexDirection: "column",
height: "100%",
justifyContent: "center",
}}
>
<Box style={{width: "100%"}}>
<LinearProgress
determinate={null !== errorMsg}
color={null === errorMsg ?
"primary" :
"danger"}/>
</Box>
<Box style={{flexGrow: 1, display: "flex", alignItems: "center"}}>
<Stepper
orientation={"vertical"}
size={"lg"}
sx={{"--Stepper-verticalGap": "2rem"}}
>
{steps}
</Stepper>
</Box>
</Sheet>
</>
);
};

export default Loading;
51 changes: 51 additions & 0 deletions components/log-viewer-webui/client/src/ui/QueryState.jsx
Copy link
Member

Choose a reason for hiding this comment

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

How about QueryStatus.jsx?

Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import {
useEffect,
useRef,
useState,
} from "react";

import {
QUERY_LOAD_STATE,
submitExtractIrJob,
} from "../api/query.js";
import Loading from "./Loading.jsx";


/**
* Submits queries and renders the query states.
*
* @return {React.ReactElement}
*/
const Query = () => {
Copy link
Member

Choose a reason for hiding this comment

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

Can we name this the same as the file?

const [queryState, setQueryState] = useState(QUERY_LOAD_STATE.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 logEventIx = searchParams.get("logEventIx");
if (null === origFileId || null === logEventIx) {
const error = "Non-IR-Extraction queries are not supported at the moment. " +
"Either origFileId or logEventIx is missing from the URL parameters.";
junhaoliao marked this conversation as resolved.
Show resolved Hide resolved

console.error(error);
setErrorMsg(error);
}

submitExtractIrJob(origFileId, Number(logEventIx), setQueryState, setErrorMsg).then();
}, []);

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

export default Query;
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
Collaborator 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
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@
.search-results-content {
font-size: 0.875rem;
line-height: var(--search-results-message-line-height);

&-clickable {
cursor: pointer;
}
}

.search-results-timestamp {
Expand Down
Loading
Loading