Created
June 12, 2024 04:30
-
-
Save gocs/bc45a304523da8d982679a2eecf21445 to your computer and use it in GitHub Desktop.
svelte go net stat
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module svelted3 | |
go 1.22.2 | |
require github.com/shirou/gopsutil/v3 v3.24.4 | |
require ( | |
github.com/go-ole/go-ole v1.2.6 // indirect | |
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect | |
github.com/yusufpapurcu/wmi v1.2.4 // indirect | |
golang.org/x/sys v0.19.0 // indirect | |
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"log/slog" | |
"net/http" | |
"strings" | |
"time" | |
"github.com/shirou/gopsutil/v3/net" | |
) | |
type NetState struct { | |
prevRx uint64 | |
prevTx uint64 | |
bytesRx []int | |
bytesTx []int | |
} | |
const ( | |
// NetInterfaceAll enables all network interfaces | |
NetInterfaceAll = "all" | |
// NetInterfaceVpn is the VPN interface | |
NetInterfaceVpn = "tun0" | |
) | |
func getNetTransfer() (uint64, uint64, error) { | |
interfaces, err := net.IOCounters(true) | |
if err != nil { | |
return 0, 0, fmt.Errorf("error getting network activity: %v", err) | |
} | |
var totalBytesRecv uint64 | |
var totalBytesSent uint64 | |
interfaceMap := make(map[string]bool) | |
// Default behaviour | |
interfaceMap[NetInterfaceAll] = true | |
interfaceMap[NetInterfaceVpn] = false | |
// Build a map with wanted status for each interfaces. | |
for _, iface := range NetInterface { | |
if strings.HasPrefix(iface, "!") { | |
interfaceMap[strings.TrimPrefix(iface, "!")] = false | |
} else { | |
// if we specify a wanted interface, remove capture on all. | |
delete(interfaceMap, NetInterfaceAll) | |
interfaceMap[iface] = true | |
} | |
} | |
for _, _interface := range interfaces { | |
wanted, ok := interfaceMap[_interface.Name] | |
if wanted && ok { // Simple case | |
totalBytesRecv += _interface.BytesRecv | |
totalBytesSent += _interface.BytesSent | |
} else if ok { // Present but unwanted | |
continue | |
} else if interfaceMap[NetInterfaceAll] { // Capture other | |
totalBytesRecv += _interface.BytesRecv | |
totalBytesSent += _interface.BytesSent | |
} | |
} | |
return totalBytesRecv, totalBytesSent, nil | |
} | |
var NetInterface = []string{"all"} | |
type NetTraffic struct { | |
Recieved uint64 | |
Sent uint64 | |
TotalRecieved uint64 | |
TotalSent uint64 | |
} | |
// String returns a string representation of the NetTraffic struct | |
func (nt NetTraffic) String() string { | |
return fmt.Sprintf("%d,%d,%d,%d", nt.Recieved, nt.Sent, nt.TotalRecieved, nt.TotalSent) | |
} | |
func (ns *NetState) events(w http.ResponseWriter, r *http.Request) { | |
// Set CORS headers to allow all origins. You may want to restrict this to specific origins in a production environment. | |
w.Header().Set("Access-Control-Allow-Origin", "*") | |
w.Header().Set("Access-Control-Expose-Headers", "Content-Type") | |
w.Header().Set("Content-Type", "text/event-stream") | |
w.Header().Set("Cache-Control", "no-cache") | |
w.Header().Set("Connection", "keep-alive") | |
// loop forever by ticks until client cancels | |
t := time.NewTicker(2 * time.Second) | |
defer t.Stop() | |
for { | |
select { | |
case <-t.C: | |
totalRx, totalTx, err := getNetTransfer() | |
if err != nil { | |
http.Error(w, err.Error(), http.StatusInternalServerError) | |
return | |
} | |
var rx uint64 | |
var tx uint64 | |
if ns.prevRx != 0 { // if this isn't the first update | |
rx = totalRx - ns.prevRx | |
} | |
if ns.prevTx != 0 { // if this isn't the first update | |
tx = totalTx - ns.prevTx | |
} | |
ns.prevRx = totalRx | |
ns.prevTx = totalTx | |
ns.bytesRx = append(ns.bytesRx, int(rx)) | |
ns.bytesTx = append(ns.bytesTx, int(tx)) | |
nt := NetTraffic{Recieved: rx, Sent: tx, TotalRecieved: totalRx, TotalSent: totalTx} | |
fmt.Fprintf(w, "data:%v\n\n", nt) | |
w.(http.Flusher).Flush() | |
slog.Info("client flushed") | |
case <-r.Context().Done(): | |
slog.Info("client disconnected") | |
return | |
} | |
} | |
} | |
func main() { | |
ns := &NetState{ | |
prevRx: 0, | |
prevTx: 0, | |
bytesRx: make([]int, 0), | |
bytesTx: make([]int, 0), | |
} | |
http.HandleFunc("/events", ns.events) | |
http.ListenAndServe(":8080", nil) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script> | |
import { onMount } from "svelte"; | |
import { writable } from "svelte/store"; | |
const messages = writable([]); | |
let width = 1000; | |
let height = 200; | |
let gap = width / 100; | |
let rx_mz = ""; | |
let tx_mz = ""; | |
// let rx_tot_mz = ""; | |
// let tx_tot_mz = ""; | |
onMount(() => { | |
const evtSource = new EventSource("http://localhost:8080/events"); | |
evtSource.onmessage = function (event) { | |
let dataobj = event.data.split(","); | |
messages.update((arr) => { | |
// remove first element if array length is greater than width / 10 | |
if (arr.length > width / 10) arr.shift(); | |
arr = arr.concat(dataobj); | |
rx_mz = | |
arr | |
.map((m, i) => `${i * gap},${height - 100 - m[0]}`) | |
.join(" ") + ` ${arr.length * gap},${height}`; | |
tx_mz = | |
arr | |
.map((m, i) => `${i * gap},${height - 100 - m[1]}`) | |
.join(" ") + ` ${arr.length * gap},${height}`; | |
// rx_tot_mz = | |
// arr | |
// .map((m, i) => `${i * gap},${height - 100 - m[2]}`) | |
// .join(" ") + ` ${arr.length * gap},${height}`; | |
// tx_tot_mz = | |
// arr | |
// .map((m, i) => `${i * gap},${height - 100 - m[3]}`) | |
// .join(" ") + ` ${arr.length * gap},${height}`; | |
return arr; | |
}); | |
}; | |
}); | |
</script> | |
<div class="w-full"> | |
<svg {width} {height}> | |
// triangular filling path | |
{#if rx_mz}<path | |
class="fill-red-600/60" | |
d="M0,{height} {rx_mz}z" | |
/>{/if} | |
{#if tx_mz}<path | |
class="fill-green-600/60" | |
d="M0,{height} {tx_mz}z" | |
/>{/if} | |
<!-- {#if rx_tot_mz}<path class="fill-orange-600/60" d="M0,{height} {rx_tot_mz}z" />{/if} | |
{#if tx_tot_mz}<path class="fill-yellow-600/60" d="M0,{height} {tx_tot_mz}z" />{/if} --> | |
</svg> | |
</div> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment