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 { if params.Duration != nil {
v, err := time.ParseDuration(*params.Duration) v, err := time.ParseDuration(*params.Duration)
if err != nil { 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() return metadata.NewGetAccountMetricsBadRequest()
} }
duration = v duration = v
} }
slice := duration / 50 slice := duration / 30
query := fmt.Sprintf("from(bucket: \"%v\")\n", h.cfg.Bucket) + query := fmt.Sprintf("from(bucket: \"%v\")\n", h.cfg.Bucket) +
fmt.Sprintf("|> range(start: -%v)\n", duration) + fmt.Sprintf("|> range(start: -%v)\n", duration) +
@ -108,12 +108,12 @@ func (h *getEnvironmentMetricsHandler) Handle(params metadata.GetEnvironmentMetr
if params.Duration != nil { if params.Duration != nil {
v, err := time.ParseDuration(*params.Duration) v, err := time.ParseDuration(*params.Duration)
if err != nil { 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() return metadata.NewGetAccountMetricsBadRequest()
} }
duration = v duration = v
} }
slice := duration / 50 slice := duration / 30
query := fmt.Sprintf("from(bucket: \"%v\")\n", h.cfg.Bucket) + query := fmt.Sprintf("from(bucket: \"%v\")\n", h.cfg.Bucket) +
fmt.Sprintf("|> range(start: -%v)\n", duration) + fmt.Sprintf("|> range(start: -%v)\n", duration) +
@ -188,12 +188,12 @@ func (h *getShareMetricsHandler) Handle(params metadata.GetShareMetricsParams, p
if params.Duration != nil { if params.Duration != nil {
v, err := time.ParseDuration(*params.Duration) v, err := time.ParseDuration(*params.Duration)
if err != nil { 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() return metadata.NewGetAccountMetricsBadRequest()
} }
duration = v duration = v
} }
slice := duration / 50 slice := duration / 30
query := fmt.Sprintf("from(bucket: \"%v\")\n", h.cfg.Bucket) + query := fmt.Sprintf("from(bucket: \"%v\")\n", h.cfg.Bucket) +
fmt.Sprintf("|> range(start: -%v)\n", duration) + fmt.Sprintf("|> range(start: -%v)\n", duration) +

28
ui/package-lock.json generated
View File

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

View File

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

View File

@ -1,11 +1,12 @@
import {mdiAccountBox} from "@mdi/js"; import {mdiAccountBox} from "@mdi/js";
import Icon from "@mdi/react"; import Icon from "@mdi/react";
import PropertyTable from "../../PropertyTable"; 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 SecretToggle from "../../SecretToggle";
import React, {useEffect, useState} from "react"; import React, {useEffect, useState} from "react";
import * as metadata from "../../../api/metadata"; 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 AccountDetail = (props) => {
const customProperties = { const customProperties = {
@ -28,14 +29,22 @@ const AccountDetail = (props) => {
} }
const MetricsTab = (props) => { const MetricsTab = (props) => {
const [metrics, setMetrics] = useState({}); const [metrics30, setMetrics30] = useState(buildMetrics([]));
const [tx, setTx] = useState(0); const [metrics7, setMetrics7] = useState(buildMetrics([]));
const [rx, setRx] = useState(0) const [metrics1, setMetrics1] = useState(buildMetrics([]));
useEffect(() => { useEffect(() => {
metadata.getAccountMetrics() metadata.getAccountMetrics()
.then(resp => { .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() metadata.getAccountMetrics()
.then(resp => { .then(resp => {
if(mounted) { 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 () => { return () => {
mounted = false; mounted = false;
clearInterval(interval); 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 ( return (
<div> <Container>
<div> <Row>
<h1>RX: {bytesToSize(rx)}, TX: {bytesToSize(tx)}</h1> <Col>
</div> <h3>Last 30 Days:</h3>
<ResponsiveContainer width={"100%"} height={300}> </Col>
<LineChart data={metrics.samples}> </Row>
<CartesianGrid strokeDasharay={"3 3"} /> <Row>
<XAxis dataKey={(v) => new Date(v.timestamp)} /> <Col><p>Received: {bytesToSize(metrics30.rx)}</p></Col>
<YAxis /> <Col><p>Sent: {bytesToSize(metrics30.tx)}</p></Col>
<Line type={"linear"} stroke={"red"} dataKey={"rx"} activeDot={{ r: 8 }}/> </Row>
<Line type={"linear"} stroke={"green"} dataKey={"tx"} /> <Row>
<Tooltip /> <Col>
</LineChart> <ResponsiveContainer width={"100%"} height={150}>
</ResponsiveContainer> <BarChart data={metrics30.data}>
</div> <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) => { const bytesToSize = (sz) => {
let absSz = sz; let absSz = sz;
if(absSz < 0) { if(absSz < 0) {
@ -105,7 +178,7 @@ const bytesToSize = (sz) => {
exp++; exp++;
} }
return '' + (sz / div).toFixed(2) + "kMGTPE"[exp]; return '' + (sz / div).toFixed(1) + ' ' + "kMGTPE"[exp] + 'B';
} }
export default AccountDetail; export default AccountDetail;