aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEmiliano Ciavatta2020-12-25 13:48:11 +0000
committerEmiliano Ciavatta2020-12-25 13:48:11 +0000
commitf8f3c91a8722e0cb5cc78f21ad163e8165ac5979 (patch)
treeb6319bc00d886bf0c3a4aeaff19cae378a9cf47a
parentad0c27ae1fde7d24c24a34a7c305edeb0ea9974e (diff)
Add stats page, add /api/statistics/totals
-rw-r--r--application_router.go10
-rw-r--r--frontend/src/components/Header.js4
-rw-r--r--frontend/src/components/pages/MainPage.js4
-rw-r--r--frontend/src/components/panels/ConfigPane.js39
-rw-r--r--frontend/src/components/panels/StatsPane.js274
-rw-r--r--frontend/src/components/panels/StatsPane.scss21
-rw-r--r--frontend/src/utils.js4
-rw-r--r--frontend/yarn.lock168
-rw-r--r--statistics_controller.go73
9 files changed, 456 insertions, 141 deletions
diff --git a/application_router.go b/application_router.go
index 21758c9..2dcdfd6 100644
--- a/application_router.go
+++ b/application_router.go
@@ -399,6 +399,16 @@ func CreateApplicationRouter(applicationContext *ApplicationContext,
success(c, applicationContext.StatisticsController.GetStatistics(c, filter))
})
+ api.GET("/statistics/totals", func(c *gin.Context) {
+ var filter StatisticsFilter
+ if err := c.ShouldBindQuery(&filter); err != nil {
+ badRequest(c, err)
+ return
+ }
+
+ success(c, applicationContext.StatisticsController.GetTotalStatistics(c, filter))
+ })
+
api.GET("/resources/system", func(c *gin.Context) {
success(c, resourcesController.GetSystemStats(c))
})
diff --git a/frontend/src/components/Header.js b/frontend/src/components/Header.js
index 4db05a5..e3fafc4 100644
--- a/frontend/src/components/Header.js
+++ b/frontend/src/components/Header.js
@@ -85,8 +85,8 @@ class Header extends Component {
<Link to={"/services" + this.props.location.search}>
<ButtonField variant="indigo" name="services" bordered/>
</Link>
- <Link to={"/config" + this.props.location.search}>
- <ButtonField variant="blue" name="config" bordered/>
+ <Link to={"/stats" + this.props.location.search}>
+ <ButtonField variant="blue" name="stats" bordered/>
</Link>
</div>
}
diff --git a/frontend/src/components/pages/MainPage.js b/frontend/src/components/pages/MainPage.js
index 6058edc..f005f44 100644
--- a/frontend/src/components/pages/MainPage.js
+++ b/frontend/src/components/pages/MainPage.js
@@ -21,13 +21,13 @@ import "react-reflex/styles.css"
import {Route, Switch} from "react-router-dom";
import Filters from "../dialogs/Filters";
import Header from "../Header";
-import ConfigPane from "../panels/ConfigPane";
import Connections from "../panels/ConnectionsPane";
import MainPane from "../panels/MainPane";
import PcapsPane from "../panels/PcapsPane";
import RulesPane from "../panels/RulesPane";
import SearchPane from "../panels/SearchPane";
import ServicesPane from "../panels/ServicesPane";
+import StatsPane from "../panels/StatsPane";
import StreamsPane from "../panels/StreamsPane";
import Timeline from "../Timeline";
import "./MainPage.scss";
@@ -76,7 +76,7 @@ class MainPage extends Component {
<Route path="/pcaps" children={<PcapsPane/>}/>
<Route path="/rules" children={<RulesPane/>}/>
<Route path="/services" children={<ServicesPane/>}/>
- <Route path="/config" children={<ConfigPane/>}/>
+ <Route path="/stats" children={<StatsPane/>}/>
<Route exact path="/connections/:id"
children={<StreamsPane connection={this.state.selectedConnection}/>}/>
<Route children={<MainPane version={this.props.version}/>}/>
diff --git a/frontend/src/components/panels/ConfigPane.js b/frontend/src/components/panels/ConfigPane.js
deleted file mode 100644
index 9710abd..0000000
--- a/frontend/src/components/panels/ConfigPane.js
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * This file is part of caronte (https://github.com/eciavatta/caronte).
- * Copyright (c) 2020 Emiliano Ciavatta.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, version 3.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-import React, {Component} from "react";
-import "./common.scss";
-
-class ConfigPane extends Component {
-
- state = {};
-
- render() {
- return (
- <div className="pane-container">
- <div className="main-pane">
- <div className="pane-section">
- Not implemented
- </div>
- </div>
- </div>
- );
- }
-
-}
-
-export default ConfigPane;
diff --git a/frontend/src/components/panels/StatsPane.js b/frontend/src/components/panels/StatsPane.js
new file mode 100644
index 0000000..dd774d0
--- /dev/null
+++ b/frontend/src/components/panels/StatsPane.js
@@ -0,0 +1,274 @@
+/*
+ * This file is part of caronte (https://github.com/eciavatta/caronte).
+ * Copyright (c) 2020 Emiliano Ciavatta.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import React, {Component} from "react";
+import Table from "react-bootstrap/Table";
+import backend from "../../backend";
+import dispatcher from "../../dispatcher";
+import {formatSize} from "../../utils";
+import ButtonField from "../fields/ButtonField";
+import CopyLinkPopover from "../objects/CopyLinkPopover";
+import LinkPopover from "../objects/LinkPopover";
+import "./common.scss";
+import "./StatsPane.scss";
+
+class StatsPane extends Component {
+
+ state = {
+ rules: []
+ };
+
+ componentDidMount() {
+ this.loadStats();
+ this.loadResourcesStats();
+ this.loadRules();
+ dispatcher.register("notifications", this.handleNotifications);
+ document.title = "caronte:~/stats$";
+ this.intervalToken = setInterval(() => this.loadResourcesStats(), 3000);
+ }
+
+ componentWillUnmount() {
+ dispatcher.unregister(this.handleNotifications);
+ clearInterval(this.intervalToken);
+ }
+
+ handleNotifications = (payload) => {
+ if (payload.event.startsWith("pcap")) {
+ this.loadStats();
+ } else if (payload.event.startsWith("rules")) {
+ this.loadRules();
+ }
+ };
+
+ loadStats = () => {
+ backend.get("/api/statistics/totals")
+ .then((res) => this.setState({stats: res.json, statsStatusCode: res.status}))
+ .catch((res) => this.setState({
+ stats: res.json, statsStatusCode: res.status,
+ statsResponse: JSON.stringify(res.json)
+ }));
+ };
+
+ loadResourcesStats = () => {
+ backend.get("/api/resources/system")
+ .then((res) => this.setState({resourcesStats: res.json, resourcesStatsStatusCode: res.status}))
+ .catch((res) => this.setState({
+ resourcesStats: res.json, resourcesStatsStatusCode: res.status,
+ resourcesStatsResponse: JSON.stringify(res.json)
+ }));
+ };
+
+ loadRules = () => {
+ backend.get("/api/rules").then((res) => this.setState({rules: res.json}));
+ };
+
+ render() {
+ const s = this.state.stats;
+ const rs = this.state.resourcesStats;
+
+ const ports = s ? Object.keys(s["connections_per_service"]) : [];
+ let connections = 0, clientBytes = 0, serverBytes = 0, totalBytes = 0, duration = 0;
+ let servicesStats = ports.map((port) => {
+ connections += s["connections_per_service"][port];
+ clientBytes += s["client_bytes_per_service"][port];
+ serverBytes += s["server_bytes_per_service"][port];
+ totalBytes += s["total_bytes_per_service"][port];
+ duration += s["duration_per_service"][port];
+
+ return <tr key={port} className="row-small row-clickable">
+ <td>{port}</td>
+ <td>{formatSize(s["connections_per_service"][port])}</td>
+ <td>{formatSize(s["client_bytes_per_service"][port])}B</td>
+ <td>{formatSize(s["server_bytes_per_service"][port])}B</td>
+ <td>{formatSize(s["total_bytes_per_service"][port])}B</td>
+ <td>{formatSize(s["duration_per_service"][port] / 1000)}s</td>
+ </tr>;
+ });
+ servicesStats.push(<tr key="totals" className="row-small row-clickable font-weight-bold">
+ <td>totals</td>
+ <td>{formatSize(connections)}</td>
+ <td>{formatSize(clientBytes)}B</td>
+ <td>{formatSize(serverBytes)}B</td>
+ <td>{formatSize(totalBytes)}B</td>
+ <td>{formatSize(duration / 1000)}s</td>
+ </tr>);
+
+ const rulesStats = this.state.rules.map((r) =>
+ <tr key={r.id} className="row-small row-clickable">
+ <td><CopyLinkPopover text={r["id"].substring(0, 8)} value={r["id"]}/></td>
+ <td>{r["name"]}</td>
+ <td><ButtonField name={r["color"]} color={r["color"]} small/></td>
+ <td>{formatSize(s && s["matched_rules"] && s["matched_rules"][r.id] ? s["matched_rules"][r.id] : 0)}</td>
+ </tr>
+ );
+
+ const cpuStats = (rs ? rs["cpu_times"] : []).map((cpu, index) =>
+ <tr key={cpu["cpu"]} className="row-small row-clickable">
+ <td>{cpu["cpu"]}</td>
+ <td>{cpu["user"]}</td>
+ <td>{cpu["system"]}</td>
+ <td>{cpu["idle"]}</td>
+ <td>{cpu["nice"]}</td>
+ <td>{cpu["iowait"]}</td>
+ <td>{rs["cpu_percents"][index].toFixed(2)} %</td>
+ </tr>
+ );
+
+ return (
+ <div className="pane-container stats-pane">
+ <div className="pane-section stats-list">
+ <div className="section-header">
+ <span className="api-request">GET /api/statistics/totals</span>
+ <span className="api-response"><LinkPopover text={this.state.statsStatusCode}
+ content={this.state.statsResponse}
+ placement="left"/></span>
+ </div>
+
+ <div className="section-content">
+ <div className="section-table">
+ <Table borderless size="sm">
+ <thead>
+ <tr>
+ <th>service</th>
+ <th>connections</th>
+ <th>client_bytes</th>
+ <th>server_bytes</th>
+ <th>total_bytes</th>
+ <th>duration</th>
+ </tr>
+ </thead>
+ <tbody>
+ {servicesStats}
+ </tbody>
+ </Table>
+ </div>
+
+ <div className="section-table">
+ <Table borderless size="sm">
+ <thead>
+ <tr>
+ <th>rule_id</th>
+ <th>rule_name</th>
+ <th>rule_color</th>
+ <th>occurrences</th>
+ </tr>
+ </thead>
+ <tbody>
+ {rulesStats}
+ </tbody>
+ </Table>
+ </div>
+ </div>
+ </div>
+
+ <div className="pane-section stats-list" style={{"paddingTop": "10px"}}>
+ <div className="section-header">
+ <span className="api-request">GET /api/resources/system</span>
+ <span className="api-response"><LinkPopover text={this.state.resourcesStatsStatusCode}
+ content={this.state.resourcesStatsResponse}
+ placement="left"/></span>
+ </div>
+
+ <div className="section-content">
+ <div className="section-table">
+ <Table borderless size="sm">
+ <thead>
+ <tr>
+ <th>type</th>
+ <th>total</th>
+ <th>used</th>
+ <th>free</th>
+ <th>shared</th>
+ <th>buff/cache</th>
+ <th>available</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr className="row-small row-clickable">
+ <td>mem</td>
+ <td>{rs && formatSize(rs["virtual_memory"]["total"])}</td>
+ <td>{rs && formatSize(rs["virtual_memory"]["used"])}</td>
+ <td>{rs && formatSize(rs["virtual_memory"]["free"])}</td>
+ <td>{rs && formatSize(rs["virtual_memory"]["shared"])}</td>
+ <td>{rs && formatSize(rs["virtual_memory"]["cached"])}</td>
+ <td>{rs && formatSize(rs["virtual_memory"]["available"])}</td>
+ </tr>
+ <tr className="row-small row-clickable">
+ <td>swap</td>
+ <td>{rs && formatSize(rs["virtual_memory"]["swaptotal"])}</td>
+ <td>{rs && formatSize(rs["virtual_memory"]["swaptotal"])}</td>
+ <td>{rs && formatSize(rs["virtual_memory"]["swapfree"])}</td>
+ <td>-</td>
+ <td>-</td>
+ <td>-</td>
+ </tr>
+ </tbody>
+ </Table>
+ </div>
+
+ <div className="section-table">
+ <Table borderless size="sm">
+ <thead>
+ <tr>
+ <th>cpu</th>
+ <th>user</th>
+ <th>system</th>
+ <th>idle</th>
+ <th>nice</th>
+ <th>iowait</th>
+ <th>used_percent</th>
+ </tr>
+ </thead>
+ <tbody>
+ {cpuStats}
+ </tbody>
+ </Table>
+ </div>
+
+ <div className="section-table">
+ <Table borderless size="sm">
+ <thead>
+ <tr>
+ <th>disk_path</th>
+ <th>fs_type</th>
+ <th>total</th>
+ <th>free</th>
+ <th>used</th>
+ <th>used_percent</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr className="row-small row-clickable">
+ <td>{rs && rs["disk_usage"]["path"]}</td>
+ <td>{rs && rs["disk_usage"]["fstype"]}</td>
+ <td>{rs && formatSize(rs["disk_usage"]["total"])}</td>
+ <td>{rs && formatSize(rs["disk_usage"]["free"])}</td>
+ <td>{rs && formatSize(rs["disk_usage"]["used"])}</td>
+ <td>{rs && rs["disk_usage"]["usedPercent"].toFixed(2)} %</td>
+ </tr>
+ </tbody>
+ </Table>
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+ }
+
+}
+
+export default StatsPane;
diff --git a/frontend/src/components/panels/StatsPane.scss b/frontend/src/components/panels/StatsPane.scss
new file mode 100644
index 0000000..24a0a33
--- /dev/null
+++ b/frontend/src/components/panels/StatsPane.scss
@@ -0,0 +1,21 @@
+@import "../../colors.scss";
+
+.stats-pane {
+ display: flex;
+ flex-direction: column;
+
+ .stats-list {
+ position: relative;
+ flex: 1;
+ height: 50%;
+
+ .section-content {
+ overflow-y: scroll;
+ height: calc(100% - 10px);
+ }
+
+ .section-table {
+ margin-bottom: 10px;
+ }
+ }
+}
diff --git a/frontend/src/utils.js b/frontend/src/utils.js
index 445e576..4849a92 100644
--- a/frontend/src/utils.js
+++ b/frontend/src/utils.js
@@ -120,8 +120,10 @@ export function formatSize(size) {
return `${size}`;
} else if (size < 1000000) {
return `${(size / 1000).toFixed(1)}K`;
- } else {
+ } else if (size < 1000000000) {
return `${(size / 1000000).toFixed(1)}M`;
+ } else {
+ return `${(size / 1000000000).toFixed(1)}Gi`;
}
}
diff --git a/frontend/yarn.lock b/frontend/yarn.lock
index f1de5ae..d8718db 100644
--- a/frontend/yarn.lock
+++ b/frontend/yarn.lock
@@ -9,7 +9,7 @@
dependencies:
"@babel/highlight" "^7.8.3"
-"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.8.3":
+"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.11", "@babel/code-frame@^7.8.3":
version "7.12.11"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f"
integrity sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==
@@ -64,7 +64,7 @@
semver "^5.4.1"
source-map "^0.5.0"
-"@babel/generator@^7.12.10", "@babel/generator@^7.4.0", "@babel/generator@^7.9.0":
+"@babel/generator@^7.12.10", "@babel/generator@^7.12.11", "@babel/generator@^7.4.0", "@babel/generator@^7.9.0":
version "7.12.11"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.12.11.tgz#98a7df7b8c358c9a37ab07a24056853016aba3af"
integrity sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==
@@ -88,23 +88,6 @@
"@babel/helper-explode-assignable-expression" "^7.10.4"
"@babel/types" "^7.10.4"
-"@babel/helper-builder-react-jsx-experimental@^7.12.11":
- version "7.12.11"
- resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx-experimental/-/helper-builder-react-jsx-experimental-7.12.11.tgz#a39616d7e4cf8f9da1f82b5fc3ee1f7406beeb11"
- integrity sha512-4oGVOekPI8dh9JphkPXC68iIuP6qp/RPbaPmorRmEFbRAHZjSqxPjqHudn18GVDPgCuFM/KdFXc63C17Ygfa9w==
- dependencies:
- "@babel/helper-annotate-as-pure" "^7.12.10"
- "@babel/helper-module-imports" "^7.12.5"
- "@babel/types" "^7.12.11"
-
-"@babel/helper-builder-react-jsx@^7.10.4":
- version "7.10.4"
- resolved "https://registry.yarnpkg.com/@babel/helper-builder-react-jsx/-/helper-builder-react-jsx-7.10.4.tgz#8095cddbff858e6fa9c326daee54a2f2732c1d5d"
- integrity sha512-5nPcIZ7+KKDxT1427oBivl9V9YTal7qk0diccnh7RrcgrT/pGFOjgGw1dgryyx1GvHEpXVfoDF6Ak3rTiWh8Rg==
- dependencies:
- "@babel/helper-annotate-as-pure" "^7.10.4"
- "@babel/types" "^7.10.4"
-
"@babel/helper-compilation-targets@^7.12.5", "@babel/helper-compilation-targets@^7.8.7":
version "7.12.5"
resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.12.5.tgz#cb470c76198db6a24e9dbc8987275631e5d29831"
@@ -150,7 +133,7 @@
dependencies:
"@babel/types" "^7.12.1"
-"@babel/helper-function-name@^7.10.4":
+"@babel/helper-function-name@^7.10.4", "@babel/helper-function-name@^7.12.11":
version "7.12.11"
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz#1fd7738aee5dcf53c3ecff24f1da9c511ec47b42"
integrity sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==
@@ -247,7 +230,7 @@
dependencies:
"@babel/types" "^7.12.1"
-"@babel/helper-split-export-declaration@^7.10.4", "@babel/helper-split-export-declaration@^7.11.0":
+"@babel/helper-split-export-declaration@^7.10.4", "@babel/helper-split-export-declaration@^7.11.0", "@babel/helper-split-export-declaration@^7.12.11":
version "7.12.11"
resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz#1b4cc424458643c47d37022223da33d76ea4603a"
integrity sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==
@@ -292,15 +275,15 @@
chalk "^2.0.0"
js-tokens "^4.0.0"
-"@babel/parser@^7.1.0", "@babel/parser@^7.12.10", "@babel/parser@^7.12.7", "@babel/parser@^7.4.3", "@babel/parser@^7.7.0", "@babel/parser@^7.9.0":
+"@babel/parser@^7.1.0", "@babel/parser@^7.12.10", "@babel/parser@^7.12.11", "@babel/parser@^7.12.7", "@babel/parser@^7.4.3", "@babel/parser@^7.7.0", "@babel/parser@^7.9.0":
version "7.12.11"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.11.tgz#9ce3595bcd74bc5c466905e86c535b8b25011e79"
integrity sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==
"@babel/plugin-proposal-async-generator-functions@^7.12.1", "@babel/plugin-proposal-async-generator-functions@^7.8.3":
- version "7.12.1"
- resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.12.1.tgz#dc6c1170e27d8aca99ff65f4925bd06b1c90550e"
- integrity sha512-d+/o30tJxFxrA1lhzJqiUcEJdI6jKlNregCv5bASeGf2Q4MXmnwH7viDo7nhx1/ohf09oaH8j1GVYG/e3Yqk6A==
+ version "7.12.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.12.12.tgz#04b8f24fd4532008ab4e79f788468fd5a8476566"
+ integrity sha512-nrz9y0a4xmUrRq51bYkWJIO5SBZyG2ys2qinHsN0zHDHVsUaModrkpyWWWXfGqYQmOL3x9sQIcTNN/pBGpo09A==
dependencies:
"@babel/helper-plugin-utils" "^7.10.4"
"@babel/helper-remap-async-to-generator" "^7.12.1"
@@ -581,9 +564,9 @@
"@babel/helper-plugin-utils" "^7.10.4"
"@babel/plugin-transform-block-scoping@^7.12.11", "@babel/plugin-transform-block-scoping@^7.8.3":
- version "7.12.11"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.11.tgz#83ae92a104dbb93a7d6c6dd1844f351083c46b4f"
- integrity sha512-atR1Rxc3hM+VPg/NvNvfYw0npQEAcHuJ+MGZnFn6h3bo+1U3BWXMdFMlvVRApBTWKQMX7SOwRJZA5FBF/JQbvA==
+ version "7.12.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.12.tgz#d93a567a152c22aea3b1929bb118d1d0a175cdca"
+ integrity sha512-VOEPQ/ExOVqbukuP7BYJtI5ZxxsmegTwzZ04j1aF0dkSypGo9XpDHuOrABsJu+ie+penpSJheDJ11x1BEZNiyQ==
dependencies:
"@babel/helper-plugin-utils" "^7.10.4"
@@ -771,13 +754,11 @@
"@babel/helper-plugin-utils" "^7.10.4"
"@babel/plugin-transform-react-jsx-development@^7.12.7", "@babel/plugin-transform-react-jsx-development@^7.9.0":
- version "7.12.11"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.12.11.tgz#078aa7e1f5f75a68ee9598ebed90000fcb11092f"
- integrity sha512-5MvsGschXeXJsbzQGR/BH89ATMzCsM7rx95n+R7/852cGoK2JgMbacDw/A9Pmrfex4tArdMab0L5SBV4SB/Nxg==
+ version "7.12.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.12.12.tgz#bccca33108fe99d95d7f9e82046bfe762e71f4e7"
+ integrity sha512-i1AxnKxHeMxUaWVXQOSIco4tvVvvCxMSfeBMnMM06mpaJt3g+MpxYQQrDfojUQldP1xxraPSJYSMEljoWM/dCg==
dependencies:
- "@babel/helper-builder-react-jsx-experimental" "^7.12.11"
- "@babel/helper-plugin-utils" "^7.10.4"
- "@babel/plugin-syntax-jsx" "^7.12.1"
+ "@babel/plugin-transform-react-jsx" "^7.12.12"
"@babel/plugin-transform-react-jsx-self@^7.9.0":
version "7.12.1"
@@ -793,15 +774,16 @@
dependencies:
"@babel/helper-plugin-utils" "^7.10.4"
-"@babel/plugin-transform-react-jsx@^7.12.10", "@babel/plugin-transform-react-jsx@^7.9.1":
- version "7.12.11"
- resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.12.11.tgz#09a7319195946b0ddc09f9a5f01346f2cb80dfdd"
- integrity sha512-5nWOw6mTylaFU72BdZfa0dP1HsGdY3IMExpxn8LBE8dNmkQjB+W+sR+JwIdtbzkPvVuFviT3zyNbSUkuVTVxbw==
+"@babel/plugin-transform-react-jsx@^7.12.10", "@babel/plugin-transform-react-jsx@^7.12.12", "@babel/plugin-transform-react-jsx@^7.9.1":
+ version "7.12.12"
+ resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.12.12.tgz#b0da51ffe5f34b9a900e9f1f5fb814f9e512d25e"
+ integrity sha512-JDWGuzGNWscYcq8oJVCtSE61a5+XAOos+V0HrxnDieUus4UMnBEosDnY1VJqU5iZ4pA04QY7l0+JvHL1hZEfsw==
dependencies:
- "@babel/helper-builder-react-jsx" "^7.10.4"
- "@babel/helper-builder-react-jsx-experimental" "^7.12.11"
+ "@babel/helper-annotate-as-pure" "^7.12.10"
+ "@babel/helper-module-imports" "^7.12.5"
"@babel/helper-plugin-utils" "^7.10.4"
"@babel/plugin-syntax-jsx" "^7.12.1"
+ "@babel/types" "^7.12.12"
"@babel/plugin-transform-react-pure-annotations@^7.12.1":
version "7.12.1"
@@ -1115,24 +1097,24 @@
"@babel/types" "^7.12.7"
"@babel/traverse@^7.1.0", "@babel/traverse@^7.10.4", "@babel/traverse@^7.12.1", "@babel/traverse@^7.12.10", "@babel/traverse@^7.12.5", "@babel/traverse@^7.4.3", "@babel/traverse@^7.7.0", "@babel/traverse@^7.9.0":
- version "7.12.10"
- resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.12.10.tgz#2d1f4041e8bf42ea099e5b2dc48d6a594c00017a"
- integrity sha512-6aEtf0IeRgbYWzta29lePeYSk+YAFIC3kyqESeft8o5CkFlYIMX+EQDDWEiAQ9LHOA3d0oHdgrSsID/CKqXJlg==
- dependencies:
- "@babel/code-frame" "^7.10.4"
- "@babel/generator" "^7.12.10"
- "@babel/helper-function-name" "^7.10.4"
- "@babel/helper-split-export-declaration" "^7.11.0"
- "@babel/parser" "^7.12.10"
- "@babel/types" "^7.12.10"
+ version "7.12.12"
+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.12.12.tgz#d0cd87892704edd8da002d674bc811ce64743376"
+ integrity sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==
+ dependencies:
+ "@babel/code-frame" "^7.12.11"
+ "@babel/generator" "^7.12.11"
+ "@babel/helper-function-name" "^7.12.11"
+ "@babel/helper-split-export-declaration" "^7.12.11"
+ "@babel/parser" "^7.12.11"
+ "@babel/types" "^7.12.12"
debug "^4.1.0"
globals "^11.1.0"
lodash "^4.17.19"
-"@babel/types@^7.0.0", "@babel/types@^7.10.4", "@babel/types@^7.10.5", "@babel/types@^7.12.1", "@babel/types@^7.12.10", "@babel/types@^7.12.11", "@babel/types@^7.12.5", "@babel/types@^7.12.7", "@babel/types@^7.3.0", "@babel/types@^7.4.0", "@babel/types@^7.4.4", "@babel/types@^7.7.0", "@babel/types@^7.9.0":
- version "7.12.11"
- resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.11.tgz#a86e4d71e30a9b6ee102590446c98662589283ce"
- integrity sha512-ukA9SQtKThINm++CX1CwmliMrE54J6nIYB5XTwL5f/CLFW9owfls+YSU8tVW15RQ2w+a3fSbPjC6HdQNtWZkiA==
+"@babel/types@^7.0.0", "@babel/types@^7.10.4", "@babel/types@^7.10.5", "@babel/types@^7.12.1", "@babel/types@^7.12.10", "@babel/types@^7.12.11", "@babel/types@^7.12.12", "@babel/types@^7.12.5", "@babel/types@^7.12.7", "@babel/types@^7.3.0", "@babel/types@^7.4.0", "@babel/types@^7.4.4", "@babel/types@^7.7.0", "@babel/types@^7.9.0":
+ version "7.12.12"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.12.tgz#4608a6ec313abbd87afa55004d373ad04a96c299"
+ integrity sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==
dependencies:
"@babel/helper-validator-identifier" "^7.12.11"
lodash "^4.17.19"
@@ -1176,9 +1158,9 @@
"@fortawesome/fontawesome-common-types" "^0.2.32"
"@fortawesome/react-fontawesome@^0.1.9":
- version "0.1.13"
- resolved "https://registry.yarnpkg.com/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.13.tgz#ce9654bf4e537108014ec022f2423dbe8114fd62"
- integrity sha512-/HrLnIft5Ks2511Pz6TxHBIctC9QalVscAC64sufQ4sJH/sXaQlG3uR9LCu6VpEwkBemgcBLrz/QPNP/ddbjDg==
+ version "0.1.14"
+ resolved "https://registry.yarnpkg.com/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.14.tgz#bf28875c3935b69ce2dc620e1060b217a47f64ca"
+ integrity sha512-4wqNb0gRLVaBm/h+lGe8UfPPivcbuJ6ecI4hIgW0LjI7kzpYB9FkN0L9apbVzg+lsBdcTf0AlBtODjcSX5mmKA==
dependencies:
prop-types "^15.7.2"
@@ -1684,9 +1666,9 @@
integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==
"@types/node@*":
- version "14.14.14"
- resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.14.tgz#f7fd5f3cc8521301119f63910f0fb965c7d761ae"
- integrity sha512-UHnOPWVWV1z+VV8k6L1HhG7UbGBgIdghqF3l9Ny9ApPghbjICXkUJSd/b9gOgQfjM1r+37cipdw/HJ3F6ICEnQ==
+ version "14.14.16"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.16.tgz#3cc351f8d48101deadfed4c9e4f116048d437b4b"
+ integrity sha512-naXYePhweTi+BMv11TgioE2/FXU4fSl29HAH1ffxVciNsH3rYXjNP2yM8wqmSm7jS20gM8TIklKiTen+1iVncw==
"@types/parse-json@^4.0.0":
version "4.0.0"
@@ -1767,9 +1749,9 @@
integrity sha1-DSUBJorY+ZYrdA04fEZU9fjiPlI=
"@types/yargs-parser@*":
- version "15.0.0"
- resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d"
- integrity sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==
+ version "20.2.0"
+ resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.0.tgz#dd3e6699ba3237f0348cd085e4698780204842f9"
+ integrity sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA==
"@types/yargs@^13.0.0":
version "13.0.11"
@@ -2998,9 +2980,9 @@ caniuse-api@^3.0.0:
lodash.uniq "^4.5.0"
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001035, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001165:
- version "1.0.30001168"
- resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001168.tgz#6fcd098c139d003b9bd484cbb9ca26cb89907f9a"
- integrity sha512-P2zmX7swIXKu+GMMR01TWa4csIKELTNnZKc+f1CjebmZJQtTAEXmpQSoKVJVVcvPGAA0TEYTOUp3VehavZSFPQ==
+ version "1.0.30001170"
+ resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001170.tgz#0088bfecc6a14694969e391cc29d7eb6362ca6a7"
+ integrity sha512-Dd4d/+0tsK0UNLrZs3CvNukqalnVTRrxb5mcQm8rHL49t7V5ZaTygwXkrq+FB+dVDf++4ri8eJnFEJAB8332PA==
capture-exit@^2.0.0:
version "2.0.0"
@@ -4228,9 +4210,9 @@ domhandler@^2.3.0:
domelementtype "1"
dompurify@^2.1.1:
- version "2.2.4"
- resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.2.4.tgz#a98cd182b729bdd8715c3eb7a8bf8eafb2ff7410"
- integrity sha512-jE21SelIgWrGKoXGfGPA524Zt1IJFBnktwfFMHDlEYRx5FZOdc+4eEH9mkA6PuhExrq3HVpJnY8hMYUzAMl0OA==
+ version "2.2.6"
+ resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.2.6.tgz#54945dc5c0b45ce5ae228705777e8e59d7b2edc4"
+ integrity sha512-7b7ZArhhH0SP6W2R9cqK6RjaU82FZ2UPM7RO8qN1b1wyvC/NY1FNWcX1Pu00fFOAnzEORtwXe4bPaClg6pUybQ==
domutils@1.5.1:
version "1.5.1"
@@ -4302,9 +4284,9 @@ ee-first@1.1.1:
integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
electron-to-chromium@^1.3.378, electron-to-chromium@^1.3.621:
- version "1.3.627"
- resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.627.tgz#4acdbbbbe31eb605fba8380063fd9c8a7e5ca4a0"
- integrity sha512-O5IVRS4sCxP2+vECAp7uHkaI8V+dKYpuCyBcLn+hqVAOy/RONd8zx+6eH7TuWSTBYs/oUrzBXkNMZuVsQd58kQ==
+ version "1.3.633"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.633.tgz#16dd5aec9de03894e8d14a1db4cda8a369b9b7fe"
+ integrity sha512-bsVCsONiVX1abkWdH7KtpuDAhsQ3N3bjPYhROSAXE78roJKet0Y5wznA14JE9pzbwSZmSMAW6KiKYf1RvbTJkA==
elliptic@^6.5.3:
version "6.5.3"
@@ -4378,9 +4360,9 @@ entities@^2.0.0:
integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==
errno@^0.1.3, errno@~0.1.7:
- version "0.1.7"
- resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618"
- integrity sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==
+ version "0.1.8"
+ resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f"
+ integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==
dependencies:
prr "~1.0.1"
@@ -5311,9 +5293,9 @@ get-caller-file@^2.0.1:
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
get-intrinsic@^1.0.0, get-intrinsic@^1.0.1:
- version "1.0.1"
- resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.0.1.tgz#94a9768fcbdd0595a1c9273aacf4c89d075631be"
- integrity sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg==
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.0.2.tgz#6820da226e50b24894e08859469dc68361545d49"
+ integrity sha512-aeX0vrFm21ILl3+JpFFRNe9aUvp6VFZb2/CTbgLb8j75kOhvoNYjt9d8KA/tJG4gSo8nzEDedRl0h7vDmBYRVg==
dependencies:
function-bind "^1.1.1"
has "^1.0.3"
@@ -5658,9 +5640,9 @@ html-encoding-sniffer@^1.0.2:
whatwg-encoding "^1.0.1"
html-entities@^1.2.1:
- version "1.3.3"
- resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.3.3.tgz#3dca638a43ee7de316fc23067398491152ad4736"
- integrity sha512-/VulV3SYni1taM7a4RMdceqzJWR39gpZHjBwUnsCFKWV/GJkD14CJ5F7eWcZozmHJK0/f/H5U3b3SiPkuvxMgg==
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.4.0.tgz#cfbd1b01d2afaf9adca1b10ae7dffab98c71d2dc"
+ integrity sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA==
html-escaper@^2.0.0:
version "2.0.2"
@@ -5869,9 +5851,9 @@ import-fresh@^2.0.0:
resolve-from "^3.0.0"
import-fresh@^3.0.0, import-fresh@^3.1.0:
- version "3.2.2"
- resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.2.2.tgz#fc129c160c5d68235507f4331a6baad186bdbc3e"
- integrity sha512-cTPNrlvJT6twpYy+YmKUKrTSjWFs3bjYjAhCwm+z4EOCubZxAuO+hHpRN64TqjEaYSHs7tJAE0w1CKMGmsG/lw==
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
+ integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==
dependencies:
parent-module "^1.0.0"
resolve-from "^4.0.0"
@@ -7202,9 +7184,9 @@ locate-path@^5.0.0:
p-locate "^4.1.0"
lodash-es@^4.17.15:
- version "4.17.15"
- resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.15.tgz#21bd96839354412f23d7a10340e5eac6ee455d78"
- integrity sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ==
+ version "4.17.20"
+ resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.20.tgz#29f6332eefc60e849f869c264bc71126ad61e8f7"
+ integrity sha512-JD1COMZsq8maT6mnuz1UMV0jvYD0E0aUsSOdrr1/nAG3dhqQXwRRgeW0cSqH1U43INKcqxaiVIQNOUDld7gRDA==
lodash._reinterpolate@^3.0.0:
version "3.0.0"
@@ -9157,9 +9139,9 @@ postcss-selector-matches@^4.0.0:
postcss "^7.0.2"
postcss-selector-not@^4.0.0:
- version "4.0.0"
- resolved "https://registry.yarnpkg.com/postcss-selector-not/-/postcss-selector-not-4.0.0.tgz#c68ff7ba96527499e832724a2674d65603b645c0"
- integrity sha512-W+bkBZRhqJaYN8XAnbbZPLWMvZD1wKTu0UxtFKdhtGjWYmxhkUneoeOhRJKdAE5V7ZTlnbHfCR+6bNwK9e1dTQ==
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/postcss-selector-not/-/postcss-selector-not-4.0.1.tgz#263016eef1cf219e0ade9a913780fc1f48204cbf"
+ integrity sha512-YolvBgInEK5/79C+bdFMyzqTg6pkYqDbzZIST/PDMqa/o3qtXenD05apBG2jLgT0/BQ77d4U2UK12jWpilqMAQ==
dependencies:
balanced-match "^1.0.0"
postcss "^7.0.2"
@@ -9259,9 +9241,9 @@ prepend-http@^1.0.0:
integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=
pretty-bytes@^5.1.0:
- version "5.4.1"
- resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.4.1.tgz#cd89f79bbcef21e3d21eb0da68ffe93f803e884b"
- integrity sha512-s1Iam6Gwz3JI5Hweaz4GoCD1WUNUIyzePFy5+Js2hjwGVt2Z79wNN+ZKOZ2vB6C+Xs6njyB84Z1IthQg8d9LxA==
+ version "5.5.0"
+ resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.5.0.tgz#0cecda50a74a941589498011cf23275aa82b339e"
+ integrity sha512-p+T744ZyjjiaFlMUZZv6YPC5JrkNj8maRmPaQCWFJFplUAzpIUTRaTcS+7wmZtUoFXHtESJb23ISliaWyz3SHA==
pretty-error@^2.1.1:
version "2.1.2"
diff --git a/statistics_controller.go b/statistics_controller.go
index 29f3fec..989a2e4 100644
--- a/statistics_controller.go
+++ b/statistics_controller.go
@@ -26,10 +26,11 @@ import (
type StatisticRecord struct {
RangeStart time.Time `json:"range_start" bson:"_id"`
- ConnectionsPerService map[uint16]int `json:"connections_per_service" bson:"connections_per_service"`
- ClientBytesPerService map[uint16]int `json:"client_bytes_per_service" bson:"client_bytes_per_service"`
- ServerBytesPerService map[uint16]int `json:"server_bytes_per_service" bson:"server_bytes_per_service"`
- TotalBytesPerService map[uint16]int `json:"total_bytes_per_service" bson:"total_bytes_per_service"`
+ RangeEnd time.Time `json:"range_end"`
+ ConnectionsPerService map[uint16]int64 `json:"connections_per_service" bson:"connections_per_service"`
+ ClientBytesPerService map[uint16]int64 `json:"client_bytes_per_service" bson:"client_bytes_per_service"`
+ ServerBytesPerService map[uint16]int64 `json:"server_bytes_per_service" bson:"server_bytes_per_service"`
+ TotalBytesPerService map[uint16]int64 `json:"total_bytes_per_service" bson:"total_bytes_per_service"`
DurationPerService map[uint16]int64 `json:"duration_per_service" bson:"duration_per_service"`
MatchedRules map[string]int64 `json:"matched_rules" bson:"matched_rules"`
}
@@ -99,5 +100,69 @@ func (sc *StatisticsController) GetStatistics(context context.Context, filter St
return []StatisticRecord{}
}
+ for i, _ := range statisticRecords {
+ statisticRecords[i].RangeEnd = statisticRecords[i].RangeStart.Add(time.Minute)
+ }
+
return statisticRecords
}
+
+func (sc *StatisticsController) GetTotalStatistics(context context.Context, filter StatisticsFilter) StatisticRecord {
+ totalStats := StatisticRecord{}
+ statisticsPerMinute := sc.GetStatistics(context, filter)
+
+ if len(statisticsPerMinute) == 0 {
+ return totalStats
+ }
+
+ totalStats.RangeStart = statisticsPerMinute[0].RangeStart
+ totalStats.RangeEnd = statisticsPerMinute[len(statisticsPerMinute) - 1].RangeEnd
+
+ if statisticsPerMinute[0].ConnectionsPerService != nil {
+ totalStats.ConnectionsPerService = make(map[uint16]int64)
+ }
+ if statisticsPerMinute[0].ClientBytesPerService != nil {
+ totalStats.ClientBytesPerService = make(map[uint16]int64)
+ }
+ if statisticsPerMinute[0].ServerBytesPerService != nil {
+ totalStats.ServerBytesPerService = make(map[uint16]int64)
+ }
+ if statisticsPerMinute[0].TotalBytesPerService != nil {
+ totalStats.TotalBytesPerService = make(map[uint16]int64)
+ }
+ if statisticsPerMinute[0].DurationPerService != nil {
+ totalStats.DurationPerService = make(map[uint16]int64)
+ }
+ if statisticsPerMinute[0].MatchedRules != nil {
+ totalStats.MatchedRules = make(map[string]int64)
+ }
+
+ aggregateServicesMap := func(accumulator map[uint16]int64, record map[uint16]int64) {
+ if accumulator == nil || record == nil {
+ return
+ }
+ for k, v := range record {
+ accumulator[k] += v
+ }
+ }
+
+ aggregateMatchedRulesMap := func(accumulator map[string]int64, record map[string]int64) {
+ if accumulator == nil || record == nil {
+ return
+ }
+ for k, v := range record {
+ accumulator[k] += v
+ }
+ }
+
+ for _, record := range statisticsPerMinute {
+ aggregateServicesMap(totalStats.ConnectionsPerService, record.ConnectionsPerService)
+ aggregateServicesMap(totalStats.ClientBytesPerService, record.ClientBytesPerService)
+ aggregateServicesMap(totalStats.ServerBytesPerService, record.ServerBytesPerService)
+ aggregateServicesMap(totalStats.TotalBytesPerService, record.TotalBytesPerService)
+ aggregateServicesMap(totalStats.DurationPerService, record.DurationPerService)
+ aggregateMatchedRulesMap(totalStats.MatchedRules, record.MatchedRules)
+ }
+
+ return totalStats
+}