Built system load and load history widgets

This commit is contained in:
Alicia Sykes 2022-01-18 16:03:44 +00:00
parent 63a0d18813
commit 9148195b84
6 changed files with 257 additions and 23 deletions

View File

@ -7,7 +7,7 @@ Dashy has support for displaying dynamic content in the form of widgets. There a
> Adding / editing widgets through the UI isn't yet supported, you will need to do this in the YAML config file. > Adding / editing widgets through the UI isn't yet supported, you will need to do this in the YAML config file.
##### Contents ##### Contents
- [General Widgets](#general-widgets) - **[General Widgets](#general-widgets)**
- [Clock](#clock) - [Clock](#clock)
- [Weather](#weather) - [Weather](#weather)
- [Weather Forecast](#weather-forecast) - [Weather Forecast](#weather-forecast)
@ -32,7 +32,7 @@ Dashy has support for displaying dynamic content in the form of widgets. There a
- [GitHub Trending](#github-trending) - [GitHub Trending](#github-trending)
- [GitHub Profile Stats](#github-profile-stats) - [GitHub Profile Stats](#github-profile-stats)
- [Public IP Address](#public-ip) - [Public IP Address](#public-ip)
- [Self-Hosted Services Widgets](#self-hosted-services-widgets) - **[Self-Hosted Services Widgets](#self-hosted-services-widgets)**
- [System Info](#system-info) - [System Info](#system-info)
- [Cron Monitoring](#cron-monitoring-health-checks) - [Cron Monitoring](#cron-monitoring-health-checks)
- [CPU History](#cpu-history-netdata) - [CPU History](#cpu-history-netdata)
@ -42,7 +42,7 @@ Dashy has support for displaying dynamic content in the form of widgets. There a
- [Pi Hole Queries](#pi-hole-queries) - [Pi Hole Queries](#pi-hole-queries)
- [Recent Traffic](#recent-traffic) - [Recent Traffic](#recent-traffic)
- [Stat Ping Statuses](#stat-ping-statuses) - [Stat Ping Statuses](#stat-ping-statuses)
- [System Resource Monitoring](#system-resource-monitoring) - **[System Resource Monitoring](#system-resource-monitoring)**
- [CPU Usage Current](#current-cpu-usage) - [CPU Usage Current](#current-cpu-usage)
- [CPU Usage Per Core](#cpu-usage-per-core) - [CPU Usage Per Core](#cpu-usage-per-core)
- [CPU Usage History](#cpu-usage-history) - [CPU Usage History](#cpu-usage-history)
@ -50,16 +50,20 @@ Dashy has support for displaying dynamic content in the form of widgets. There a
- [Memory Usage History](#memory-usage-history) - [Memory Usage History](#memory-usage-history)
- [Disk Space](#disk-space) - [Disk Space](#disk-space)
- [Disk IO](#disk-io) - [Disk IO](#disk-io)
- [Dynamic Widgets](#dynamic-widgets) - [System Load](#system-load)
- [System Load History](#system-load-history)
- **[Dynamic Widgets](#dynamic-widgets)**
- [Iframe Widget](#iframe-widget) - [Iframe Widget](#iframe-widget)
- [HTML Embed Widget](#html-embedded-widget) - [HTML Embed Widget](#html-embedded-widget)
- [API Response](#api-response) - [API Response](#api-response)
- [Prometheus Data](#prometheus-data) - [Prometheus Data](#prometheus-data)
- [Data Feed](#data-feed) - [Data Feed](#data-feed)
- [Usage & Customizations](#usage--customizations) - **[Usage & Customizations](#usage--customizations)**
- [Widget Usage Guide](#widget-usage-guide) - [Widget Usage Guide](#widget-usage-guide)
- [Continuous Updates](#continuous-updates) - [Continuous Updates](#continuous-updates)
- [Proxying Requests](#proxying-requests)
- [Custom CSS Styling](#widget-styling) - [Custom CSS Styling](#widget-styling)
- [Customizing Charts](#customizing-charts)
- [Language Translations](#language-translations) - [Language Translations](#language-translations)
- [Widget UI Options](#widget-ui-options) - [Widget UI Options](#widget-ui-options)
- [Building a Widget](#build-your-own-widget) - [Building a Widget](#build-your-own-widget)
@ -1168,7 +1172,9 @@ The easiest method for displaying system info and resource usage in Dashy is wit
Glances is a cross-platform monitoring tool developed by [@nicolargo](https://github.com/nicolargo). It's similar to top/htop but with a [Rest API](https://glances.readthedocs.io/en/latest/api.html) and many [data exporters](https://glances.readthedocs.io/en/latest/gw/index.html) available. Under the hood, it uses [psutil](https://github.com/giampaolo/psutil) for retrieving system info. Glances is a cross-platform monitoring tool developed by [@nicolargo](https://github.com/nicolargo). It's similar to top/htop but with a [Rest API](https://glances.readthedocs.io/en/latest/api.html) and many [data exporters](https://glances.readthedocs.io/en/latest/gw/index.html) available. Under the hood, it uses [psutil](https://github.com/giampaolo/psutil) for retrieving system info.
If you don't already have it installed, either follow the [Installation Guide](https://github.com/nicolargo/glances/blob/master/README.rst) for your system, or setup [with Docker](https://glances.readthedocs.io/en/latest/docker.html), or use the one-line install script: `curl -L https://bit.ly/glances | /bin/bash`. You'll need to run Glances as a web server, using the `-w` option, see the [command reference docs](https://glances.readthedocs.io/en/latest/cmds.html) for more info. If you don't already have it installed, either follow the [Installation Guide](https://github.com/nicolargo/glances/blob/master/README.rst) for your system, or setup [with Docker](https://glances.readthedocs.io/en/latest/docker.html), or use the one-line install script: `curl -L https://bit.ly/glances | /bin/bash`.
Glances can be launched with the `glances` command. You'll need to run it in web server mode, using the `-w` option for the API to be reachable. If you don't plan on using the Web UI, then you can disable it using `--disable-webui`. See the [command reference docs](https://glances.readthedocs.io/en/latest/cmds.html) for more info.
##### Options ##### Options
@ -1316,6 +1322,38 @@ Shows real-time read and write speeds and operations per sec for each disk
--- ---
### System Load
Shows the number of processes waiting in the run-queue, averaged across all cores. Displays for past 5, 10 and 15 minutes
<p align="center"><img width="400" src="https://i.ibb.co/090FfNy/gl-system-load.png" /></p>
##### Example
```yaml
- type: gl-system-load
options:
hostname: http://192.168.130.2:61208
```
---
### System Load History
Shows recent historical system load, calculated from the number of processes waiting in the run-queue, in 1, 5 and 15 minute intervals, and averaged across all cores. Optionally specify `limit` to set number of results returned, defaults to `500`, max `100000`, but the higher the number the longer the load and render times will be.
<p align="center"><img width="500" src="https://i.ibb.co/C2rGMLg/system-load-history.png" /></p>
##### Example
```yaml
- type: gl-load-history
options:
hostname: http://192.168.130.2:61208
```
---
## Dynamic Widgets ## Dynamic Widgets
### Iframe Widget ### Iframe Widget
@ -1465,19 +1503,6 @@ Note that if you have many widgets, and set them to continuously update frequent
--- ---
### Widget Styling
Like elsewhere in Dashy, all colours can be easily modified with CSS variables.
Widgets use the following color variables, which can be overridden if desired:
- `--widget-text-color` - Text color, defaults to `--primary`
- `--widget-background-color` - Background color, defaults to `--background-darker`
- `--widget-accent-color` - Accent color, defaults to `--background`
For more info on how to apply custom variables, see the [Theming Docs](/docs/theming.md#setting-custom-css-in-the-ui)
---
### Proxying Requests ### Proxying Requests
If a widget fails to make a data request, and the console shows a CORS error, this means the server is blocking client-side requests. If a widget fails to make a data request, and the console shows a CORS error, this means the server is blocking client-side requests.
@ -1503,6 +1528,33 @@ Vary: Origin
--- ---
### Widget Styling
Like elsewhere in Dashy, all colours can be easily modified with CSS variables.
Widgets use the following color variables, which can be overridden if desired:
- `--widget-text-color` - Text color, defaults to `--primary`
- `--widget-background-color` - Background color, defaults to `--background-darker`
- `--widget-accent-color` - Accent color, defaults to `--background`
For more info on how to apply custom variables, see the [Theming Docs](/docs/theming.md#setting-custom-css-in-the-ui)
---
### Customizing Charts
For widgets that contain charts, you can set an array of colors under `chartColors`. To specify the chart height, set `chartHeight` to an integer (in `px`), defaults to `300`. For example:
```yaml
- type: gl-load-history
options:
hostname: http://192.168.130.2:61208
chartColors: ['#9b5de5', '#f15bb5', '#00bbf9', '#00f5d4']
chartHeight: 450
```
---
### Language Translations ### Language Translations
Since most of the content displayed within widgets is fetched from an external API, unless that API supports multiple languages, translating dynamic content is not possible. Since most of the content displayed within widgets is fetched from an external API, unless that API supports multiple languages, translating dynamic content is not possible.
@ -1531,7 +1583,7 @@ Widgets are built in a modular fashion, making it easy for anyone to create thei
For a full tutorial on creating your own widget, you can follow [this guide](/docs/development-guides.md#building-a-widget), or take a look at [here](https://github.com/Lissy93/dashy/commit/3da76ce2999f57f76a97454c0276301e39957b8e) for a code example. For a full tutorial on creating your own widget, you can follow [this guide](/docs/development-guides.md#building-a-widget), or take a look at [here](https://github.com/Lissy93/dashy/commit/3da76ce2999f57f76a97454c0276301e39957b8e) for a code example.
Alternatively, for displaying simple data, you could also just use the either the [iframe](#iframe-widget), [embed](#html-embedded-widget), [Data Feed](#data-feed) or [API response](#api-response) widgets. Alternatively, for displaying simple data, you could also just use the either the [iframe](#iframe-widget), [embed](#html-embedded-widget), [data feed](#data-feed) or [API response](#api-response) widgets.
--- ---

View File

@ -273,7 +273,10 @@
"disk-space-free": "Free", "disk-space-free": "Free",
"disk-space-used": "Used", "disk-space-used": "Used",
"disk-mount-point": "Mount Point", "disk-mount-point": "Mount Point",
"disk-file-system": "File System" "disk-file-system": "File System",
"disk-io-read": "Read",
"disk-io-write": "Write",
"system-load-desc": "Number of processes waiting in the run-queue, averaged across all cores"
}, },
"system-info": { "system-info": {
"uptime": "Uptime" "uptime": "Uptime"

View File

@ -4,13 +4,13 @@
<p class="disk-name">{{ disk.name }}</p> <p class="disk-name">{{ disk.name }}</p>
<!-- Read Data --> <!-- Read Data -->
<div class="io-data read" v-tooltip="disk.readC ? `Count: ${disk.readC}` : ''"> <div class="io-data read" v-tooltip="disk.readC ? `Count: ${disk.readC}` : ''">
<span class="lbl">Read:</span> <span class="lbl">{{ $t('widgets.glances.disk-io-read') }}:</span>
<span class="val">{{ disk.readB | formatSize }}</span> <span class="val">{{ disk.readB | formatSize }}</span>
<span :class="`direction ${disk.readD}`">{{ disk.readD | getArrow }}</span> <span :class="`direction ${disk.readD}`">{{ disk.readD | getArrow }}</span>
</div> </div>
<!-- Write Data --> <!-- Write Data -->
<div class="io-data write" v-tooltip="disk.writeC ? `Count: ${disk.writeC}` : ''"> <div class="io-data write" v-tooltip="disk.writeC ? `Count: ${disk.writeC}` : ''">
<span class="lbl">Write:</span> <span class="lbl">{{ $t('widgets.glances.disk-io-write') }}:</span>
<span class="val">{{ disk.writeB | formatSize }}</span> <span class="val">{{ disk.writeB | formatSize }}</span>
<span :class="`direction ${disk.writeD}`">{{ disk.writeD | getArrow }}</span> <span :class="`direction ${disk.writeD}`">{{ disk.writeD | getArrow }}</span>
</div> </div>

View File

@ -0,0 +1,95 @@
<template>
<div class="glances-load-history-wrapper">
<div class="gl-history-chart" :id="chartId"></div>
</div>
</template>
<script>
import WidgetMixin from '@/mixins/WidgetMixin';
import ChartingMixin from '@/mixins/ChartingMixin';
import { timestampToTime, getTimeAgo } from '@/utils/MiscHelpers';
export default {
mixins: [WidgetMixin, ChartingMixin],
components: {},
data() {
return {};
},
computed: {
hostname() {
if (!this.options.hostname) this.error('You must specify a \'hostname\' for Glaces');
return this.options.hostname;
},
limit() {
return this.options.limit || 500;
},
endpoint() {
return `${this.hostname}/api/3/load/history/${this.limit}`;
},
},
methods: {
fetchData() {
this.makeRequest(this.endpoint).then(this.processData);
},
processData(loadData) {
const labels = [];
const min1 = [];
const min5 = [];
const min15 = [];
loadData.min1.forEach((dataPoint) => {
labels.push(timestampToTime(dataPoint[0]));
min1.push(dataPoint[1]);
});
loadData.min5.forEach((dataPoint) => {
min5.push(dataPoint[1]);
});
loadData.min15.forEach((dataPoint) => {
min15.push(dataPoint[1]);
});
const chartTitle = this.makeTitle(loadData.min1);
const datasets = [
{ name: '1 Minute', type: 'bar', values: min1 },
{ name: '5 Minutes', type: 'bar', values: min5 },
{ name: '15 Minutes', type: 'bar', values: min15 },
];
this.generateChart({ labels, datasets }, chartTitle);
},
makeTitle(system) {
return `System Load over past ${getTimeAgo(system[0][0]).replace('ago', '')}`;
},
generateChart(timeChartData, chartTitle) {
return new this.Chart(`#${this.chartId}`, {
title: chartTitle,
data: timeChartData,
type: 'axis-mixed',
height: this.chartHeight,
colors: this.chartColors,
truncateLegends: true,
lineOptions: {
regionFill: 1,
hideDots: 1,
},
axisOptions: {
xIsSeries: true,
xAxisMode: 'tick',
},
tooltipOptions: {
formatTooltipY: d => `${d} Processes`,
// formatTooltipX: d => timestampToTime(d),
},
});
},
},
created() {
this.overrideUpdateInterval = 20;
},
};
</script>
<style scoped lang="scss">
.glances-load-history-wrapper {
.gl-history-chart {}
}
</style>

View File

@ -0,0 +1,68 @@
<template>
<div class="glances-load-wrapper">
<div
:id="`load-${chartId}`" class="load-chart"
v-tooltip="$t('widgets.glances.system-load-desc')"></div>
</div>
</template>
<script>
import WidgetMixin from '@/mixins/WidgetMixin';
import ChartingMixin from '@/mixins/ChartingMixin';
export default {
mixins: [WidgetMixin, ChartingMixin],
computed: {
hostname() {
if (!this.options.hostname) this.error('You must specify a \'hostname\' for Glaces');
return this.options.hostname;
},
endpoint() {
return `${this.hostname}/api/3/load`;
},
},
methods: {
fetchData() {
this.makeRequest(this.endpoint).then(this.processData);
},
processData(loadData) {
const chartData = {
labels: ['1 Min', '5 Mins', '15 Mins'],
datasets: [
{ values: [loadData.min1, loadData.min5, loadData.min15] },
],
};
const chartTitle = `Load Averages over ${loadData.cpucore} Cores`;
this.renderChart(chartData, chartTitle);
},
renderChart(loadBarChartData, chartTitle) {
return new this.Chart(`#load-${this.chartId}`, {
title: chartTitle,
data: loadBarChartData,
type: 'bar',
height: 180,
colors: ['#04e4f4'],
barOptions: {
spaceRatio: 0.2,
},
tooltipOptions: {
formatTooltipY: d => `${d} Tasks`,
},
});
},
},
};
</script>
<style scoped lang="scss">
.glances-load-wrapper {
p.desc {
color: var(--widget-text-color);
opacity: var(--dimming-factor);
font-size: 0.8rem;
margin: 0;
visibility: hidden;
}
&:hover p.desc { visibility: visible; }
}
</style>

View File

@ -144,6 +144,13 @@
@error="handleError" @error="handleError"
:ref="widgetRef" :ref="widgetRef"
/> />
<GlLoadHistory
v-else-if="widgetType === 'gl-load-history'"
:options="widgetOptions"
@loading="setLoaderState"
@error="handleError"
:ref="widgetRef"
/>
<GlMemGauge <GlMemGauge
v-else-if="widgetType === 'gl-current-mem'" v-else-if="widgetType === 'gl-current-mem'"
:options="widgetOptions" :options="widgetOptions"
@ -158,6 +165,13 @@
@error="handleError" @error="handleError"
:ref="widgetRef" :ref="widgetRef"
/> />
<GlSystemLoad
v-else-if="widgetType === 'gl-system-load'"
:options="widgetOptions"
@loading="setLoaderState"
@error="handleError"
:ref="widgetRef"
/>
<HealthChecks <HealthChecks
v-else-if="widgetType === 'health-checks'" v-else-if="widgetType === 'health-checks'"
:options="widgetOptions" :options="widgetOptions"
@ -353,8 +367,10 @@ export default {
GlCpuHistory: () => import('@/components/Widgets/GlCpuHistory.vue'), GlCpuHistory: () => import('@/components/Widgets/GlCpuHistory.vue'),
GlDiskIo: () => import('@/components/Widgets/GlDiskIo.vue'), GlDiskIo: () => import('@/components/Widgets/GlDiskIo.vue'),
GlDiskSpace: () => import('@/components/Widgets/GlDiskSpace.vue'), GlDiskSpace: () => import('@/components/Widgets/GlDiskSpace.vue'),
GlLoadHistory: () => import('@/components/Widgets/GlLoadHistory.vue'),
GlMemGauge: () => import('@/components/Widgets/GlMemGauge.vue'), GlMemGauge: () => import('@/components/Widgets/GlMemGauge.vue'),
GlMemHistory: () => import('@/components/Widgets/GlMemHistory.vue'), GlMemHistory: () => import('@/components/Widgets/GlMemHistory.vue'),
GlSystemLoad: () => import('@/components/Widgets/GlSystemLoad.vue'),
HealthChecks: () => import('@/components/Widgets/HealthChecks.vue'), HealthChecks: () => import('@/components/Widgets/HealthChecks.vue'),
IframeWidget: () => import('@/components/Widgets/IframeWidget.vue'), IframeWidget: () => import('@/components/Widgets/IframeWidget.vue'),
Jokes: () => import('@/components/Widgets/Jokes.vue'), Jokes: () => import('@/components/Widgets/Jokes.vue'),