diff --git a/README.md b/README.md index 12b76d5..d7e0f15 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![Project Logo](https://cdn.jsdelivr.net/gh/yoobool/flask-state@1.0.6/src/flask_state/static/flask_state.png) +![Project Logo](https://cdn.jsdelivr.net/gh/yoobool/flask-state@1.0.7/src/flask_state/static/flask_state.png) [![Contributor Badge](https://img.shields.io/badge/Contributions-Welcome-0059b3)](https://github.com/yoobool/flask-state/tree/master/.github/ISSUE_TEMPLATE) [![Gitter Badge](https://img.shields.io/badge/Chat-Gitter-ff69b4.svg?label=Chat&logo=gitter)](https://gitter.im/flaskstate/community) @@ -11,7 +11,7 @@ English | [简体中文](https://github.com/yoobool/flask-state/blob/master/READ Flask-State is a lightweight chart plugin for displaying machine state data in your web application. -* **Monitored Metric:** CPU, memory, disk usage, LoadAVG and boot time. +* **Monitored Metric:** CPU, memory, disk usage, disk IO, Network IO, LoadAVG and boot time. * **Extensible:** Offers rich customization options, including redis monitoring, user authentication, custom logging, i18n and etc. * **Stable:** Solves multiprocessing concurrency problems (if you use [gunicorn](https://gunicorn.org/)) @@ -20,7 +20,7 @@ built on top of lightweight dependencies. This project is in active development and thoroughly tested to ensure that Flask-State stays up-to-date with its project roadmap. -![Screenshot](https://cdn.jsdelivr.net/gh/yoobool/flask-state@1.0.6/examples/static/flask_state.png) +![Screenshot](https://cdn.jsdelivr.net/gh/yoobool/flask-state@1.0.7/examples/static/flask_state.png) ## Documentation @@ -39,8 +39,8 @@ Alternatively, install Flask-State via NPM or include this script tag to the hea section of your HTML document: ```html - - + + ``` ```bash diff --git a/README.zh-CN.md b/README.zh-CN.md index 0c1d3e4..a2be850 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -1,6 +1,6 @@ [English](https://github.com/yoobool/flask-state/blob/master/README.md) | 简体中文 -![Project Logo](https://cdn.jsdelivr.net/gh/yoobool/flask-state@1.0.6/src/flask_state/static/flask_state.png) +![Project Logo](https://cdn.jsdelivr.net/gh/yoobool/flask-state@1.0.7/src/flask_state/static/flask_state.png) [![Contributor Badge](https://img.shields.io/badge/Contributions-Welcome-0059b3)](https://github.com/yoobool/flask-state/tree/master/.github/ISSUE_TEMPLATE) [![Gitter Badge](https://img.shields.io/badge/Chat-Gitter-ff69b4.svg?label=Chat&logo=gitter)](https://gitter.im/flaskstate/community) @@ -12,7 +12,7 @@ Flask-State是一款在您浏览器上使用的轻便、图表化插件。 -* **监控状态**:CPU,内存,磁盘,LoadAvg,启动时长。 +* **监控状态**:CPU,内存,磁盘,磁盘IO, 网络IO, LoadAvg,启动时长。 * **可扩展**:除记录本机状态外,还包括丰富的扩展功能选择。其中有Redis监控、用户验证、自定义logging和i18n等。 * **稳定**:轻量级的依赖关系,同时解决了多进程并发问题。 @@ -20,7 +20,7 @@ Flask-State是一个活跃的项目,经过了充分的测试以及有一系列 ### -![Screenshot](https://cdn.jsdelivr.net/gh/yoobool/flask-state@1.0.6/examples/static/flask_state.png) +![Screenshot](https://cdn.jsdelivr.net/gh/yoobool/flask-state@1.0.7/examples/static/flask_state.png) ## Documentation @@ -31,6 +31,21 @@ Flask-State是一个活跃的项目,经过了充分的测试以及有一系列 从这里 [PyPI](https://pip.pypa.io/en/stable/quickstart/) 下载: +```bash +pip install Flask-State +``` + +通过NPM安装Flask-State或将此脚本标签放在HTML文件的开头部分: + +```html + + +``` + +```bash +npm install flask-state --save +``` + ## Usage Flask-State插件安装后,还需要引入JavaScript文件和CSS文件,然后初始化组件运行方式。在某些配置上,你也可以选择修改。 diff --git a/examples/static/flask_state.png b/examples/static/flask_state.png old mode 100644 new mode 100755 index 625c437..820490c Binary files a/examples/static/flask_state.png and b/examples/static/flask_state.png differ diff --git a/examples/static/initial.js b/examples/static/initial.js index 60b872b..3d87e42 100644 --- a/examples/static/initial.js +++ b/examples/static/initial.js @@ -43,9 +43,9 @@ class MachineStatus { /* Insert window element */ initFlaskStateContainer() { - let _chart = this.mobile ? `
` - : `
`; - let _content = `

days

Host Status

Memory
CPU
Disk Usage
Load Avg
Uptime

Redis Status

Used Mem
Used Mem Rss
Mem Fragmentation Ratio
Cache Hits Ratio
24h Hits Ratio
Uptime
Connections
` + _chart + `
`; + let _chart = this.mobile ? `
` + : `
`; + let _content = `

days

Host Status

Memory
CPU
Disk Usage
Load Avg
Disk IO
Network IO
Uptime

Redis Status

Used Mem
Used Mem Rss
Mem Fragmentation Ratio
Cache Hits Ratio
24h Hits Ratio
Uptime
Connections
` + _chart + `
`; document.getElementsByTagName('body')[0].insertAdjacentHTML('beforeend', _content); } @@ -94,7 +94,7 @@ class MachineStatus { const elemDict = { 0: 'fs-info-tab-memory', 1: 'fs-info-tab-cpu', - 2: 'fs-info-tab-disk-usage', + 2: 'fs-info-network-io', 3: 'fs-info-tab-loadavg' }; for (let item of liArr) { @@ -123,6 +123,8 @@ class MachineStatus { document.getElementById('fs-memory').innerHTML = this.language.memory; document.getElementById('fs-disk-usage').innerHTML = this.language.disk_usage; document.getElementById('fs-load-avg').innerHTML = this.language.load_avg; + document.getElementById('fs-disk-io').innerHTML = this.language.disk_io; + document.getElementById('fs-network-io').innerHTML = this.language.network_io; document.getElementById('fs-boot-seconds').innerHTML = this.language.boot_seconds; document.getElementById('fs-used-memory').innerHTML = this.language.used_memory; document.getElementById('fs-used-memory-rss').innerHTML = this.language.used_memory_rss; @@ -140,10 +142,10 @@ class MachineStatus { this.consoleCpuChart = echarts.init(document.getElementById('fs-info-cpu-chart'), null, {renderer: 'svg'}); this.consoleMemoryChart = echarts.init(document.getElementById('fs-info-memory-chart'), null, {renderer: 'svg'}); this.consoleLoadavgChart = echarts.init(document.getElementById('fs-info-loadavg-chart'), null, {renderer: 'svg'}); - this.consoleDiskUsageChart = echarts.init(document.getElementById('fs-info-diskusage-chart'), null, {renderer: 'svg'}); + this.consoleDiskUsageChart = echarts.init(document.getElementById('fs-info-networkio-chart'), null, {renderer: 'svg'}); this.cpuOption = MachineStatus.generateChatOption(this.mobile, this.language.cpu || 'CPU', '', this.language.today || 'Today'); this.memoryOption = MachineStatus.generateChatOption(this.mobile, this.language.memory || 'Memory', '', this.language.today || 'Today'); - this.diskUsageOption = MachineStatus.generateChatOption(this.mobile, this.language.disk_usage || 'Disk Usage', '', this.language.today || 'Today'); + this.networkIOOption = MachineStatus.generateChatOption(this.mobile, this.language.network_io || 'Network IO', 'networkIO', this.language.today || 'Today'); this.loadavgOption = MachineStatus.generateChatOption(this.mobile, 'Load Avg', 'loadavg', this.language.minutes || 'min'); } @@ -170,17 +172,8 @@ class MachineStatus { return; } - const fields = ["ts", "cpu", "memory", "load_avg", "disk_usage"]; const data = response.data; - data.items = data.items.map(item => { - let element = {}; - fields.forEach((field, index) => { - if (field === "ts") return element[field] = SECONDS_TO_MILLISECONDS * item[index]; - element[field] = item[index]; - }); - return element; - }); let currentStatistic = data.currentStatistic; if (Object.keys(currentStatistic).length) { let hostInfoSpan = document.getElementById('fs-host-status').getElementsByClassName('fs-badge-content'); @@ -188,8 +181,10 @@ class MachineStatus { hostInfoSpan[1].innerHTML = currentStatistic.cpu + '%'; hostInfoSpan[2].innerHTML = currentStatistic.disk_usage + '%'; hostInfoSpan[3].innerHTML = currentStatistic.load_avg[0] + "," + currentStatistic.load_avg[1] + "," + currentStatistic.load_avg[2]; + hostInfoSpan[4].innerHTML = "R " + MachineStatus.getFormatBit(currentStatistic.disk_read) + " | " + "W " + MachineStatus.getFormatBit(currentStatistic.disk_write) + ""; + hostInfoSpan[5].innerHTML = "⬇ " + MachineStatus.getFormatBit(currentStatistic.net_recv) + " | " + "⬆ " + MachineStatus.getFormatBit(currentStatistic.net_sent) + ""; - hostInfoSpan[4].innerHTML = MachineStatus.getFormatSeconds(currentStatistic.boot_seconds || 0, this.language.days, this.language.hours, this.language.minutes, this.language.seconds); + hostInfoSpan[6].innerHTML = MachineStatus.getFormatSeconds(currentStatistic.boot_seconds || 0, this.language.days, this.language.hours, this.language.minutes, this.language.seconds); const machineIndex = ['memory', 'cpu', 'disk_usage', 'load_avg']; machineIndex.forEach(function (item, index) { @@ -252,8 +247,19 @@ class MachineStatus { } } + const fields = ["ts", "cpu", "memory", "load_avg"]; + data.items = data.items.map(item => { + let element = {}; + fields.forEach((field, index) => { + if (field === "ts") return element[field] = SECONDS_TO_MILLISECONDS * item[index]; + element[field] = item[index]; + }); + return element; + }); data.items.reverse(); + data.io.reverse(); let dataMap = MachineStatus.getChartsData(data.items); + let ioMap = MachineStatus.getIOChartsData(data.io); let tsList = dataMap.ts_list; let cpuList = dataMap.cpu_list; @@ -261,16 +267,18 @@ class MachineStatus { let loadavgList = dataMap.load_avg_list[0]; let loadavg5MinList = dataMap.load_avg_list[1]; let loadavg15MinList = dataMap.load_avg_list[2]; - let diskUsageList = dataMap.disk_usage_list; + + this.networkIOOption.xAxis.data = ioMap.ts_list; + this.networkIOOption.series[0].data = ioMap.net_recv; + this.networkIOOption.series[1].data = ioMap.net_sent; + this.consoleDiskUsageChart.setOption(this.networkIOOption); this.memoryOption.xAxis.data = tsList; this.cpuOption.xAxis.data = tsList; this.loadavgOption.xAxis.data = tsList; - this.diskUsageOption.xAxis.data = tsList; this.memoryOption.series[0].data = memoryList; this.cpuOption.series[0].data = cpuList; - this.diskUsageOption.series[0].data = diskUsageList; this.loadavgOption.series[0].data = loadavgList; this.loadavgOption.series[1].data = loadavg5MinList; this.loadavgOption.series[2].data = loadavg15MinList; @@ -278,7 +286,6 @@ class MachineStatus { this.consoleMemoryChart.setOption(this.memoryOption); this.consoleCpuChart.setOption(this.cpuOption); this.consoleLoadavgChart.setOption(this.loadavgOption); - this.consoleDiskUsageChart.setOption(this.diskUsageOption); MachineStatus.resizeChart([this.consoleMemoryChart, this.consoleCpuChart, this.consoleLoadavgChart, this.consoleDiskUsageChart]); }).then(() => { this.consoleMemoryChart.hideLoading(); @@ -332,7 +339,7 @@ class MachineStatus { /* Initialize echart */ static generateChatOption(isMobile, titleText, tableName = '', lineName = '') { let baseData = { - color: tableName === 'loadavg' ? ['#ffa726', '#42a5f5', '#66bb6a'] : ['#42a5f5'], + color: tableName === 'loadavg' ? ['#ffa726', '#42a5f5', '#66bb6a'] : tableName === 'networkIO' ? ['#ffa726', '#42a5f5'] : ['#42a5f5'], title: { show: !isMobile, text: titleText @@ -341,6 +348,12 @@ class MachineStatus { trigger: 'axis', formatter: (params) => { let value = echarts.format.formatTime('yyyy-MM-dd hh:mm:ss', new Date(parseInt(params[0].axisValue)), false) + '
'; + if (tableName === 'networkIO') { + for (let i = 0; i < params.length; i++) { + value += (params[i].marker + params[i].seriesName + ': ' + MachineStatus.getFormatBit(params[i].value) + '
') + } + return value; + } for (let i = 0; i < params.length; i++) { value += (params[i].marker + params[i].seriesName + ': ' + params[i].value + (tableName === 'loadavg' ? '' : '%') + '
') @@ -353,7 +366,7 @@ class MachineStatus { textStyle: { fontSize: 14 }, - show: titleText === 'Load Avg', + show: titleText === 'Load Avg' || tableName === "networkIO", }, grid: { left: '3%', @@ -382,7 +395,13 @@ class MachineStatus { yAxis: { type: 'value', axisLabel: { - formatter: (value) => value + (titleText === 'Load Avg' ? '' : '%'), + formatter: (value) => { + if (tableName === 'networkIO') { + let result = MachineStatus.getFormatBit(value, 0); + return result.substring(0, result.length - 2); + } + return value + (titleText === 'Load Avg' ? '' : '%') + }, }, }, series: [ @@ -406,13 +425,24 @@ class MachineStatus { }); }); } + if (tableName === 'networkIO') { + baseData.legend.data = ['recv', 'sent']; + baseData.series = []; + baseData.legend.data.forEach((name) => { + baseData.series.push({ + name: name, + type: 'line', + symbol: 'none', + hoverAnimation: false + }); + }); + } return baseData; } /* Get Echarts data */ static getChartsData(rawData) { let cpuList = []; - let diskUsageList = []; let loadAvgList = []; let loadAvg5minList = []; let loadAvg15minList = []; @@ -421,7 +451,6 @@ class MachineStatus { for (let i = 0; i < rawData.length; i++) { let item = rawData[i]; cpuList.push(item.cpu); - diskUsageList.push(item.disk_usage); loadAvgList.push(item.load_avg[0]); loadAvg5minList.push(item.load_avg[1]); loadAvg15minList.push(item.load_avg[2]); @@ -429,12 +458,50 @@ class MachineStatus { tsList.push(item.ts); } return { - 'cpu_list': cpuList, 'disk_usage_list': diskUsageList, - 'load_avg_list': [loadAvgList, loadAvg5minList, loadAvg15minList], + 'cpu_list': cpuList, 'load_avg_list': [loadAvgList, loadAvg5minList, loadAvg15minList], 'memory_list': memoryList, 'ts_list': tsList }; }; + /* Get Echarts data */ + static getIOChartsData(rawData) { + let netRecvList = []; + let netSentList = []; + let tsList = []; + for (let i = 0; i < rawData.length; i++) { + let item = rawData[i]; + tsList.push(item[0] * SECONDS_TO_MILLISECONDS); + netRecvList.push(item[1]); + netSentList.push(item[2]); + } + return { + 'net_recv': netRecvList, 'net_sent': netSentList, 'ts_list': tsList + }; + }; + + /* Get format Bit */ + static getFormatBit(value, fixed = 2) { + let suffix; + let b_value = value / 8; + if (b_value >= 10e11) { + b_value = (b_value / 10e11).toFixed(fixed); + suffix = "TB/s"; + } else if (b_value >= 10e8) { + b_value = (b_value / 10e8).toFixed(fixed); + suffix = "GB/s"; + } else if (b_value >= 10e5) { + b_value = (b_value / 10e5).toFixed(fixed); + suffix = "MB/s"; + } else if (b_value >= 10e2) { + b_value = (b_value / 10e2).toFixed(fixed); + suffix = "KB/s"; + } else { + suffix = "B/s"; + b_value = b_value.toFixed(2); + } + return b_value + ' ' + suffix; + }; + /* Get format time */ static getFormatSeconds(value, days = 'days', hours = 'hours', minutes = 'min', seconds = 'seconds') { let secondTime = parseInt(value); diff --git a/examples/templates/index.html b/examples/templates/index.html index 2b28895..3a608be 100644 --- a/examples/templates/index.html +++ b/examples/templates/index.html @@ -5,16 +5,17 @@ index - - + + + -
+
click me
+ - \ No newline at end of file + diff --git a/package.json b/package.json index 586c402..bde8ff2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "flask-state", - "version": "1.0.6", + "version": "1.0.7", "description": "Flask-States is a visual plug-in based on flask. It can record the local state every minute and read the status of redis if you have configured redis,and generate data chart to show to users through Echarts.", "main": "./packages/index.js", "directories": { diff --git a/packages/README.md b/packages/README.md index 6fb1eb8..f8ecab4 100644 --- a/packages/README.md +++ b/packages/README.md @@ -1,4 +1,4 @@ -![Project Logo](https://cdn.jsdelivr.net/gh/yoobool/flask-state@1.0.6/src/flask_state/static/flask_state.png) +![Project Logo](https://cdn.jsdelivr.net/gh/yoobool/flask-state@1.0.7/src/flask_state/static/flask_state.png) [![Contributor Badge](https://img.shields.io/badge/Contributions-Welcome-0059b3)](https://github.com/yoobool/flask-state/tree/master/.github/ISSUE_TEMPLATE) [![Gitter Badge](https://img.shields.io/badge/Chat-Gitter-ff69b4.svg?label=Chat&logo=gitter)](https://gitter.im/flaskstate/community) @@ -10,7 +10,7 @@ Flask-State is a lightweight chart plugin for displaying machine state data in your web application. -* **Monitored Metric:** CPU, memory, disk usage, LoadAVG and boot time. +* **Monitored Metric:** CPU, memory, disk usage, disk IO, Network IO, LoadAVG and boot time. * **Extensible:** Offers rich customization options, including redis monitoring, user authentication, custom logging, i18n and etc. * **Stable:** Solves multiprocessing concurrency problems (if you use [gunicorn](https://gunicorn.org/)) @@ -19,7 +19,7 @@ built on top of lightweight dependencies. This project is in active development and thoroughly tested to ensure that Flask-State stays up-to-date with its project roadmap. -![Screenshot](https://cdn.jsdelivr.net/gh/yoobool/flask-state@1.0.6/examples/static/flask_state.png) +![Screenshot](https://cdn.jsdelivr.net/gh/yoobool/flask-state@1.0.7/examples/static/flask_state.png) ## Documentation @@ -38,7 +38,8 @@ Alternatively, install Flask-State via NPM or include this script tag to the hea section of your HTML document: ```html - + + ``` ```bash diff --git a/packages/cjs/flask-state.js b/packages/cjs/flask-state.js index a3d952e..b405ad9 100644 --- a/packages/cjs/flask-state.js +++ b/packages/cjs/flask-state.js @@ -56,9 +56,9 @@ /* Insert window element */ initFlaskStateContainer() { - let _chart = this.mobile ? `
` - : `
`; - let _content = `

days

Host Status

Memory
CPU
Disk Usage
Load Avg
Uptime

Redis Status

Used Mem
Used Mem Rss
Mem Fragmentation Ratio
Cache Hits Ratio
24h Hits Ratio
Uptime
Connections
` + _chart + `
`; + let _chart = this.mobile ? `
` + : `
`; + let _content = `

days

Host Status

Memory
CPU
Disk Usage
Load Avg
Disk IO
Network IO
Uptime

Redis Status

Used Mem
Used Mem Rss
Mem Fragmentation Ratio
Cache Hits Ratio
24h Hits Ratio
Uptime
Connections
` + _chart + `
`; document.getElementsByTagName('body')[0].insertAdjacentHTML('beforeend', _content); } @@ -107,7 +107,7 @@ const elemDict = { 0: 'fs-info-tab-memory', 1: 'fs-info-tab-cpu', - 2: 'fs-info-tab-disk-usage', + 2: 'fs-info-network-io', 3: 'fs-info-tab-loadavg' }; for (let item of liArr) { @@ -136,6 +136,8 @@ document.getElementById('fs-memory').innerHTML = this.language.memory; document.getElementById('fs-disk-usage').innerHTML = this.language.disk_usage; document.getElementById('fs-load-avg').innerHTML = this.language.load_avg; + document.getElementById('fs-disk-io').innerHTML = this.language.disk_io; + document.getElementById('fs-network-io').innerHTML = this.language.network_io; document.getElementById('fs-boot-seconds').innerHTML = this.language.boot_seconds; document.getElementById('fs-used-memory').innerHTML = this.language.used_memory; document.getElementById('fs-used-memory-rss').innerHTML = this.language.used_memory_rss; @@ -153,10 +155,10 @@ this.consoleCpuChart = echarts.init(document.getElementById('fs-info-cpu-chart'), null, {renderer: 'svg'}); this.consoleMemoryChart = echarts.init(document.getElementById('fs-info-memory-chart'), null, {renderer: 'svg'}); this.consoleLoadavgChart = echarts.init(document.getElementById('fs-info-loadavg-chart'), null, {renderer: 'svg'}); - this.consoleDiskUsageChart = echarts.init(document.getElementById('fs-info-diskusage-chart'), null, {renderer: 'svg'}); + this.consoleDiskUsageChart = echarts.init(document.getElementById('fs-info-networkio-chart'), null, {renderer: 'svg'}); this.cpuOption = MachineStatus.generateChatOption(this.mobile, this.language.cpu || 'CPU', '', this.language.today || 'Today'); this.memoryOption = MachineStatus.generateChatOption(this.mobile, this.language.memory || 'Memory', '', this.language.today || 'Today'); - this.diskUsageOption = MachineStatus.generateChatOption(this.mobile, this.language.disk_usage || 'Disk Usage', '', this.language.today || 'Today'); + this.networkIOOption = MachineStatus.generateChatOption(this.mobile, this.language.network_io || 'Network IO', 'networkIO', this.language.today || 'Today'); this.loadavgOption = MachineStatus.generateChatOption(this.mobile, 'Load Avg', 'loadavg', this.language.minutes || 'min'); } @@ -183,17 +185,8 @@ return; } - const fields = ["ts", "cpu", "memory", "load_avg", "disk_usage"]; const data = response.data; - data.items = data.items.map(item => { - let element = {}; - fields.forEach((field, index) => { - if (field === "ts") return element[field] = SECONDS_TO_MILLISECONDS * item[index]; - element[field] = item[index]; - }); - return element; - }); let currentStatistic = data.currentStatistic; if (Object.keys(currentStatistic).length) { let hostInfoSpan = document.getElementById('fs-host-status').getElementsByClassName('fs-badge-content'); @@ -201,8 +194,10 @@ hostInfoSpan[1].innerHTML = currentStatistic.cpu + '%'; hostInfoSpan[2].innerHTML = currentStatistic.disk_usage + '%'; hostInfoSpan[3].innerHTML = currentStatistic.load_avg[0] + "," + currentStatistic.load_avg[1] + "," + currentStatistic.load_avg[2]; + hostInfoSpan[4].innerHTML = "R " + MachineStatus.getFormatBit(currentStatistic.disk_read) + " | " + "W " + MachineStatus.getFormatBit(currentStatistic.disk_write) + ""; + hostInfoSpan[5].innerHTML = "⬇ " + MachineStatus.getFormatBit(currentStatistic.net_recv) + " | " + "⬆ " + MachineStatus.getFormatBit(currentStatistic.net_sent) + ""; - hostInfoSpan[4].innerHTML = MachineStatus.getFormatSeconds(currentStatistic.boot_seconds || 0, this.language.days, this.language.hours, this.language.minutes, this.language.seconds); + hostInfoSpan[6].innerHTML = MachineStatus.getFormatSeconds(currentStatistic.boot_seconds || 0, this.language.days, this.language.hours, this.language.minutes, this.language.seconds); const machineIndex = ['memory', 'cpu', 'disk_usage', 'load_avg']; machineIndex.forEach(function (item, index) { @@ -265,8 +260,19 @@ } } + const fields = ["ts", "cpu", "memory", "load_avg"]; + data.items = data.items.map(item => { + let element = {}; + fields.forEach((field, index) => { + if (field === "ts") return element[field] = SECONDS_TO_MILLISECONDS * item[index]; + element[field] = item[index]; + }); + return element; + }); data.items.reverse(); + data.io.reverse(); let dataMap = MachineStatus.getChartsData(data.items); + let ioMap = MachineStatus.getIOChartsData(data.io); let tsList = dataMap.ts_list; let cpuList = dataMap.cpu_list; @@ -274,16 +280,18 @@ let loadavgList = dataMap.load_avg_list[0]; let loadavg5MinList = dataMap.load_avg_list[1]; let loadavg15MinList = dataMap.load_avg_list[2]; - let diskUsageList = dataMap.disk_usage_list; + + this.networkIOOption.xAxis.data = ioMap.ts_list; + this.networkIOOption.series[0].data = ioMap.net_recv; + this.networkIOOption.series[1].data = ioMap.net_sent; + this.consoleDiskUsageChart.setOption(this.networkIOOption); this.memoryOption.xAxis.data = tsList; this.cpuOption.xAxis.data = tsList; this.loadavgOption.xAxis.data = tsList; - this.diskUsageOption.xAxis.data = tsList; this.memoryOption.series[0].data = memoryList; this.cpuOption.series[0].data = cpuList; - this.diskUsageOption.series[0].data = diskUsageList; this.loadavgOption.series[0].data = loadavgList; this.loadavgOption.series[1].data = loadavg5MinList; this.loadavgOption.series[2].data = loadavg15MinList; @@ -291,7 +299,6 @@ this.consoleMemoryChart.setOption(this.memoryOption); this.consoleCpuChart.setOption(this.cpuOption); this.consoleLoadavgChart.setOption(this.loadavgOption); - this.consoleDiskUsageChart.setOption(this.diskUsageOption); MachineStatus.resizeChart([this.consoleMemoryChart, this.consoleCpuChart, this.consoleLoadavgChart, this.consoleDiskUsageChart]); }).then(() => { this.consoleMemoryChart.hideLoading(); @@ -345,7 +352,7 @@ /* Initialize echart */ static generateChatOption(isMobile, titleText, tableName = '', lineName = '') { let baseData = { - color: tableName === 'loadavg' ? ['#ffa726', '#42a5f5', '#66bb6a'] : ['#42a5f5'], + color: tableName === 'loadavg' ? ['#ffa726', '#42a5f5', '#66bb6a'] : tableName === 'networkIO' ? ['#ffa726', '#42a5f5'] : ['#42a5f5'], title: { show: !isMobile, text: titleText @@ -354,6 +361,12 @@ trigger: 'axis', formatter: (params) => { let value = echarts.format.formatTime('yyyy-MM-dd hh:mm:ss', new Date(parseInt(params[0].axisValue)), false) + '
'; + if (tableName === 'networkIO') { + for (let i = 0; i < params.length; i++) { + value += (params[i].marker + params[i].seriesName + ': ' + MachineStatus.getFormatBit(params[i].value) + '
') + } + return value; + } for (let i = 0; i < params.length; i++) { value += (params[i].marker + params[i].seriesName + ': ' + params[i].value + (tableName === 'loadavg' ? '' : '%') + '
') @@ -366,7 +379,7 @@ textStyle: { fontSize: 14 }, - show: titleText === 'Load Avg', + show: titleText === 'Load Avg' || tableName === "networkIO", }, grid: { left: '3%', @@ -395,7 +408,13 @@ yAxis: { type: 'value', axisLabel: { - formatter: (value) => value + (titleText === 'Load Avg' ? '' : '%'), + formatter: (value) => { + if (tableName === 'networkIO') { + let result = MachineStatus.getFormatBit(value, 0); + return result.substring(0, result.length - 2); + } + return value + (titleText === 'Load Avg' ? '' : '%') + }, }, }, series: [ @@ -419,13 +438,24 @@ }); }); } + if (tableName === 'networkIO') { + baseData.legend.data = ['recv', 'sent']; + baseData.series = []; + baseData.legend.data.forEach((name) => { + baseData.series.push({ + name: name, + type: 'line', + symbol: 'none', + hoverAnimation: false + }); + }); + } return baseData; } /* Get Echarts data */ static getChartsData(rawData) { let cpuList = []; - let diskUsageList = []; let loadAvgList = []; let loadAvg5minList = []; let loadAvg15minList = []; @@ -434,7 +464,6 @@ for (let i = 0; i < rawData.length; i++) { let item = rawData[i]; cpuList.push(item.cpu); - diskUsageList.push(item.disk_usage); loadAvgList.push(item.load_avg[0]); loadAvg5minList.push(item.load_avg[1]); loadAvg15minList.push(item.load_avg[2]); @@ -442,12 +471,50 @@ tsList.push(item.ts); } return { - 'cpu_list': cpuList, 'disk_usage_list': diskUsageList, - 'load_avg_list': [loadAvgList, loadAvg5minList, loadAvg15minList], + 'cpu_list': cpuList, 'load_avg_list': [loadAvgList, loadAvg5minList, loadAvg15minList], 'memory_list': memoryList, 'ts_list': tsList }; }; + /* Get Echarts data */ + static getIOChartsData(rawData) { + let netRecvList = []; + let netSentList = []; + let tsList = []; + for (let i = 0; i < rawData.length; i++) { + let item = rawData[i]; + tsList.push(item[0] * SECONDS_TO_MILLISECONDS); + netRecvList.push(item[1]); + netSentList.push(item[2]); + } + return { + 'net_recv': netRecvList, 'net_sent': netSentList, 'ts_list': tsList + }; + }; + + /* Get format Bit */ + static getFormatBit(value, fixed = 2) { + let suffix; + let b_value = value / 8; + if (b_value >= 10e11) { + b_value = (b_value / 10e11).toFixed(fixed); + suffix = "TB/s"; + } else if (b_value >= 10e8) { + b_value = (b_value / 10e8).toFixed(fixed); + suffix = "GB/s"; + } else if (b_value >= 10e5) { + b_value = (b_value / 10e5).toFixed(fixed); + suffix = "MB/s"; + } else if (b_value >= 10e2) { + b_value = (b_value / 10e2).toFixed(fixed); + suffix = "KB/s"; + } else { + suffix = "B/s"; + b_value = b_value.toFixed(2); + } + return b_value + ' ' + suffix; + }; + /* Get format time */ static getFormatSeconds(value, days = 'days', hours = 'hours', minutes = 'min', seconds = 'seconds') { let secondTime = parseInt(value); diff --git a/packages/cjs/flask-state.min.js b/packages/cjs/flask-state.min.js index 1b71336..1b02322 100644 --- a/packages/cjs/flask-state.min.js +++ b/packages/cjs/flask-state.min.js @@ -10,4 +10,4 @@ 'use strict'; -module.exports=function(e){var t={};function s(a){if(t[a])return t[a].exports;var n=t[a]={i:a,l:!1,exports:{}};return e[a].call(n.exports,n,n.exports,s),n.l=!0,n.exports}return s.m=e,s.c=t,s.d=function(e,t,a){s.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:a})},s.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},s.t=function(e,t){if(1&t&&(e=s(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var a=Object.create(null);if(s.r(a),Object.defineProperty(a,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)s.d(a,n,function(t){return e[t]}.bind(null,n));return a},s.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return s.d(t,"a",t),t},s.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},s.p="",s(s.s=0)}([function(e,t,s){"use strict";const a=85,n=75,i=10,o=5,d=1048576;class r{constructor(e){this.language=e,this.mobile=r.isMobile(),this.index=0,this.initFlaskStateContainer(this.mobile),this.setEventListener(),this.initFlaskStateLanguage(this.language),this.setInitParams(),this.mobile&&this.setTagChangeEventListener(this.consoleCpuChart,this.consoleMemoryChart,this.consoleLoadavgChart,this.consoleDiskUsageChart),window.addEventListener("resize",(()=>{r.resizeChartTimer([this.consoleMemoryChart,this.consoleCpuChart,this.consoleLoadavgChart,this.consoleDiskUsageChart])}))}setFlaskStateData(){document.getElementById("fs-background").style.display="block",document.getElementById("fs-info-container").style.display="block",document.getElementsByTagName("body")[0].style.overflowX="hidden",document.getElementsByTagName("body")[0].style.overflowY="hidden",document.getElementById("fs-select-days").value="1",this.setCharts("1")}initFlaskStateContainer(){let e='

days

Host Status

Memory
CPU
Disk Usage
Load Avg
Uptime

Redis Status

Used Mem
Used Mem Rss
Mem Fragmentation Ratio
Cache Hits Ratio
24h Hits Ratio
Uptime
Connections
'+(this.mobile?'
':"
")+"
";document.getElementsByTagName("body")[0].insertAdjacentHTML("beforeend",e)}setEventListener(){if(window.addEventListener){document.getElementById("fs-info-close").addEventListener("click",(function(){document.getElementById("fs-background").style.display="none",document.getElementById("fs-info-container").style.display="none",document.getElementsByTagName("body")[0].style.overflowX="auto",document.getElementsByTagName("body")[0].style.overflowY="auto",document.getElementById("fs-state-circular")&&document.getElementById("fs-state-circular").classList.remove("fs-circular-out")})),document.getElementById("fs-background").addEventListener("click",(function(e){"fs-background"===String(e.target.id)&&(document.getElementById("fs-background").style.display="none",document.getElementById("fs-info-container").style.display="none",document.getElementsByTagName("body")[0].style.overflowX="auto",document.getElementsByTagName("body")[0].style.overflowY="auto",document.getElementById("fs-state-circular")&&document.getElementById("fs-state-circular").classList.remove("fs-circular-out"))}));let e=document.getElementById("fs-select-days");e.addEventListener("change",(()=>{this.setCharts(e.value)}))}}setTagChangeEventListener(...e){if(document.getElementById("fs-info-tab")){let t=document.getElementById("fs-info-tab").getElementsByTagName("li"),s=document.getElementById("fs-info-tab-memory"),a=t[0],n=0;const i={0:"fs-info-tab-memory",1:"fs-info-tab-cpu",2:"fs-info-tab-disk-usage",3:"fs-info-tab-loadavg"};for(let o of t){let t=document.getElementById(i[n]);o.children[0].addEventListener("click",(()=>{o.classList.add("active"),a.classList.remove("active"),s.classList.remove("fs-show"),t.classList.add("fs-show"),r.resizeChart(e),s=t,a=o})),n++}}}initFlaskStateLanguage(){0!==Object.keys(this.language).length&&(document.getElementById("fs-host-status-title").innerHTML=this.language.host_status,document.getElementById("fs-redis-status-title").innerHTML=this.language.redis_status,document.getElementById("fs-cpu").innerHTML=this.language.cpu,document.getElementById("fs-memory").innerHTML=this.language.memory,document.getElementById("fs-disk-usage").innerHTML=this.language.disk_usage,document.getElementById("fs-load-avg").innerHTML=this.language.load_avg,document.getElementById("fs-boot-seconds").innerHTML=this.language.boot_seconds,document.getElementById("fs-used-memory").innerHTML=this.language.used_memory,document.getElementById("fs-used-memory-rss").innerHTML=this.language.used_memory_rss,document.getElementById("fs-mem-fragmentation-ratio").innerHTML=this.language.mem_fragmentation_ratio,document.getElementById("fs-hits-ratio").innerHTML=this.language.hits_ratio,document.getElementById("fs-delta-hits-ratio").innerHTML=this.language.delta_hits_ratio,document.getElementById("fs-uptime-in-seconds").innerHTML=this.language.uptime_in_seconds,document.getElementById("fs-connected-clients").innerHTML=this.language.connected_clients,document.getElementById("fs-days").innerHTML=this.language.days)}setInitParams(){this.consoleCpuChart=echarts.init(document.getElementById("fs-info-cpu-chart"),null,{renderer:"svg"}),this.consoleMemoryChart=echarts.init(document.getElementById("fs-info-memory-chart"),null,{renderer:"svg"}),this.consoleLoadavgChart=echarts.init(document.getElementById("fs-info-loadavg-chart"),null,{renderer:"svg"}),this.consoleDiskUsageChart=echarts.init(document.getElementById("fs-info-diskusage-chart"),null,{renderer:"svg"}),this.cpuOption=r.generateChatOption(this.mobile,this.language.cpu||"CPU","",this.language.today||"Today"),this.memoryOption=r.generateChatOption(this.mobile,this.language.memory||"Memory","",this.language.today||"Today"),this.diskUsageOption=r.generateChatOption(this.mobile,this.language.disk_usage||"Disk Usage","",this.language.today||"Today"),this.loadavgOption=r.generateChatOption(this.mobile,"Load Avg","loadavg",this.language.minutes||"min")}setCharts(e){this.consoleCpuChart.showLoading(),this.consoleMemoryChart.showLoading(),this.consoleLoadavgChart.showLoading(),this.consoleDiskUsageChart.showLoading(),fetch("/v0/state/hoststatus",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json;charset=UTF-8"},body:JSON.stringify({timeQuantum:e})}).then((e=>{e.ok&&e.json().then((e=>{if(200!==e.code)return;const t=["ts","cpu","memory","load_avg","disk_usage"],s=e.data;s.items=s.items.map((e=>{let s={};return t.forEach(((t,a)=>{if("ts"===t)return s[t]=1e3*e[a];s[t]=e[a]})),s}));let l=s.currentStatistic;if(Object.keys(l).length){let e=document.getElementById("fs-host-status").getElementsByClassName("fs-badge-content");e[0].innerHTML=l.memory+"%",e[1].innerHTML=l.cpu+"%",e[2].innerHTML=l.disk_usage+"%",e[3].innerHTML=l.load_avg[0]+","+l.load_avg[1]+","+l.load_avg[2],e[4].innerHTML=r.getFormatSeconds(l.boot_seconds||0,this.language.days,this.language.hours,this.language.minutes,this.language.seconds);["memory","cpu","disk_usage","load_avg"].forEach((function(t,s){let d;if("load_avg"===t){let e=(l.load_avg[0]+l.load_avg[1]+l.load_avg[2])/3;d=e>=o?e>=i?"background-red":"background-orange":"background-green"}else d=l[t]>=n?l.memory>=a?"background-red":"background-orange":"background-green";let r=e[s].classList;r.remove("background-green","background-orange","background-red"),r.add(d)}));let t=document.getElementById("fs-redis-status").getElementsByClassName("fs-badge-content"),s=["used_memory","used_memory_rss","mem_fragmentation_ratio","hits_ratio","delta_hits_ratio","uptime_in_seconds","connected_clients"],c=!0;for(let e of s)if(l[e]){c=!1;break}c?(document.getElementById("fs-redis-status-title").innerHTML="",document.getElementById("fs-redis-status-title").style.marginTop="0",document.getElementById("fs-redis-status").style.display="none"):s.forEach(((e,s)=>{switch(e){case"used_memory":case"used_memory_rss":t[s].innerHTML=Math.ceil(l[e]/d)+" M";break;case"mem_fragmentation_ratio":let a=l[e];if(t[s].innerHTML=l[e],null!=a&&a>1){let e=t[s].classList;e.remove("background-green"),e.add("background-red")}break;case"hits_ratio":case"delta_hits_ratio":t[s].innerHTML=l[e]+"%";break;case"uptime_in_seconds":t[s].innerHTML=r.getFormatSeconds(l[e],this.language.days,this.language.hours,this.language.minutes,this.language.seconds);break;case"connected_clients":t[s].innerHTML=l[e]}}))}s.items.reverse();let c=r.getChartsData(s.items),g=c.ts_list,u=c.cpu_list,m=c.memory_list,h=c.load_avg_list[0],f=c.load_avg_list[1],p=c.load_avg_list[2],y=c.disk_usage_list;this.memoryOption.xAxis.data=g,this.cpuOption.xAxis.data=g,this.loadavgOption.xAxis.data=g,this.diskUsageOption.xAxis.data=g,this.memoryOption.series[0].data=m,this.cpuOption.series[0].data=u,this.diskUsageOption.series[0].data=y,this.loadavgOption.series[0].data=h,this.loadavgOption.series[1].data=f,this.loadavgOption.series[2].data=p,this.consoleMemoryChart.setOption(this.memoryOption),this.consoleCpuChart.setOption(this.cpuOption),this.consoleLoadavgChart.setOption(this.loadavgOption),this.consoleDiskUsageChart.setOption(this.diskUsageOption),r.resizeChart([this.consoleMemoryChart,this.consoleCpuChart,this.consoleLoadavgChart,this.consoleDiskUsageChart])})).then((()=>{this.consoleMemoryChart.hideLoading(),this.consoleCpuChart.hideLoading(),this.consoleLoadavgChart.hideLoading(),this.consoleDiskUsageChart.hideLoading()}))})).catch((function(e){}))}static isMobile(){let e=navigator.userAgent,t={trident:e.indexOf("Trident")>-1,presto:e.indexOf("Presto")>-1,webKit:e.indexOf("AppleWebKit")>-1,gecko:e.indexOf("Gecko")>-1&&-1===e.indexOf("KHTML"),mobile:e.match(/AppleWebKit.*Mobile.*/)&&!0,ios:e.match(/\(i[^;]+;( U;)? CPU.Mac OS X/)&&!0,android:e.indexOf("Android")>-1||e.indexOf("Linux")>-1,iPhone:e.indexOf("iPhone")>-1,iPad:e.indexOf("iPad")>-1,webApp:-1===e.indexOf("Safari"),wechat:e.indexOf("MicroMessenger")>-1,qq:e.match(/\sQQ/i)&&" qq"===e.match(/\sQQ/i)[0]};return t.iPhone||t.iPad||t.webApp||t.wechat||t.qq||t.ios||t.mobile||!1}static resizeChartTimer(e,t){clearTimeout(this.clearId),this.clearId=setTimeout((function(){r.resizeChart(e)}),t||200)}static resizeChart(e){e.forEach((e=>e.resize()))}static generateChatOption(e,t,s="",a=""){let n={color:"loadavg"===s?["#ffa726","#42a5f5","#66bb6a"]:["#42a5f5"],title:{show:!e,text:t},tooltip:{trigger:"axis",formatter:e=>{let t=echarts.format.formatTime("yyyy-MM-dd hh:mm:ss",new Date(parseInt(e[0].axisValue)),!1)+"
";for(let a=0;a";return t}},legend:{data:[a],textStyle:{fontSize:14},show:"Load Avg"===t},grid:{left:"3%",right:"4%",bottom:"3%",top:e?30:60,containLabel:!0},toolbox:{show:!e,feature:{saveAsImage:{title:" "}}},xAxis:{type:"category",boundaryGap:!1,axisLabel:{formatter:function(e){return echarts.format.formatTime("hh:mm",new Date(parseInt(e)),!1)}}},yAxis:{type:"value",axisLabel:{formatter:e=>e+("Load Avg"===t?"":"%")}},series:[{name:a,type:"line",symbol:"none",hoverAnimation:!1}]};return"loadavg"===s&&(n.legend.data=["1 "+a,"5 "+a,"15 "+a],n.series=[],n.legend.data.forEach((e=>{n.series.push({name:e,type:"line",symbol:"none",hoverAnimation:!1})}))),n}static getChartsData(e){let t=[],s=[],a=[],n=[],i=[],o=[],d=[];for(let r=0;r=60?(o=i/60,o>=60&&(d=o/60,o%=60),d>=24&&(r=d/24,d%=24)):l=i+n,o>0&&(l=Math.floor(o)+" "+a),d>0&&(l=Math.floor(d)+" "+s),r>0&&(l=Math.floor(r)+" "+t),l}}const l=function(){let e=null;return function(t){return e||(e=new r(t))}}();t.init=function(e){let t=null,s={};if(null!==e&&"object"==typeof e&&(t=e.hasOwnProperty("dom")?e.dom:null,s=e.hasOwnProperty("lang")&&e.lang.hasOwnProperty("language")?e.lang:{}),t instanceof HTMLElement){if(t.getAttribute("flaskState"))return;t.setAttribute("flaskState","true"),t.addEventListener("click",(()=>l(s).setFlaskStateData()))}else{if(document.getElementById("fs-state-circular"))return;let e="
";document.getElementsByTagName("body")[0].insertAdjacentHTML("beforeend",e);let t,n=document.getElementById("fs-state-circular");function a(e){n.style.top=e.clientY-t+300+"px"}n.onclick=function(){this.classList.add("fs-circular-out"),l(s).setFlaskStateData()},n.onmousedown=function(e){t=t||e.clientY,document.addEventListener("mousemove",a)},document.onmouseup=function(){document.removeEventListener("mousemove",a);const e=parseInt(n.style.top);n.classList.add("fs-circular-animation"),n.style.top=Math.min(Math.max(e,50),window.screen.height-200)+"px",setTimeout((()=>n.classList.remove("fs-circular-animation")),500)}}}}]); \ No newline at end of file +module.exports=function(e){var t={};function s(a){if(t[a])return t[a].exports;var n=t[a]={i:a,l:!1,exports:{}};return e[a].call(n.exports,n,n.exports,s),n.l=!0,n.exports}return s.m=e,s.c=t,s.d=function(e,t,a){s.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:a})},s.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},s.t=function(e,t){if(1&t&&(e=s(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var a=Object.create(null);if(s.r(a),Object.defineProperty(a,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)s.d(a,n,function(t){return e[t]}.bind(null,n));return a},s.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return s.d(t,"a",t),t},s.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},s.p="",s(s.s=0)}([function(e,t,s){"use strict";const a=85,n=75,i=10,o=5,r=1048576;class d{constructor(e){this.language=e,this.mobile=d.isMobile(),this.index=0,this.initFlaskStateContainer(this.mobile),this.setEventListener(),this.initFlaskStateLanguage(this.language),this.setInitParams(),this.mobile&&this.setTagChangeEventListener(this.consoleCpuChart,this.consoleMemoryChart,this.consoleLoadavgChart,this.consoleDiskUsageChart),window.addEventListener("resize",(()=>{d.resizeChartTimer([this.consoleMemoryChart,this.consoleCpuChart,this.consoleLoadavgChart,this.consoleDiskUsageChart])}))}setFlaskStateData(){document.getElementById("fs-background").style.display="block",document.getElementById("fs-info-container").style.display="block",document.getElementsByTagName("body")[0].style.overflowX="hidden",document.getElementsByTagName("body")[0].style.overflowY="hidden",document.getElementById("fs-select-days").value="1",this.setCharts("1")}initFlaskStateContainer(){let e='

days

Host Status

Memory
CPU
Disk Usage
Load Avg
Disk IO
Network IO
Uptime

Redis Status

Used Mem
Used Mem Rss
Mem Fragmentation Ratio
Cache Hits Ratio
24h Hits Ratio
Uptime
Connections
'+(this.mobile?'
':"
")+"
";document.getElementsByTagName("body")[0].insertAdjacentHTML("beforeend",e)}setEventListener(){if(window.addEventListener){document.getElementById("fs-info-close").addEventListener("click",(function(){document.getElementById("fs-background").style.display="none",document.getElementById("fs-info-container").style.display="none",document.getElementsByTagName("body")[0].style.overflowX="auto",document.getElementsByTagName("body")[0].style.overflowY="auto",document.getElementById("fs-state-circular")&&document.getElementById("fs-state-circular").classList.remove("fs-circular-out")})),document.getElementById("fs-background").addEventListener("click",(function(e){"fs-background"===String(e.target.id)&&(document.getElementById("fs-background").style.display="none",document.getElementById("fs-info-container").style.display="none",document.getElementsByTagName("body")[0].style.overflowX="auto",document.getElementsByTagName("body")[0].style.overflowY="auto",document.getElementById("fs-state-circular")&&document.getElementById("fs-state-circular").classList.remove("fs-circular-out"))}));let e=document.getElementById("fs-select-days");e.addEventListener("change",(()=>{this.setCharts(e.value)}))}}setTagChangeEventListener(...e){if(document.getElementById("fs-info-tab")){let t=document.getElementById("fs-info-tab").getElementsByTagName("li"),s=document.getElementById("fs-info-tab-memory"),a=t[0],n=0;const i={0:"fs-info-tab-memory",1:"fs-info-tab-cpu",2:"fs-info-network-io",3:"fs-info-tab-loadavg"};for(let o of t){let t=document.getElementById(i[n]);o.children[0].addEventListener("click",(()=>{o.classList.add("active"),a.classList.remove("active"),s.classList.remove("fs-show"),t.classList.add("fs-show"),d.resizeChart(e),s=t,a=o})),n++}}}initFlaskStateLanguage(){0!==Object.keys(this.language).length&&(document.getElementById("fs-host-status-title").innerHTML=this.language.host_status,document.getElementById("fs-redis-status-title").innerHTML=this.language.redis_status,document.getElementById("fs-cpu").innerHTML=this.language.cpu,document.getElementById("fs-memory").innerHTML=this.language.memory,document.getElementById("fs-disk-usage").innerHTML=this.language.disk_usage,document.getElementById("fs-load-avg").innerHTML=this.language.load_avg,document.getElementById("fs-disk-io").innerHTML=this.language.disk_io,document.getElementById("fs-network-io").innerHTML=this.language.network_io,document.getElementById("fs-boot-seconds").innerHTML=this.language.boot_seconds,document.getElementById("fs-used-memory").innerHTML=this.language.used_memory,document.getElementById("fs-used-memory-rss").innerHTML=this.language.used_memory_rss,document.getElementById("fs-mem-fragmentation-ratio").innerHTML=this.language.mem_fragmentation_ratio,document.getElementById("fs-hits-ratio").innerHTML=this.language.hits_ratio,document.getElementById("fs-delta-hits-ratio").innerHTML=this.language.delta_hits_ratio,document.getElementById("fs-uptime-in-seconds").innerHTML=this.language.uptime_in_seconds,document.getElementById("fs-connected-clients").innerHTML=this.language.connected_clients,document.getElementById("fs-days").innerHTML=this.language.days)}setInitParams(){this.consoleCpuChart=echarts.init(document.getElementById("fs-info-cpu-chart"),null,{renderer:"svg"}),this.consoleMemoryChart=echarts.init(document.getElementById("fs-info-memory-chart"),null,{renderer:"svg"}),this.consoleLoadavgChart=echarts.init(document.getElementById("fs-info-loadavg-chart"),null,{renderer:"svg"}),this.consoleDiskUsageChart=echarts.init(document.getElementById("fs-info-networkio-chart"),null,{renderer:"svg"}),this.cpuOption=d.generateChatOption(this.mobile,this.language.cpu||"CPU","",this.language.today||"Today"),this.memoryOption=d.generateChatOption(this.mobile,this.language.memory||"Memory","",this.language.today||"Today"),this.networkIOOption=d.generateChatOption(this.mobile,this.language.network_io||"Network IO","networkIO",this.language.today||"Today"),this.loadavgOption=d.generateChatOption(this.mobile,"Load Avg","loadavg",this.language.minutes||"min")}setCharts(e){this.consoleCpuChart.showLoading(),this.consoleMemoryChart.showLoading(),this.consoleLoadavgChart.showLoading(),this.consoleDiskUsageChart.showLoading(),fetch("/v0/state/hoststatus",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json;charset=UTF-8"},body:JSON.stringify({timeQuantum:e})}).then((e=>{e.ok&&e.json().then((e=>{if(200!==e.code)return;const t=e.data;let s=t.currentStatistic;if(Object.keys(s).length){let e=document.getElementById("fs-host-status").getElementsByClassName("fs-badge-content");e[0].innerHTML=s.memory+"%",e[1].innerHTML=s.cpu+"%",e[2].innerHTML=s.disk_usage+"%",e[3].innerHTML=s.load_avg[0]+","+s.load_avg[1]+","+s.load_avg[2],e[4].innerHTML="R "+d.getFormatBit(s.disk_read)+" | W "+d.getFormatBit(s.disk_write)+"",e[5].innerHTML="⬇ "+d.getFormatBit(s.net_recv)+" | ⬆ "+d.getFormatBit(s.net_sent)+"",e[6].innerHTML=d.getFormatSeconds(s.boot_seconds||0,this.language.days,this.language.hours,this.language.minutes,this.language.seconds);["memory","cpu","disk_usage","load_avg"].forEach((function(t,r){let d;if("load_avg"===t){let e=(s.load_avg[0]+s.load_avg[1]+s.load_avg[2])/3;d=e>=o?e>=i?"background-red":"background-orange":"background-green"}else d=s[t]>=n?s.memory>=a?"background-red":"background-orange":"background-green";let l=e[r].classList;l.remove("background-green","background-orange","background-red"),l.add(d)}));let t=document.getElementById("fs-redis-status").getElementsByClassName("fs-badge-content"),l=["used_memory","used_memory_rss","mem_fragmentation_ratio","hits_ratio","delta_hits_ratio","uptime_in_seconds","connected_clients"],c=!0;for(let e of l)if(s[e]){c=!1;break}c?(document.getElementById("fs-redis-status-title").innerHTML="",document.getElementById("fs-redis-status-title").style.marginTop="0",document.getElementById("fs-redis-status").style.display="none"):l.forEach(((e,a)=>{switch(e){case"used_memory":case"used_memory_rss":t[a].innerHTML=Math.ceil(s[e]/r)+" M";break;case"mem_fragmentation_ratio":let n=s[e];if(t[a].innerHTML=s[e],null!=n&&n>1){let e=t[a].classList;e.remove("background-green"),e.add("background-red")}break;case"hits_ratio":case"delta_hits_ratio":t[a].innerHTML=s[e]+"%";break;case"uptime_in_seconds":t[a].innerHTML=d.getFormatSeconds(s[e],this.language.days,this.language.hours,this.language.minutes,this.language.seconds);break;case"connected_clients":t[a].innerHTML=s[e]}}))}const l=["ts","cpu","memory","load_avg"];t.items=t.items.map((e=>{let t={};return l.forEach(((s,a)=>{if("ts"===s)return t[s]=1e3*e[a];t[s]=e[a]})),t})),t.items.reverse(),t.io.reverse();let c=d.getChartsData(t.items),g=d.getIOChartsData(t.io),u=c.ts_list,m=c.cpu_list,f=c.memory_list,h=c.load_avg_list[0],p=c.load_avg_list[1],y=c.load_avg_list[2];this.networkIOOption.xAxis.data=g.ts_list,this.networkIOOption.series[0].data=g.net_recv,this.networkIOOption.series[1].data=g.net_sent,this.consoleDiskUsageChart.setOption(this.networkIOOption),this.memoryOption.xAxis.data=u,this.cpuOption.xAxis.data=u,this.loadavgOption.xAxis.data=u,this.memoryOption.series[0].data=f,this.cpuOption.series[0].data=m,this.loadavgOption.series[0].data=h,this.loadavgOption.series[1].data=p,this.loadavgOption.series[2].data=y,this.consoleMemoryChart.setOption(this.memoryOption),this.consoleCpuChart.setOption(this.cpuOption),this.consoleLoadavgChart.setOption(this.loadavgOption),d.resizeChart([this.consoleMemoryChart,this.consoleCpuChart,this.consoleLoadavgChart,this.consoleDiskUsageChart])})).then((()=>{this.consoleMemoryChart.hideLoading(),this.consoleCpuChart.hideLoading(),this.consoleLoadavgChart.hideLoading(),this.consoleDiskUsageChart.hideLoading()}))})).catch((function(e){}))}static isMobile(){let e=navigator.userAgent,t={trident:e.indexOf("Trident")>-1,presto:e.indexOf("Presto")>-1,webKit:e.indexOf("AppleWebKit")>-1,gecko:e.indexOf("Gecko")>-1&&-1===e.indexOf("KHTML"),mobile:e.match(/AppleWebKit.*Mobile.*/)&&!0,ios:e.match(/\(i[^;]+;( U;)? CPU.Mac OS X/)&&!0,android:e.indexOf("Android")>-1||e.indexOf("Linux")>-1,iPhone:e.indexOf("iPhone")>-1,iPad:e.indexOf("iPad")>-1,webApp:-1===e.indexOf("Safari"),wechat:e.indexOf("MicroMessenger")>-1,qq:e.match(/\sQQ/i)&&" qq"===e.match(/\sQQ/i)[0]};return t.iPhone||t.iPad||t.webApp||t.wechat||t.qq||t.ios||t.mobile||!1}static resizeChartTimer(e,t){clearTimeout(this.clearId),this.clearId=setTimeout((function(){d.resizeChart(e)}),t||200)}static resizeChart(e){e.forEach((e=>e.resize()))}static generateChatOption(e,t,s="",a=""){let n={color:"loadavg"===s?["#ffa726","#42a5f5","#66bb6a"]:"networkIO"===s?["#ffa726","#42a5f5"]:["#42a5f5"],title:{show:!e,text:t},tooltip:{trigger:"axis",formatter:e=>{let t=echarts.format.formatTime("yyyy-MM-dd hh:mm:ss",new Date(parseInt(e[0].axisValue)),!1)+"
";if("networkIO"===s){for(let s=0;s";return t}for(let a=0;a";return t}},legend:{data:[a],textStyle:{fontSize:14},show:"Load Avg"===t||"networkIO"===s},grid:{left:"3%",right:"4%",bottom:"3%",top:e?30:60,containLabel:!0},toolbox:{show:!e,feature:{saveAsImage:{title:" "}}},xAxis:{type:"category",boundaryGap:!1,axisLabel:{formatter:function(e){return echarts.format.formatTime("hh:mm",new Date(parseInt(e)),!1)}}},yAxis:{type:"value",axisLabel:{formatter:e=>{if("networkIO"===s){let t=d.getFormatBit(e,0);return t.substring(0,t.length-2)}return e+("Load Avg"===t?"":"%")}}},series:[{name:a,type:"line",symbol:"none",hoverAnimation:!1}]};return"loadavg"===s&&(n.legend.data=["1 "+a,"5 "+a,"15 "+a],n.series=[],n.legend.data.forEach((e=>{n.series.push({name:e,type:"line",symbol:"none",hoverAnimation:!1})}))),"networkIO"===s&&(n.legend.data=["recv","sent"],n.series=[],n.legend.data.forEach((e=>{n.series.push({name:e,type:"line",symbol:"none",hoverAnimation:!1})}))),n}static getChartsData(e){let t=[],s=[],a=[],n=[],i=[],o=[];for(let r=0;r=1e12?(a=(a/1e12).toFixed(t),s="TB/s"):a>=1e9?(a=(a/1e9).toFixed(t),s="GB/s"):a>=1e6?(a=(a/1e6).toFixed(t),s="MB/s"):a>=1e3?(a=(a/1e3).toFixed(t),s="KB/s"):(s="B/s",a=a.toFixed(2)),a+" "+s}static getFormatSeconds(e,t="days",s="hours",a="min",n="seconds"){let i=parseInt(e),o=0,r=0,d=0,l="";return i>=60?(o=i/60,o>=60&&(r=o/60,o%=60),r>=24&&(d=r/24,r%=24)):l=i+n,o>0&&(l=Math.floor(o)+" "+a),r>0&&(l=Math.floor(r)+" "+s),d>0&&(l=Math.floor(d)+" "+t),l}}const l=function(){let e=null;return function(t){return e||(e=new d(t))}}();t.init=function(e){let t=null,s={};if(null!==e&&"object"==typeof e&&(t=e.hasOwnProperty("dom")?e.dom:null,s=e.hasOwnProperty("lang")&&e.lang.hasOwnProperty("language")?e.lang:{}),t instanceof HTMLElement){if(t.getAttribute("flaskState"))return;t.setAttribute("flaskState","true"),t.addEventListener("click",(()=>l(s).setFlaskStateData()))}else{if(document.getElementById("fs-state-circular"))return;let e="
";document.getElementsByTagName("body")[0].insertAdjacentHTML("beforeend",e);let t,n=document.getElementById("fs-state-circular");function a(e){n.style.top=e.clientY-t+300+"px"}n.onclick=function(){this.classList.add("fs-circular-out"),l(s).setFlaskStateData()},n.onmousedown=function(e){t=t||e.clientY,document.addEventListener("mousemove",a)},document.onmouseup=function(){document.removeEventListener("mousemove",a);const e=parseInt(n.style.top);n.classList.add("fs-circular-animation"),n.style.top=Math.min(Math.max(e,50),window.screen.height-200)+"px",setTimeout((()=>n.classList.remove("fs-circular-animation")),500)}}}}]); \ No newline at end of file diff --git a/packages/flask-state.css b/packages/flask-state.css index 482662b..5780560 100644 --- a/packages/flask-state.css +++ b/packages/flask-state.css @@ -298,3 +298,7 @@ .b-99cb3d { background-color: #99cb3d !important; } + +.b-564970 { + background-color: #564970 !important; +} \ No newline at end of file diff --git a/packages/flask-state.min.css b/packages/flask-state.min.css index debdc87..827a644 100644 --- a/packages/flask-state.min.css +++ b/packages/flask-state.min.css @@ -1 +1 @@ -.flask-state-elem button,.flask-state-elem div,.flask-state-elem h4,.flask-state-elem li,.flask-state-elem span,.flask-state-elem svg,.flask-state-elem ul{font-size:14px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.fs-background{position:fixed;width:100%;height:100%;overflow-y:auto;top:0;left:0;z-index:2001;background:hsla(0,0%,100%,.6)}.fs-background::-webkit-scrollbar-thumb{background-color:#bfbfbf;border-radius:10px}.fs-container-width{display:none;width:75%}.fs-container{position:relative;z-index:2002;display:block;margin:30px auto;padding:0 15px;background:#fff;border-top:3px solid #d2d6de;border-radius:3px;box-shadow:0 0 5px #ccc}.fs-select-container{z-index:3;position:absolute;top:10px;right:64px}.fs-select-arrow{position:absolute;display:flex;top:0;left:12px;padding-left:8px;align-items:center;pointer-events:none}.fs-select-days{margin-right:5px;padding:0 20px 0 5px;border-radius:3px;line-height:15px;appearance:none;-moz-appearance:none;-webkit-appearance:none}.fs-days,.fs-select-days{display:inline-block;vertical-align:top}.fs-days{margin:0;line-height:17px;text-transform:capitalize}.fs-close{z-index:2;position:absolute;top:6px;right:15px;padding:0;line-height:1;background:0 0;border:0;cursor:pointer}.fs-h4-style{margin-top:10px;margin-bottom:12px;font-size:18px!important;font-weight:500;line-height:15px}#fs-host-status>div,#fs-redis-status>div{display:inline-block;margin-bottom:10px;margin-right:18px}.fs-badge-intro{display:inline-block;padding:3px 5px;line-height:20px;color:#fff!important}.fs-badge-content{display:inline-block;height:26px;padding:0 8px;font-size:12px!important;font-weight:700;line-height:26px;color:#fff;text-align:center;white-space:nowrap;vertical-align:top}.console-info-line-style{margin:-6px -15px 6px;border-top:1px solid #ddd}.fs-ul-tabs{padding-left:0;margin:0;border-top-right-radius:3px;border-top-left-radius:3px;border-bottom:1px solid #f4f4f4;list-style:none}.fs-ul-tabs:after{clear:both;display:table;content:" ";-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.fs-ul-tabs>li{float:left;margin:0 5px -2px 0;border-top:3px solid transparent}.fs-ul-tabs>li.active{border-top-color:#3c8dbc}.fs-ul-tabs>li>a{display:block;padding:10px 15px;line-height:1.42857143;text-decoration:none;color:#444;border-left:1px solid transparent;border-right:1px solid transparent}.fs-ul-tabs>li.active>a{border-color:#f4f4f4}.fs-mChart-box{display:none}.fs-mChart-box>div{max-height:250px;overflow-y:auto}.fs-show{display:block!important}.fs-chart-style{margin-top:10px;height:320px}.fs-chart-content{margin-top:10px;width:100%;max-height:710px;overflow-y:auto;overflow-x:hidden}.fs-chart-content::-webkit-scrollbar{padding-left:5px;width:5px}.fs-chart-content::-webkit-scrollbar-thumb{background-color:#bfbfbf;border-radius:10px}.fs-charts-width{float:left;width:50%}.fs-charts-box{position:relative;margin-bottom:20px;border-top:3px solid #00c0ef;border-radius:3px;box-shadow:0 1px 1px rgba(0,0,0,.1)}.fs-border{border-right:1px solid #d2d6de}.fs-circular{z-index:2000;position:fixed;right:15px;width:3rem;height:3rem;background-color:#fff;cursor:pointer}.fs-circular-animation{transition:.3s}.fs-circular-out{transform:translateX(100px);-ms-transform:translateX(100px);-webkit-transform:translateX(100px);transition:.3s}@media (max-width:768px){.fs-charts-width{float:left;width:100%}.fs-container-width{display:none;width:91.66666667%}}@media (max-width:992px){.fs-border{border:0}}.background-green{background-color:#00a65a!important}.background-red{background-color:#dd4b39!important}.background-orange{background-color:#ff851b!important}.b-0079cc{background-color:#0079cc!important}.b-007dc8{background-color:#007dc8!important}.b-0051b9{background-color:#0051b9!important}.b-534c6d{background-color:#534c6d!important}.b-99cb3d{background-color:#99cb3d!important} \ No newline at end of file +.flask-state-elem button,.flask-state-elem div,.flask-state-elem h4,.flask-state-elem li,.flask-state-elem span,.flask-state-elem svg,.flask-state-elem ul{font-size:14px;-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.fs-background{position:fixed;width:100%;height:100%;overflow-y:auto;top:0;left:0;z-index:2001;background:hsla(0,0%,100%,.6)}.fs-background::-webkit-scrollbar-thumb{background-color:#bfbfbf;border-radius:10px}.fs-container-width{display:none;width:75%}.fs-container{position:relative;z-index:2002;display:block;margin:30px auto;padding:0 15px;background:#fff;border-top:3px solid #d2d6de;border-radius:3px;box-shadow:0 0 5px #ccc}.fs-select-container{z-index:3;position:absolute;top:10px;right:64px}.fs-select-arrow{position:absolute;display:flex;top:0;left:12px;padding-left:8px;align-items:center;pointer-events:none}.fs-select-days{margin-right:5px;padding:0 20px 0 5px;border-radius:3px;line-height:15px;appearance:none;-moz-appearance:none;-webkit-appearance:none}.fs-days,.fs-select-days{display:inline-block;vertical-align:top}.fs-days{margin:0;line-height:17px;text-transform:capitalize}.fs-close{z-index:2;position:absolute;top:6px;right:15px;padding:0;line-height:1;background:0 0;border:0;cursor:pointer}.fs-h4-style{margin-top:10px;margin-bottom:12px;font-size:18px!important;font-weight:500;line-height:15px}#fs-host-status>div,#fs-redis-status>div{display:inline-block;margin-bottom:10px;margin-right:18px}.fs-badge-intro{display:inline-block;padding:3px 5px;line-height:20px;color:#fff!important}.fs-badge-content{display:inline-block;height:26px;padding:0 8px;font-size:12px!important;font-weight:700;line-height:26px;color:#fff;text-align:center;white-space:nowrap;vertical-align:top}.console-info-line-style{margin:-6px -15px 6px;border-top:1px solid #ddd}.fs-ul-tabs{padding-left:0;margin:0;border-top-right-radius:3px;border-top-left-radius:3px;border-bottom:1px solid #f4f4f4;list-style:none}.fs-ul-tabs:after{clear:both;display:table;content:" ";-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}.fs-ul-tabs>li{float:left;margin:0 5px -2px 0;border-top:3px solid transparent}.fs-ul-tabs>li.active{border-top-color:#3c8dbc}.fs-ul-tabs>li>a{display:block;padding:10px 15px;line-height:1.42857143;text-decoration:none;color:#444;border-left:1px solid transparent;border-right:1px solid transparent}.fs-ul-tabs>li.active>a{border-color:#f4f4f4}.fs-mChart-box{display:none}.fs-mChart-box>div{max-height:250px;overflow-y:auto}.fs-show{display:block!important}.fs-chart-style{margin-top:10px;height:320px}.fs-chart-content{margin-top:10px;width:100%;max-height:710px;overflow-y:auto;overflow-x:hidden}.fs-chart-content::-webkit-scrollbar{padding-left:5px;width:5px}.fs-chart-content::-webkit-scrollbar-thumb{background-color:#bfbfbf;border-radius:10px}.fs-charts-width{float:left;width:50%}.fs-charts-box{position:relative;margin-bottom:20px;border-top:3px solid #00c0ef;border-radius:3px;box-shadow:0 1px 1px rgba(0,0,0,.1)}.fs-border{border-right:1px solid #d2d6de}.fs-circular{z-index:2000;position:fixed;right:15px;width:3rem;height:3rem;background-color:#fff;cursor:pointer}.fs-circular-animation{transition:.3s}.fs-circular-out{transform:translateX(100px);-ms-transform:translateX(100px);-webkit-transform:translateX(100px);transition:.3s}@media (max-width:768px){.fs-charts-width{float:left;width:100%}.fs-container-width{display:none;width:91.66666667%}}@media (max-width:992px){.fs-border{border:0}}.background-green{background-color:#00a65a!important}.background-red{background-color:#dd4b39!important}.background-orange{background-color:#ff851b!important}.b-0079cc{background-color:#0079cc!important}.b-007dc8{background-color:#007dc8!important}.b-0051b9{background-color:#0051b9!important}.b-534c6d{background-color:#534c6d!important}.b-99cb3d{background-color:#99cb3d!important}.b-564970{background-color:#564970!important} \ No newline at end of file diff --git a/packages/i18n.js b/packages/i18n.js index b742c9a..17cf685 100644 --- a/packages/i18n.js +++ b/packages/i18n.js @@ -6,6 +6,8 @@ const en = { "memory": "Memory", "disk_usage": "Disk Usage", "load_avg": "Load Avg", + "disk_io": "Disk IO", + "network_io": "Network IO", "boot_seconds": "Uptime", "redis_status": "Redis Status", "used_memory": "Used Mem", @@ -30,6 +32,8 @@ const zh = { "memory": "内存", "disk_usage": "磁盘使用率", "load_avg": "Load Avg", + "disk_io": "磁盘IO", + "network_io": "网络IO", "boot_seconds": "启动时长", "redis_status": "Redis状态", "used_memory": "分配总内存", @@ -54,6 +58,8 @@ const ja = { "memory": "メモリ", "disk_usage": "ディスクの使用率", "load_avg": "平均読み込み時間", + "disk_io": "ディスクIO", + "network_io": "ネットワークIO", "boot_seconds": "起動時間の秒", "redis_status": "Redisの状態", "used_memory": "使用メモリ", @@ -78,6 +84,8 @@ const es = { "memory": "Memoria", "disk_usage": "Uso del disco", "load_avg": "Promedio de carga", + "disk_io": "E / S de disco", + "network_io": "E / S de red", "boot_seconds": "Tiempo de actividad", "redis_status": "Estado de Redis", "used_memory": "Memoria total", @@ -102,6 +110,8 @@ const IN = { "memory": "Penyimpanan", "disk_usage": "Penggunaan Disk", "load_avg": "Beban Rata", + "disk_io": "Disk IO", + "network_io": "Jaringan IO", "boot_seconds": "Uptime", "redis_status": "Status Redis", "used_memory": "Memori Total", @@ -126,6 +136,8 @@ const fr = { "memory": "Mémoire", "disk_usage": "Utilisation du disque", "load_avg": "Load Avg", + "disk_io": "Disque IO", + "network_io": "Réseau IO", "boot_seconds": "Temps de démarrage", "redis_status": "Statut Redis", "used_memory": "Mémoire totale", @@ -150,6 +162,8 @@ const fil = { "memory": "Memorya", "disk_usage": "Paggamit ng disk", "load_avg": "Load Avg", + "disk_io": "Disk IO", + "network_io": "Network IO", "boot_seconds": "Uptime", "redis_status": "Redis Katayuan", "used_memory": "Kabuuang memorya", @@ -174,6 +188,8 @@ const vi = { "memory": "Ký ức", "disk_usage": "Sử dụng đĩa", "load_avg": "Tải trung bình", + "disk_io": "IO đĩa", + "network_io": "Mạng IO", "boot_seconds": "Gian hoạt", "redis_status": "Trạng thái Redis", "used_memory": "Phân bổ bộ nhớ", @@ -198,6 +214,8 @@ const hi = { "memory": "राम", "disk_usage": "डिस्क उपयोग", "load_avg": "औसत भार", + "disk_io": "डिस्क IO", + "network_io": "नेटवर्क आईओ", "boot_seconds": "अपटाइम", "redis_status": "रेडिस राज्य", "used_memory": "कुल मेमोरी आवंटित", @@ -214,4 +232,4 @@ const hi = { "today": "आज" }; -export {zh, en, ja, es, IN, fr, fil}; \ No newline at end of file +export {zh, en, ja, es, IN, fr, fil, vi, hi}; \ No newline at end of file diff --git a/packages/package.json b/packages/package.json index dded41e..23d35e8 100644 --- a/packages/package.json +++ b/packages/package.json @@ -1,6 +1,6 @@ { "name": "flask-state", - "version": "1.0.6", + "version": "1.0.7", "description": "Flask-States is a visual plug-in based on flask. It can record the local state every minute and read the status of redis if you have configured redis,and generate data chart to show to users through Echarts.", "main": "index.js", "scripts": { diff --git a/packages/umd/IN.js b/packages/umd/IN.js index 7ebb374..66e7d43 100644 --- a/packages/umd/IN.js +++ b/packages/umd/IN.js @@ -6,6 +6,8 @@ const IN = { "memory": "Penyimpanan", "disk_usage": "Penggunaan Disk", "load_avg": "Beban Rata", + "disk_io": "Disk IO", + "network_io": "Jaringan IO", "boot_seconds": "Uptime", "redis_status": "Status Redis", "used_memory": "Memori Total", diff --git a/packages/umd/en.js b/packages/umd/en.js index 9d86df7..00c4479 100644 --- a/packages/umd/en.js +++ b/packages/umd/en.js @@ -6,6 +6,8 @@ const en = { "memory": "Memory", "disk_usage": "Disk Usage", "load_avg": "Load Avg", + "disk_io": "Disk IO", + "network_io": "Network IO", "boot_seconds": "Uptime", "redis_status": "Redis Status", "used_memory": "Used Mem", diff --git a/packages/umd/es.js b/packages/umd/es.js index 5e80a25..0baa8fb 100644 --- a/packages/umd/es.js +++ b/packages/umd/es.js @@ -6,6 +6,8 @@ const es = { "memory": "Memoria", "disk_usage": "Uso del disco", "load_avg": "Promedio de carga", + "disk_io": "E / S de disco", + "network_io": "E / S de red", "boot_seconds": "Tiempo de actividad", "redis_status": "Estado de Redis", "used_memory": "Memoria total", diff --git a/packages/umd/fil.js b/packages/umd/fil.js index 44f070c..44f28f2 100644 --- a/packages/umd/fil.js +++ b/packages/umd/fil.js @@ -6,6 +6,8 @@ const fil = { "memory": "Memorya", "disk_usage": "Paggamit ng disk", "load_avg": "Load Avg", + "disk_io": "Disk IO", + "network_io": "Network IO", "boot_seconds": "Uptime", "redis_status": "Redis Katayuan", "used_memory": "Kabuuang memorya", diff --git a/packages/umd/flask-state.js b/packages/umd/flask-state.js index 147b611..6c7f8e5 100644 --- a/packages/umd/flask-state.js +++ b/packages/umd/flask-state.js @@ -60,9 +60,9 @@ /* Insert window element */ initFlaskStateContainer() { - let _chart = this.mobile ? `
` - : `
`; - let _content = `

days

Host Status

Memory
CPU
Disk Usage
Load Avg
Uptime

Redis Status

Used Mem
Used Mem Rss
Mem Fragmentation Ratio
Cache Hits Ratio
24h Hits Ratio
Uptime
Connections
` + _chart + `
`; + let _chart = this.mobile ? `
` + : `
`; + let _content = `

days

Host Status

Memory
CPU
Disk Usage
Load Avg
Disk IO
Network IO
Uptime

Redis Status

Used Mem
Used Mem Rss
Mem Fragmentation Ratio
Cache Hits Ratio
24h Hits Ratio
Uptime
Connections
` + _chart + `
`; document.getElementsByTagName('body')[0].insertAdjacentHTML('beforeend', _content); } @@ -111,7 +111,7 @@ const elemDict = { 0: 'fs-info-tab-memory', 1: 'fs-info-tab-cpu', - 2: 'fs-info-tab-disk-usage', + 2: 'fs-info-network-io', 3: 'fs-info-tab-loadavg' }; for (let item of liArr) { @@ -140,6 +140,8 @@ document.getElementById('fs-memory').innerHTML = this.language.memory; document.getElementById('fs-disk-usage').innerHTML = this.language.disk_usage; document.getElementById('fs-load-avg').innerHTML = this.language.load_avg; + document.getElementById('fs-disk-io').innerHTML = this.language.disk_io; + document.getElementById('fs-network-io').innerHTML = this.language.network_io; document.getElementById('fs-boot-seconds').innerHTML = this.language.boot_seconds; document.getElementById('fs-used-memory').innerHTML = this.language.used_memory; document.getElementById('fs-used-memory-rss').innerHTML = this.language.used_memory_rss; @@ -157,10 +159,10 @@ this.consoleCpuChart = echarts.init(document.getElementById('fs-info-cpu-chart'), null, {renderer: 'svg'}); this.consoleMemoryChart = echarts.init(document.getElementById('fs-info-memory-chart'), null, {renderer: 'svg'}); this.consoleLoadavgChart = echarts.init(document.getElementById('fs-info-loadavg-chart'), null, {renderer: 'svg'}); - this.consoleDiskUsageChart = echarts.init(document.getElementById('fs-info-diskusage-chart'), null, {renderer: 'svg'}); + this.consoleDiskUsageChart = echarts.init(document.getElementById('fs-info-networkio-chart'), null, {renderer: 'svg'}); this.cpuOption = MachineStatus.generateChatOption(this.mobile, this.language.cpu || 'CPU', '', this.language.today || 'Today'); this.memoryOption = MachineStatus.generateChatOption(this.mobile, this.language.memory || 'Memory', '', this.language.today || 'Today'); - this.diskUsageOption = MachineStatus.generateChatOption(this.mobile, this.language.disk_usage || 'Disk Usage', '', this.language.today || 'Today'); + this.networkIOOption = MachineStatus.generateChatOption(this.mobile, this.language.network_io || 'Network IO', 'networkIO', this.language.today || 'Today'); this.loadavgOption = MachineStatus.generateChatOption(this.mobile, 'Load Avg', 'loadavg', this.language.minutes || 'min'); } @@ -187,17 +189,8 @@ return; } - const fields = ["ts", "cpu", "memory", "load_avg", "disk_usage"]; const data = response.data; - data.items = data.items.map(item => { - let element = {}; - fields.forEach((field, index) => { - if (field === "ts") return element[field] = SECONDS_TO_MILLISECONDS * item[index]; - element[field] = item[index]; - }); - return element; - }); let currentStatistic = data.currentStatistic; if (Object.keys(currentStatistic).length) { let hostInfoSpan = document.getElementById('fs-host-status').getElementsByClassName('fs-badge-content'); @@ -205,8 +198,10 @@ hostInfoSpan[1].innerHTML = currentStatistic.cpu + '%'; hostInfoSpan[2].innerHTML = currentStatistic.disk_usage + '%'; hostInfoSpan[3].innerHTML = currentStatistic.load_avg[0] + "," + currentStatistic.load_avg[1] + "," + currentStatistic.load_avg[2]; + hostInfoSpan[4].innerHTML = "R " + MachineStatus.getFormatBit(currentStatistic.disk_read) + " | " + "W " + MachineStatus.getFormatBit(currentStatistic.disk_write) + ""; + hostInfoSpan[5].innerHTML = "⬇ " + MachineStatus.getFormatBit(currentStatistic.net_recv) + " | " + "⬆ " + MachineStatus.getFormatBit(currentStatistic.net_sent) + ""; - hostInfoSpan[4].innerHTML = MachineStatus.getFormatSeconds(currentStatistic.boot_seconds || 0, this.language.days, this.language.hours, this.language.minutes, this.language.seconds); + hostInfoSpan[6].innerHTML = MachineStatus.getFormatSeconds(currentStatistic.boot_seconds || 0, this.language.days, this.language.hours, this.language.minutes, this.language.seconds); const machineIndex = ['memory', 'cpu', 'disk_usage', 'load_avg']; machineIndex.forEach(function (item, index) { @@ -269,8 +264,19 @@ } } + const fields = ["ts", "cpu", "memory", "load_avg"]; + data.items = data.items.map(item => { + let element = {}; + fields.forEach((field, index) => { + if (field === "ts") return element[field] = SECONDS_TO_MILLISECONDS * item[index]; + element[field] = item[index]; + }); + return element; + }); data.items.reverse(); + data.io.reverse(); let dataMap = MachineStatus.getChartsData(data.items); + let ioMap = MachineStatus.getIOChartsData(data.io); let tsList = dataMap.ts_list; let cpuList = dataMap.cpu_list; @@ -278,16 +284,18 @@ let loadavgList = dataMap.load_avg_list[0]; let loadavg5MinList = dataMap.load_avg_list[1]; let loadavg15MinList = dataMap.load_avg_list[2]; - let diskUsageList = dataMap.disk_usage_list; + + this.networkIOOption.xAxis.data = ioMap.ts_list; + this.networkIOOption.series[0].data = ioMap.net_recv; + this.networkIOOption.series[1].data = ioMap.net_sent; + this.consoleDiskUsageChart.setOption(this.networkIOOption); this.memoryOption.xAxis.data = tsList; this.cpuOption.xAxis.data = tsList; this.loadavgOption.xAxis.data = tsList; - this.diskUsageOption.xAxis.data = tsList; this.memoryOption.series[0].data = memoryList; this.cpuOption.series[0].data = cpuList; - this.diskUsageOption.series[0].data = diskUsageList; this.loadavgOption.series[0].data = loadavgList; this.loadavgOption.series[1].data = loadavg5MinList; this.loadavgOption.series[2].data = loadavg15MinList; @@ -295,7 +303,6 @@ this.consoleMemoryChart.setOption(this.memoryOption); this.consoleCpuChart.setOption(this.cpuOption); this.consoleLoadavgChart.setOption(this.loadavgOption); - this.consoleDiskUsageChart.setOption(this.diskUsageOption); MachineStatus.resizeChart([this.consoleMemoryChart, this.consoleCpuChart, this.consoleLoadavgChart, this.consoleDiskUsageChart]); }).then(() => { this.consoleMemoryChart.hideLoading(); @@ -349,7 +356,7 @@ /* Initialize echart */ static generateChatOption(isMobile, titleText, tableName = '', lineName = '') { let baseData = { - color: tableName === 'loadavg' ? ['#ffa726', '#42a5f5', '#66bb6a'] : ['#42a5f5'], + color: tableName === 'loadavg' ? ['#ffa726', '#42a5f5', '#66bb6a'] : tableName === 'networkIO' ? ['#ffa726', '#42a5f5'] : ['#42a5f5'], title: { show: !isMobile, text: titleText @@ -358,6 +365,12 @@ trigger: 'axis', formatter: (params) => { let value = echarts.format.formatTime('yyyy-MM-dd hh:mm:ss', new Date(parseInt(params[0].axisValue)), false) + '
'; + if (tableName === 'networkIO') { + for (let i = 0; i < params.length; i++) { + value += (params[i].marker + params[i].seriesName + ': ' + MachineStatus.getFormatBit(params[i].value) + '
') + } + return value; + } for (let i = 0; i < params.length; i++) { value += (params[i].marker + params[i].seriesName + ': ' + params[i].value + (tableName === 'loadavg' ? '' : '%') + '
') @@ -370,7 +383,7 @@ textStyle: { fontSize: 14 }, - show: titleText === 'Load Avg', + show: titleText === 'Load Avg' || tableName === "networkIO", }, grid: { left: '3%', @@ -399,7 +412,13 @@ yAxis: { type: 'value', axisLabel: { - formatter: (value) => value + (titleText === 'Load Avg' ? '' : '%'), + formatter: (value) => { + if (tableName === 'networkIO') { + let result = MachineStatus.getFormatBit(value, 0); + return result.substring(0, result.length - 2); + } + return value + (titleText === 'Load Avg' ? '' : '%') + }, }, }, series: [ @@ -423,13 +442,24 @@ }); }); } + if (tableName === 'networkIO') { + baseData.legend.data = ['recv', 'sent']; + baseData.series = []; + baseData.legend.data.forEach((name) => { + baseData.series.push({ + name: name, + type: 'line', + symbol: 'none', + hoverAnimation: false + }); + }); + } return baseData; } /* Get Echarts data */ static getChartsData(rawData) { let cpuList = []; - let diskUsageList = []; let loadAvgList = []; let loadAvg5minList = []; let loadAvg15minList = []; @@ -438,7 +468,6 @@ for (let i = 0; i < rawData.length; i++) { let item = rawData[i]; cpuList.push(item.cpu); - diskUsageList.push(item.disk_usage); loadAvgList.push(item.load_avg[0]); loadAvg5minList.push(item.load_avg[1]); loadAvg15minList.push(item.load_avg[2]); @@ -446,12 +475,50 @@ tsList.push(item.ts); } return { - 'cpu_list': cpuList, 'disk_usage_list': diskUsageList, - 'load_avg_list': [loadAvgList, loadAvg5minList, loadAvg15minList], + 'cpu_list': cpuList, 'load_avg_list': [loadAvgList, loadAvg5minList, loadAvg15minList], 'memory_list': memoryList, 'ts_list': tsList }; }; + /* Get Echarts data */ + static getIOChartsData(rawData) { + let netRecvList = []; + let netSentList = []; + let tsList = []; + for (let i = 0; i < rawData.length; i++) { + let item = rawData[i]; + tsList.push(item[0] * SECONDS_TO_MILLISECONDS); + netRecvList.push(item[1]); + netSentList.push(item[2]); + } + return { + 'net_recv': netRecvList, 'net_sent': netSentList, 'ts_list': tsList + }; + }; + + /* Get format Bit */ + static getFormatBit(value, fixed = 2) { + let suffix; + let b_value = value / 8; + if (b_value >= 10e11) { + b_value = (b_value / 10e11).toFixed(fixed); + suffix = "TB/s"; + } else if (b_value >= 10e8) { + b_value = (b_value / 10e8).toFixed(fixed); + suffix = "GB/s"; + } else if (b_value >= 10e5) { + b_value = (b_value / 10e5).toFixed(fixed); + suffix = "MB/s"; + } else if (b_value >= 10e2) { + b_value = (b_value / 10e2).toFixed(fixed); + suffix = "KB/s"; + } else { + suffix = "B/s"; + b_value = b_value.toFixed(2); + } + return b_value + ' ' + suffix; + }; + /* Get format time */ static getFormatSeconds(value, days = 'days', hours = 'hours', minutes = 'min', seconds = 'seconds') { let secondTime = parseInt(value); diff --git a/packages/umd/flask-state.min.js b/packages/umd/flask-state.min.js index a0318ee..edbe66e 100644 --- a/packages/umd/flask-state.min.js +++ b/packages/umd/flask-state.min.js @@ -10,4 +10,4 @@ 'use strict'; -!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.flaskState=t():e.flaskState=t()}(window,(function(){return function(e){var t={};function s(a){if(t[a])return t[a].exports;var n=t[a]={i:a,l:!1,exports:{}};return e[a].call(n.exports,n,n.exports,s),n.l=!0,n.exports}return s.m=e,s.c=t,s.d=function(e,t,a){s.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:a})},s.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},s.t=function(e,t){if(1&t&&(e=s(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var a=Object.create(null);if(s.r(a),Object.defineProperty(a,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)s.d(a,n,function(t){return e[t]}.bind(null,n));return a},s.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return s.d(t,"a",t),t},s.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},s.p="",s(s.s=0)}([function(e,t,s){"use strict";const a=85,n=75,i=10,o=5,d=1048576;class r{constructor(e){this.language=e,this.mobile=r.isMobile(),this.index=0,this.initFlaskStateContainer(this.mobile),this.setEventListener(),this.initFlaskStateLanguage(this.language),this.setInitParams(),this.mobile&&this.setTagChangeEventListener(this.consoleCpuChart,this.consoleMemoryChart,this.consoleLoadavgChart,this.consoleDiskUsageChart),window.addEventListener("resize",(()=>{r.resizeChartTimer([this.consoleMemoryChart,this.consoleCpuChart,this.consoleLoadavgChart,this.consoleDiskUsageChart])}))}setFlaskStateData(){document.getElementById("fs-background").style.display="block",document.getElementById("fs-info-container").style.display="block",document.getElementsByTagName("body")[0].style.overflowX="hidden",document.getElementsByTagName("body")[0].style.overflowY="hidden",document.getElementById("fs-select-days").value="1",this.setCharts("1")}initFlaskStateContainer(){let e='

days

Host Status

Memory
CPU
Disk Usage
Load Avg
Uptime

Redis Status

Used Mem
Used Mem Rss
Mem Fragmentation Ratio
Cache Hits Ratio
24h Hits Ratio
Uptime
Connections
'+(this.mobile?'
':"
")+"
";document.getElementsByTagName("body")[0].insertAdjacentHTML("beforeend",e)}setEventListener(){if(window.addEventListener){document.getElementById("fs-info-close").addEventListener("click",(function(){document.getElementById("fs-background").style.display="none",document.getElementById("fs-info-container").style.display="none",document.getElementsByTagName("body")[0].style.overflowX="auto",document.getElementsByTagName("body")[0].style.overflowY="auto",document.getElementById("fs-state-circular")&&document.getElementById("fs-state-circular").classList.remove("fs-circular-out")})),document.getElementById("fs-background").addEventListener("click",(function(e){"fs-background"===String(e.target.id)&&(document.getElementById("fs-background").style.display="none",document.getElementById("fs-info-container").style.display="none",document.getElementsByTagName("body")[0].style.overflowX="auto",document.getElementsByTagName("body")[0].style.overflowY="auto",document.getElementById("fs-state-circular")&&document.getElementById("fs-state-circular").classList.remove("fs-circular-out"))}));let e=document.getElementById("fs-select-days");e.addEventListener("change",(()=>{this.setCharts(e.value)}))}}setTagChangeEventListener(...e){if(document.getElementById("fs-info-tab")){let t=document.getElementById("fs-info-tab").getElementsByTagName("li"),s=document.getElementById("fs-info-tab-memory"),a=t[0],n=0;const i={0:"fs-info-tab-memory",1:"fs-info-tab-cpu",2:"fs-info-tab-disk-usage",3:"fs-info-tab-loadavg"};for(let o of t){let t=document.getElementById(i[n]);o.children[0].addEventListener("click",(()=>{o.classList.add("active"),a.classList.remove("active"),s.classList.remove("fs-show"),t.classList.add("fs-show"),r.resizeChart(e),s=t,a=o})),n++}}}initFlaskStateLanguage(){0!==Object.keys(this.language).length&&(document.getElementById("fs-host-status-title").innerHTML=this.language.host_status,document.getElementById("fs-redis-status-title").innerHTML=this.language.redis_status,document.getElementById("fs-cpu").innerHTML=this.language.cpu,document.getElementById("fs-memory").innerHTML=this.language.memory,document.getElementById("fs-disk-usage").innerHTML=this.language.disk_usage,document.getElementById("fs-load-avg").innerHTML=this.language.load_avg,document.getElementById("fs-boot-seconds").innerHTML=this.language.boot_seconds,document.getElementById("fs-used-memory").innerHTML=this.language.used_memory,document.getElementById("fs-used-memory-rss").innerHTML=this.language.used_memory_rss,document.getElementById("fs-mem-fragmentation-ratio").innerHTML=this.language.mem_fragmentation_ratio,document.getElementById("fs-hits-ratio").innerHTML=this.language.hits_ratio,document.getElementById("fs-delta-hits-ratio").innerHTML=this.language.delta_hits_ratio,document.getElementById("fs-uptime-in-seconds").innerHTML=this.language.uptime_in_seconds,document.getElementById("fs-connected-clients").innerHTML=this.language.connected_clients,document.getElementById("fs-days").innerHTML=this.language.days)}setInitParams(){this.consoleCpuChart=echarts.init(document.getElementById("fs-info-cpu-chart"),null,{renderer:"svg"}),this.consoleMemoryChart=echarts.init(document.getElementById("fs-info-memory-chart"),null,{renderer:"svg"}),this.consoleLoadavgChart=echarts.init(document.getElementById("fs-info-loadavg-chart"),null,{renderer:"svg"}),this.consoleDiskUsageChart=echarts.init(document.getElementById("fs-info-diskusage-chart"),null,{renderer:"svg"}),this.cpuOption=r.generateChatOption(this.mobile,this.language.cpu||"CPU","",this.language.today||"Today"),this.memoryOption=r.generateChatOption(this.mobile,this.language.memory||"Memory","",this.language.today||"Today"),this.diskUsageOption=r.generateChatOption(this.mobile,this.language.disk_usage||"Disk Usage","",this.language.today||"Today"),this.loadavgOption=r.generateChatOption(this.mobile,"Load Avg","loadavg",this.language.minutes||"min")}setCharts(e){this.consoleCpuChart.showLoading(),this.consoleMemoryChart.showLoading(),this.consoleLoadavgChart.showLoading(),this.consoleDiskUsageChart.showLoading(),fetch("/v0/state/hoststatus",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json;charset=UTF-8"},body:JSON.stringify({timeQuantum:e})}).then((e=>{e.ok&&e.json().then((e=>{if(200!==e.code)return;const t=["ts","cpu","memory","load_avg","disk_usage"],s=e.data;s.items=s.items.map((e=>{let s={};return t.forEach(((t,a)=>{if("ts"===t)return s[t]=1e3*e[a];s[t]=e[a]})),s}));let l=s.currentStatistic;if(Object.keys(l).length){let e=document.getElementById("fs-host-status").getElementsByClassName("fs-badge-content");e[0].innerHTML=l.memory+"%",e[1].innerHTML=l.cpu+"%",e[2].innerHTML=l.disk_usage+"%",e[3].innerHTML=l.load_avg[0]+","+l.load_avg[1]+","+l.load_avg[2],e[4].innerHTML=r.getFormatSeconds(l.boot_seconds||0,this.language.days,this.language.hours,this.language.minutes,this.language.seconds);["memory","cpu","disk_usage","load_avg"].forEach((function(t,s){let d;if("load_avg"===t){let e=(l.load_avg[0]+l.load_avg[1]+l.load_avg[2])/3;d=e>=o?e>=i?"background-red":"background-orange":"background-green"}else d=l[t]>=n?l.memory>=a?"background-red":"background-orange":"background-green";let r=e[s].classList;r.remove("background-green","background-orange","background-red"),r.add(d)}));let t=document.getElementById("fs-redis-status").getElementsByClassName("fs-badge-content"),s=["used_memory","used_memory_rss","mem_fragmentation_ratio","hits_ratio","delta_hits_ratio","uptime_in_seconds","connected_clients"],c=!0;for(let e of s)if(l[e]){c=!1;break}c?(document.getElementById("fs-redis-status-title").innerHTML="",document.getElementById("fs-redis-status-title").style.marginTop="0",document.getElementById("fs-redis-status").style.display="none"):s.forEach(((e,s)=>{switch(e){case"used_memory":case"used_memory_rss":t[s].innerHTML=Math.ceil(l[e]/d)+" M";break;case"mem_fragmentation_ratio":let a=l[e];if(t[s].innerHTML=l[e],null!=a&&a>1){let e=t[s].classList;e.remove("background-green"),e.add("background-red")}break;case"hits_ratio":case"delta_hits_ratio":t[s].innerHTML=l[e]+"%";break;case"uptime_in_seconds":t[s].innerHTML=r.getFormatSeconds(l[e],this.language.days,this.language.hours,this.language.minutes,this.language.seconds);break;case"connected_clients":t[s].innerHTML=l[e]}}))}s.items.reverse();let c=r.getChartsData(s.items),g=c.ts_list,u=c.cpu_list,m=c.memory_list,f=c.load_avg_list[0],h=c.load_avg_list[1],p=c.load_avg_list[2],y=c.disk_usage_list;this.memoryOption.xAxis.data=g,this.cpuOption.xAxis.data=g,this.loadavgOption.xAxis.data=g,this.diskUsageOption.xAxis.data=g,this.memoryOption.series[0].data=m,this.cpuOption.series[0].data=u,this.diskUsageOption.series[0].data=y,this.loadavgOption.series[0].data=f,this.loadavgOption.series[1].data=h,this.loadavgOption.series[2].data=p,this.consoleMemoryChart.setOption(this.memoryOption),this.consoleCpuChart.setOption(this.cpuOption),this.consoleLoadavgChart.setOption(this.loadavgOption),this.consoleDiskUsageChart.setOption(this.diskUsageOption),r.resizeChart([this.consoleMemoryChart,this.consoleCpuChart,this.consoleLoadavgChart,this.consoleDiskUsageChart])})).then((()=>{this.consoleMemoryChart.hideLoading(),this.consoleCpuChart.hideLoading(),this.consoleLoadavgChart.hideLoading(),this.consoleDiskUsageChart.hideLoading()}))})).catch((function(e){}))}static isMobile(){let e=navigator.userAgent,t={trident:e.indexOf("Trident")>-1,presto:e.indexOf("Presto")>-1,webKit:e.indexOf("AppleWebKit")>-1,gecko:e.indexOf("Gecko")>-1&&-1===e.indexOf("KHTML"),mobile:e.match(/AppleWebKit.*Mobile.*/)&&!0,ios:e.match(/\(i[^;]+;( U;)? CPU.Mac OS X/)&&!0,android:e.indexOf("Android")>-1||e.indexOf("Linux")>-1,iPhone:e.indexOf("iPhone")>-1,iPad:e.indexOf("iPad")>-1,webApp:-1===e.indexOf("Safari"),wechat:e.indexOf("MicroMessenger")>-1,qq:e.match(/\sQQ/i)&&" qq"===e.match(/\sQQ/i)[0]};return t.iPhone||t.iPad||t.webApp||t.wechat||t.qq||t.ios||t.mobile||!1}static resizeChartTimer(e,t){clearTimeout(this.clearId),this.clearId=setTimeout((function(){r.resizeChart(e)}),t||200)}static resizeChart(e){e.forEach((e=>e.resize()))}static generateChatOption(e,t,s="",a=""){let n={color:"loadavg"===s?["#ffa726","#42a5f5","#66bb6a"]:["#42a5f5"],title:{show:!e,text:t},tooltip:{trigger:"axis",formatter:e=>{let t=echarts.format.formatTime("yyyy-MM-dd hh:mm:ss",new Date(parseInt(e[0].axisValue)),!1)+"
";for(let a=0;a";return t}},legend:{data:[a],textStyle:{fontSize:14},show:"Load Avg"===t},grid:{left:"3%",right:"4%",bottom:"3%",top:e?30:60,containLabel:!0},toolbox:{show:!e,feature:{saveAsImage:{title:" "}}},xAxis:{type:"category",boundaryGap:!1,axisLabel:{formatter:function(e){return echarts.format.formatTime("hh:mm",new Date(parseInt(e)),!1)}}},yAxis:{type:"value",axisLabel:{formatter:e=>e+("Load Avg"===t?"":"%")}},series:[{name:a,type:"line",symbol:"none",hoverAnimation:!1}]};return"loadavg"===s&&(n.legend.data=["1 "+a,"5 "+a,"15 "+a],n.series=[],n.legend.data.forEach((e=>{n.series.push({name:e,type:"line",symbol:"none",hoverAnimation:!1})}))),n}static getChartsData(e){let t=[],s=[],a=[],n=[],i=[],o=[],d=[];for(let r=0;r=60?(o=i/60,o>=60&&(d=o/60,o%=60),d>=24&&(r=d/24,d%=24)):l=i+n,o>0&&(l=Math.floor(o)+" "+a),d>0&&(l=Math.floor(d)+" "+s),r>0&&(l=Math.floor(r)+" "+t),l}}const l=function(){let e=null;return function(t){return e||(e=new r(t))}}();t.init=function(e){let t=null,s={};if(null!==e&&"object"==typeof e&&(t=e.hasOwnProperty("dom")?e.dom:null,s=e.hasOwnProperty("lang")&&e.lang.hasOwnProperty("language")?e.lang:{}),t instanceof HTMLElement){if(t.getAttribute("flaskState"))return;t.setAttribute("flaskState","true"),t.addEventListener("click",(()=>l(s).setFlaskStateData()))}else{if(document.getElementById("fs-state-circular"))return;let e="
";document.getElementsByTagName("body")[0].insertAdjacentHTML("beforeend",e);let t,n=document.getElementById("fs-state-circular");function a(e){n.style.top=e.clientY-t+300+"px"}n.onclick=function(){this.classList.add("fs-circular-out"),l(s).setFlaskStateData()},n.onmousedown=function(e){t=t||e.clientY,document.addEventListener("mousemove",a)},document.onmouseup=function(){document.removeEventListener("mousemove",a);const e=parseInt(n.style.top);n.classList.add("fs-circular-animation"),n.style.top=Math.min(Math.max(e,50),window.screen.height-200)+"px",setTimeout((()=>n.classList.remove("fs-circular-animation")),500)}}}}])})); \ No newline at end of file +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.flaskState=t():e.flaskState=t()}(window,(function(){return function(e){var t={};function s(a){if(t[a])return t[a].exports;var n=t[a]={i:a,l:!1,exports:{}};return e[a].call(n.exports,n,n.exports,s),n.l=!0,n.exports}return s.m=e,s.c=t,s.d=function(e,t,a){s.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:a})},s.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},s.t=function(e,t){if(1&t&&(e=s(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var a=Object.create(null);if(s.r(a),Object.defineProperty(a,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var n in e)s.d(a,n,function(t){return e[t]}.bind(null,n));return a},s.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return s.d(t,"a",t),t},s.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},s.p="",s(s.s=0)}([function(e,t,s){"use strict";const a=85,n=75,i=10,o=5,r=1048576;class d{constructor(e){this.language=e,this.mobile=d.isMobile(),this.index=0,this.initFlaskStateContainer(this.mobile),this.setEventListener(),this.initFlaskStateLanguage(this.language),this.setInitParams(),this.mobile&&this.setTagChangeEventListener(this.consoleCpuChart,this.consoleMemoryChart,this.consoleLoadavgChart,this.consoleDiskUsageChart),window.addEventListener("resize",(()=>{d.resizeChartTimer([this.consoleMemoryChart,this.consoleCpuChart,this.consoleLoadavgChart,this.consoleDiskUsageChart])}))}setFlaskStateData(){document.getElementById("fs-background").style.display="block",document.getElementById("fs-info-container").style.display="block",document.getElementsByTagName("body")[0].style.overflowX="hidden",document.getElementsByTagName("body")[0].style.overflowY="hidden",document.getElementById("fs-select-days").value="1",this.setCharts("1")}initFlaskStateContainer(){let e='

days

Host Status

Memory
CPU
Disk Usage
Load Avg
Disk IO
Network IO
Uptime

Redis Status

Used Mem
Used Mem Rss
Mem Fragmentation Ratio
Cache Hits Ratio
24h Hits Ratio
Uptime
Connections
'+(this.mobile?'
':"
")+"
";document.getElementsByTagName("body")[0].insertAdjacentHTML("beforeend",e)}setEventListener(){if(window.addEventListener){document.getElementById("fs-info-close").addEventListener("click",(function(){document.getElementById("fs-background").style.display="none",document.getElementById("fs-info-container").style.display="none",document.getElementsByTagName("body")[0].style.overflowX="auto",document.getElementsByTagName("body")[0].style.overflowY="auto",document.getElementById("fs-state-circular")&&document.getElementById("fs-state-circular").classList.remove("fs-circular-out")})),document.getElementById("fs-background").addEventListener("click",(function(e){"fs-background"===String(e.target.id)&&(document.getElementById("fs-background").style.display="none",document.getElementById("fs-info-container").style.display="none",document.getElementsByTagName("body")[0].style.overflowX="auto",document.getElementsByTagName("body")[0].style.overflowY="auto",document.getElementById("fs-state-circular")&&document.getElementById("fs-state-circular").classList.remove("fs-circular-out"))}));let e=document.getElementById("fs-select-days");e.addEventListener("change",(()=>{this.setCharts(e.value)}))}}setTagChangeEventListener(...e){if(document.getElementById("fs-info-tab")){let t=document.getElementById("fs-info-tab").getElementsByTagName("li"),s=document.getElementById("fs-info-tab-memory"),a=t[0],n=0;const i={0:"fs-info-tab-memory",1:"fs-info-tab-cpu",2:"fs-info-network-io",3:"fs-info-tab-loadavg"};for(let o of t){let t=document.getElementById(i[n]);o.children[0].addEventListener("click",(()=>{o.classList.add("active"),a.classList.remove("active"),s.classList.remove("fs-show"),t.classList.add("fs-show"),d.resizeChart(e),s=t,a=o})),n++}}}initFlaskStateLanguage(){0!==Object.keys(this.language).length&&(document.getElementById("fs-host-status-title").innerHTML=this.language.host_status,document.getElementById("fs-redis-status-title").innerHTML=this.language.redis_status,document.getElementById("fs-cpu").innerHTML=this.language.cpu,document.getElementById("fs-memory").innerHTML=this.language.memory,document.getElementById("fs-disk-usage").innerHTML=this.language.disk_usage,document.getElementById("fs-load-avg").innerHTML=this.language.load_avg,document.getElementById("fs-disk-io").innerHTML=this.language.disk_io,document.getElementById("fs-network-io").innerHTML=this.language.network_io,document.getElementById("fs-boot-seconds").innerHTML=this.language.boot_seconds,document.getElementById("fs-used-memory").innerHTML=this.language.used_memory,document.getElementById("fs-used-memory-rss").innerHTML=this.language.used_memory_rss,document.getElementById("fs-mem-fragmentation-ratio").innerHTML=this.language.mem_fragmentation_ratio,document.getElementById("fs-hits-ratio").innerHTML=this.language.hits_ratio,document.getElementById("fs-delta-hits-ratio").innerHTML=this.language.delta_hits_ratio,document.getElementById("fs-uptime-in-seconds").innerHTML=this.language.uptime_in_seconds,document.getElementById("fs-connected-clients").innerHTML=this.language.connected_clients,document.getElementById("fs-days").innerHTML=this.language.days)}setInitParams(){this.consoleCpuChart=echarts.init(document.getElementById("fs-info-cpu-chart"),null,{renderer:"svg"}),this.consoleMemoryChart=echarts.init(document.getElementById("fs-info-memory-chart"),null,{renderer:"svg"}),this.consoleLoadavgChart=echarts.init(document.getElementById("fs-info-loadavg-chart"),null,{renderer:"svg"}),this.consoleDiskUsageChart=echarts.init(document.getElementById("fs-info-networkio-chart"),null,{renderer:"svg"}),this.cpuOption=d.generateChatOption(this.mobile,this.language.cpu||"CPU","",this.language.today||"Today"),this.memoryOption=d.generateChatOption(this.mobile,this.language.memory||"Memory","",this.language.today||"Today"),this.networkIOOption=d.generateChatOption(this.mobile,this.language.network_io||"Network IO","networkIO",this.language.today||"Today"),this.loadavgOption=d.generateChatOption(this.mobile,"Load Avg","loadavg",this.language.minutes||"min")}setCharts(e){this.consoleCpuChart.showLoading(),this.consoleMemoryChart.showLoading(),this.consoleLoadavgChart.showLoading(),this.consoleDiskUsageChart.showLoading(),fetch("/v0/state/hoststatus",{method:"POST",credentials:"include",headers:{"Content-Type":"application/json;charset=UTF-8"},body:JSON.stringify({timeQuantum:e})}).then((e=>{e.ok&&e.json().then((e=>{if(200!==e.code)return;const t=e.data;let s=t.currentStatistic;if(Object.keys(s).length){let e=document.getElementById("fs-host-status").getElementsByClassName("fs-badge-content");e[0].innerHTML=s.memory+"%",e[1].innerHTML=s.cpu+"%",e[2].innerHTML=s.disk_usage+"%",e[3].innerHTML=s.load_avg[0]+","+s.load_avg[1]+","+s.load_avg[2],e[4].innerHTML="R "+d.getFormatBit(s.disk_read)+" | W "+d.getFormatBit(s.disk_write)+"",e[5].innerHTML="⬇ "+d.getFormatBit(s.net_recv)+" | ⬆ "+d.getFormatBit(s.net_sent)+"",e[6].innerHTML=d.getFormatSeconds(s.boot_seconds||0,this.language.days,this.language.hours,this.language.minutes,this.language.seconds);["memory","cpu","disk_usage","load_avg"].forEach((function(t,r){let d;if("load_avg"===t){let e=(s.load_avg[0]+s.load_avg[1]+s.load_avg[2])/3;d=e>=o?e>=i?"background-red":"background-orange":"background-green"}else d=s[t]>=n?s.memory>=a?"background-red":"background-orange":"background-green";let l=e[r].classList;l.remove("background-green","background-orange","background-red"),l.add(d)}));let t=document.getElementById("fs-redis-status").getElementsByClassName("fs-badge-content"),l=["used_memory","used_memory_rss","mem_fragmentation_ratio","hits_ratio","delta_hits_ratio","uptime_in_seconds","connected_clients"],c=!0;for(let e of l)if(s[e]){c=!1;break}c?(document.getElementById("fs-redis-status-title").innerHTML="",document.getElementById("fs-redis-status-title").style.marginTop="0",document.getElementById("fs-redis-status").style.display="none"):l.forEach(((e,a)=>{switch(e){case"used_memory":case"used_memory_rss":t[a].innerHTML=Math.ceil(s[e]/r)+" M";break;case"mem_fragmentation_ratio":let n=s[e];if(t[a].innerHTML=s[e],null!=n&&n>1){let e=t[a].classList;e.remove("background-green"),e.add("background-red")}break;case"hits_ratio":case"delta_hits_ratio":t[a].innerHTML=s[e]+"%";break;case"uptime_in_seconds":t[a].innerHTML=d.getFormatSeconds(s[e],this.language.days,this.language.hours,this.language.minutes,this.language.seconds);break;case"connected_clients":t[a].innerHTML=s[e]}}))}const l=["ts","cpu","memory","load_avg"];t.items=t.items.map((e=>{let t={};return l.forEach(((s,a)=>{if("ts"===s)return t[s]=1e3*e[a];t[s]=e[a]})),t})),t.items.reverse(),t.io.reverse();let c=d.getChartsData(t.items),g=d.getIOChartsData(t.io),u=c.ts_list,m=c.cpu_list,f=c.memory_list,h=c.load_avg_list[0],p=c.load_avg_list[1],y=c.load_avg_list[2];this.networkIOOption.xAxis.data=g.ts_list,this.networkIOOption.series[0].data=g.net_recv,this.networkIOOption.series[1].data=g.net_sent,this.consoleDiskUsageChart.setOption(this.networkIOOption),this.memoryOption.xAxis.data=u,this.cpuOption.xAxis.data=u,this.loadavgOption.xAxis.data=u,this.memoryOption.series[0].data=f,this.cpuOption.series[0].data=m,this.loadavgOption.series[0].data=h,this.loadavgOption.series[1].data=p,this.loadavgOption.series[2].data=y,this.consoleMemoryChart.setOption(this.memoryOption),this.consoleCpuChart.setOption(this.cpuOption),this.consoleLoadavgChart.setOption(this.loadavgOption),d.resizeChart([this.consoleMemoryChart,this.consoleCpuChart,this.consoleLoadavgChart,this.consoleDiskUsageChart])})).then((()=>{this.consoleMemoryChart.hideLoading(),this.consoleCpuChart.hideLoading(),this.consoleLoadavgChart.hideLoading(),this.consoleDiskUsageChart.hideLoading()}))})).catch((function(e){}))}static isMobile(){let e=navigator.userAgent,t={trident:e.indexOf("Trident")>-1,presto:e.indexOf("Presto")>-1,webKit:e.indexOf("AppleWebKit")>-1,gecko:e.indexOf("Gecko")>-1&&-1===e.indexOf("KHTML"),mobile:e.match(/AppleWebKit.*Mobile.*/)&&!0,ios:e.match(/\(i[^;]+;( U;)? CPU.Mac OS X/)&&!0,android:e.indexOf("Android")>-1||e.indexOf("Linux")>-1,iPhone:e.indexOf("iPhone")>-1,iPad:e.indexOf("iPad")>-1,webApp:-1===e.indexOf("Safari"),wechat:e.indexOf("MicroMessenger")>-1,qq:e.match(/\sQQ/i)&&" qq"===e.match(/\sQQ/i)[0]};return t.iPhone||t.iPad||t.webApp||t.wechat||t.qq||t.ios||t.mobile||!1}static resizeChartTimer(e,t){clearTimeout(this.clearId),this.clearId=setTimeout((function(){d.resizeChart(e)}),t||200)}static resizeChart(e){e.forEach((e=>e.resize()))}static generateChatOption(e,t,s="",a=""){let n={color:"loadavg"===s?["#ffa726","#42a5f5","#66bb6a"]:"networkIO"===s?["#ffa726","#42a5f5"]:["#42a5f5"],title:{show:!e,text:t},tooltip:{trigger:"axis",formatter:e=>{let t=echarts.format.formatTime("yyyy-MM-dd hh:mm:ss",new Date(parseInt(e[0].axisValue)),!1)+"
";if("networkIO"===s){for(let s=0;s";return t}for(let a=0;a";return t}},legend:{data:[a],textStyle:{fontSize:14},show:"Load Avg"===t||"networkIO"===s},grid:{left:"3%",right:"4%",bottom:"3%",top:e?30:60,containLabel:!0},toolbox:{show:!e,feature:{saveAsImage:{title:" "}}},xAxis:{type:"category",boundaryGap:!1,axisLabel:{formatter:function(e){return echarts.format.formatTime("hh:mm",new Date(parseInt(e)),!1)}}},yAxis:{type:"value",axisLabel:{formatter:e=>{if("networkIO"===s){let t=d.getFormatBit(e,0);return t.substring(0,t.length-2)}return e+("Load Avg"===t?"":"%")}}},series:[{name:a,type:"line",symbol:"none",hoverAnimation:!1}]};return"loadavg"===s&&(n.legend.data=["1 "+a,"5 "+a,"15 "+a],n.series=[],n.legend.data.forEach((e=>{n.series.push({name:e,type:"line",symbol:"none",hoverAnimation:!1})}))),"networkIO"===s&&(n.legend.data=["recv","sent"],n.series=[],n.legend.data.forEach((e=>{n.series.push({name:e,type:"line",symbol:"none",hoverAnimation:!1})}))),n}static getChartsData(e){let t=[],s=[],a=[],n=[],i=[],o=[];for(let r=0;r=1e12?(a=(a/1e12).toFixed(t),s="TB/s"):a>=1e9?(a=(a/1e9).toFixed(t),s="GB/s"):a>=1e6?(a=(a/1e6).toFixed(t),s="MB/s"):a>=1e3?(a=(a/1e3).toFixed(t),s="KB/s"):(s="B/s",a=a.toFixed(2)),a+" "+s}static getFormatSeconds(e,t="days",s="hours",a="min",n="seconds"){let i=parseInt(e),o=0,r=0,d=0,l="";return i>=60?(o=i/60,o>=60&&(r=o/60,o%=60),r>=24&&(d=r/24,r%=24)):l=i+n,o>0&&(l=Math.floor(o)+" "+a),r>0&&(l=Math.floor(r)+" "+s),d>0&&(l=Math.floor(d)+" "+t),l}}const l=function(){let e=null;return function(t){return e||(e=new d(t))}}();t.init=function(e){let t=null,s={};if(null!==e&&"object"==typeof e&&(t=e.hasOwnProperty("dom")?e.dom:null,s=e.hasOwnProperty("lang")&&e.lang.hasOwnProperty("language")?e.lang:{}),t instanceof HTMLElement){if(t.getAttribute("flaskState"))return;t.setAttribute("flaskState","true"),t.addEventListener("click",(()=>l(s).setFlaskStateData()))}else{if(document.getElementById("fs-state-circular"))return;let e="
";document.getElementsByTagName("body")[0].insertAdjacentHTML("beforeend",e);let t,n=document.getElementById("fs-state-circular");function a(e){n.style.top=e.clientY-t+300+"px"}n.onclick=function(){this.classList.add("fs-circular-out"),l(s).setFlaskStateData()},n.onmousedown=function(e){t=t||e.clientY,document.addEventListener("mousemove",a)},document.onmouseup=function(){document.removeEventListener("mousemove",a);const e=parseInt(n.style.top);n.classList.add("fs-circular-animation"),n.style.top=Math.min(Math.max(e,50),window.screen.height-200)+"px",setTimeout((()=>n.classList.remove("fs-circular-animation")),500)}}}}])})); \ No newline at end of file diff --git a/packages/umd/fr.js b/packages/umd/fr.js index 3841c43..28c2a67 100644 --- a/packages/umd/fr.js +++ b/packages/umd/fr.js @@ -6,6 +6,8 @@ const fr = { "memory": "Mémoire", "disk_usage": "Utilisation du disque", "load_avg": "Load Avg", + "disk_io": "Disque IO", + "network_io": "Réseau IO", "boot_seconds": "Temps de démarrage", "redis_status": "Statut Redis", "used_memory": "Mémoire totale", diff --git a/packages/umd/hi.js b/packages/umd/hi.js index 8fa9e7f..ad5db07 100644 --- a/packages/umd/hi.js +++ b/packages/umd/hi.js @@ -6,6 +6,8 @@ const hi = { "memory": "राम", "disk_usage": "डिस्क उपयोग", "load_avg": "औसत भार", + "disk_io": "डिस्क IO", + "network_io": "नेटवर्क आईओ", "boot_seconds": "अपटाइम", "redis_status": "रेडिस राज्य", "used_memory": "कुल मेमोरी आवंटित", diff --git a/packages/umd/ja.js b/packages/umd/ja.js index c2cd8aa..2fcef69 100644 --- a/packages/umd/ja.js +++ b/packages/umd/ja.js @@ -6,6 +6,8 @@ const ja = { "memory": "メモリ", "disk_usage": "ディスクの使用率", "load_avg": "平均読み込み時間", + "disk_io": "ディスクIO", + "network_io": "ネットワークIO", "boot_seconds": "起動時間の秒", "redis_status": "Redisの状態", "used_memory": "使用メモリ", diff --git a/packages/umd/vi.js b/packages/umd/vi.js index e27c878..8eff1fa 100644 --- a/packages/umd/vi.js +++ b/packages/umd/vi.js @@ -6,6 +6,8 @@ const vi = { "memory": "Ký ức", "disk_usage": "Sử dụng đĩa", "load_avg": "Tải trung bình", + "disk_io": "IO đĩa", + "network_io": "Mạng IO", "boot_seconds": "Gian hoạt", "redis_status": "Trạng thái Redis", "used_memory": "Phân bổ bộ nhớ", diff --git a/packages/umd/zh.js b/packages/umd/zh.js index abc0498..7895c66 100644 --- a/packages/umd/zh.js +++ b/packages/umd/zh.js @@ -6,6 +6,8 @@ const zh = { "memory": "内存", "disk_usage": "磁盘使用率", "load_avg": "Load Avg", + "disk_io": "磁盘IO", + "network_io": "网络IO", "boot_seconds": "启动时长", "redis_status": "Redis状态", "used_memory": "分配总内存", diff --git a/src/flask_state/__about__.py b/src/flask_state/__about__.py index 382021f..9e604c0 100644 --- a/src/flask_state/__about__.py +++ b/src/flask_state/__about__.py @@ -1 +1 @@ -__version__ = "1.0.6" +__version__ = "1.0.7" diff --git a/src/flask_state/conf/config.py b/src/flask_state/conf/config.py index ed55033..6f4687c 100644 --- a/src/flask_state/conf/config.py +++ b/src/flask_state/conf/config.py @@ -1,7 +1,7 @@ class Config: """ Config """ - REDIS_CONNECT_TIMEOUT = 1 # Redis socket connection timeout + REDIS_CONNECT_TIMEOUT = 3 # Redis socket connection timeout REDIS_TIMEOUT = 5 # Redis socket timeout CPU_PERCENT_INTERVAL = 0 # Time interval to calculate CPU utilization using psutil DEFAULT_BIND_SQLITE = "flask_state_sqlite" # Default binding database URL key @@ -10,3 +10,4 @@ class Config: DEFAULT_WINDOWS_LOAD_AVG = "0, 0, 0" # Windows system cannot calculate load AVG MAX_RETURN_RECORDS = 480 # Return the maximum number of records ABANDON_THRESHOLD = 60 # Maximum timeout time of scheduled tasks + ABANDON_IO_THRESHOLD = 10 # Maximum timeout time of scheduled tasks diff --git a/src/flask_state/controller/manager.py b/src/flask_state/controller/manager.py index 99cabb3..2cf4559 100644 --- a/src/flask_state/controller/manager.py +++ b/src/flask_state/controller/manager.py @@ -12,7 +12,11 @@ from ..exceptions.log_msg import ErrorMsg, InfoMsg from ..models import model_init_app from ..services import redis_conn -from ..services.host_status import query_flask_state_host, record_flask_state_host +from ..services.host_status import ( + query_flask_state_host, + record_flask_state_host, + record_flask_state_io_host, +) from ..utils.auth import auth_method, auth_user from ..utils.constants import HttpMethod, HTTPStatus from ..utils.file_lock import Lock @@ -47,15 +51,22 @@ def init_app(app, interval=60, log_instance=None): ) # Timing recorder - t = threading.Thread( + t_host = threading.Thread( target=record_timer, args=( app, interval, ), ) - t.setDaemon(True) - t.start() + t_host.setDaemon(True) + t_host.start() + + t_io = threading.Thread( + target=record_io_timer, + args=(app, 10), + ) + t_io.setDaemon(True) + t_io.start() def init_redis(app): @@ -79,7 +90,7 @@ def init_db(app): def record_timer(app, interval): - app.lock_flask_state = Lock.get_file_lock() + app.lock_flask_state = Lock.get_file_lock("host") with app.app_context(): try: current_app.lock_flask_state.acquire() @@ -102,6 +113,29 @@ def record_timer(app, interval): raise e +def record_io_timer(app, interval): + app.io_lock_flask_state = Lock.get_file_lock("io") + with app.app_context(): + try: + current_app.io_lock_flask_state.acquire() + + s = sched.scheduler(time.time, time.sleep) + in_time = time.time() + target_time = int(int((time.time()) / 60 + 1) * 60) + time.sleep(60 - in_time % 60) + record_flask_state_io_host(interval, target_time) + while True: + target_time += interval + now_time = time.time() + s.enter(target_time - now_time, 1, record_flask_state_io_host, (interval, target_time)) + s.run() + except BlockingIOError: + pass + except Exception as e: + current_app.lock_flask_state.release() + raise e + + @auth_user @auth_method @json_required diff --git a/src/flask_state/dao/host_status.py b/src/flask_state/dao/host_status.py index 1a66321..fa36e18 100644 --- a/src/flask_state/dao/host_status.py +++ b/src/flask_state/dao/host_status.py @@ -1,6 +1,7 @@ from ..exceptions.log_msg import InfoMsg from ..models import db from ..models.flask_state_host import FlaskStateHost +from ..models.flask_state_io import FlaskStateIO from ..utils.date import get_current_ms, get_query_ms from ..utils.logger import logger @@ -20,7 +21,6 @@ def retrieve_host_status(days) -> list: FlaskStateHost.cpu, FlaskStateHost.memory, FlaskStateHost.load_avg, - FlaskStateHost.disk_usage, FlaskStateHost.ts, ) .filter(FlaskStateHost.ts > target_time) @@ -30,6 +30,27 @@ def retrieve_host_status(days) -> list: return result +def retrieve_io_status(days) -> list: + """ + Query the status within the time period and flashback + + """ + target_time = get_current_ms() - get_query_ms(days) + result = ( + FlaskStateHost.query.with_entities( + FlaskStateIO.disk_read, + FlaskStateIO.disk_write, + FlaskStateIO.net_recv, + FlaskStateIO.net_sent, + FlaskStateIO.ts, + ) + .filter(FlaskStateIO.ts > target_time) + .order_by(FlaskStateIO.ts.desc()) + .all() + ) + return result + + def retrieve_latest_host_status() -> dict: """ Query the latest status @@ -40,10 +61,19 @@ def retrieve_latest_host_status() -> dict: return result +def retrieve_latest_io_status() -> dict: + """ + Query the latest io status + + """ + result = FlaskStateIO.query.with_entities(FlaskStateIO.__table__).order_by(FlaskStateIO.ts.desc()).first() + result = result._asdict() if result else {} + return result + + def create_host_status(kwargs): """ Create a new record - """ try: flask_state_host = FlaskStateHost(**kwargs) @@ -55,6 +85,19 @@ def create_host_status(kwargs): raise e +def create_host_io(kwargs): + """ + Create a new io record + """ + try: + flask_state_io = FlaskStateIO(**kwargs) + db.session.add(flask_state_io) + db.session.commit() + except Exception as e: + db.session.rollback() + raise e + + def delete_thirty_days_status(): """ Delete thirty days records ago @@ -71,6 +114,22 @@ def delete_thirty_days_status(): raise e +def delete_thirty_days_io_status(): + """ + Delete thirty days io records ago + + """ + try: + target_time = get_current_ms() - get_query_ms(THIRTY_DAT) + result = FlaskStateIO.query.filter(FlaskStateIO.ts < target_time).delete(synchronize_session=False) + if result: + db.session.commit() + logger.info(InfoMsg.DELETE_SUCCESS.get_msg()) + except Exception as e: + db.session.rollback() + raise e + + def retrieve_host_status_yesterday() -> FlaskStateHost: """ Returns the closest time status between yesterday and the current diff --git a/src/flask_state/exceptions/log_msg.py b/src/flask_state/exceptions/log_msg.py index 218d2e9..2fa86d1 100644 --- a/src/flask_state/exceptions/log_msg.py +++ b/src/flask_state/exceptions/log_msg.py @@ -28,6 +28,7 @@ class ErrorMsg(ExceptionMsg): class WarningMsg(ExceptionMsg): TIME_SMALL = {"msg": "Setting the recording time is too short", "level": "warning"} + LACK_REDIS = {"msg": "Redis module is not installed", "level": "warning"} class InfoMsg(ExceptionMsg): diff --git a/src/flask_state/models/flask_state_host.py b/src/flask_state/models/flask_state_host.py index 063eb4d..a3d4b30 100644 --- a/src/flask_state/models/flask_state_host.py +++ b/src/flask_state/models/flask_state_host.py @@ -1,37 +1,39 @@ -from sqlalchemy import func +# -*- coding: utf-8 -*- + +from sqlalchemy import String, func +from sqlalchemy.dialects.mysql import BIGINT, DATETIME, FLOAT, INTEGER, SMALLINT from sqlalchemy.sql import text from ..conf.config import Config from . import db -# model class FlaskStateHost(db.Model): __bind_key__ = Config.DEFAULT_BIND_SQLITE __tablename__ = "flask_state_host" - id = db.Column(db.Integer, autoincrement=True) - create_time = db.Column(db.DateTime, server_default=func.now()) - update_time = db.Column(db.DateTime, server_default=func.now(), onupdate=func.now()) + id = db.Column(INTEGER(unsigned=True), autoincrement=True) + create_time = db.Column(DATETIME, server_default=func.now()) + update_time = db.Column(DATETIME, server_default=func.now(), onupdate=func.now()) # host - cpu = db.Column(db.Float, server_default=text("0")) - memory = db.Column(db.Float, server_default=text("0")) - load_avg = db.Column(db.String(32), server_default="") - disk_usage = db.Column(db.Float, server_default=text("0")) - boot_seconds = db.Column(db.Integer, server_default=text("0")) - ts = db.Column(db.Integer, server_default=text("0")) + cpu = db.Column(FLOAT(unsigned=True), server_default=text("0")) + memory = db.Column(FLOAT(unsigned=True), server_default=text("0")) + load_avg = db.Column(String(32), server_default="") + disk_usage = db.Column(FLOAT(unsigned=True), server_default=text("0")) + boot_seconds = db.Column(INTEGER(unsigned=True), server_default=text("0")) + ts = db.Column(BIGINT(unsigned=True), server_default=text("0")) # redis - used_memory = db.Column(db.Integer, server_default=text("0")) - used_memory_rss = db.Column(db.Integer, server_default=text("0")) - connected_clients = db.Column(db.SmallInteger, server_default=text("0")) - uptime_in_seconds = db.Column(db.Integer, server_default=text("0")) - mem_fragmentation_ratio = db.Column(db.Float, server_default=text("0")) - keyspace_hits = db.Column(db.Integer, server_default=text("0")) - keyspace_misses = db.Column(db.Integer, server_default=text("0")) - hits_ratio = db.Column(db.Float, server_default=text("0")) - delta_hits_ratio = db.Column(db.Float, server_default=text("0")) + used_memory = db.Column(INTEGER(unsigned=True), server_default=text("0")) + used_memory_rss = db.Column(INTEGER(unsigned=True), server_default=text("0")) + connected_clients = db.Column(SMALLINT(unsigned=True), server_default=text("0")) + uptime_in_seconds = db.Column(INTEGER(unsigned=True), server_default=text("0")) + mem_fragmentation_ratio = db.Column(FLOAT(unsigned=True), server_default=text("0")) + keyspace_hits = db.Column(INTEGER(unsigned=True), server_default=text("0")) + keyspace_misses = db.Column(INTEGER(unsigned=True), server_default=text("0")) + hits_ratio = db.Column(FLOAT(unsigned=True), server_default=text("0")) + delta_hits_ratio = db.Column(FLOAT(unsigned=True), server_default=text("0")) __table_args__ = ( db.PrimaryKeyConstraint("id"), diff --git a/src/flask_state/models/flask_state_io.py b/src/flask_state/models/flask_state_io.py new file mode 100644 index 0000000..fb79efa --- /dev/null +++ b/src/flask_state/models/flask_state_io.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- + +from sqlalchemy import func +from sqlalchemy.dialects.mysql import BIGINT, DATETIME, FLOAT, INTEGER +from sqlalchemy.sql import text + +from ..conf.config import Config +from . import db + + +class FlaskStateIO(db.Model): + __bind_key__ = Config.DEFAULT_BIND_SQLITE + __tablename__ = "flask_state_io" + + id = db.Column(INTEGER(unsigned=True), autoincrement=True) + create_time = db.Column(DATETIME, server_default=func.now()) + update_time = db.Column(DATETIME, server_default=func.now(), onupdate=func.now()) + + # network + net_sent = db.Column(BIGINT(unsigned=True), server_default=text("0")) + net_recv = db.Column(BIGINT(unsigned=True), server_default=text("0")) + + # disk + disk_read = db.Column(BIGINT(unsigned=True), server_default=text("0")) + disk_write = db.Column(BIGINT(unsigned=True), server_default=text("0")) + ts = db.Column(BIGINT(unsigned=True), server_default=text("0")) + __table_args__ = ( + db.PrimaryKeyConstraint("id"), + db.Index("idx_ct", create_time.desc()), + { + "extend_existing": True, + }, + ) + + def __repr__(self): + return "".format( + self.net_sent, + self.net_recv, + self.disk_read_bytes, + self.disk_write_bytes, + ) diff --git a/src/flask_state/services/__init__.py b/src/flask_state/services/__init__.py index 5317fb6..c82c618 100644 --- a/src/flask_state/services/__init__.py +++ b/src/flask_state/services/__init__.py @@ -1,4 +1,6 @@ from ..conf.config import Config +from ..exceptions.log_msg import WarningMsg +from ..utils.logger import logger # Create redis object @@ -18,6 +20,7 @@ def set_redis(self, redis_conf): socket_timeout=Config.REDIS_TIMEOUT, ) except ImportError: + logger.warning(WarningMsg.LACK_REDIS.get_msg()) self.redis = None def get_redis(self): diff --git a/src/flask_state/services/host_status.py b/src/flask_state/services/host_status.py index 2de0abf..10b1105 100644 --- a/src/flask_state/services/host_status.py +++ b/src/flask_state/services/host_status.py @@ -1,16 +1,22 @@ +import math import os import platform +from collections import namedtuple from datetime import datetime, timezone import psutil from ..conf.config import Config from ..dao.host_status import ( + create_host_io, create_host_status, + delete_thirty_days_io_status, delete_thirty_days_status, retrieve_host_status, retrieve_host_status_yesterday, + retrieve_io_status, retrieve_latest_host_status, + retrieve_latest_io_status, ) from ..exceptions import FlaskStateError, FlaskStateResponse, SuccessResponse from ..exceptions.error_code import MsgCode @@ -32,23 +38,12 @@ def record_flask_state_host(interval, target_time): return try: - cpu = psutil.cpu_percent(interval=Config.CPU_PERCENT_INTERVAL) - memory = psutil.virtual_memory().percent - if platform.system() == "Windows": - load_avg = Config.DEFAULT_WINDOWS_LOAD_AVG - else: - load_avg = ",".join([str(float("%.2f" % x)) for x in os.getloadavg()]) - disk_usage = psutil.disk_usage("/").percent - boot_ts = psutil.boot_time() - result_conf = { - "ts": get_current_ms(), - "cpu": cpu, - "memory": memory, - "load_avg": load_avg, - "disk_usage": disk_usage, - "boot_seconds": int(get_current_s() - boot_ts), - } - result_conf.update(query_redis_info()) + result_conf = {} + host_status = query_host_info() + result_conf.update(host_status) + redis_status = query_redis_info() + result_conf.update(redis_status) + create_host_status(result_conf) now_time = get_current_s() new_day_utc = ( @@ -61,13 +56,38 @@ def record_flask_state_host(interval, target_time): logger.exception(e) +def query_host_info(): + """ + Collect host status + :return host status dict + :rtype: dict + """ + cpu = psutil.cpu_percent(interval=Config.CPU_PERCENT_INTERVAL) + memory = psutil.virtual_memory().percent + if platform.system() == "Windows": + load_avg = Config.DEFAULT_WINDOWS_LOAD_AVG + else: + load_avg = ",".join([str(float("%.2f" % x)) for x in os.getloadavg()]) + disk_usage = psutil.disk_usage("/").percent + boot_ts = psutil.boot_time() + result = { + "ts": get_current_ms(), + "cpu": cpu, + "memory": memory, + "load_avg": load_avg, + "disk_usage": disk_usage, + "boot_seconds": int(get_current_s() - boot_ts), + } + return result + + def query_redis_info(): """ Collect redis status :return: redis status dict :rtype: dict """ - result = dict() + result = {} redis_handler = redis_conn.get_redis() if redis_handler: try: @@ -117,6 +137,59 @@ def query_redis_info(): return result +def record_flask_state_io_host(interval, target_time): + """ + Record local status and monitor redis status + + """ + if get_current_s() - target_time > Config.ABANDON_IO_THRESHOLD: + format_date = get_formatted_timestamp(target_time) + logger.error(ErrorMsg.RUN_TIME_ERROR.get_msg(". Target time is {}".format(format_date))) + return + + try: + result_conf = {} + host_io_status = query_host_io_info() + result_conf.update(host_io_status) + + create_host_io(result_conf) + now_time = get_current_s() + new_day_utc = ( + datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0, tzinfo=timezone.utc).timestamp() + ) + if now_time <= new_day_utc + interval: + delete_thirty_days_io_status() + + except Exception as e: + logger.exception(e) + + +def query_host_io_info(): + """ + Collect host io status + :return host status dict + :rtype: dict + """ + try: + net_sent = psutil.net_io_counters().bytes_sent + net_recv = psutil.net_io_counters().bytes_recv + disk_read = psutil.disk_io_counters().read_bytes + disk_write = psutil.disk_io_counters().write_bytes + except: + net_sent = 0 + net_recv = 0 + disk_read = 0 + disk_write = 0 + result = { + "net_sent": net_sent, + "net_recv": net_recv, + "disk_read": disk_read, + "disk_write": disk_write, + "ts": get_current_ms(), + } + return result + + def query_flask_state_host(days) -> FlaskStateResponse: """ Query the local status and redis status of [1,3,7,30] days @@ -130,11 +203,43 @@ def query_flask_state_host(days) -> FlaskStateResponse: if days not in TimeConstants.DAYS_SCOPE: raise FlaskStateError(**MsgCode.OVERSTEP_DAYS_SCOPE.value, status_code=HTTPStatus.BAD_REQUEST) - current_status = retrieve_latest_host_status() - current_status["load_avg"] = (current_status.get("load_avg") or "").split(",") + try: + io_info = {} + now_ts = get_current_ms() + now_io = query_host_io_info() + latest_io = retrieve_latest_io_status() + if latest_io and math.ceil((now_ts - latest_io.get("ts")) / 1000) <= 60: + interval = math.ceil((now_ts - latest_io.get("ts")) / 1000) + io_info.update( + { + "net_sent": (now_io.get("net_sent") - latest_io.get("net_sent")) / interval, + "net_recv": (now_io.get("net_recv") - latest_io.get("net_recv")) / interval, + "disk_read": (now_io.get("disk_read") - latest_io.get("disk_read")) / interval, + "disk_write": (now_io.get("disk_write") - latest_io.get("disk_write")) / interval, + } + ) + else: + io_info.update( + { + "net_sent": 0, + "net_recv": 0, + "disk_read": 0, + "disk_write": 0, + } + ) + current_status = query_host_info() + current_status.update(query_redis_info()) + current_status.update(io_info) + current_status["load_avg"] = (current_status.get("load_avg") or "").split(",") + except: + current_status = retrieve_latest_host_status() + current_status["load_avg"] = (current_status.get("load_avg") or "").split(",") result = retrieve_host_status(days) result = control_result_counts(result) + io_result = retrieve_io_status(days) + io_result = control_io_counts(io_result) arr = [] + io_arr = [] for status in result: arr.append( [ @@ -142,10 +247,17 @@ def query_flask_state_host(days) -> FlaskStateResponse: status.cpu, status.memory, status.load_avg.split(","), - status.disk_usage, ] ) - data = {"currentStatistic": current_status, "items": arr} + for io_state in io_result: + io_arr.append( + [ + int(io_state.ts / TimeConstants.SECONDS_TO_MILLISECOND_MULTIPLE), + io_state.net_recv, + io_state.net_sent, + ] + ) + data = {"currentStatistic": current_status, "items": arr, "io": io_arr} return SuccessResponse(msg="Search success", data=data) @@ -165,3 +277,43 @@ def control_result_counts(result) -> list: index += interval result = refine_result return result + + +def control_io_counts(result) -> list: + result_length = len(result) + io_tuple = namedtuple("io", "net_recv, net_sent, ts") + if result_length > Config.MAX_RETURN_RECORDS: + refine_result = [] + interval = round(result_length / Config.MAX_RETURN_RECORDS, 2) + index = 0 + while index < result_length - 1 and len(refine_result) < Config.MAX_RETURN_RECORDS: + new_tmp = result[int(index)] + old_tmp = result[int(index + 1)] + now_item = io_tuple(new_tmp.net_recv - old_tmp.net_recv, new_tmp.net_sent - old_tmp.net_sent, new_tmp.ts) + refine_result.append(now_item) + index += interval + result = refine_result + else: + refine_result = [] + for index in range(result_length - 1): + now_item = io_tuple( + result[index].net_recv - result[index + 1].net_recv, + result[index].net_sent - result[index + 1].net_sent, + result[index].ts, + ) + refine_result.append(now_item) + result = refine_result + return result + + +def row2dict(field): + """ + Model class to dictionary class + :param field: database query results + :return: database query results dictionary + """ + d = {} + for column in field.__table__.columns: + if column.name not in ("create_time", "update_time"): + d[column.name] = getattr(field, column.name) + return d diff --git a/src/flask_state/utils/file_lock.py b/src/flask_state/utils/file_lock.py index ee64161..26f6795 100644 --- a/src/flask_state/utils/file_lock.py +++ b/src/flask_state/utils/file_lock.py @@ -14,13 +14,13 @@ class Lock: @staticmethod - def get_file_lock(): - return FileLock() + def get_file_lock(io=""): + return FileLock(io) class FileLock: - def __init__(self): - lock_file = "821e9dab54fec92e3d054b3367a50b70d328caed" + def __init__(self, type): + lock_file = "821e9dab54fec92e3d054b3367a50b70d328caed_{type}".format(type=type) if SYSTEM == OperatingSystem.WINDOWS_SYSTEM: lock_dir = os.environ["tmp"] else: