DEV Community

M
M

Posted on

1

golang + sse + Data-Star for real time sys stats

Hello everyone,
today I want to show you, how simple with https://data-star.dev hypermedia framework to is to make real time "things"

lets start with some code.

SysStats using golang and data-star.dev

tools we need today is:
https://templ.guide for html templating
https://data-star.dev for realtime frontend updates via SSE
https://github.com/inhies/go-bytesize to convert bytes to normal representation like Mb Gb
https://github.com/shirou/gopsutil to get sys stats
and https://github.com/go-chi/chi for routing

first, we need to collect sys stats (memory and cpu) so there is our helper function:

type Stats struct {
    MemTotal   string
    MemPercent float64
    MemUsed    string
    MemFree    string
    SwapTotal  string
    CpuUser        int
    CpuSystem      int
    CpuIdle        int
}

func collectStats() Stats {
    v, _ := mem.VirtualMemory()
    cp, _ := cpu.Times(false)
    var stats Stats
    stats.MemUsed = bytesize.New(float64(v.Used)).String()
    stats.MemTotal = bytesize.New(float64(v.Total)).String()
    stats.MemFree = bytesize.New(float64(v.Free)).String()
    stats.MemPercent = v.UsedPercent

    for _, c := range cp {
        stats.CpuSystem += int(c.System)
        stats.CpuUser += int(c.User)
        stats.CpuIdle += int(c.Idle)
    }

    stats.CpuSystem /= len(cp)
    stats.CpuUser /= len(cp)
    stats.CpuIdle /= len(cp)
    return stats
}

Enter fullscreen mode Exit fullscreen mode

now once we have our stats we could implement our realtime updates. here is some main code for it:

r.Get("/live/stats", func(w http.ResponseWriter, r *http.Request) {
        sse := datastar.NewSSE(w, r)
        ticker := time.NewTicker(1 * time.Second)
        defer ticker.Stop()
        for {
            select {
            case <-r.Context().Done():
                return
            case <-ticker.C:
                stats := collectStats()
                sse.MergeFragmentTempl(Stat(stats))
            }
        }
    })
Enter fullscreen mode Exit fullscreen mode

this collects stats every one second, and publishes it to Data-Star via SSE

and our index page renders everything at initial load:

r.Get("/", func(w http.ResponseWriter, r *http.Request) {
        stats := collectStats()
        Main(stats).Render(r.Context(), w)
    })
Enter fullscreen mode Exit fullscreen mode

Main(statS) is main layout with doctype and all partials like "stats" section. In piece of code for /live/stats - it renders only part of page and sends that part as html to data-star which updates stats element. (not whole page are send)

and here is our templ.guide template:


templ Main(stats Stats) {
    <!DOCTYPE html>
    <html>
        <head>
            <title>Sys Stats</title>
            <script type="module" src="https://cdn.jsdelivr.net/gh/starfederation/datastar@v1.0.0-beta.9/bundles/datastar.js"></script>
        </head>
        <body data-on-load="@get('/live/stats')"></body>
        @Stat(stats)
    </html>
}

templ Stat(stats Stats) {
    <div id="stats">
    <p><strong>MemFree</strong> { fmt.Sprintf("%s", stats.MemFree) }</p>
    <p><strong>MemUsed</strong> { fmt.Sprintf("%s",stats.MemUsed) }</p>
    <p><strong>MemTotal</strong> { fmt.Sprintf("%s",stats.MemTotal) }</p>
    <p><strong>MemPercent</strong> { fmt.Sprintf("%f",stats.MemPercent) }</p>
    <p><strong>CPU system</strong> { fmt.Sprintf("%d",stats.CpuSystem) }</p>
    <p><strong>CPU user</strong> { fmt.Sprintf("%d",stats.CpuUser) }</p>
    <p><strong>CPU idle</strong> { fmt.Sprintf("%d",stats.CpuIdle) }</p>
    </div>
}

Enter fullscreen mode Exit fullscreen mode

templ Main - accepts stats as argument, renders whole html and subscribes for changes on /live/stats url.

templ Stat - accepts stats as argument, renders only that part and will be sent to SSE (idiomorph finds element with same id and replaces it )

example could be found at https://github.com/blinkinglight/go-sysstats clone, run go run . and navigate to https://localhost:8088 and it should just work.

Hostinger image

Get n8n VPS hosting 3x cheaper than a cloud solution

Get fast, easy, secure n8n VPS hosting from $4.99/mo at Hostinger. Automate any workflow using a pre-installed n8n application and no-code customization.

Start now

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay