forked from FoundKeyGang/FoundKey
Resolve #2328
This commit is contained in:
parent
e615a3fdf3
commit
c985fed3e4
7 changed files with 200 additions and 2 deletions
|
@ -518,6 +518,7 @@ desktop/views/components/charts.vue:
|
||||||
notes: "投稿"
|
notes: "投稿"
|
||||||
users: "ユーザー"
|
users: "ユーザー"
|
||||||
drive: "ドライブ"
|
drive: "ドライブ"
|
||||||
|
network: "ネットワーク"
|
||||||
charts:
|
charts:
|
||||||
notes: "投稿の増減 (統合)"
|
notes: "投稿の増減 (統合)"
|
||||||
local-notes: "投稿の増減 (ローカル)"
|
local-notes: "投稿の増減 (ローカル)"
|
||||||
|
@ -529,6 +530,9 @@ desktop/views/components/charts.vue:
|
||||||
drive-total: "ドライブ使用量の累計"
|
drive-total: "ドライブ使用量の累計"
|
||||||
drive-files: "ドライブのファイル数の増減"
|
drive-files: "ドライブのファイル数の増減"
|
||||||
drive-files-total: "ドライブのファイル数の累計"
|
drive-files-total: "ドライブのファイル数の累計"
|
||||||
|
network-requests: "リクエスト"
|
||||||
|
network-time: "応答時間"
|
||||||
|
network-usage: "通信量"
|
||||||
|
|
||||||
desktop/views/components/choose-file-from-drive-window.vue:
|
desktop/views/components/choose-file-from-drive-window.vue:
|
||||||
choose-file: "ファイル選択中"
|
choose-file: "ファイル選択中"
|
||||||
|
|
|
@ -179,6 +179,7 @@
|
||||||
"redis": "2.8.0",
|
"redis": "2.8.0",
|
||||||
"request": "2.88.0",
|
"request": "2.88.0",
|
||||||
"request-promise-native": "1.0.5",
|
"request-promise-native": "1.0.5",
|
||||||
|
"request-stats": "3.0.0",
|
||||||
"rimraf": "2.6.2",
|
"rimraf": "2.6.2",
|
||||||
"rndstr": "1.0.0",
|
"rndstr": "1.0.0",
|
||||||
"s-age": "1.1.2",
|
"s-age": "1.1.2",
|
||||||
|
|
|
@ -19,6 +19,11 @@
|
||||||
<option value="drive">%i18n:@charts.drive%</option>
|
<option value="drive">%i18n:@charts.drive%</option>
|
||||||
<option value="drive-total">%i18n:@charts.drive-total%</option>
|
<option value="drive-total">%i18n:@charts.drive-total%</option>
|
||||||
</optgroup>
|
</optgroup>
|
||||||
|
<optgroup label="%i18n:@network%">
|
||||||
|
<option value="network-requests">%i18n:@charts.network-requests%</option>
|
||||||
|
<option value="network-time">%i18n:@charts.network-time%</option>
|
||||||
|
<option value="network-usage">%i18n:@charts.network-usage%</option>
|
||||||
|
</optgroup>
|
||||||
</select>
|
</select>
|
||||||
<div>
|
<div>
|
||||||
<span @click="span = 'day'" :class="{ active: span == 'day' }">%i18n:@per-day%</span> | <span @click="span = 'hour'" :class="{ active: span == 'hour' }">%i18n:@per-hour%</span>
|
<span @click="span = 'day'" :class="{ active: span == 'day' }">%i18n:@per-day%</span> | <span @click="span = 'hour'" :class="{ active: span == 'hour' }">%i18n:@per-hour%</span>
|
||||||
|
@ -41,7 +46,10 @@ const colors = {
|
||||||
localPlus: 'rgb(52, 178, 118)',
|
localPlus: 'rgb(52, 178, 118)',
|
||||||
remotePlus: 'rgb(158, 255, 209)',
|
remotePlus: 'rgb(158, 255, 209)',
|
||||||
localMinus: 'rgb(255, 97, 74)',
|
localMinus: 'rgb(255, 97, 74)',
|
||||||
remoteMinus: 'rgb(255, 149, 134)'
|
remoteMinus: 'rgb(255, 149, 134)',
|
||||||
|
|
||||||
|
incoming: 'rgb(52, 178, 118)',
|
||||||
|
outgoing: 'rgb(255, 97, 74)',
|
||||||
};
|
};
|
||||||
|
|
||||||
const rgba = (color: string): string => {
|
const rgba = (color: string): string => {
|
||||||
|
@ -75,6 +83,9 @@ export default Vue.extend({
|
||||||
case 'drive-total': return this.driveTotalChart();
|
case 'drive-total': return this.driveTotalChart();
|
||||||
case 'drive-files': return this.driveFilesChart();
|
case 'drive-files': return this.driveFilesChart();
|
||||||
case 'drive-files-total': return this.driveFilesTotalChart();
|
case 'drive-files-total': return this.driveFilesTotalChart();
|
||||||
|
case 'network-requests': return this.networkRequestsChart();
|
||||||
|
case 'network-time': return this.networkTimeChart();
|
||||||
|
case 'network-usage': return this.networkUsageChart();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -544,8 +555,96 @@ export default Vue.extend({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}];
|
}];
|
||||||
|
},
|
||||||
|
|
||||||
|
networkRequestsChart(): any {
|
||||||
|
const data = this.stats.slice().reverse().map(x => ({
|
||||||
|
date: new Date(x.date),
|
||||||
|
requests: x.network.requests
|
||||||
|
}));
|
||||||
|
|
||||||
|
return [{
|
||||||
|
datasets: [{
|
||||||
|
label: 'Requests',
|
||||||
|
fill: true,
|
||||||
|
backgroundColor: rgba(colors.localPlus),
|
||||||
|
borderColor: colors.localPlus,
|
||||||
|
borderWidth: 2,
|
||||||
|
pointBackgroundColor: '#fff',
|
||||||
|
lineTension: 0,
|
||||||
|
data: data.map(x => ({ t: x.date, y: x.requests }))
|
||||||
|
}]
|
||||||
|
}];
|
||||||
|
},
|
||||||
|
|
||||||
|
networkTimeChart(): any {
|
||||||
|
const data = this.stats.slice().reverse().map(x => ({
|
||||||
|
date: new Date(x.date),
|
||||||
|
time: x.network.requests != 0 ? (x.network.totalTime / x.network.requests) : 0,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return [{
|
||||||
|
datasets: [{
|
||||||
|
label: 'Avg time (ms)',
|
||||||
|
fill: true,
|
||||||
|
backgroundColor: rgba(colors.localPlus),
|
||||||
|
borderColor: colors.localPlus,
|
||||||
|
borderWidth: 2,
|
||||||
|
pointBackgroundColor: '#fff',
|
||||||
|
lineTension: 0,
|
||||||
|
data: data.map(x => ({ t: x.date, y: x.time }))
|
||||||
|
}]
|
||||||
|
}];
|
||||||
|
},
|
||||||
|
|
||||||
|
networkUsageChart(): any {
|
||||||
|
const data = this.stats.slice().reverse().map(x => ({
|
||||||
|
date: new Date(x.date),
|
||||||
|
incoming: x.network.incomingBytes,
|
||||||
|
outgoing: x.network.outgoingBytes
|
||||||
|
}));
|
||||||
|
|
||||||
|
return [{
|
||||||
|
datasets: [{
|
||||||
|
label: 'Incoming',
|
||||||
|
fill: true,
|
||||||
|
backgroundColor: rgba(colors.incoming),
|
||||||
|
borderColor: colors.incoming,
|
||||||
|
borderWidth: 2,
|
||||||
|
pointBackgroundColor: '#fff',
|
||||||
|
lineTension: 0,
|
||||||
|
data: data.map(x => ({ t: x.date, y: x.incoming }))
|
||||||
|
}, {
|
||||||
|
label: 'Outgoing',
|
||||||
|
fill: true,
|
||||||
|
backgroundColor: rgba(colors.outgoing),
|
||||||
|
borderColor: colors.outgoing,
|
||||||
|
borderWidth: 2,
|
||||||
|
pointBackgroundColor: '#fff',
|
||||||
|
lineTension: 0,
|
||||||
|
data: data.map(x => ({ t: x.date, y: x.outgoing }))
|
||||||
|
}]
|
||||||
|
}, {
|
||||||
|
scales: {
|
||||||
|
yAxes: [{
|
||||||
|
ticks: {
|
||||||
|
callback: value => {
|
||||||
|
return Vue.filter('bytes')(value, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
tooltips: {
|
||||||
|
callbacks: {
|
||||||
|
label: (tooltipItem, data) => {
|
||||||
|
const label = data.datasets[tooltipItem.datasetIndex].label || '';
|
||||||
|
return `${label}: ${Vue.filter('bytes')(tooltipItem.yLabel, 1)}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
},
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -204,4 +204,30 @@ export interface IStats {
|
||||||
decSize: number;
|
decSize: number;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ネットワークに関する統計
|
||||||
|
*/
|
||||||
|
network: {
|
||||||
|
/**
|
||||||
|
* サーバーへのリクエスト数
|
||||||
|
*/
|
||||||
|
requests: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 応答時間の合計
|
||||||
|
* TIP: (totalTime / requests) でひとつのリクエストに平均でどれくらいの時間がかかったか知れる
|
||||||
|
*/
|
||||||
|
totalTime: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 合計受信データ量
|
||||||
|
*/
|
||||||
|
incomingBytes: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 合計送信データ量
|
||||||
|
*/
|
||||||
|
outgoingBytes: number;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,15 @@ type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
|
||||||
|
|
||||||
function migrateStats(stats: IStats[]) {
|
function migrateStats(stats: IStats[]) {
|
||||||
stats.forEach(stat => {
|
stats.forEach(stat => {
|
||||||
|
if (stat.network == null) {
|
||||||
|
stat.network = {
|
||||||
|
requests: 0,
|
||||||
|
totalTime: 0,
|
||||||
|
incomingBytes: 0,
|
||||||
|
outgoingBytes: 0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const isOldData =
|
const isOldData =
|
||||||
stat.users.local.inc == null ||
|
stat.users.local.inc == null ||
|
||||||
stat.users.local.dec == null ||
|
stat.users.local.dec == null ||
|
||||||
|
@ -180,6 +189,12 @@ export default (params: any) => new Promise(async (res, rej) => {
|
||||||
decCount: 0,
|
decCount: 0,
|
||||||
decSize: 0
|
decSize: 0
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
network: {
|
||||||
|
requests: 0,
|
||||||
|
totalTime: 0,
|
||||||
|
incomingBytes: 0,
|
||||||
|
outgoingBytes: 0
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -236,6 +251,12 @@ export default (params: any) => new Promise(async (res, rej) => {
|
||||||
decCount: 0,
|
decCount: 0,
|
||||||
decSize: 0
|
decSize: 0
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
network: {
|
||||||
|
requests: 0,
|
||||||
|
totalTime: 0,
|
||||||
|
incomingBytes: 0,
|
||||||
|
outgoingBytes: 0
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,11 +11,13 @@ import * as Router from 'koa-router';
|
||||||
import * as mount from 'koa-mount';
|
import * as mount from 'koa-mount';
|
||||||
import * as compress from 'koa-compress';
|
import * as compress from 'koa-compress';
|
||||||
import * as logger from 'koa-logger';
|
import * as logger from 'koa-logger';
|
||||||
|
const requestStats = require('request-stats');
|
||||||
//const slow = require('koa-slow');
|
//const slow = require('koa-slow');
|
||||||
|
|
||||||
import activityPub from './activitypub';
|
import activityPub from './activitypub';
|
||||||
import webFinger from './webfinger';
|
import webFinger from './webfinger';
|
||||||
import config from '../config';
|
import config from '../config';
|
||||||
|
import { updateNetworkStats } from '../services/update-chart';
|
||||||
|
|
||||||
// Init app
|
// Init app
|
||||||
const app = new Koa();
|
const app = new Koa();
|
||||||
|
@ -81,4 +83,27 @@ export default () => new Promise(resolve => {
|
||||||
|
|
||||||
// Listen
|
// Listen
|
||||||
server.listen(config.port, resolve);
|
server.listen(config.port, resolve);
|
||||||
|
|
||||||
|
//#region Network stats
|
||||||
|
let queue: any[] = [];
|
||||||
|
|
||||||
|
requestStats(server, (stats: any) => {
|
||||||
|
if (stats.ok) {
|
||||||
|
queue.push(stats);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Bulk write
|
||||||
|
setInterval(() => {
|
||||||
|
if (queue.length == 0) return;
|
||||||
|
|
||||||
|
const requests = queue.length;
|
||||||
|
const time = queue.reduce((a, b) => a + b.time, 0);
|
||||||
|
const incomingBytes = queue.reduce((a, b) => a + b.req.bytes, 0);
|
||||||
|
const outgoingBytes = queue.reduce((a, b) => a + b.res.bytes, 0);
|
||||||
|
queue = [];
|
||||||
|
|
||||||
|
updateNetworkStats(requests, time, incomingBytes, outgoingBytes);
|
||||||
|
}, 5000);
|
||||||
|
//#endregion
|
||||||
});
|
});
|
||||||
|
|
|
@ -96,6 +96,12 @@ async function getCurrentStats(span: 'day' | 'hour'): Promise<IStats> {
|
||||||
decCount: 0,
|
decCount: 0,
|
||||||
decSize: 0
|
decSize: 0
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
network: {
|
||||||
|
requests: 0,
|
||||||
|
totalTime: 0,
|
||||||
|
incomingBytes: 0,
|
||||||
|
outgoingBytes: 0
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -161,6 +167,12 @@ async function getCurrentStats(span: 'day' | 'hour'): Promise<IStats> {
|
||||||
decCount: 0,
|
decCount: 0,
|
||||||
decSize: 0
|
decSize: 0
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
network: {
|
||||||
|
requests: 0,
|
||||||
|
totalTime: 0,
|
||||||
|
incomingBytes: 0,
|
||||||
|
outgoingBytes: 0
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -243,3 +255,13 @@ export async function updateDriveStats(file: IDriveFile, isAdditional: boolean)
|
||||||
|
|
||||||
await update(inc);
|
await update(inc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function updateNetworkStats(requests: number, time: number, incomingBytes: number, outgoingBytes: number) {
|
||||||
|
const inc = {} as any;
|
||||||
|
inc['network.requests'] = requests;
|
||||||
|
inc['network.totalTime'] = time;
|
||||||
|
inc['network.incomingBytes'] = incomingBytes;
|
||||||
|
inc['network.outgoingBytes'] = outgoingBytes;
|
||||||
|
|
||||||
|
await update(inc);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue