mirror of
https://github.com/henrygd/beszel.git
synced 2026-03-24 06:26:17 +01:00
Compare commits
7 Commits
v0.0.1-alp
...
v0.0.1-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae0f5c938f | ||
|
|
78dc269538 | ||
|
|
f6967eab35 | ||
|
|
e787b6ea1b | ||
|
|
844b95dfd0 | ||
|
|
c5776541a0 | ||
|
|
5ba7568acf |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -6,4 +6,5 @@ temp
|
|||||||
beszel
|
beszel
|
||||||
beszel-agent
|
beszel-agent
|
||||||
beszel_data
|
beszel_data
|
||||||
|
beszel_data*
|
||||||
dist
|
dist
|
||||||
@@ -5,7 +5,7 @@ services:
|
|||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
network_mode: host
|
network_mode: host
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
environment:
|
environment:
|
||||||
PORT: 45876
|
PORT: 45876
|
||||||
KEY: 'ssh-ed25519 YOUR_PUBLIC_KEY'
|
KEY: 'ssh-ed25519 YOUR_PUBLIC_KEY'
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
@@ -11,7 +10,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
sshServer "github.com/gliderlabs/ssh"
|
sshServer "github.com/gliderlabs/ssh"
|
||||||
@@ -23,10 +21,11 @@ import (
|
|||||||
psutilNet "github.com/shirou/gopsutil/v4/net"
|
psutilNet "github.com/shirou/gopsutil/v4/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Version = "0.0.1-alpha.6"
|
var Version = "0.0.1-alpha.7"
|
||||||
|
|
||||||
var containerCpuMap = make(map[string][2]uint64)
|
var containerCpuMap = make(map[string][2]uint64)
|
||||||
var containerCpuMutex = &sync.Mutex{}
|
|
||||||
|
// var containerCpuMutex = &sync.Mutex{}
|
||||||
|
|
||||||
var diskIoStats = DiskIoStats{
|
var diskIoStats = DiskIoStats{
|
||||||
Read: 0,
|
Read: 0,
|
||||||
@@ -44,16 +43,16 @@ var netIoStats = NetIoStats{
|
|||||||
|
|
||||||
// client for docker engine api
|
// client for docker engine api
|
||||||
var client = &http.Client{
|
var client = &http.Client{
|
||||||
Timeout: time.Second * 5,
|
Timeout: time.Second,
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
Dial: func(proto, addr string) (net.Conn, error) {
|
Dial: func(proto, addr string) (net.Conn, error) {
|
||||||
return net.Dial("unix", "/var/run/docker.sock")
|
return net.Dial("unix", "/var/run/docker.sock")
|
||||||
},
|
},
|
||||||
ForceAttemptHTTP2: false,
|
ForceAttemptHTTP2: false,
|
||||||
MaxIdleConns: 100,
|
IdleConnTimeout: 90 * time.Second,
|
||||||
MaxIdleConnsPerHost: 100,
|
DisableCompression: true,
|
||||||
IdleConnTimeout: 90 * time.Second,
|
MaxIdleConns: 10,
|
||||||
DisableKeepAlives: false,
|
DisableKeepAlives: false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,20 +193,19 @@ func getDockerStats() ([]ContainerStats, error) {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
var containerStats []ContainerStats
|
var containerStats []ContainerStats
|
||||||
|
|
||||||
for _, ctr := range containers {
|
for _, ctr := range containers {
|
||||||
wg.Add(1)
|
cstats, err := getContainerStats(ctr)
|
||||||
go func() {
|
if err != nil {
|
||||||
defer wg.Done()
|
// retry once
|
||||||
cstats, err := getContainerStats(ctr)
|
cstats, err = getContainerStats(ctr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Error getting container stats: %+v\n", err)
|
log.Printf("Error getting container stats: %+v\n", err)
|
||||||
return
|
continue
|
||||||
}
|
}
|
||||||
containerStats = append(containerStats, cstats)
|
}
|
||||||
}()
|
containerStats = append(containerStats, cstats)
|
||||||
}
|
}
|
||||||
|
|
||||||
// clean up old containers from map
|
// clean up old containers from map
|
||||||
@@ -221,8 +219,6 @@ func getDockerStats() ([]ContainerStats, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
return containerStats, nil
|
return containerStats, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,8 +242,8 @@ func getContainerStats(ctr Container) (ContainerStats, error) {
|
|||||||
|
|
||||||
// cpu
|
// cpu
|
||||||
// add default values to containerCpu if it doesn't exist
|
// add default values to containerCpu if it doesn't exist
|
||||||
containerCpuMutex.Lock()
|
// containerCpuMutex.Lock()
|
||||||
defer containerCpuMutex.Unlock()
|
// defer containerCpuMutex.Unlock()
|
||||||
if _, ok := containerCpuMap[name]; !ok {
|
if _, ok := containerCpuMap[name]; !ok {
|
||||||
containerCpuMap[name] = [2]uint64{0, 0}
|
containerCpuMap[name] = [2]uint64{0, 0}
|
||||||
}
|
}
|
||||||
@@ -255,7 +251,7 @@ func getContainerStats(ctr Container) (ContainerStats, error) {
|
|||||||
systemDelta := statsJson.CPUStats.SystemUsage - containerCpuMap[name][1]
|
systemDelta := statsJson.CPUStats.SystemUsage - containerCpuMap[name][1]
|
||||||
cpuPct := float64(cpuDelta) / float64(systemDelta) * 100
|
cpuPct := float64(cpuDelta) / float64(systemDelta) * 100
|
||||||
if cpuPct > 100 {
|
if cpuPct > 100 {
|
||||||
return ContainerStats{}, errors.New("cpu pct is greater than 100")
|
return ContainerStats{}, fmt.Errorf("%s cpu pct greater than 100: %+v", name, cpuPct)
|
||||||
}
|
}
|
||||||
containerCpuMap[name] = [2]uint64{statsJson.CPUStats.CPUUsage.TotalUsage, statsJson.CPUStats.SystemUsage}
|
containerCpuMap[name] = [2]uint64{statsJson.CPUStats.CPUUsage.TotalUsage, statsJson.CPUStats.SystemUsage}
|
||||||
|
|
||||||
|
|||||||
46
hub/main.go
46
hub/main.go
@@ -15,24 +15,26 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/labstack/echo/v5"
|
"github.com/labstack/echo/v5"
|
||||||
"github.com/pocketbase/dbx"
|
|
||||||
"github.com/pocketbase/pocketbase"
|
"github.com/pocketbase/pocketbase"
|
||||||
"github.com/pocketbase/pocketbase/apis"
|
"github.com/pocketbase/pocketbase/apis"
|
||||||
"github.com/pocketbase/pocketbase/core"
|
"github.com/pocketbase/pocketbase/core"
|
||||||
"github.com/pocketbase/pocketbase/models"
|
"github.com/pocketbase/pocketbase/models"
|
||||||
"github.com/pocketbase/pocketbase/plugins/migratecmd"
|
"github.com/pocketbase/pocketbase/plugins/migratecmd"
|
||||||
"github.com/pocketbase/pocketbase/tools/cron"
|
"github.com/pocketbase/pocketbase/tools/cron"
|
||||||
|
"github.com/pocketbase/pocketbase/tools/types"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Version = "0.0.1-alpha.6"
|
var Version = "0.0.1-alpha.7"
|
||||||
|
|
||||||
var app *pocketbase.PocketBase
|
var app *pocketbase.PocketBase
|
||||||
var serverConnections = make(map[string]Server)
|
var serverConnections = make(map[string]*Server)
|
||||||
|
var serverConnectionsLock = sync.Mutex{}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
app = pocketbase.NewWithConfig(pocketbase.Config{
|
app = pocketbase.NewWithConfig(pocketbase.Config{
|
||||||
@@ -207,49 +209,53 @@ func startSystemUpdateTicker() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func updateSystems() {
|
func updateSystems() {
|
||||||
// handle max of 1/3 + 1 servers at a time
|
|
||||||
numServers := len(serverConnections)/3 + 1
|
|
||||||
// find systems that are not paused and updated more than 58 seconds ago
|
|
||||||
fiftyEightSecondsAgo := time.Now().UTC().Add(-58 * time.Second).Format("2006-01-02 15:04:05")
|
|
||||||
records, err := app.Dao().FindRecordsByFilter(
|
records, err := app.Dao().FindRecordsByFilter(
|
||||||
"2hz5ncl8tizk5nx", // collection
|
"2hz5ncl8tizk5nx", // collection
|
||||||
"status != 'paused' && updated < {:updated}", // filter
|
"status != 'paused'", // filter
|
||||||
"updated", // sort
|
"updated", // sort
|
||||||
numServers, // limit
|
-1, // limit
|
||||||
0, // offset
|
0, // offset
|
||||||
dbx.Params{"updated": fiftyEightSecondsAgo},
|
|
||||||
)
|
)
|
||||||
|
// log.Println("records", len(records))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
app.Logger().Error("Failed to query systems: ", "err", err.Error())
|
app.Logger().Error("Failed to query systems: ", "err", err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, record := range records {
|
fiftyFiveSecondsAgo := time.Now().UTC().Add(-55 * time.Second)
|
||||||
updateSystem(record)
|
batchSize := len(records)/4 + 1
|
||||||
|
for i := 0; i < batchSize; i++ {
|
||||||
|
if records[i].Get("updated").(types.DateTime).Time().After(fiftyFiveSecondsAgo) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// log.Println("updating", records[i].Get(("name")))
|
||||||
|
go updateSystem(records[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateSystem(record *models.Record) {
|
func updateSystem(record *models.Record) {
|
||||||
var server Server
|
var server *Server
|
||||||
// check if server connection data exists
|
// check if server connection data exists
|
||||||
if _, ok := serverConnections[record.Id]; ok {
|
if _, ok := serverConnections[record.Id]; ok {
|
||||||
server = serverConnections[record.Id]
|
server = serverConnections[record.Id]
|
||||||
} else {
|
} else {
|
||||||
// create server connection struct
|
// create server connection struct
|
||||||
server = Server{
|
server = &Server{
|
||||||
Host: record.Get("host").(string),
|
Host: record.Get("host").(string),
|
||||||
Port: record.Get("port").(string),
|
Port: record.Get("port").(string),
|
||||||
}
|
}
|
||||||
client, err := getServerConnection(&server)
|
client, err := getServerConnection(server)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
app.Logger().Error("Failed to connect:", "err", err.Error(), "server", server.Host, "port", server.Port)
|
app.Logger().Error("Failed to connect:", "err", err.Error(), "server", server.Host, "port", server.Port)
|
||||||
updateServerStatus(record, "down")
|
updateServerStatus(record, "down")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
server.Client = client
|
server.Client = client
|
||||||
|
serverConnectionsLock.Lock()
|
||||||
serverConnections[record.Id] = server
|
serverConnections[record.Id] = server
|
||||||
|
serverConnectionsLock.Unlock()
|
||||||
}
|
}
|
||||||
// get server stats from agent
|
// get server stats from agent
|
||||||
systemData, err := requestJson(&server)
|
systemData, err := requestJson(server)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err.Error() == "retry" {
|
if err.Error() == "retry" {
|
||||||
// if previous connection was closed, try again
|
// if previous connection was closed, try again
|
||||||
@@ -310,6 +316,8 @@ func deleteServerConnection(record *models.Record) {
|
|||||||
if serverConnections[record.Id].Client != nil {
|
if serverConnections[record.Id].Client != nil {
|
||||||
serverConnections[record.Id].Client.Close()
|
serverConnections[record.Id].Client.Close()
|
||||||
}
|
}
|
||||||
|
serverConnectionsLock.Lock()
|
||||||
|
defer serverConnectionsLock.Unlock()
|
||||||
delete(serverConnections, record.Id)
|
delete(serverConnections, record.Id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export function AddSystemButton() {
|
|||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
network_mode: host
|
network_mode: host
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
environment:
|
environment:
|
||||||
PORT: ${port}
|
PORT: ${port}
|
||||||
KEY: "${publicKey}"
|
KEY: "${publicKey}"
|
||||||
|
|||||||
@@ -47,7 +47,13 @@ export default function BandwidthChart({
|
|||||||
<YAxis
|
<YAxis
|
||||||
className="tracking-tighter"
|
className="tracking-tighter"
|
||||||
width={75}
|
width={75}
|
||||||
domain={[0, (max: number) => (max < 0.4 ? 0.4 : Math.ceil(max))]}
|
domain={[0, (max: number) => (max <= 0.4 ? 0.4 : Math.ceil(max))]}
|
||||||
|
tickFormatter={(value) => {
|
||||||
|
if (value >= 100) {
|
||||||
|
return value.toFixed(0)
|
||||||
|
}
|
||||||
|
return value.toFixed((value * 100) % 1 === 0 ? 1 : 2)
|
||||||
|
}}
|
||||||
tickLine={false}
|
tickLine={false}
|
||||||
axisLine={false}
|
axisLine={false}
|
||||||
unit={' MB/s'}
|
unit={' MB/s'}
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ export default function ContainerCpuChart({
|
|||||||
>
|
>
|
||||||
<CartesianGrid vertical={false} />
|
<CartesianGrid vertical={false} />
|
||||||
<YAxis
|
<YAxis
|
||||||
domain={[0, (max: number) => Math.max(Math.ceil(max), 0.4)]}
|
// domain={[0, (max: number) => Math.max(Math.ceil(max), 0.4)]}
|
||||||
width={47}
|
width={47}
|
||||||
tickLine={false}
|
tickLine={false}
|
||||||
axisLine={false}
|
axisLine={false}
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export default function DiskIoChart({
|
|||||||
<YAxis
|
<YAxis
|
||||||
className="tracking-tighter"
|
className="tracking-tighter"
|
||||||
width={75}
|
width={75}
|
||||||
domain={[0, (max: number) => (max < 0.4 ? 0.4 : Math.ceil(max))]}
|
domain={[0, (max: number) => (max <= 0.4 ? 0.4 : Math.ceil(max))]}
|
||||||
tickFormatter={(value) => {
|
tickFormatter={(value) => {
|
||||||
if (value >= 100) {
|
if (value >= 100) {
|
||||||
return value.toFixed(0)
|
return value.toFixed(0)
|
||||||
|
|||||||
@@ -71,21 +71,21 @@ export default function AlertsButton({ system }: { system: SystemRecord }) {
|
|||||||
system={system}
|
system={system}
|
||||||
alerts={systemAlerts}
|
alerts={systemAlerts}
|
||||||
name="CPU"
|
name="CPU"
|
||||||
title="CPU usage"
|
title="CPU Usage"
|
||||||
description="Triggers when CPU usage exceeds a threshold."
|
description="Triggers when CPU usage exceeds a threshold."
|
||||||
/>
|
/>
|
||||||
<AlertWithSlider
|
<AlertWithSlider
|
||||||
system={system}
|
system={system}
|
||||||
alerts={systemAlerts}
|
alerts={systemAlerts}
|
||||||
name="Memory"
|
name="Memory"
|
||||||
title="Memory usage"
|
title="Memory Usage"
|
||||||
description="Triggers when memory usage exceeds a threshold."
|
description="Triggers when memory usage exceeds a threshold."
|
||||||
/>
|
/>
|
||||||
<AlertWithSlider
|
<AlertWithSlider
|
||||||
system={system}
|
system={system}
|
||||||
alerts={systemAlerts}
|
alerts={systemAlerts}
|
||||||
name="Disk"
|
name="Disk"
|
||||||
title="Disk usage"
|
title="Disk Usage"
|
||||||
description="Triggers when disk usage exceeds a threshold."
|
description="Triggers when disk usage exceeds a threshold."
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -104,11 +104,11 @@ function AlertStatus({ system, alerts }: { system: SystemRecord; alerts: AlertRe
|
|||||||
return (
|
return (
|
||||||
<label
|
<label
|
||||||
htmlFor="alert-status"
|
htmlFor="alert-status"
|
||||||
className="space-y-2 flex flex-row items-center justify-between rounded-lg border p-4 cursor-pointer"
|
className="flex flex-row items-center justify-between gap-4 rounded-lg border p-4 cursor-pointer"
|
||||||
>
|
>
|
||||||
<div className="grid gap-0.5 select-none">
|
<div className="grid gap-1 select-none">
|
||||||
<p className="font-medium text-[1.05em]">System status</p>
|
<p className="font-semibold">System Status</p>
|
||||||
<span className="block text-[0.85em] text-foreground opacity-80">
|
<span className="block text-sm text-foreground opacity-80">
|
||||||
Triggers when status switches between up and down.
|
Triggers when status switches between up and down.
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -171,13 +171,13 @@ function AlertWithSlider({
|
|||||||
<div className="rounded-lg border">
|
<div className="rounded-lg border">
|
||||||
<label
|
<label
|
||||||
htmlFor={`alert-${name}`}
|
htmlFor={`alert-${name}`}
|
||||||
className={cn('space-y-2 flex flex-row items-center justify-between cursor-pointer p-4', {
|
className={cn('flex flex-row items-center justify-between gap-4 cursor-pointer p-4', {
|
||||||
'pb-0': !!alert,
|
'pb-0': !!alert,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<div className="grid gap-0.5 select-none">
|
<div className="grid gap-1 select-none">
|
||||||
<p className="font-medium text-[1.05em]">{title}</p>
|
<p className="font-semibold">{title}</p>
|
||||||
<span className="block text-[0.85em] text-foreground opacity-80">{description}</span>
|
<span className="block text-sm text-foreground opacity-80">{description}</span>
|
||||||
</div>
|
</div>
|
||||||
<Switch
|
<Switch
|
||||||
id={`alert-${name}`}
|
id={`alert-${name}`}
|
||||||
|
|||||||
36
readme.md
36
readme.md
@@ -1,28 +1,22 @@
|
|||||||
# Beszel \*WIP\*
|
# Beszel
|
||||||
|
|
||||||
A lightweight server resource monitoring hub with historical data, docker stats, and alerts.
|
A lightweight server resource monitoring hub with historical data, docker stats, and alerts.
|
||||||
|
|
||||||
<!-- <table width="100%">
|
[](https://hub.docker.com/r/henrygd/beszel-agent)
|
||||||
<tbody>
|
[](https://hub.docker.com/r/henrygd/beszel)
|
||||||
<tr>
|
|
||||||
<td width="50%"><img src="https://henrygd-assets.b-cdn.net/social-image-server/before-capture.png" alt="example of turso.tech/pricing link which is missing an og:image as of may 11 2024"/></td>
|
|
||||||
<td width="50%"><img src="https://henrygd-assets.b-cdn.net/social-image-server/after-capture.webp" alt="example of turso.tech/pricing link using an image generated by the server as it's og:image"/></td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table> -->
|
|
||||||
|
|
||||||
<!-- ## Features
|

|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
- **Lightweight**: Much smaller and less demanding than leading solutions.
|
- **Lightweight**: Much smaller and less demanding than leading solutions.
|
||||||
- **Historical data**: Stats are available for up to 30 days.
|
|
||||||
- **Docker stats**: CPU and memory usage history for each container.
|
- **Docker stats**: CPU and memory usage history for each container.
|
||||||
- **Alerts**: Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
- **Alerts**: Configurable alerts for CPU, memory, and disk usage, and system status.
|
||||||
- **Simple**: Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
|
||||||
- **Multi-user**: Each user has their own systems. Admins can share systems across users.
|
- **Multi-user**: Each user has their own systems. Admins can share systems across users.
|
||||||
- **Secure**: Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
- **Simple**: Easy setup and doesn't require anything to be publicly available online.
|
||||||
- **Oauth / OIDC**: Supports many OAuth2 providers and password auth can be disabled.
|
- **OAuth / OIDC**: Supports many OAuth2 providers. Password auth can be disabled.
|
||||||
- **Automated backups**: Automatically back up your data to S3-compatible storage.
|
- **Automated backups**: Automatically back up your data to disk or S3-compatible storage.
|
||||||
- **Open source**: MIT license and no paywalled features. -->
|
- **REST API**: Use your metrics in your own scripts and applications.
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
|
|
||||||
@@ -32,9 +26,6 @@ The hub is a web application that provides a dashboard to view and manage your c
|
|||||||
|
|
||||||
The agent runs on each system you want to monitor. It creates a minimal SSH server through which it communicates system metrics to the hub.
|
The agent runs on each system you want to monitor. It creates a minimal SSH server through which it communicates system metrics to the hub.
|
||||||
|
|
||||||
[](https://hub.docker.com/r/henrygd/beszel-agent)
|
|
||||||
[](https://hub.docker.com/r/henrygd/beszel)
|
|
||||||
|
|
||||||
## Getting started
|
## Getting started
|
||||||
|
|
||||||
If using the binary instead of docker, ignore 4-5 and run the agent using the binary instead.
|
If using the binary instead of docker, ignore 4-5 and run the agent using the binary instead.
|
||||||
@@ -169,3 +160,8 @@ If it's not set, the agent will try to find the filesystem mounted on `/` and us
|
|||||||
Records for longer time periods are made by averaging stats from the shorter time periods. They require the agent to be running uninterrupted for long enough to get a full set of data.
|
Records for longer time periods are made by averaging stats from the shorter time periods. They require the agent to be running uninterrupted for long enough to get a full set of data.
|
||||||
|
|
||||||
If you pause / unpause the agent for longer than one minute, the data will be incomplete and the timing for the current interval will reset.
|
If you pause / unpause the agent for longer than one minute, the data will be incomplete and the timing for the current interval will reset.
|
||||||
|
|
||||||
|
<!--
|
||||||
|
## Support
|
||||||
|
|
||||||
|
My country, the USA, and many others, are actively involved in the genocide of the Palestinian people. I would greatly appreciate any effort you could make to pressure your government to stop enabling this violence. -->
|
||||||
|
|||||||
Reference in New Issue
Block a user