Skip to content

Commit 3ac4f49

Browse files
Integrate jupyterlab-system-monitor extension (#202)
* Use a unified model for memory and cpu metrics * Add components from jupyterlab system monitor Minor modifications to make linter happy Add system monitor plugin into @jupyter-server/resource-usage:topbar-item. Disable topbar panels by default and make it configurable via settings file. * Add necessary npm packages for topbar panels Pin @types/react to avoid duplicates * Remove executable perms * More perms cleanup
1 parent a101faa commit 3ac4f49

File tree

12 files changed

+856
-298
lines changed

12 files changed

+856
-298
lines changed

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@
4141
"prettier": "^2.4.1",
4242
"rimraf": "^3.0.2"
4343
},
44+
"resolutions": {
45+
"@types/react": "18.2.16"
46+
},
4447
"eslintIgnore": [
4548
"**/*.d.ts",
4649
"dist",

packages/labextension/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,12 @@
5151
"@jupyterlab/statusbar": "^4.0.0",
5252
"@jupyterlab/translation": "^4.0.0",
5353
"@lumino/polling": "^2.1.1",
54+
"react-sparklines": "^1.7.0",
5455
"typestyle": "^2.4.0"
5556
},
5657
"devDependencies": {
5758
"@jupyterlab/builder": "^4.0.0",
59+
"@types/react-sparklines": "^1.7.0",
5860
"@typescript-eslint/eslint-plugin": "^4.8.1",
5961
"@typescript-eslint/parser": "^4.8.1",
6062
"eslint": "^7.14.0",
@@ -86,6 +88,7 @@
8688
}
8789
},
8890
"extension": true,
91+
"schemaDir": "schema",
8992
"outputDir": "../../jupyter_resource_usage/labextension"
9093
}
9194
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
{
2+
"title": "Resource Usage Menu Bar",
3+
"description": "Resource Usage Menu Bar settings.",
4+
"jupyter.lab.toolbars": {
5+
"TopBar": [
6+
{
7+
"name": "cpu",
8+
"rank": 120
9+
},
10+
{
11+
"name": "memory",
12+
"rank": 130
13+
}
14+
]
15+
},
16+
"properties": {
17+
"enable": {
18+
"title": "Enable resource usage panels",
19+
"description": "Whether to enable resource usage panels on topbar",
20+
"default": false,
21+
"type": "boolean"
22+
},
23+
"refreshRate": {
24+
"title": "Refresh Rate (ms)",
25+
"description": "Refresh Rate to sync metrics data",
26+
"default": 5000,
27+
"type": "number"
28+
},
29+
"memory": {
30+
"title": "Memory Settings",
31+
"description": "Settings for the memory indicator",
32+
"default": {
33+
"label": "Mem: "
34+
},
35+
"type": "object"
36+
},
37+
"cpu": {
38+
"title": "CPU Settings",
39+
"description": "Settings for the CPU indicator",
40+
"default": {
41+
"label": "CPU: "
42+
},
43+
"type": "object"
44+
}
45+
},
46+
"additionalProperties": false,
47+
"type": "object"
48+
}

packages/labextension/src/cpuView.tsx

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { ReactWidget } from '@jupyterlab/apputils';
2+
3+
import React, { useEffect, useState, ReactElement } from 'react';
4+
5+
import { IndicatorComponent } from './indicator';
6+
7+
import { ResourceUsage } from './model';
8+
9+
/**
10+
* A CpuView component to display CPU usage.
11+
*/
12+
const CpuViewComponent = ({
13+
model,
14+
label,
15+
}: {
16+
model: ResourceUsage.Model;
17+
label: string;
18+
}): ReactElement => {
19+
const [text, setText] = useState('');
20+
const [values, setValues] = useState<number[]>([]);
21+
22+
const update = (): void => {
23+
const { cpuLimit, currentCpuPercent } = model;
24+
const newValues = model.values.map((value) =>
25+
Math.min(1, value.cpuPercent / (cpuLimit || 1))
26+
);
27+
const newText = `${(currentCpuPercent * 100).toFixed(0)}%`;
28+
setText(newText);
29+
setValues(newValues);
30+
};
31+
32+
useEffect(() => {
33+
model.stateChanged.connect(update);
34+
return (): void => {
35+
model.stateChanged.disconnect(update);
36+
};
37+
}, [model]);
38+
39+
return (
40+
<IndicatorComponent
41+
enabled={model.cpuAvailable}
42+
values={values}
43+
label={label}
44+
color={'#0072B3'}
45+
text={text}
46+
/>
47+
);
48+
};
49+
50+
/**
51+
* A namespace for CpuView statics.
52+
*/
53+
export namespace CpuView {
54+
/**
55+
* Create a new CpuView React Widget.
56+
*
57+
* @param model The resource usage model.
58+
* @param label The label next to the component.
59+
*/
60+
export const createCpuView = (
61+
model: ResourceUsage.Model,
62+
label: string
63+
): ReactWidget => {
64+
return ReactWidget.create(<CpuViewComponent model={model} label={label} />);
65+
};
66+
}

packages/labextension/src/index.ts

Lines changed: 103 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,29 +3,75 @@ import {
33
JupyterFrontEnd,
44
JupyterFrontEndPlugin,
55
} from '@jupyterlab/application';
6+
7+
import { IToolbarWidgetRegistry } from '@jupyterlab/apputils';
8+
9+
import { ISettingRegistry } from '@jupyterlab/settingregistry';
10+
611
import { INotebookTracker } from '@jupyterlab/notebook';
12+
713
import { LabIcon } from '@jupyterlab/ui-components';
14+
815
import { ICommandPalette } from '@jupyterlab/apputils';
16+
917
import { IConsoleTracker } from '@jupyterlab/console';
10-
import { KernelUsagePanel } from './panel';
11-
import tachometer from '../style/tachometer.svg';
1218

1319
import { IStatusBar } from '@jupyterlab/statusbar';
1420

1521
import { ITranslator } from '@jupyterlab/translation';
1622

17-
import { MemoryUsage } from './memoryUsage';
23+
import { JSONObject } from '@lumino/coreutils';
24+
25+
import { KernelUsagePanel } from './panel';
26+
27+
import tachometer from '../style/tachometer.svg';
28+
29+
import { ResourceUsage } from './model';
30+
31+
import { ResourceUsageStatus } from './resourceUsage';
32+
1833
import { KernelWidgetTracker } from './tracker';
1934

35+
import { CpuView } from './cpuView';
36+
37+
import { MemoryView } from './memoryView';
38+
39+
/**
40+
* Disable system monitor panels by default.
41+
*/
42+
const DEFAULT_ENABLE_SYSTEM_MONITOR = false;
43+
44+
/**
45+
* The default refresh rate.
46+
*/
47+
const DEFAULT_REFRESH_RATE = 5000;
48+
49+
/**
50+
* The default memory label.
51+
*/
52+
const DEFAULT_MEMORY_LABEL = 'Mem: ';
53+
54+
/**
55+
* The default CPU label.
56+
*/
57+
const DEFAULT_CPU_LABEL = 'CPU: ';
58+
59+
/**
60+
* An interface for resource settings.
61+
*/
62+
interface IResourceSettings extends JSONObject {
63+
label: string;
64+
}
65+
2066
namespace CommandIDs {
2167
export const getKernelUsage = 'kernel-usage:get';
2268
}
2369

2470
/**
2571
* Initialization data for the jupyter-resource-usage extension.
2672
*/
27-
const memoryStatusPlugin: JupyterFrontEndPlugin<void> = {
28-
id: '@jupyter-server/resource-usage:memory-status-item',
73+
const resourceStatusPlugin: JupyterFrontEndPlugin<void> = {
74+
id: '@jupyter-server/resource-usage:status-item',
2975
autoStart: true,
3076
requires: [IStatusBar, ITranslator],
3177
activate: (
@@ -34,9 +80,9 @@ const memoryStatusPlugin: JupyterFrontEndPlugin<void> = {
3480
translator: ITranslator
3581
) => {
3682
const trans = translator.load('jupyter-resource-usage');
37-
const item = new MemoryUsage(trans);
83+
const item = new ResourceUsageStatus(trans);
3884

39-
statusBar.registerStatusItem(memoryStatusPlugin.id, {
85+
statusBar.registerStatusItem(resourceStatusPlugin.id, {
4086
item,
4187
align: 'left',
4288
rank: 2,
@@ -46,6 +92,54 @@ const memoryStatusPlugin: JupyterFrontEndPlugin<void> = {
4692
},
4793
};
4894

95+
/**
96+
* Initialization data for the jupyterlab-system-monitor extension.
97+
*/
98+
const systemMonitorPlugin: JupyterFrontEndPlugin<void> = {
99+
id: '@jupyter-server/resource-usage:topbar-item',
100+
autoStart: true,
101+
requires: [IToolbarWidgetRegistry],
102+
optional: [ISettingRegistry],
103+
activate: async (
104+
app: JupyterFrontEnd,
105+
toolbarRegistry: IToolbarWidgetRegistry,
106+
settingRegistry: ISettingRegistry
107+
) => {
108+
let enablePlugin = DEFAULT_ENABLE_SYSTEM_MONITOR;
109+
let refreshRate = DEFAULT_REFRESH_RATE;
110+
let cpuLabel = DEFAULT_CPU_LABEL;
111+
let memoryLabel = DEFAULT_MEMORY_LABEL;
112+
113+
if (settingRegistry) {
114+
const settings = await settingRegistry.load(systemMonitorPlugin.id);
115+
enablePlugin = settings.get('enable').composite as boolean;
116+
refreshRate = settings.get('refreshRate').composite as number;
117+
const cpuSettings = settings.get('cpu').composite as IResourceSettings;
118+
cpuLabel = cpuSettings.label;
119+
const memorySettings = settings.get('memory')
120+
.composite as IResourceSettings;
121+
memoryLabel = memorySettings.label;
122+
}
123+
124+
const model = new ResourceUsage.Model({ refreshRate });
125+
await model.refresh();
126+
127+
if (enablePlugin && model.cpuAvailable) {
128+
toolbarRegistry.addFactory('TopBar', 'cpu', () => {
129+
const cpu = CpuView.createCpuView(model, cpuLabel);
130+
return cpu;
131+
});
132+
}
133+
134+
if (enablePlugin && model.memoryAvailable) {
135+
toolbarRegistry.addFactory('TopBar', 'memory', () => {
136+
const memory = MemoryView.createMemoryView(model, memoryLabel);
137+
return memory;
138+
});
139+
}
140+
},
141+
};
142+
49143
const kernelUsagePlugin: JupyterFrontEndPlugin<void> = {
50144
id: '@jupyter-server/resource-usage:kernel-panel-item',
51145
autoStart: true,
@@ -101,7 +195,8 @@ const kernelUsagePlugin: JupyterFrontEndPlugin<void> = {
101195
};
102196

103197
const plugins: JupyterFrontEndPlugin<any>[] = [
104-
memoryStatusPlugin,
198+
resourceStatusPlugin,
199+
systemMonitorPlugin,
105200
kernelUsagePlugin,
106201
];
107202
export default plugins;

0 commit comments

Comments
 (0)