recharts; account metrics (#319, #321)

This commit is contained in:
Michael Quigley 2023-05-10 14:17:10 -04:00
parent 7d611fda30
commit b5b3385b46
No known key found for this signature in database
GPG Key ID: 9B60314A9DD20A62
4 changed files with 141 additions and 53 deletions

View File

@ -34,12 +34,12 @@ func (h *getAccountMetricsHandler) Handle(params metadata.GetAccountMetricsParam
if params.Duration != nil {
v, err := time.ParseDuration(*params.Duration)
if err != nil {
logrus.Errorf("bad duration '%v' for '%v': %v", params.Duration, principal.Email, err)
logrus.Errorf("bad duration '%v' for '%v': %v", *params.Duration, principal.Email, err)
return metadata.NewGetAccountMetricsBadRequest()
}
duration = v
}
slice := duration / 50
slice := duration / 30
query := fmt.Sprintf("from(bucket: \"%v\")\n", h.cfg.Bucket) +
fmt.Sprintf("|> range(start: -%v)\n", duration) +
@ -108,12 +108,12 @@ func (h *getEnvironmentMetricsHandler) Handle(params metadata.GetEnvironmentMetr
if params.Duration != nil {
v, err := time.ParseDuration(*params.Duration)
if err != nil {
logrus.Errorf("bad duration '%v' for '%v': %v", params.Duration, principal.Email, err)
logrus.Errorf("bad duration '%v' for '%v': %v", *params.Duration, principal.Email, err)
return metadata.NewGetAccountMetricsBadRequest()
}
duration = v
}
slice := duration / 50
slice := duration / 30
query := fmt.Sprintf("from(bucket: \"%v\")\n", h.cfg.Bucket) +
fmt.Sprintf("|> range(start: -%v)\n", duration) +
@ -188,12 +188,12 @@ func (h *getShareMetricsHandler) Handle(params metadata.GetShareMetricsParams, p
if params.Duration != nil {
v, err := time.ParseDuration(*params.Duration)
if err != nil {
logrus.Errorf("bad duration '%v' for '%v': %v", params.Duration, principal.Email, err)
logrus.Errorf("bad duration '%v' for '%v': %v", *params.Duration, principal.Email, err)
return metadata.NewGetAccountMetricsBadRequest()
}
duration = v
}
slice := duration / 50
slice := duration / 30
query := fmt.Sprintf("from(bucket: \"%v\")\n", h.cfg.Bucket) +
fmt.Sprintf("|> range(start: -%v)\n", duration) +

28
ui/package-lock.json generated
View File

@ -17,6 +17,7 @@
"dagre": "^0.8.5",
"eslint-config-react-app": "^7.0.1",
"humanize-duration": "^3.27.3",
"moment": "^2.29.4",
"react": "^18.2.0",
"react-bootstrap": "^2.7.0",
"react-data-table-component": "^7.5.2",
@ -24,7 +25,7 @@
"react-force-graph": "^1.43.0",
"react-router-dom": "^6.4.0",
"react-sizeme": "^3.0.2",
"recharts": "^2.5.0",
"recharts": "^2.6.1",
"styled-components": "^5.3.5",
"svgo": "^3.0.2"
},
@ -13651,6 +13652,14 @@
"mkdirp": "bin/cmd.js"
}
},
"node_modules/moment": {
"version": "2.29.4",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==",
"engines": {
"node": "*"
}
},
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@ -16545,9 +16554,9 @@
}
},
"node_modules/recharts": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/recharts/-/recharts-2.5.0.tgz",
"integrity": "sha512-0EQYz3iA18r1Uq8VqGZ4dABW52AKBnio37kJgnztIqprELJXpOEsa0SzkqU1vjAhpCXCv52Dx1hiL9119xsqsQ==",
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/recharts/-/recharts-2.6.1.tgz",
"integrity": "sha512-eGNNqQTSg737HB0tfFkPZbPW8ji7Q8joQM0P2yAEkJkB8CO+LJPgLpx/NUxNHJsxoXvSblMFoy5RSVBYfLU+HA==",
"dependencies": {
"classnames": "^2.2.5",
"eventemitter3": "^4.0.1",
@ -29808,6 +29817,11 @@
"minimist": "^1.2.6"
}
},
"moment": {
"version": "2.29.4",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz",
"integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w=="
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
@ -31796,9 +31810,9 @@
}
},
"recharts": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/recharts/-/recharts-2.5.0.tgz",
"integrity": "sha512-0EQYz3iA18r1Uq8VqGZ4dABW52AKBnio37kJgnztIqprELJXpOEsa0SzkqU1vjAhpCXCv52Dx1hiL9119xsqsQ==",
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/recharts/-/recharts-2.6.1.tgz",
"integrity": "sha512-eGNNqQTSg737HB0tfFkPZbPW8ji7Q8joQM0P2yAEkJkB8CO+LJPgLpx/NUxNHJsxoXvSblMFoy5RSVBYfLU+HA==",
"requires": {
"classnames": "^2.2.5",
"eventemitter3": "^4.0.1",

View File

@ -12,6 +12,7 @@
"dagre": "^0.8.5",
"eslint-config-react-app": "^7.0.1",
"humanize-duration": "^3.27.3",
"moment": "^2.29.4",
"react": "^18.2.0",
"react-bootstrap": "^2.7.0",
"react-data-table-component": "^7.5.2",
@ -19,7 +20,7 @@
"react-force-graph": "^1.43.0",
"react-router-dom": "^6.4.0",
"react-sizeme": "^3.0.2",
"recharts": "^2.5.0",
"recharts": "^2.6.1",
"styled-components": "^5.3.5",
"svgo": "^3.0.2"
},

View File

@ -1,11 +1,12 @@
import {mdiAccountBox} from "@mdi/js";
import Icon from "@mdi/react";
import PropertyTable from "../../PropertyTable";
import {Tab, Tabs, Tooltip} from "react-bootstrap";
import {Col, Container, Row, Tab, Tabs, Tooltip} from "react-bootstrap";
import SecretToggle from "../../SecretToggle";
import React, {useEffect, useState} from "react";
import * as metadata from "../../../api/metadata";
import {Area, AreaChart, CartesianGrid, Line, LineChart, ResponsiveContainer, XAxis, YAxis} from "recharts";
import { Bar, BarChart, CartesianGrid, ResponsiveContainer, XAxis, YAxis } from "recharts";
import moment from "moment";
const AccountDetail = (props) => {
const customProperties = {
@ -28,14 +29,22 @@ const AccountDetail = (props) => {
}
const MetricsTab = (props) => {
const [metrics, setMetrics] = useState({});
const [tx, setTx] = useState(0);
const [rx, setRx] = useState(0)
const [metrics30, setMetrics30] = useState(buildMetrics([]));
const [metrics7, setMetrics7] = useState(buildMetrics([]));
const [metrics1, setMetrics1] = useState(buildMetrics([]));
useEffect(() => {
metadata.getAccountMetrics()
.then(resp => {
setMetrics(resp.data);
setMetrics30(buildMetrics(resp.data));
});
metadata.getAccountMetrics({duration: "168h"})
.then(resp => {
setMetrics7(buildMetrics(resp.data));
});
metadata.getAccountMetrics({duration: "24h"})
.then(resp => {
setMetrics1(buildMetrics(resp.data));
});
}, []);
@ -45,50 +54,114 @@ const MetricsTab = (props) => {
metadata.getAccountMetrics()
.then(resp => {
if(mounted) {
setMetrics(resp.data);
setMetrics30(buildMetrics(resp.data));
}
});
}, 1000);
metadata.getAccountMetrics({duration: "168h"})
.then(resp => {
setMetrics7(buildMetrics(resp.data));
});
metadata.getAccountMetrics({duration: "24h"})
.then(resp => {
setMetrics1(buildMetrics(resp.data));
});
}, 5000);
return () => {
mounted = false;
clearInterval(interval);
}
}, []);
useEffect(() => {
let txAccum = 0
let rxAccum = 0
if(metrics.samples) {
metrics.samples.forEach(sample => {
txAccum += sample.tx
rxAccum += sample.rx
})
}
setTx(txAccum);
setRx(rxAccum);
}, [metrics])
console.log(metrics);
return (
<div>
<div>
<h1>RX: {bytesToSize(rx)}, TX: {bytesToSize(tx)}</h1>
</div>
<ResponsiveContainer width={"100%"} height={300}>
<LineChart data={metrics.samples}>
<CartesianGrid strokeDasharay={"3 3"} />
<XAxis dataKey={(v) => new Date(v.timestamp)} />
<YAxis />
<Line type={"linear"} stroke={"red"} dataKey={"rx"} activeDot={{ r: 8 }}/>
<Line type={"linear"} stroke={"green"} dataKey={"tx"} />
<Tooltip />
</LineChart>
</ResponsiveContainer>
</div>
<Container>
<Row>
<Col>
<h3>Last 30 Days:</h3>
</Col>
</Row>
<Row>
<Col><p>Received: {bytesToSize(metrics30.rx)}</p></Col>
<Col><p>Sent: {bytesToSize(metrics30.tx)}</p></Col>
</Row>
<Row>
<Col>
<ResponsiveContainer width={"100%"} height={150}>
<BarChart data={metrics30.data}>
<CartesianGrid strokeDasharay={"3 3"} />
<XAxis dataKey={(v) => v.timestamp} scale={"time"} tickFormatter={(v) => moment(v).format("MMM DD") } style={{ fontSize: '75%'}}/>
<YAxis tickFormatter={(v) => bytesToSize(v)} style={{ fontSize: '75%' }}/>
<Bar stroke={"#231069"} fill={"#04adef"} dataKey={"rx"} legendType={"circle"}/>
<Bar stroke={"#231069"} fill={"#9BF316"} dataKey={"tx"} />
<Tooltip />
</BarChart>
</ResponsiveContainer>
</Col>
</Row>
<Row>
<Col>
<h3>Last 7 Days:</h3>
</Col>
</Row>
<Row>
<Col><p>Received: {bytesToSize(metrics7.rx)}</p></Col>
<Col><p>Sent: {bytesToSize(metrics7.tx)}</p></Col>
</Row>
<Row>
<Col>
<ResponsiveContainer width={"100%"} height={150}>
<BarChart data={metrics7.data}>
<CartesianGrid strokeDasharay={"3 3"} />
<XAxis dataKey={(v) => v.timestamp} scale={"time"} tickFormatter={(v) => moment(v).format("MMM DD") } style={{ fontSize: '75%'}}/>
<YAxis tickFormatter={(v) => bytesToSize(v)} style={{ fontSize: '75%' }}/>
<Bar stroke={"#231069"} fill={"#04adef"} dataKey={"rx"} legendType={"circle"}/>
<Bar stroke={"#231069"} fill={"#9BF316"} dataKey={"tx"} />
<Tooltip />
</BarChart>
</ResponsiveContainer>
</Col>
</Row>
<Row>
<Col>
<h3>Last 24 Hours:</h3>
</Col>
</Row>
<Row>
<Col><p>Received: {bytesToSize(metrics1.rx)}</p></Col>
<Col><p>Sent: {bytesToSize(metrics1.tx)}</p></Col>
</Row>
<Row>
<Col>
<ResponsiveContainer width={"100%"} height={150}>
<BarChart data={metrics1.data}>
<CartesianGrid strokeDasharay={"3 3"} />
<XAxis dataKey={(v) => v.timestamp} scale={"time"} tickFormatter={(v) => moment(v).format("MMM DD") } style={{ fontSize: '75%'}}/>
<YAxis tickFormatter={(v) => bytesToSize(v)} style={{ fontSize: '75%' }}/>
<Bar stroke={"#231069"} fill={"#04adef"} dataKey={"rx"} legendType={"circle"}/>
<Bar stroke={"#231069"} fill={"#9BF316"} dataKey={"tx"} />
<Tooltip />
</BarChart>
</ResponsiveContainer>
</Col>
</Row>
</Container>
);
}
const buildMetrics = (m) => {
let metrics = {
data: m.samples,
rx: 0,
tx: 0
}
if(m.samples) {
m.samples.forEach(s => {
metrics.rx += s.rx;
metrics.tx += s.tx;
});
}
return metrics;
}
const bytesToSize = (sz) => {
let absSz = sz;
if(absSz < 0) {
@ -105,7 +178,7 @@ const bytesToSize = (sz) => {
exp++;
}
return '' + (sz / div).toFixed(2) + "kMGTPE"[exp];
return '' + (sz / div).toFixed(1) + ' ' + "kMGTPE"[exp] + 'B';
}
export default AccountDetail;