Skip to content

Commit

Permalink
Live debugging front (#977)
Browse files Browse the repository at this point in the history
* add live debugging page in UI

* change button size

* move icon in front of text in buttons

* add warning if the number of incoming lines is greater than the buffer size
  • Loading branch information
wildum authored Jun 5, 2024
1 parent ab08e82 commit 5736565
Show file tree
Hide file tree
Showing 12 changed files with 2,502 additions and 27 deletions.
4 changes: 4 additions & 0 deletions internal/web/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@
"name": "alloy",
"private": true,
"dependencies": {
"@brianmcallister/react-auto-scroll": "^1.1.0",
"@fortawesome/fontawesome-svg-core": "^6.4.0",
"@fortawesome/free-solid-svg-icons": "^6.4.0",
"@fortawesome/react-fontawesome": "^0.2.0",
"@grafana/data": "^11.0.0",
"@grafana/ui": "^11.0.0",
"d3": "^7.8.4",
"d3-dag": "^0.11.5",
"d3-zoom": "^3.0.0",
"normalize.css": "^8.0.1",
"rc-slider": "^10.6.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.11.1",
Expand Down
2 changes: 2 additions & 0 deletions internal/web/ui/src/Router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Navbar from './features/layout/Navbar';
import PageClusteringPeers from './pages/Clustering';
import ComponentDetailPage from './pages/ComponentDetailPage';
import Graph from './pages/Graph';
import PageLiveDebugging from './pages/LiveDebugging';
import PageComponentList from './pages/PageComponentList';

interface Props {
Expand All @@ -21,6 +22,7 @@ const Router = ({ basePath }: Props) => {
<Route path="/component/*" element={<ComponentDetailPage />} />
<Route path="/graph" element={<Graph />} />
<Route path="/clustering" element={<PageClusteringPeers />} />
<Route path="/debug/*" element={<PageLiveDebugging />} />
</Routes>
</main>
</BrowserRouter>
Expand Down
21 changes: 20 additions & 1 deletion internal/web/ui/src/features/component/ComponentView.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,20 @@ h6 {
}

.content .docsLink {
display: block;
display: inline-block;
width: fit-content;
height: fit-content;
font-size: 10px;
padding: 5px;
color: #ffffff;
background-color: rgb(56, 133, 220);
border: 1px solid rgb(56, 133, 220);
border-radius: 3px;
margin-right: 5px;
}

.content .debugLink {
display: inline-block;
width: fit-content;
height: fit-content;
font-size: 10px;
Expand All @@ -101,13 +114,19 @@ h6 {
background-color: rgb(56, 133, 220);
border: 1px solid rgb(56, 133, 220);
border-radius: 3px;
margin-right: 10px;
}

.docsLink a {
color: #ffffff;
text-decoration: none;
}

.debugLink a {
color: #ffffff;
text-decoration: none;
}

.content blockquote {
border: 1px solid #e4e5e6;
border-radius: 3px;
Expand Down
10 changes: 8 additions & 2 deletions internal/web/ui/src/features/component/ComponentView.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { FC, Fragment, ReactElement } from 'react';
import { Link } from 'react-router-dom';
import { faCubes, faLink } from '@fortawesome/free-solid-svg-icons';
import { faBug, faCubes, faLink } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';

import { partitionBody } from '../../utils/partition';
Expand Down Expand Up @@ -96,7 +96,13 @@ export const ComponentView: FC<ComponentViewProps> = (props) => {

<div className={styles.docsLink}>
<a href={`https://grafana.com/docs/alloy/latest/reference/components/${props.component.name}`}>
Documentation <FontAwesomeIcon icon={faLink} />
<FontAwesomeIcon icon={faLink} /> Documentation
</a>
</div>

<div className={styles.debugLink}>
<a href={`debug/${pathJoin([props.component.moduleID, props.component.localID])}`}>
<FontAwesomeIcon icon={faBug} /> Live debugging
</a>
</div>

Expand Down
6 changes: 5 additions & 1 deletion internal/web/ui/src/features/layout/Page.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -57,4 +57,8 @@ div.page main {
color: rgba(36, 41, 46, 0.75);
}


.controls {
display: flex;
align-items: center;
margin-left: auto;
}
2 changes: 2 additions & 0 deletions internal/web/ui/src/features/layout/Page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export interface PageProps {
name: string;
desc: string;
icon: IconProp;
controls?: ReactNode;
children?: ReactNode;
}

Expand All @@ -22,6 +23,7 @@ const Page: FC<PageProps> = (props) => {
<h1>{props.name}</h1>
<h2>{props.desc}</h2>
</div>
<div className={styles.controls}>{props.controls}</div>
</header>
<main>{props.children}</main>
</div>
Expand Down
83 changes: 83 additions & 0 deletions internal/web/ui/src/hooks/liveDebugging.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { useEffect, useState } from 'react';

export const useLiveDebugging = (
componentID: string,
enabled: boolean,
sampleProb: number,
setData: React.Dispatch<React.SetStateAction<string[]>>
) => {
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const maxLines = 5000; // TODO: should we make this configurable?

useEffect(() => {
const abortController = new AbortController();

const fetchData = async () => {
if (!enabled) {
setLoading(false);
return;
}

setLoading(true);

try {
const response = await fetch(`./api/v0/web/debug/${componentID}?sampleProb=${sampleProb}`, {
signal: abortController.signal,
cache: 'no-cache',
credentials: 'same-origin',
});
if (!response.ok || !response.body) {
const text = await response.text();
const errorMessage = `Failed to connect, status code: ${response.status}, reason: ${text}`;
throw new Error(errorMessage);
}

const reader = response.body.getReader();
const decoder = new TextDecoder();

while (enabled) {
const { value, done } = await reader.read();
if (done) {
break;
}

const decodedChunk = decoder.decode(value, { stream: true });

setData((prevValue) => {
const newValue = decodedChunk.split('|;|');
newValue.pop(); // last element is empty because of the split, we discard it

if (newValue.length > maxLines) {
console.warn(
'Received %s lines but the buffer has a maximum of %s. Some lines will be dropped.',
newValue.length,
maxLines
);
}

let dataArr = prevValue.concat(newValue);
if (dataArr.length > maxLines) {
dataArr = dataArr.slice(-maxLines); // truncate the array to keep the last {maxLines} lines
}
return dataArr;
});
}
} catch (error) {
if ((error as Error).name !== 'AbortError') {
setError((error as Error).message);
}
} finally {
setLoading(false);
}
};

fetchData();

return () => {
abortController.abort();
};
}, [componentID, enabled, sampleProb, setData]);

return { loading, error };
};
12 changes: 9 additions & 3 deletions internal/web/ui/src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import React from 'react';
import ReactDOM from 'react-dom/client';

import { createTheme } from '@grafana/data';
import { ThemeContext } from '@grafana/ui';

import App from './App';

import 'normalize.css';
import './index.css';
import 'rc-slider/assets/index.css';

const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
<ThemeContext.Provider value={createTheme({ colors: { mode: 'light' } })}>
<React.StrictMode>
<App />
</React.StrictMode>
</ThemeContext.Provider>
);
4 changes: 2 additions & 2 deletions internal/web/ui/src/pages/ComponentDetailPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ const ComponentDetailPage: FC = () => {
cache: 'no-cache',
credentials: 'same-origin',
});
const moduleCompoents = (await moduleComponentsResp.json()) as ComponentInfo[];
const moduleComponents = (await moduleComponentsResp.json()) as ComponentInfo[];

data.moduleInfo = (data.moduleInfo || []).concat(moduleCompoents);
data.moduleInfo = (data.moduleInfo || []).concat(moduleComponents);
}

setComponent(data);
Expand Down
106 changes: 106 additions & 0 deletions internal/web/ui/src/pages/LiveDebugging.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
.filter {
margin-right: 20px;
margin-top: 14px;
width: 300px;
}

.slider {
width: 300px;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
margin-right: 10px;
}

.sliderLabel {
display: inline-block;
margin-left: 10px;
white-space: nowrap;
}

.debugLink {
display: inline-block;
margin-left: 10px;
margin-bottom: 5px;
}

.debugLink button {
font-size: .8em;
line-height: 30px;
width: 100px;
margin-left: auto;
margin-top: 6px;
padding: 0 15px;
text-decoration: none;
background: none;
background-color: #3885dc;
border: 1px solid #3885dc;
border-radius: 3px;
color: #fff;
cursor: pointer;
transition: all 0.3s ease;
}

.debugLink button:focus {
outline: none;
box-shadow: 0 0 0 0.2rem rgba(220, 220, 220, 0.5);
}

.debugLink .stopButton {
background-color: #E0226E;
color: #ffffff;
border-color: #E0226E;
}

.debugLink .stopButton:hover {
background-color: rgb(179, 27, 88);
border-color: rgb(179, 27, 88);
}

.debugLink .clearButton {
background-color: #FF9900;
color: #ffffff;
border-color: #FF9900;
}

.debugLink .clearButton:hover {
background-color:rgb(204, 122, 0);
border-color: rgb(204, 122, 0);
}

.debugLink .resumeButton {
background-color: #1B855E;
color: #ffffff;
border-color: #1B855E;
}

.debugLink .resumeButton:hover {
background-color: rgb(21, 106, 75);
border-color: rgb(21, 106, 75);
}

.debugLink .copyButton {
background-color: #3871DC;
color: #ffffff;
border-color: #3871DC;
}

.debugLink .copyButton:hover {
background-color: rgb(44, 90, 176);
border-color: rgb(44, 90, 176);
}


.logLine {
white-space: pre-wrap;
color: #24292e;
}

.autoScroll .logLine:nth-child(odd) {
background-color: white;
}

.autoScroll .logLine:nth-child(even) {
background-color: rgb(250, 250, 250);
}
Loading

0 comments on commit 5736565

Please sign in to comment.