Relay stats

This commit is contained in:
Kieran 2023-01-11 13:05:32 +00:00
parent 357937b403
commit 6f3a0e83ce
Signed by: Kieran
GPG Key ID: DE71CEB3925BE941
4 changed files with 98 additions and 25 deletions

View File

@ -1,10 +1,24 @@
.relay {
margin-bottom: 10px;
margin-top: 10px;
background-color: var(--gray-secondary);
border-radius: 5px;
text-align: start;
display: grid;
grid-template-columns: min-content auto;
overflow: hidden;
}
.relay > div {
padding: 5px;
}
.relay > div:first-child {
}
.relay-extra {
padding: 5px;
margin: 0 5px;
background-color: var(--gray-tertiary);
border-radius: 0 0 5px 5px;
white-space: nowrap;
}

View File

@ -1,9 +1,9 @@
import "./Relay.css"
import { faPlug, faTrash, faSquareCheck, faSquareXmark } from "@fortawesome/free-solid-svg-icons";
import { faPlug, faTrash, faSquareCheck, faSquareXmark, faWifi, faUpload, faDownload, faPlugCircleXmark, faEllipsisVertical } from "@fortawesome/free-solid-svg-icons";
import useRelayState from "../feed/RelayState";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { useMemo } from "react";
import { useMemo, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { removeRelay, setRelays } from "../state/Login";
@ -13,6 +13,7 @@ export default function Relay(props) {
const relaySettings = useSelector(s => s.login.relays[props.addr]);
const state = useRelayState(props.addr);
const name = useMemo(() => new URL(props.addr).host, [props.addr]);
const [showExtra, setShowExtra] = useState(false);
function configure(o) {
dispatch(setRelays({
@ -20,31 +21,59 @@ export default function Relay(props) {
}));
}
let latency = parseInt(state?.avgLatency ?? 0);
return (
<>
<div className="flex relay w-max">
<div>
<FontAwesomeIcon icon={faPlug} color={state?.connected ? "var(--success)" : "var(--error)"} />
<div className={`relay w-max`}>
<div className={`flex ${state?.connected ? "bg-success" : "bg-error"}`}>
<FontAwesomeIcon icon={faPlug} />
</div>
<div className="f-grow f-col">
<b>{name}</b>
<div>
Write
<span className="pill" onClick={() => configure({ write: !relaySettings.write, read: relaySettings.read })}>
<FontAwesomeIcon icon={relaySettings.write ? faSquareCheck : faSquareXmark} />
</span>
Read
<span className="pill" onClick={() => configure({ write: relaySettings.write, read: !relaySettings.read })}>
<FontAwesomeIcon icon={relaySettings.read ? faSquareCheck : faSquareXmark} />
</span>
<div className="flex mb10">
<b className="f-2">{name}</b>
<div className="f-1">
Write
<span className="pill" onClick={() => configure({ write: !relaySettings.write, read: relaySettings.read })}>
<FontAwesomeIcon icon={relaySettings.write ? faSquareCheck : faSquareXmark} />
</span>
</div>
<div className="f-1">
Read
<span className="pill" onClick={() => configure({ write: relaySettings.write, read: !relaySettings.read })}>
<FontAwesomeIcon icon={relaySettings.read ? faSquareCheck : faSquareXmark} />
</span>
</div>
</div>
<div className="flex">
<div className="f-grow">
<FontAwesomeIcon icon={faWifi} /> {latency > 2000 ? `${(latency / 1000).toFixed(0)} secs` : `${latency.toLocaleString()} ms`}
&nbsp;
<FontAwesomeIcon icon={faPlugCircleXmark} /> {state?.disconnects}
</div>
<div>
<span className="pill" onClick={() => setShowExtra(s => !s)}>
<FontAwesomeIcon icon={faEllipsisVertical} />
</span>
</div>
</div>
</div>
<div>
<span className="pill">
<FontAwesomeIcon icon={faTrash} onClick={() => dispatch(removeRelay(props.addr))} />
</div>
{showExtra ? <div className="flex relay-extra w-max">
<div className="f-1">
<FontAwesomeIcon icon={faUpload} /> {state?.events.send}
</div>
<div className="f-1">
<FontAwesomeIcon icon={faDownload} /> {state?.events.received}
</div>
<div className="f-1">
Delete
<span className="pill" onClick={() => dispatch(removeRelay(props.addr))}>
<FontAwesomeIcon icon={faTrash} />
</span>
</div>
</div>
</div> : null}
</>
)
}

View File

@ -133,6 +133,10 @@ textarea:placeholder {
flex: 1;
}
.f-2 {
flex: 2;
}
.f-grow {
flex-grow: 1;
}
@ -271,6 +275,14 @@ body.scroll-lock {
color: var(--error);
}
.bg-error {
background-color: var(--error);
}
.bg-success {
background-color: var(--success);
}
.root-tabs {
padding: 0;
align-items: center;

View File

@ -12,6 +12,7 @@ export class ConnectionStats {
this.SubsTimeout = 0;
this.EventsReceived = 0;
this.EventsSent = 0;
this.Disconnects = 0;
}
}
@ -28,7 +29,13 @@ export default class Connection {
this.StateHooks = {};
this.HasStateChange = true;
this.CurrentState = {
connected: false
connected: false,
disconnects: 0,
avgLatency: 0,
events: {
received: 0,
send: 0
}
};
this.LastState = Object.freeze({ ...this.CurrentState });
this.IsClosed = false;
@ -47,7 +54,7 @@ export default class Connection {
Close() {
this.IsClosed = true;
if(this.ReconnectTimer !== null) {
if (this.ReconnectTimer !== null) {
clearTimeout(this.ReconnectTimer);
this.ReconnectTimer = null;
}
@ -74,6 +81,7 @@ export default class Connection {
this.ReconnectTimer = setTimeout(() => {
this.Connect();
}, this.ConnectTimeout);
this.Stats.Disconnects++;
} else {
console.log(`[${this.Address}] Closed!`);
this.ReconnectTimer = null;
@ -88,6 +96,8 @@ export default class Connection {
switch (tag) {
case "EVENT": {
this._OnEvent(msg[1], msg[2]);
this.Stats.EventsReceived++;
this._UpdateState();
break;
}
case "EOSE": {
@ -96,7 +106,7 @@ export default class Connection {
}
case "OK": {
// feedback to broadcast call
console.debug("OK: ", msg[1]);
console.debug("OK: ", msg);
break;
}
case "NOTICE": {
@ -126,6 +136,8 @@ export default class Connection {
}
let req = ["EVENT", e.ToObject()];
this._SendJson(req);
this.Stats.EventsSent++;
this._UpdateState();
}
/**
@ -199,6 +211,11 @@ export default class Connection {
_UpdateState() {
this.CurrentState.connected = this.Socket?.readyState === WebSocket.OPEN;
this.CurrentState.events.received = this.Stats.EventsReceived;
this.CurrentState.events.send = this.Stats.EventsSent;
this.CurrentState.avgLatency = this.Stats.Latency.length > 0 ? (this.Stats.Latency.reduce((acc, v) => acc + v, 0) / this.Stats.Latency.length) : 0;
this.CurrentState.disconnects = this.Stats.Disconnects;
this.Stats.Latency = this.Stats.Latency.slice(this.Stats.Latency.length - 20); // trim
this.HasStateChange = true;
this._NotifyState();
}
@ -239,9 +256,10 @@ export default class Connection {
console.warn(`[${this.Address}][${subId}] Slow response time ${(responseTime / 1000).toFixed(1)} seconds`);
}
sub.OnEnd(this);
this.Stats.Latency.push(responseTime);
this._UpdateState();
} else {
// console.warn(`No subscription for end! ${subId}`);
// ignored for now, track as "dropped event" with connection stats
console.warn(`No subscription for end! ${subId}`);
}
}