cmd, dashboard: use webpack dev server, remove custom assets (#16263)
* cmd, dashboard: remove custom assets, webpack dev server * dashboard: yarn commands, small fixes
This commit is contained in:
parent
3ec1b9a92d
commit
704840a8ad
3
.gitignore
vendored
3
.gitignore
vendored
@ -42,3 +42,6 @@ profile.cov
|
|||||||
/dashboard/assets/node_modules
|
/dashboard/assets/node_modules
|
||||||
/dashboard/assets/stats.json
|
/dashboard/assets/stats.json
|
||||||
/dashboard/assets/bundle.js
|
/dashboard/assets/bundle.js
|
||||||
|
/dashboard/assets/package-lock.json
|
||||||
|
|
||||||
|
**/yarn-error.log
|
||||||
|
@ -65,7 +65,6 @@ var (
|
|||||||
utils.DashboardAddrFlag,
|
utils.DashboardAddrFlag,
|
||||||
utils.DashboardPortFlag,
|
utils.DashboardPortFlag,
|
||||||
utils.DashboardRefreshFlag,
|
utils.DashboardRefreshFlag,
|
||||||
utils.DashboardAssetsFlag,
|
|
||||||
utils.EthashCacheDirFlag,
|
utils.EthashCacheDirFlag,
|
||||||
utils.EthashCachesInMemoryFlag,
|
utils.EthashCachesInMemoryFlag,
|
||||||
utils.EthashCachesOnDiskFlag,
|
utils.EthashCachesOnDiskFlag,
|
||||||
|
@ -209,11 +209,6 @@ var (
|
|||||||
Usage: "Dashboard metrics collection refresh rate",
|
Usage: "Dashboard metrics collection refresh rate",
|
||||||
Value: dashboard.DefaultConfig.Refresh,
|
Value: dashboard.DefaultConfig.Refresh,
|
||||||
}
|
}
|
||||||
DashboardAssetsFlag = cli.StringFlag{
|
|
||||||
Name: "dashboard.assets",
|
|
||||||
Usage: "Developer flag to serve the dashboard from the local file system",
|
|
||||||
Value: dashboard.DefaultConfig.Assets,
|
|
||||||
}
|
|
||||||
// Ethash settings
|
// Ethash settings
|
||||||
EthashCacheDirFlag = DirectoryFlag{
|
EthashCacheDirFlag = DirectoryFlag{
|
||||||
Name: "ethash.cachedir",
|
Name: "ethash.cachedir",
|
||||||
@ -1120,7 +1115,6 @@ func SetDashboardConfig(ctx *cli.Context, cfg *dashboard.Config) {
|
|||||||
cfg.Host = ctx.GlobalString(DashboardAddrFlag.Name)
|
cfg.Host = ctx.GlobalString(DashboardAddrFlag.Name)
|
||||||
cfg.Port = ctx.GlobalInt(DashboardPortFlag.Name)
|
cfg.Port = ctx.GlobalInt(DashboardPortFlag.Name)
|
||||||
cfg.Refresh = ctx.GlobalDuration(DashboardRefreshFlag.Name)
|
cfg.Refresh = ctx.GlobalDuration(DashboardRefreshFlag.Name)
|
||||||
cfg.Assets = ctx.GlobalString(DashboardAssetsFlag.Name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterEthService adds an Ethereum client to the stack.
|
// RegisterEthService adds an Ethereum client to the stack.
|
||||||
|
@ -12,28 +12,27 @@ The client's UI uses [React][React] with JSX syntax, which is validated by the [
|
|||||||
As the dashboard depends on certain NPM packages (which are not included in the `go-ethereum` repo), these need to be installed first:
|
As the dashboard depends on certain NPM packages (which are not included in the `go-ethereum` repo), these need to be installed first:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ (cd dashboard/assets && npm install)
|
$ (cd dashboard/assets && yarn install && yarn flow)
|
||||||
$ (cd dashboard/assets && ./node_modules/.bin/flow-typed install)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Normally the dashboard assets are bundled into Geth via `go-bindata` to avoid external dependencies. Rebuilding Geth after each UI modification however is not feasible from a developer perspective. Instead, we can run `webpack` in watch mode to automatically rebundle the UI, and ask `geth` to use external assets to not rely on compiled resources:
|
Normally the dashboard assets are bundled into Geth via `go-bindata` to avoid external dependencies. Rebuilding Geth after each UI modification however is not feasible from a developer perspective. Instead, we can run `yarn dev` to watch for file system changes and refresh the browser automatically.
|
||||||
|
|
||||||
```
|
```
|
||||||
$ (cd dashboard/assets && ./node_modules/.bin/webpack --watch)
|
$ geth --dashboard --vmodule=dashboard=5
|
||||||
$ geth --dashboard --dashboard.assets=dashboard/assets --vmodule=dashboard=5
|
$ (cd dashboard/assets && yarn dev)
|
||||||
```
|
```
|
||||||
|
|
||||||
To bundle up the final UI into Geth, run `go generate`:
|
To bundle up the final UI into Geth, run `go generate`:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ go generate ./dashboard
|
$ (cd dashboard && go generate)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Static type checking
|
### Static type checking
|
||||||
|
|
||||||
Since JavaScript doesn't provide type safety, [Flow][Flow] is used to check types. These are only useful during development, so at the end of the process Babel will strip them.
|
Since JavaScript doesn't provide type safety, [Flow][Flow] is used to check types. These are only useful during development, so at the end of the process Babel will strip them.
|
||||||
|
|
||||||
To take advantage of static type checking, your IDE needs to be prepared for it. In case of [Atom][Atom] a configuration guide can be found [here][Atom config]: Install the [Nuclide][Nuclide] package for Flow support, making sure it installs all of its support packages by enabling `Install Recommended Packages on Startup`, and set the path of the `flow-bin` which were installed previously by `npm`.
|
To take advantage of static type checking, your IDE needs to be prepared for it. In case of [Atom][Atom] a configuration guide can be found [here][Atom config]: Install the [Nuclide][Nuclide] package for Flow support, making sure it installs all of its support packages by enabling `Install Recommended Packages on Startup`, and set the path of the `flow-bin` which were installed previously by `yarn`.
|
||||||
|
|
||||||
For more IDE support install the `linter-eslint` package too, which finds the `.eslintrc` file, and provides real-time linting. Atom warns, that these two packages are incompatible, but they seem to work well together. For third-party library errors and auto-completion [flow-typed][flow-typed] is used.
|
For more IDE support install the `linter-eslint` package too, which finds the `.eslintrc` file, and provides real-time linting. Atom warns, that these two packages are incompatible, but they seem to work well together. For third-party library errors and auto-completion [flow-typed][flow-typed] is used.
|
||||||
|
|
||||||
@ -41,7 +40,7 @@ For more IDE support install the `linter-eslint` package too, which finds the `.
|
|||||||
|
|
||||||
[Webpack][Webpack] offers handy tools for visualizing the bundle's dependency tree and space usage.
|
[Webpack][Webpack] offers handy tools for visualizing the bundle's dependency tree and space usage.
|
||||||
|
|
||||||
* Generate the bundle's profile running `webpack --profile --json > stats.json`
|
* Generate the bundle's profile running `yarn stats`
|
||||||
* For the _dependency tree_ go to [Webpack Analyze][WA], and import `stats.json`
|
* For the _dependency tree_ go to [Webpack Analyze][WA], and import `stats.json`
|
||||||
* For the _space usage_ go to [Webpack Visualizer][WV], and import `stats.json`
|
* For the _space usage_ go to [Webpack Visualizer][WV], and import `stats.json`
|
||||||
|
|
||||||
|
3737
dashboard/assets.go
3737
dashboard/assets.go
File diff suppressed because one or more lines are too long
@ -38,15 +38,15 @@ export const percentPlotter = <T>(text: string, mapper: (T => T) = multiplier(1)
|
|||||||
};
|
};
|
||||||
|
|
||||||
// unit contains the units for the bytePlotter.
|
// unit contains the units for the bytePlotter.
|
||||||
const unit = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
|
const unit = ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi'];
|
||||||
|
|
||||||
// simplifyBytes returns the simplified version of the given value followed by the unit.
|
// simplifyBytes returns the simplified version of the given value followed by the unit.
|
||||||
const simplifyBytes = (x: number) => {
|
const simplifyBytes = (x: number) => {
|
||||||
let i = 0;
|
let i = 0;
|
||||||
for (; x > 1024 && i < 5; i++) {
|
for (; x > 1024 && i < 8; i++) {
|
||||||
x /= 1024;
|
x /= 1024;
|
||||||
}
|
}
|
||||||
return x.toFixed(2).toString().concat(' ', unit[i]);
|
return x.toFixed(2).toString().concat(' ', unit[i], 'B');
|
||||||
};
|
};
|
||||||
|
|
||||||
// bytePlotter renders a tooltip, which displays the payload as a byte value.
|
// bytePlotter renders a tooltip, which displays the payload as a byte value.
|
||||||
|
@ -81,7 +81,11 @@ const defaultContent: Content = {
|
|||||||
version: null,
|
version: null,
|
||||||
commit: null,
|
commit: null,
|
||||||
},
|
},
|
||||||
home: {
|
home: {},
|
||||||
|
chain: {},
|
||||||
|
txpool: {},
|
||||||
|
network: {},
|
||||||
|
system: {
|
||||||
activeMemory: [],
|
activeMemory: [],
|
||||||
virtualMemory: [],
|
virtualMemory: [],
|
||||||
networkIngress: [],
|
networkIngress: [],
|
||||||
@ -91,10 +95,6 @@ const defaultContent: Content = {
|
|||||||
diskRead: [],
|
diskRead: [],
|
||||||
diskWrite: [],
|
diskWrite: [],
|
||||||
},
|
},
|
||||||
chain: {},
|
|
||||||
txpool: {},
|
|
||||||
network: {},
|
|
||||||
system: {},
|
|
||||||
logs: {
|
logs: {
|
||||||
log: [],
|
log: [],
|
||||||
},
|
},
|
||||||
@ -108,7 +108,11 @@ const updaters = {
|
|||||||
version: replacer,
|
version: replacer,
|
||||||
commit: replacer,
|
commit: replacer,
|
||||||
},
|
},
|
||||||
home: {
|
home: null,
|
||||||
|
chain: null,
|
||||||
|
txpool: null,
|
||||||
|
network: null,
|
||||||
|
system: {
|
||||||
activeMemory: appender(200),
|
activeMemory: appender(200),
|
||||||
virtualMemory: appender(200),
|
virtualMemory: appender(200),
|
||||||
networkIngress: appender(200),
|
networkIngress: appender(200),
|
||||||
@ -118,10 +122,6 @@ const updaters = {
|
|||||||
diskRead: appender(200),
|
diskRead: appender(200),
|
||||||
diskWrite: appender(200),
|
diskWrite: appender(200),
|
||||||
},
|
},
|
||||||
chain: null,
|
|
||||||
txpool: null,
|
|
||||||
network: null,
|
|
||||||
system: null,
|
|
||||||
logs: {
|
logs: {
|
||||||
log: appender(200),
|
log: appender(200),
|
||||||
},
|
},
|
||||||
@ -136,7 +136,7 @@ const styles = {
|
|||||||
height: '100%',
|
height: '100%',
|
||||||
zIndex: 1,
|
zIndex: 1,
|
||||||
overflow: 'hidden',
|
overflow: 'hidden',
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// themeStyles returns the styles generated from the theme for the component.
|
// themeStyles returns the styles generated from the theme for the component.
|
||||||
@ -178,7 +178,8 @@ class Dashboard extends Component<Props, State> {
|
|||||||
// reconnect establishes a websocket connection with the server, listens for incoming messages
|
// reconnect establishes a websocket connection with the server, listens for incoming messages
|
||||||
// and tries to reconnect on connection loss.
|
// and tries to reconnect on connection loss.
|
||||||
reconnect = () => {
|
reconnect = () => {
|
||||||
const server = new WebSocket(`${((window.location.protocol === 'https:') ? 'wss://' : 'ws://') + window.location.host}/api`);
|
// PROD is defined by webpack.
|
||||||
|
const server = new WebSocket(`${((window.location.protocol === 'https:') ? 'wss://' : 'ws://')}${PROD ? window.location.host : 'localhost:8080'}/api`);
|
||||||
server.onopen = () => {
|
server.onopen = () => {
|
||||||
this.setState({content: defaultContent, shouldUpdate: {}});
|
this.setState({content: defaultContent, shouldUpdate: {}});
|
||||||
};
|
};
|
||||||
@ -217,7 +218,6 @@ class Dashboard extends Component<Props, State> {
|
|||||||
return (
|
return (
|
||||||
<div className={this.props.classes.dashboard} style={styles.dashboard}>
|
<div className={this.props.classes.dashboard} style={styles.dashboard}>
|
||||||
<Header
|
<Header
|
||||||
opened={this.state.sideBar}
|
|
||||||
switchSideBar={this.switchSideBar}
|
switchSideBar={this.switchSideBar}
|
||||||
/>
|
/>
|
||||||
<Body
|
<Body
|
||||||
|
@ -26,7 +26,17 @@ import {ResponsiveContainer, AreaChart, Area, Tooltip} from 'recharts';
|
|||||||
import ChartRow from './ChartRow';
|
import ChartRow from './ChartRow';
|
||||||
import CustomTooltip, {bytePlotter, bytePerSecPlotter, percentPlotter, multiplier} from './CustomTooltip';
|
import CustomTooltip, {bytePlotter, bytePerSecPlotter, percentPlotter, multiplier} from './CustomTooltip';
|
||||||
import {styles as commonStyles} from '../common';
|
import {styles as commonStyles} from '../common';
|
||||||
import type {Content} from '../types/content';
|
import type {General, System} from '../types/content';
|
||||||
|
|
||||||
|
const FOOTER_SYNC_ID = 'footerSyncId';
|
||||||
|
|
||||||
|
const CPU = 'cpu';
|
||||||
|
const MEMORY = 'memory';
|
||||||
|
const DISK = 'disk';
|
||||||
|
const TRAFFIC = 'traffic';
|
||||||
|
|
||||||
|
const TOP = 'Top';
|
||||||
|
const BOTTOM = 'Bottom';
|
||||||
|
|
||||||
// styles contains the constant styles of the component.
|
// styles contains the constant styles of the component.
|
||||||
const styles = {
|
const styles = {
|
||||||
@ -42,15 +52,14 @@ const styles = {
|
|||||||
doubleChartWrapper: {
|
doubleChartWrapper: {
|
||||||
height: '100%',
|
height: '100%',
|
||||||
width: '99%',
|
width: '99%',
|
||||||
paddingTop: 5,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// themeStyles returns the styles generated from the theme for the component.
|
// themeStyles returns the styles generated from the theme for the component.
|
||||||
const themeStyles: Object = (theme: Object) => ({
|
const themeStyles: Object = (theme: Object) => ({
|
||||||
footer: {
|
footer: {
|
||||||
backgroundColor: theme.palette.background.appBar,
|
backgroundColor: theme.palette.grey[900],
|
||||||
color: theme.palette.getContrastText(theme.palette.background.appBar),
|
color: theme.palette.getContrastText(theme.palette.grey[900]),
|
||||||
zIndex: theme.zIndex.appBar,
|
zIndex: theme.zIndex.appBar,
|
||||||
height: theme.spacing.unit * 10,
|
height: theme.spacing.unit * 10,
|
||||||
},
|
},
|
||||||
@ -59,111 +68,108 @@ const themeStyles: Object = (theme: Object) => ({
|
|||||||
export type Props = {
|
export type Props = {
|
||||||
classes: Object, // injected by withStyles()
|
classes: Object, // injected by withStyles()
|
||||||
theme: Object,
|
theme: Object,
|
||||||
content: Content,
|
general: General,
|
||||||
|
system: System,
|
||||||
shouldUpdate: Object,
|
shouldUpdate: Object,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Footer renders the footer of the dashboard.
|
// Footer renders the footer of the dashboard.
|
||||||
class Footer extends Component<Props> {
|
class Footer extends Component<Props> {
|
||||||
shouldComponentUpdate(nextProps) {
|
shouldComponentUpdate(nextProps) {
|
||||||
return typeof nextProps.shouldUpdate.home !== 'undefined';
|
return typeof nextProps.shouldUpdate.general !== 'undefined' || typeof nextProps.shouldUpdate.system !== 'undefined';
|
||||||
}
|
}
|
||||||
|
|
||||||
// info renders a label with the given values.
|
// halfHeightChart renders an area chart with half of the height of its parent.
|
||||||
info = (about: string, value: ?string) => (value ? (
|
halfHeightChart = (chartProps, tooltip, areaProps) => (
|
||||||
<Typography type='caption' color='inherit'>
|
<ResponsiveContainer width='100%' height='50%'>
|
||||||
<span style={commonStyles.light}>{about}</span> {value}
|
<AreaChart {...chartProps} >
|
||||||
</Typography>
|
{!tooltip || (<Tooltip cursor={false} content={<CustomTooltip tooltip={tooltip} />} />)}
|
||||||
) : null);
|
<Area isAnimationActive={false} type='monotone' {...areaProps} />
|
||||||
|
</AreaChart>
|
||||||
|
</ResponsiveContainer>
|
||||||
|
);
|
||||||
|
|
||||||
// doubleChart renders a pair of charts separated by the baseline.
|
// doubleChart renders a pair of charts separated by the baseline.
|
||||||
doubleChart = (syncId, topChart, bottomChart) => {
|
doubleChart = (syncId, chartKey, topChart, bottomChart) => {
|
||||||
const topKey = 'topKey';
|
if (!Array.isArray(topChart.data) || !Array.isArray(bottomChart.data)) {
|
||||||
const bottomKey = 'bottomKey';
|
return null;
|
||||||
const topDefault = topChart.default ? topChart.default : 0;
|
}
|
||||||
const bottomDefault = bottomChart.default ? bottomChart.default : 0;
|
const topDefault = topChart.default || 0;
|
||||||
const topTooltip = topChart.tooltip ? (
|
const bottomDefault = bottomChart.default || 0;
|
||||||
<Tooltip cursor={false} content={<CustomTooltip tooltip={topChart.tooltip} />} />
|
const topKey = `${chartKey}${TOP}`;
|
||||||
) : null;
|
const bottomKey = `${chartKey}${BOTTOM}`;
|
||||||
const bottomTooltip = bottomChart.tooltip ? (
|
|
||||||
<Tooltip cursor={false} content={<CustomTooltip tooltip={bottomChart.tooltip} />} />
|
|
||||||
) : null;
|
|
||||||
const topColor = '#8884d8';
|
const topColor = '#8884d8';
|
||||||
const bottomColor = '#82ca9d';
|
const bottomColor = '#82ca9d';
|
||||||
|
|
||||||
// Put the samples of the two charts into the same array in order to avoid problems
|
|
||||||
// at the synchronized area charts. If one of the two arrays doesn't have value at
|
|
||||||
// a given position, give it a 0 default value.
|
|
||||||
let data = [...topChart.data.map(({value}) => {
|
|
||||||
const d = {};
|
|
||||||
d[topKey] = value || topDefault;
|
|
||||||
return d;
|
|
||||||
})];
|
|
||||||
for (let i = 0; i < data.length && i < bottomChart.data.length; i++) {
|
|
||||||
// The value needs to be negative in order to plot it upside down.
|
|
||||||
const d = bottomChart.data[i];
|
|
||||||
data[i][bottomKey] = d && d.value ? -d.value : bottomDefault;
|
|
||||||
}
|
|
||||||
data = [...data, ...bottomChart.data.slice(data.length).map(({value}) => {
|
|
||||||
const d = {};
|
|
||||||
d[topKey] = topDefault;
|
|
||||||
d[bottomKey] = -value || bottomDefault;
|
|
||||||
return d;
|
|
||||||
})];
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={styles.doubleChartWrapper}>
|
<div style={styles.doubleChartWrapper}>
|
||||||
<ResponsiveContainer width='100%' height='50%'>
|
{this.halfHeightChart(
|
||||||
<AreaChart data={data} syncId={syncId} >
|
{
|
||||||
{topTooltip}
|
syncId,
|
||||||
<Area type='monotone' dataKey={topKey} stroke={topColor} fill={topColor} />
|
data: topChart.data.map(({value}) => ({[topKey]: value || topDefault})),
|
||||||
</AreaChart>
|
margin: {top: 5, right: 5, bottom: 0, left: 5},
|
||||||
</ResponsiveContainer>
|
},
|
||||||
<div style={{marginTop: -10, width: '100%', height: '50%'}}>
|
topChart.tooltip,
|
||||||
<ResponsiveContainer width='100%' height='100%'>
|
{dataKey: topKey, stroke: topColor, fill: topColor},
|
||||||
<AreaChart data={data} syncId={syncId} >
|
)}
|
||||||
{bottomTooltip}
|
{this.halfHeightChart(
|
||||||
<Area type='monotone' dataKey={bottomKey} stroke={bottomColor} fill={bottomColor} />
|
{
|
||||||
</AreaChart>
|
syncId,
|
||||||
</ResponsiveContainer>
|
data: bottomChart.data.map(({value}) => ({[bottomKey]: -value || -bottomDefault})),
|
||||||
</div>
|
margin: {top: 0, right: 5, bottom: 5, left: 5},
|
||||||
|
},
|
||||||
|
bottomChart.tooltip,
|
||||||
|
{dataKey: bottomKey, stroke: bottomColor, fill: bottomColor},
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {content} = this.props;
|
const {general, system} = this.props;
|
||||||
const {general, home} = content;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid container className={this.props.classes.footer} direction='row' alignItems='center' style={styles.footer}>
|
<Grid container className={this.props.classes.footer} direction='row' alignItems='center' style={styles.footer}>
|
||||||
<Grid item xs style={styles.chartRowWrapper}>
|
<Grid item xs style={styles.chartRowWrapper}>
|
||||||
<ChartRow>
|
<ChartRow>
|
||||||
{this.doubleChart(
|
{this.doubleChart(
|
||||||
'all',
|
FOOTER_SYNC_ID,
|
||||||
{data: home.processCPU, tooltip: percentPlotter('Process')},
|
CPU,
|
||||||
{data: home.systemCPU, tooltip: percentPlotter('System', multiplier(-1))},
|
{data: system.processCPU, tooltip: percentPlotter('Process load')},
|
||||||
|
{data: system.systemCPU, tooltip: percentPlotter('System load', multiplier(-1))},
|
||||||
)}
|
)}
|
||||||
{this.doubleChart(
|
{this.doubleChart(
|
||||||
'all',
|
FOOTER_SYNC_ID,
|
||||||
{data: home.activeMemory, tooltip: bytePlotter('Active')},
|
MEMORY,
|
||||||
{data: home.virtualMemory, tooltip: bytePlotter('Virtual', multiplier(-1))},
|
{data: system.activeMemory, tooltip: bytePlotter('Active memory')},
|
||||||
|
{data: system.virtualMemory, tooltip: bytePlotter('Virtual memory', multiplier(-1))},
|
||||||
)}
|
)}
|
||||||
{this.doubleChart(
|
{this.doubleChart(
|
||||||
'all',
|
FOOTER_SYNC_ID,
|
||||||
{data: home.diskRead, tooltip: bytePerSecPlotter('Disk Read')},
|
DISK,
|
||||||
{data: home.diskWrite, tooltip: bytePerSecPlotter('Disk Write', multiplier(-1))},
|
{data: system.diskRead, tooltip: bytePerSecPlotter('Disk read')},
|
||||||
|
{data: system.diskWrite, tooltip: bytePerSecPlotter('Disk write', multiplier(-1))},
|
||||||
)}
|
)}
|
||||||
{this.doubleChart(
|
{this.doubleChart(
|
||||||
'all',
|
FOOTER_SYNC_ID,
|
||||||
{data: home.networkIngress, tooltip: bytePerSecPlotter('Download')},
|
TRAFFIC,
|
||||||
{data: home.networkEgress, tooltip: bytePerSecPlotter('Upload', multiplier(-1))},
|
{data: system.networkIngress, tooltip: bytePerSecPlotter('Download')},
|
||||||
|
{data: system.networkEgress, tooltip: bytePerSecPlotter('Upload', multiplier(-1))},
|
||||||
)}
|
)}
|
||||||
</ChartRow>
|
</ChartRow>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item >
|
<Grid item >
|
||||||
{this.info('Geth', general.version)}
|
<Typography type='caption' color='inherit'>
|
||||||
{this.info('Commit', general.commit ? general.commit.substring(0, 7) : null)}
|
<span style={commonStyles.light}>Geth</span> {general.version}
|
||||||
|
</Typography>
|
||||||
|
{general.commit && (
|
||||||
|
<Typography type='caption' color='inherit'>
|
||||||
|
<span style={commonStyles.light}>{'Commit '}</span>
|
||||||
|
<a href={`https://github.com/ethereum/go-ethereum/commit/${general.commit}`} target='_blank' style={{color: 'inherit', textDecoration: 'none'}} >
|
||||||
|
{general.commit.substring(0, 8)}
|
||||||
|
</a>
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
);
|
);
|
||||||
|
@ -21,30 +21,16 @@ import React, {Component} from 'react';
|
|||||||
import withStyles from 'material-ui/styles/withStyles';
|
import withStyles from 'material-ui/styles/withStyles';
|
||||||
import AppBar from 'material-ui/AppBar';
|
import AppBar from 'material-ui/AppBar';
|
||||||
import Toolbar from 'material-ui/Toolbar';
|
import Toolbar from 'material-ui/Toolbar';
|
||||||
import Transition from 'react-transition-group/Transition';
|
|
||||||
import IconButton from 'material-ui/IconButton';
|
import IconButton from 'material-ui/IconButton';
|
||||||
|
import Icon from 'material-ui/Icon';
|
||||||
|
import MenuIcon from 'material-ui-icons/Menu';
|
||||||
import Typography from 'material-ui/Typography';
|
import Typography from 'material-ui/Typography';
|
||||||
import ChevronLeftIcon from 'material-ui-icons/ChevronLeft';
|
|
||||||
|
|
||||||
import {DURATION} from '../common';
|
|
||||||
|
|
||||||
// styles contains the constant styles of the component.
|
|
||||||
const styles = {
|
|
||||||
arrow: {
|
|
||||||
default: {
|
|
||||||
transition: `transform ${DURATION}ms`,
|
|
||||||
},
|
|
||||||
transition: {
|
|
||||||
entered: {transform: 'rotate(180deg)'},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// themeStyles returns the styles generated from the theme for the component.
|
// themeStyles returns the styles generated from the theme for the component.
|
||||||
const themeStyles = (theme: Object) => ({
|
const themeStyles = (theme: Object) => ({
|
||||||
header: {
|
header: {
|
||||||
backgroundColor: theme.palette.background.appBar,
|
backgroundColor: theme.palette.grey[900],
|
||||||
color: theme.palette.getContrastText(theme.palette.background.appBar),
|
color: theme.palette.getContrastText(theme.palette.grey[900]),
|
||||||
zIndex: theme.zIndex.appBar,
|
zIndex: theme.zIndex.appBar,
|
||||||
},
|
},
|
||||||
toolbar: {
|
toolbar: {
|
||||||
@ -53,42 +39,28 @@ const themeStyles = (theme: Object) => ({
|
|||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
paddingLeft: theme.spacing.unit,
|
paddingLeft: theme.spacing.unit,
|
||||||
|
fontSize: 3 * theme.spacing.unit,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export type Props = {
|
export type Props = {
|
||||||
classes: Object, // injected by withStyles()
|
classes: Object, // injected by withStyles()
|
||||||
opened: boolean,
|
|
||||||
switchSideBar: () => void,
|
switchSideBar: () => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Header renders the header of the dashboard.
|
// Header renders the header of the dashboard.
|
||||||
class Header extends Component<Props> {
|
class Header extends Component<Props> {
|
||||||
shouldComponentUpdate(nextProps) {
|
|
||||||
return nextProps.opened !== this.props.opened;
|
|
||||||
}
|
|
||||||
|
|
||||||
// arrow renders a button, which changes the sidebar's state.
|
|
||||||
arrow = (transitionState: string) => (
|
|
||||||
<IconButton onClick={this.props.switchSideBar}>
|
|
||||||
<ChevronLeftIcon
|
|
||||||
style={{
|
|
||||||
...styles.arrow.default,
|
|
||||||
...styles.arrow.transition[transitionState],
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</IconButton>
|
|
||||||
);
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {classes, opened} = this.props;
|
const {classes} = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppBar position='static' className={classes.header}>
|
<AppBar position='static' className={classes.header}>
|
||||||
<Toolbar className={classes.toolbar}>
|
<Toolbar className={classes.toolbar}>
|
||||||
<Transition mountOnEnter in={opened} timeout={{enter: DURATION}}>
|
<IconButton onClick={this.props.switchSideBar}>
|
||||||
{this.arrow}
|
<Icon>
|
||||||
</Transition>
|
<MenuIcon />
|
||||||
|
</Icon>
|
||||||
|
</IconButton>
|
||||||
<Typography type='title' color='inherit' noWrap className={classes.title}>
|
<Typography type='title' color='inherit' noWrap className={classes.title}>
|
||||||
Go Ethereum Dashboard
|
Go Ethereum Dashboard
|
||||||
</Typography>
|
</Typography>
|
||||||
|
@ -76,7 +76,8 @@ class Main extends Component<Props> {
|
|||||||
<div style={styles.wrapper}>
|
<div style={styles.wrapper}>
|
||||||
<div className={classes.content} style={styles.content}>{children}</div>
|
<div className={classes.content} style={styles.content}>{children}</div>
|
||||||
<Footer
|
<Footer
|
||||||
content={content}
|
general={content.general}
|
||||||
|
system={content.system}
|
||||||
shouldUpdate={shouldUpdate}
|
shouldUpdate={shouldUpdate}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -41,10 +41,10 @@ const styles = {
|
|||||||
// themeStyles returns the styles generated from the theme for the component.
|
// themeStyles returns the styles generated from the theme for the component.
|
||||||
const themeStyles = theme => ({
|
const themeStyles = theme => ({
|
||||||
list: {
|
list: {
|
||||||
background: theme.palette.background.appBar,
|
background: theme.palette.grey[900],
|
||||||
},
|
},
|
||||||
listItem: {
|
listItem: {
|
||||||
minWidth: theme.spacing.unit * 3,
|
minWidth: theme.spacing.unit * 7,
|
||||||
},
|
},
|
||||||
icon: {
|
icon: {
|
||||||
fontSize: theme.spacing.unit * 3,
|
fontSize: theme.spacing.unit * 3,
|
||||||
|
7678
dashboard/assets/package-lock.json
generated
7678
dashboard/assets/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -15,11 +15,11 @@
|
|||||||
"css-loader": "^0.28.9",
|
"css-loader": "^0.28.9",
|
||||||
"eslint": "^4.16.0",
|
"eslint": "^4.16.0",
|
||||||
"eslint-config-airbnb": "^16.1.0",
|
"eslint-config-airbnb": "^16.1.0",
|
||||||
"eslint-loader": "^1.9.0",
|
"eslint-loader": "^2.0.0",
|
||||||
|
"eslint-plugin-flowtype": "^2.41.0",
|
||||||
"eslint-plugin-import": "^2.8.0",
|
"eslint-plugin-import": "^2.8.0",
|
||||||
"eslint-plugin-jsx-a11y": "^6.0.3",
|
"eslint-plugin-jsx-a11y": "^6.0.3",
|
||||||
"eslint-plugin-react": "^7.5.1",
|
"eslint-plugin-react": "^7.5.1",
|
||||||
"eslint-plugin-flowtype": "^2.41.0",
|
|
||||||
"file-loader": "^1.1.6",
|
"file-loader": "^1.1.6",
|
||||||
"flow-bin": "^0.63.1",
|
"flow-bin": "^0.63.1",
|
||||||
"flow-bin-loader": "^1.0.2",
|
"flow-bin-loader": "^1.0.2",
|
||||||
@ -35,6 +35,13 @@
|
|||||||
"style-loader": "^0.19.1",
|
"style-loader": "^0.19.1",
|
||||||
"url": "^0.11.0",
|
"url": "^0.11.0",
|
||||||
"url-loader": "^0.6.2",
|
"url-loader": "^0.6.2",
|
||||||
"webpack": "^3.10.0"
|
"webpack": "^3.10.0",
|
||||||
|
"webpack-dev-server": "^2.11.1"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "NODE_ENV=production webpack",
|
||||||
|
"stats": "webpack --profile --json > stats.json",
|
||||||
|
"dev": "webpack-dev-server --port 8081",
|
||||||
|
"flow": "flow-typed install"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,27 +26,20 @@ export type Content = {
|
|||||||
logs: Logs,
|
logs: Logs,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type ChartEntries = Array<ChartEntry>;
|
||||||
|
|
||||||
|
export type ChartEntry = {
|
||||||
|
time: Date,
|
||||||
|
value: number,
|
||||||
|
};
|
||||||
|
|
||||||
export type General = {
|
export type General = {
|
||||||
version: ?string,
|
version: ?string,
|
||||||
commit: ?string,
|
commit: ?string,
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Home = {
|
export type Home = {
|
||||||
activeMemory: ChartEntries,
|
/* TODO (kurkomisi) */
|
||||||
virtualMemory: ChartEntries,
|
|
||||||
networkIngress: ChartEntries,
|
|
||||||
networkEgress: ChartEntries,
|
|
||||||
processCPU: ChartEntries,
|
|
||||||
systemCPU: ChartEntries,
|
|
||||||
diskRead: ChartEntries,
|
|
||||||
diskWrite: ChartEntries,
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ChartEntries = Array<ChartEntry>;
|
|
||||||
|
|
||||||
export type ChartEntry = {
|
|
||||||
time: Date,
|
|
||||||
value: number,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Chain = {
|
export type Chain = {
|
||||||
@ -62,7 +55,14 @@ export type Network = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type System = {
|
export type System = {
|
||||||
/* TODO (kurkomisi) */
|
activeMemory: ChartEntries,
|
||||||
|
virtualMemory: ChartEntries,
|
||||||
|
networkIngress: ChartEntries,
|
||||||
|
networkEgress: ChartEntries,
|
||||||
|
processCPU: ChartEntries,
|
||||||
|
systemCPU: ChartEntries,
|
||||||
|
diskRead: ChartEntries,
|
||||||
|
diskWrite: ChartEntries,
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Logs = {
|
export type Logs = {
|
||||||
|
@ -32,6 +32,9 @@ module.exports = {
|
|||||||
mangle: false,
|
mangle: false,
|
||||||
beautify: true,
|
beautify: true,
|
||||||
}),
|
}),
|
||||||
|
new webpack.DefinePlugin({
|
||||||
|
PROD: process.env.NODE_ENV === 'production',
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [
|
||||||
|
6551
dashboard/assets/yarn.lock
Normal file
6551
dashboard/assets/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@ -38,8 +38,4 @@ type Config struct {
|
|||||||
|
|
||||||
// Refresh is the refresh rate of the data updates, the chartEntry will be collected this often.
|
// Refresh is the refresh rate of the data updates, the chartEntry will be collected this often.
|
||||||
Refresh time.Duration `toml:",omitempty"`
|
Refresh time.Duration `toml:",omitempty"`
|
||||||
|
|
||||||
// Assets offers a possibility to manually set the dashboard website's location on the server side.
|
|
||||||
// It is useful for debugging, avoids the repeated generation of the binary.
|
|
||||||
Assets string `toml:",omitempty"`
|
|
||||||
}
|
}
|
||||||
|
@ -16,19 +16,17 @@
|
|||||||
|
|
||||||
package dashboard
|
package dashboard
|
||||||
|
|
||||||
//go:generate npm --prefix ./assets install
|
//go:generate yarn --cwd ./assets install
|
||||||
//go:generate ./assets/node_modules/.bin/webpack --config ./assets/webpack.config.js --context ./assets
|
//go:generate yarn --cwd ./assets build
|
||||||
//go:generate go-bindata -nometadata -o assets.go -prefix assets -nocompress -pkg dashboard assets/dashboard.html assets/bundle.js
|
//go:generate go-bindata -nometadata -o assets.go -prefix assets -nocompress -pkg dashboard assets/index.html assets/bundle.js
|
||||||
//go:generate sh -c "sed 's#var _bundleJs#//nolint:misspell\\\n&#' assets.go > assets.go.tmp && mv assets.go.tmp assets.go"
|
//go:generate sh -c "sed 's#var _bundleJs#//nolint:misspell\\\n&#' assets.go > assets.go.tmp && mv assets.go.tmp assets.go"
|
||||||
//go:generate sh -c "sed 's#var _dashboardHtml#//nolint:misspell\\\n&#' assets.go > assets.go.tmp && mv assets.go.tmp assets.go"
|
//go:generate sh -c "sed 's#var _indexHtml#//nolint:misspell\\\n&#' assets.go > assets.go.tmp && mv assets.go.tmp assets.go"
|
||||||
//go:generate gofmt -w -s assets.go
|
//go:generate gofmt -w -s assets.go
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
@ -62,7 +60,7 @@ type Dashboard struct {
|
|||||||
|
|
||||||
listener net.Listener
|
listener net.Listener
|
||||||
conns map[uint32]*client // Currently live websocket connections
|
conns map[uint32]*client // Currently live websocket connections
|
||||||
charts *HomeMessage
|
charts *SystemMessage
|
||||||
commit string
|
commit string
|
||||||
lock sync.RWMutex // Lock protecting the dashboard's internals
|
lock sync.RWMutex // Lock protecting the dashboard's internals
|
||||||
|
|
||||||
@ -84,7 +82,7 @@ func New(config *Config, commit string) (*Dashboard, error) {
|
|||||||
conns: make(map[uint32]*client),
|
conns: make(map[uint32]*client),
|
||||||
config: config,
|
config: config,
|
||||||
quit: make(chan chan error),
|
quit: make(chan chan error),
|
||||||
charts: &HomeMessage{
|
charts: &SystemMessage{
|
||||||
ActiveMemory: emptyChartEntries(now, activeMemorySampleLimit, config.Refresh),
|
ActiveMemory: emptyChartEntries(now, activeMemorySampleLimit, config.Refresh),
|
||||||
VirtualMemory: emptyChartEntries(now, virtualMemorySampleLimit, config.Refresh),
|
VirtualMemory: emptyChartEntries(now, virtualMemorySampleLimit, config.Refresh),
|
||||||
NetworkIngress: emptyChartEntries(now, networkIngressSampleLimit, config.Refresh),
|
NetworkIngress: emptyChartEntries(now, networkIngressSampleLimit, config.Refresh),
|
||||||
@ -180,18 +178,7 @@ func (db *Dashboard) webHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
path := r.URL.String()
|
path := r.URL.String()
|
||||||
if path == "/" {
|
if path == "/" {
|
||||||
path = "/dashboard.html"
|
path = "/index.html"
|
||||||
}
|
|
||||||
// If the path of the assets is manually set
|
|
||||||
if db.config.Assets != "" {
|
|
||||||
blob, err := ioutil.ReadFile(filepath.Join(db.config.Assets, path))
|
|
||||||
if err != nil {
|
|
||||||
log.Warn("Failed to read file", "path", path, "err", err)
|
|
||||||
http.Error(w, "not found", http.StatusNotFound)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
w.Write(blob)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
blob, err := Asset(path[1:])
|
blob, err := Asset(path[1:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -241,7 +228,7 @@ func (db *Dashboard) apiHandler(conn *websocket.Conn) {
|
|||||||
Version: fmt.Sprintf("v%d.%d.%d%s", params.VersionMajor, params.VersionMinor, params.VersionPatch, versionMeta),
|
Version: fmt.Sprintf("v%d.%d.%d%s", params.VersionMajor, params.VersionMinor, params.VersionPatch, versionMeta),
|
||||||
Commit: db.commit,
|
Commit: db.commit,
|
||||||
},
|
},
|
||||||
Home: &HomeMessage{
|
System: &SystemMessage{
|
||||||
ActiveMemory: db.charts.ActiveMemory,
|
ActiveMemory: db.charts.ActiveMemory,
|
||||||
VirtualMemory: db.charts.VirtualMemory,
|
VirtualMemory: db.charts.VirtualMemory,
|
||||||
NetworkIngress: db.charts.NetworkIngress,
|
NetworkIngress: db.charts.NetworkIngress,
|
||||||
@ -277,6 +264,8 @@ func (db *Dashboard) collectData() {
|
|||||||
systemCPUUsage := gosigar.Cpu{}
|
systemCPUUsage := gosigar.Cpu{}
|
||||||
systemCPUUsage.Get()
|
systemCPUUsage.Get()
|
||||||
var (
|
var (
|
||||||
|
mem runtime.MemStats
|
||||||
|
|
||||||
prevNetworkIngress = metrics.DefaultRegistry.Get("p2p/InboundTraffic").(metrics.Meter).Count()
|
prevNetworkIngress = metrics.DefaultRegistry.Get("p2p/InboundTraffic").(metrics.Meter).Count()
|
||||||
prevNetworkEgress = metrics.DefaultRegistry.Get("p2p/OutboundTraffic").(metrics.Meter).Count()
|
prevNetworkEgress = metrics.DefaultRegistry.Get("p2p/OutboundTraffic").(metrics.Meter).Count()
|
||||||
prevProcessCPUTime = getProcessCPUTime()
|
prevProcessCPUTime = getProcessCPUTime()
|
||||||
@ -306,7 +295,7 @@ func (db *Dashboard) collectData() {
|
|||||||
deltaNetworkIngress = float64(curNetworkIngress - prevNetworkIngress)
|
deltaNetworkIngress = float64(curNetworkIngress - prevNetworkIngress)
|
||||||
deltaNetworkEgress = float64(curNetworkEgress - prevNetworkEgress)
|
deltaNetworkEgress = float64(curNetworkEgress - prevNetworkEgress)
|
||||||
deltaProcessCPUTime = curProcessCPUTime - prevProcessCPUTime
|
deltaProcessCPUTime = curProcessCPUTime - prevProcessCPUTime
|
||||||
deltaSystemCPUUsage = systemCPUUsage.Delta(prevSystemCPUUsage)
|
deltaSystemCPUUsage = curSystemCPUUsage.Delta(prevSystemCPUUsage)
|
||||||
deltaDiskRead = curDiskRead - prevDiskRead
|
deltaDiskRead = curDiskRead - prevDiskRead
|
||||||
deltaDiskWrite = curDiskWrite - prevDiskWrite
|
deltaDiskWrite = curDiskWrite - prevDiskWrite
|
||||||
)
|
)
|
||||||
@ -319,7 +308,6 @@ func (db *Dashboard) collectData() {
|
|||||||
|
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
var mem runtime.MemStats
|
|
||||||
runtime.ReadMemStats(&mem)
|
runtime.ReadMemStats(&mem)
|
||||||
activeMemory := &ChartEntry{
|
activeMemory := &ChartEntry{
|
||||||
Time: now,
|
Time: now,
|
||||||
@ -363,7 +351,7 @@ func (db *Dashboard) collectData() {
|
|||||||
db.charts.DiskWrite = append(db.charts.DiskRead[1:], diskWrite)
|
db.charts.DiskWrite = append(db.charts.DiskRead[1:], diskWrite)
|
||||||
|
|
||||||
db.sendToAll(&Message{
|
db.sendToAll(&Message{
|
||||||
Home: &HomeMessage{
|
System: &SystemMessage{
|
||||||
ActiveMemory: ChartEntries{activeMemory},
|
ActiveMemory: ChartEntries{activeMemory},
|
||||||
VirtualMemory: ChartEntries{virtualMemory},
|
VirtualMemory: ChartEntries{virtualMemory},
|
||||||
NetworkIngress: ChartEntries{networkIngress},
|
NetworkIngress: ChartEntries{networkIngress},
|
||||||
|
@ -28,27 +28,20 @@ type Message struct {
|
|||||||
Logs *LogsMessage `json:"logs,omitempty"`
|
Logs *LogsMessage `json:"logs,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ChartEntries []*ChartEntry
|
||||||
|
|
||||||
|
type ChartEntry struct {
|
||||||
|
Time time.Time `json:"time,omitempty"`
|
||||||
|
Value float64 `json:"value,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type GeneralMessage struct {
|
type GeneralMessage struct {
|
||||||
Version string `json:"version,omitempty"`
|
Version string `json:"version,omitempty"`
|
||||||
Commit string `json:"commit,omitempty"`
|
Commit string `json:"commit,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type HomeMessage struct {
|
type HomeMessage struct {
|
||||||
ActiveMemory ChartEntries `json:"activeMemory,omitempty"`
|
/* TODO (kurkomisi) */
|
||||||
VirtualMemory ChartEntries `json:"virtualMemory,omitempty"`
|
|
||||||
NetworkIngress ChartEntries `json:"networkIngress,omitempty"`
|
|
||||||
NetworkEgress ChartEntries `json:"networkEgress,omitempty"`
|
|
||||||
ProcessCPU ChartEntries `json:"processCPU,omitempty"`
|
|
||||||
SystemCPU ChartEntries `json:"systemCPU,omitempty"`
|
|
||||||
DiskRead ChartEntries `json:"diskRead,omitempty"`
|
|
||||||
DiskWrite ChartEntries `json:"diskWrite,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ChartEntries []*ChartEntry
|
|
||||||
|
|
||||||
type ChartEntry struct {
|
|
||||||
Time time.Time `json:"time,omitempty"`
|
|
||||||
Value float64 `json:"value,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChainMessage struct {
|
type ChainMessage struct {
|
||||||
@ -64,7 +57,14 @@ type NetworkMessage struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SystemMessage struct {
|
type SystemMessage struct {
|
||||||
/* TODO (kurkomisi) */
|
ActiveMemory ChartEntries `json:"activeMemory,omitempty"`
|
||||||
|
VirtualMemory ChartEntries `json:"virtualMemory,omitempty"`
|
||||||
|
NetworkIngress ChartEntries `json:"networkIngress,omitempty"`
|
||||||
|
NetworkEgress ChartEntries `json:"networkEgress,omitempty"`
|
||||||
|
ProcessCPU ChartEntries `json:"processCPU,omitempty"`
|
||||||
|
SystemCPU ChartEntries `json:"systemCPU,omitempty"`
|
||||||
|
DiskRead ChartEntries `json:"diskRead,omitempty"`
|
||||||
|
DiskWrite ChartEntries `json:"diskWrite,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type LogsMessage struct {
|
type LogsMessage struct {
|
||||||
|
Loading…
Reference in New Issue
Block a user