[release] v0.12.0-unstable30

This commit is contained in:
Yann Stepienik 2023-11-01 20:39:29 +00:00
parent 53db1b883d
commit 0daf229b56
12 changed files with 285 additions and 79 deletions

View file

@ -37,7 +37,7 @@ const a11yProps = (index) => {
};
};
const PrettyTabbedView = ({ tabs, isLoading, currentTab, setCurrentTab }) => {
const PrettyTabbedView = ({ tabs, isLoading, currentTab, setCurrentTab, fullwidth }) => {
const [value, setValue] = useState(0);
const isMobile = useMediaQuery((theme) => theme.breakpoints.down('md'));
@ -55,7 +55,7 @@ const PrettyTabbedView = ({ tabs, isLoading, currentTab, setCurrentTab }) => {
};
return (
<Box display="flex" height="100%" flexDirection={isMobile ? 'column' : 'row'}>
<Box fullwidth={fullwidth} display="flex" height="100%" flexDirection={isMobile ? 'column' : 'row'}>
{(isMobile && !currentTab) ? (
<Select value={value} onChange={handleSelectChange} sx={{ minWidth: 120, marginBottom: '15px' }}>
{tabs.map((tab, index) => (

View file

@ -69,7 +69,7 @@ const PlotComponent = ({ title, slot, data, SimpleDesign, withSelector, xAxis, z
}
const dataSeries = [];
toProcess.forEach((serie) => {
dataSeries.push({
serie && dataSeries.push({
name: serie.Label,
dataAxis: xAxis.map((date) => {
if(slot === 'latest') {
@ -93,7 +93,8 @@ const PlotComponent = ({ title, slot, data, SimpleDesign, withSelector, xAxis, z
...prevState,
colors: [
theme.palette.primary.main.replace('rgb(', 'rgba('),
theme.palette.secondary.main.replace('rgb(', 'rgba(')
theme.palette.secondary.main.replace('rgb(', 'rgba('),
theme.palette.error.main.replace('rgb(', 'rgba('),
],
xaxis: {
categories:
@ -114,7 +115,7 @@ const PlotComponent = ({ title, slot, data, SimpleDesign, withSelector, xAxis, z
max: zoom.xaxis && zoom.xaxis.max,
},
yaxis: toProcess.map((thisdata, ida) => ({
opposite: ida === 1,
opposite: ida === 0,
labels: {
style: {
colors: [secondary],
@ -124,9 +125,9 @@ const PlotComponent = ({ title, slot, data, SimpleDesign, withSelector, xAxis, z
formatter: FormaterForMetric(thisdata)
},
title: {
text: SimpleDesign ? '' : thisdata.Label,
text: thisdata && thisdata.Label,
},
max: thisdata.Max ? thisdata.Max : undefined,
max: (thisdata && thisdata.Max) ? thisdata.Max : undefined,
})),
grid: {
borderColor: line

View file

@ -1,31 +1,49 @@
export const simplifyNumber = (num) => {
export const simplifyNumber = (num, unit) => {
if(!num) return 0;
num = Math.round(num * 100) / 100;
if (Math.abs(num) >= 1e12) {
return (num / 1e12).toFixed(1) + ' T'; // Convert to Millions
} else if (Math.abs(num) >= 1e9) {
return (num / 1e9).toFixed(1) + ' G'; // Convert to Millions
} else if (Math.abs(num) >= 1e6) {
return (num / 1e6).toFixed(1) + ' M'; // Convert to Millions
} else if (Math.abs(num) >= 1e3) {
return (num / 1e3).toFixed(1) + ' K'; // Convert to Thousands
if(unit.toLowerCase() === "b") {
if (Math.abs(num) >= 1e12) {
return (num / 1e12).toFixed(1) + ' TB'; // Convert to Millions
} else if (Math.abs(num) >= 1e9) {
return (num / 1e9).toFixed(1) + ' GB'; // Convert to Millions
} else if (Math.abs(num) >= 1e6) {
return (num / 1e6).toFixed(1) + ' MB'; // Convert to Millions
} else if (Math.abs(num) >= 1e3) {
return (num / 1e3).toFixed(1) + ' KB'; // Convert to Thousands
} else {
return num.toString();
}
} else if (unit.toLowerCase() === "ms") {
if (Math.abs(num) >= 1e3) {
return (num / 1e3).toFixed(1) + ' s'; // Convert to Seconds
} else {
return num.toString() + ' ms';
}
} else {
return num.toString();
if (Math.abs(num) >= 1e6) {
return (num / 1e6).toFixed(1) + ' M' + unit; // Convert to Millions
} else if (Math.abs(num) >= 1e3) {
return (num / 1e3).toFixed(1) + ' K' + unit; // Convert to Thousands
} else {
return num.toString() + ' ' + unit;
}
}
}
export const FormaterForMetric = (metric, displayMax) => {
return (num) => {
if(!metric) return num;
if(metric.Scale)
num /= metric.Scale;
num = simplifyNumber(num) + metric.Unit;
num = simplifyNumber(num, metric.Unit);
if(displayMax && metric.Max) {
num += ` / ${simplifyNumber(metric.Max)}`
num += ` / ${simplifyNumber(metric.Max, metric.Unit)}`
}
return num;

View file

@ -1,4 +1,4 @@
import { useEffect, useState } from 'react';
import { useEffect, useRef, useState } from 'react';
// material-ui
import {
@ -45,6 +45,9 @@ import TableComponent from './components/table';
import { HomeBackground, TransparentHeader } from '../home';
import { formatDate } from './components/utils';
import MiniPlotComponent from './components/mini-plot';
import ResourceDashboard from './resourceDashboard';
import PrettyTabbedView from '../../components/tabbedView/tabbedView';
import ProxyDashboard from './proxyDashboard';
// avatar style
const avatarSX = {
@ -84,14 +87,16 @@ const status = [
const DashboardDefault = () => {
const [value, setValue] = useState('today');
const [slot, setSlot] = useState('latest');
const [currentTab, setCurrentTab] = useState(0);
const currentTabRef = useRef(currentTab);
const [coStatus, setCoStatus] = useState(null);
const [metrics, setMetrics] = useState(null);
const [isCreatingDB, setIsCreatingDB] = useState(false);
const [zoom, setZoom] = useState({
xaxis: {}
});
const [coStatus, setCoStatus] = useState(null);
const [metrics, setMetrics] = useState(null);
const [isCreatingDB, setIsCreatingDB] = useState(false);
const resetZoom = () => {
setZoom({
@ -99,15 +104,25 @@ const DashboardDefault = () => {
});
}
const refreshMetrics = () => {
API.metrics.get(["cosmos.system.*"]).then((res) => {
let finalMetrics = {};
if(res.data) {
res.data.forEach((metric) => {
finalMetrics[metric.Key] = metric;
});
setMetrics(finalMetrics);
}
const refreshMetrics = (override) => {
let todo = [
["cosmos.system.*"],
["cosmos.proxy.*"],
]
let t = typeof override === 'number' ? override : currentTabRef.current;
API.metrics.get(todo[t]).then((res) => {
setMetrics(prevMetrics => {
let finalMetrics = prevMetrics ? { ...prevMetrics } : {};
if(res.data) {
res.data.forEach((metric) => {
finalMetrics[metric.Key] = metric;
});
return finalMetrics;
}
});
});
};
@ -131,6 +146,10 @@ const DashboardDefault = () => {
};
}, []);
useEffect(() => {
currentTabRef.current = currentTab;
}, [currentTab]);
let xAxis = [];
if(slot === 'latest') {
@ -216,6 +235,31 @@ const DashboardDefault = () => {
</Button>}
</Stack>
</Grid>
<Grid item xs={12} md={12} lg={12}>
<PrettyTabbedView
currentTab={currentTab}
setCurrentTab={(tab) => {
setCurrentTab(tab)
refreshMetrics(tab);
}}
fullwidth
isLoading={!metrics}
tabs={[
{
title: 'Resources',
children: <ResourceDashboard xAxis={xAxis} zoom={zoom} setZoom={setZoom} slot={slot} metrics={metrics} />
},
{
title: 'Proxy',
children: <ProxyDashboard xAxis={xAxis} zoom={zoom} setZoom={setZoom} slot={slot} metrics={metrics} />
},
]}
/>
</Grid>
{/*
<Grid item xs={12} sx={{ mb: -2.25 }}>
<Typography variant="h5">Dashboard</Typography>
@ -235,50 +279,6 @@ const DashboardDefault = () => {
<Grid item md={8} sx={{ display: { sm: 'none', md: 'block', lg: 'none' } }} />
*/}
<Grid item xs={12} md={7} lg={8}>
<PlotComponent xAxis={xAxis} zoom={zoom} setZoom={setZoom} slot={slot} title={'Resources'} data={[metrics["cosmos.system.cpu.0"], metrics["cosmos.system.ram"]]}/>
</Grid>
<TableComponent xAxis={xAxis} zoom={zoom} setZoom={setZoom} slot={slot} title="Containers - Resources" data={
Object.keys(metrics).filter((key) => key.startsWith("cosmos.system.docker.cpu") || key.startsWith("cosmos.system.docker.ram")).map((key) => metrics[key])
}/>
<Grid item xs={12} md={7} lg={8}>
<PlotComponent xAxis={xAxis} zoom={zoom} setZoom={setZoom} slot={slot} title={'Network'} data={[metrics["cosmos.system.netTx"], metrics["cosmos.system.netRx"]]}/>
</Grid>
<TableComponent xAxis={xAxis} zoom={zoom} setZoom={setZoom} slot={slot} title="Containers - Network" data={
Object.keys(metrics).filter((key) => key.startsWith("cosmos.system.docker.net")).map((key) => metrics[key])
}/>
<TableComponent xAxis={xAxis} zoom={zoom} setZoom={setZoom} slot={slot} title="Disk Usage" displayMax={true}
render={(metric, value, formattedValue) => {
let percent = value / metric.Max * 100;
return <span>
{formattedValue}
<LinearProgress
variant="determinate"
color={percent > 95 ? 'error' : (percent > 75 ? 'warning' : 'info')}
value={percent} />
</span>
}}
data={
Object.keys(metrics).filter((key) => key.startsWith("cosmos.system.disk")).map((key) => metrics[key])
}/>
<Grid item xs={12} md={7} lg={8}>
<PlotComponent
zoom={zoom} setZoom={setZoom}
xAxis={xAxis}
slot={slot}
title={'Temperature'}
withSelector={'cosmos.system.temp.all'}
SimpleDesign
data={Object.keys(metrics).filter((key) => key.startsWith("cosmos.system.temp")).map((key) => metrics[key])}
/>
</Grid>
{/*
<Grid item xs={12} md={7} lg={8}>
<Grid container alignItems="center" justifyContent="space-between">

View file

@ -0,0 +1,30 @@
import {
Grid,
LinearProgress,
} from '@mui/material';
import PlotComponent from './components/plot';
import TableComponent from './components/table';
const ProxyDashboard = ({ xAxis, zoom, setZoom, slot, metrics }) => {
console.log(metrics)
return (<>
<Grid container rowSpacing={4.5} columnSpacing={2.75} >
<Grid item xs={12} md={7} lg={8}>
<PlotComponent xAxis={xAxis} zoom={zoom} setZoom={setZoom} slot={slot} title={'Requests'} data={[
metrics["cosmos.proxy.all.time"],
metrics["cosmos.proxy.all.success"],
metrics["cosmos.proxy.all.error"],
]} />
</Grid>
<TableComponent xAxis={xAxis} zoom={zoom} setZoom={setZoom} slot={slot} title="Containers - Resources" data={
Object.keys(metrics).filter((key) => key.startsWith("cosmos.proxy.route.")).map((key) => metrics[key])
} />
</Grid>
</>)
}
export default ProxyDashboard;

View file

@ -0,0 +1,59 @@
import {
Grid,
LinearProgress,
} from '@mui/material';
import PlotComponent from './components/plot';
import TableComponent from './components/table';
const ResourceDashboard = ({ xAxis, zoom, setZoom, slot, metrics }) => {
return (<>
<Grid container rowSpacing={4.5} columnSpacing={2.75} >
<Grid item xs={12} md={7} lg={8}>
<PlotComponent xAxis={xAxis} zoom={zoom} setZoom={setZoom} slot={slot} title={'Resources'} data={[metrics["cosmos.system.cpu.0"], metrics["cosmos.system.ram"]]} />
</Grid>
<TableComponent xAxis={xAxis} zoom={zoom} setZoom={setZoom} slot={slot} title="Containers - Resources" data={
Object.keys(metrics).filter((key) => key.startsWith("cosmos.system.docker.cpu") || key.startsWith("cosmos.system.docker.ram")).map((key) => metrics[key])
} />
<Grid item xs={12} md={7} lg={8}>
<PlotComponent xAxis={xAxis} zoom={zoom} setZoom={setZoom} slot={slot} title={'Network'} data={[metrics["cosmos.system.netTx"], metrics["cosmos.system.netRx"]]} />
</Grid>
<TableComponent xAxis={xAxis} zoom={zoom} setZoom={setZoom} slot={slot} title="Containers - Network" data={
Object.keys(metrics).filter((key) => key.startsWith("cosmos.system.docker.net")).map((key) => metrics[key])
} />
<TableComponent xAxis={xAxis} zoom={zoom} setZoom={setZoom} slot={slot} title="Disk Usage" displayMax={true}
render={(metric, value, formattedValue) => {
let percent = value / metric.Max * 100;
return <span>
{formattedValue}
<LinearProgress
variant="determinate"
color={percent > 95 ? 'error' : (percent > 75 ? 'warning' : 'info')}
value={percent} />
</span>
}}
data={
Object.keys(metrics).filter((key) => key.startsWith("cosmos.system.disk")).map((key) => metrics[key])
} />
<Grid item xs={12} md={7} lg={8}>
<PlotComponent
zoom={zoom} setZoom={setZoom}
xAxis={xAxis}
slot={slot}
title={'Temperature'}
withSelector={'cosmos.system.temp.all'}
SimpleDesign
data={Object.keys(metrics).filter((key) => key.startsWith("cosmos.system.temp")).map((key) => metrics[key])}
/>
</Grid>
</Grid>
</>)
}
export default ResourceDashboard;

View file

@ -1,6 +1,6 @@
{
"name": "cosmos-server",
"version": "0.12.0-unstable29",
"version": "0.12.0-unstable30",
"description": "",
"main": "test-server.js",
"bugs": {

93
src/metrics/middleware.go Normal file
View file

@ -0,0 +1,93 @@
package metrics
import (
"net/http"
"fmt"
"time"
"github.com/azukaar/cosmos-server/src/utils"
)
// responseWriter wraps the original http.ResponseWriter to capture the status code.
type responseWriter struct {
http.ResponseWriter
status int
}
func (rw *responseWriter) WriteHeader(status int) {
rw.status = status
rw.ResponseWriter.WriteHeader(status)
}
func MetricsMiddleware(route utils.ProxyRouteConfig) func(next http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
startTime := time.Now()
// Call the next handler (which can be another middleware or the final handler).
wrappedWriter := &responseWriter{ResponseWriter: w}
next.ServeHTTP(wrappedWriter, r)
// Calculate and log the response time.
responseTime := time.Since(startTime)
utils.Debug(fmt.Sprintf("[%s] %s %s %v", r.Method, r.RequestURI, r.RemoteAddr, responseTime))
if !utils.GetMainConfig().MonitoringDisabled {
go func() {
if wrappedWriter.status >= 400 {
PushSetMetric("proxy.all.error", 1, DataDef{
Max: 0,
Period: time.Second * 30,
Label: "Global Request Errors",
AggloType: "sum",
SetOperation: "sum",
})
PushSetMetric("proxy.route.error."+route.Name, 1, DataDef{
Max: 0,
Period: time.Second * 30,
Label: "Request Errors " + route.Name,
AggloType: "sum",
SetOperation: "sum",
})
} else {
PushSetMetric("proxy.all.success", 1, DataDef{
Max: 0,
Period: time.Second * 30,
Label: "Global Request Success",
AggloType: "sum",
SetOperation: "sum",
})
PushSetMetric("proxy.route.success."+route.Name, 1, DataDef{
Max: 0,
Period: time.Second * 30,
Label: "Request Success " + route.Name,
AggloType: "sum",
SetOperation: "sum",
})
}
PushSetMetric("proxy.all.time", int(responseTime.Milliseconds()), DataDef{
Max: 0,
Period: time.Second * 30,
Label: "Global Response Time",
AggloType: "avg",
SetOperation: "max",
Unit: "ms",
})
PushSetMetric("proxy.route.time."+route.Name, int(responseTime.Milliseconds()), DataDef{
Max: 0,
Period: time.Second * 30,
Label: "Response Time " + route.Name,
AggloType: "avg",
SetOperation: "max",
Unit: "ms",
})
}()
}
})
}
}

View file

@ -159,6 +159,7 @@ func GetSystemMetrics() {
Max: u.Total,
Period: time.Second * 120,
Label: "Disk " + part.Mountpoint,
Unit: "B",
})
}
}

View file

@ -10,9 +10,10 @@ import (
"io/ioutil"
"strconv"
spa "github.com/roberthodgen/spa-server"
"github.com/azukaar/cosmos-server/src/utils"
"github.com/azukaar/cosmos-server/src/docker"
spa "github.com/roberthodgen/spa-server"
)

View file

@ -8,6 +8,7 @@ import (
"github.com/azukaar/cosmos-server/src/user"
"github.com/azukaar/cosmos-server/src/utils"
"github.com/azukaar/cosmos-server/src/metrics"
"github.com/go-chi/httprate"
"github.com/gorilla/mux"
)
@ -145,6 +146,8 @@ func RouterGen(route utils.ProxyRouteConfig, router *mux.Router, destination htt
destination = utils.SetSecurityHeaders(destination)
}
destination = metrics.MetricsMiddleware(route)(destination)
destination = tokenMiddleware(route.AuthEnabled, route.AdminOnly)(utils.CORSHeader(originCORS)((destination)))
origin.Handler(destination)

View file

@ -327,4 +327,4 @@ func Restrictions(RestrictToConstellation bool, WhitelistInboundIPs []string) fu
next.ServeHTTP(w, r)
})
}
}
}