Compare commits

...

6 Commits

Author SHA1 Message Date
Henry Dollman
05faa88e6a release 0.5.2 2024-10-05 18:30:10 -04:00
Henry Dollman
73aae62c2e add ZFS ARC memory accounting 2024-10-05 18:07:42 -04:00
Henry Dollman
af4877ca30 add MEM_CALC env var 2024-10-05 15:29:27 -04:00
Henry Dollman
c407fe9af0 exclude sensor if temp <=0 || temp >= 200 2024-10-05 11:14:20 -04:00
hank
13c9497951 Merge pull request #199 from Bot-wxt1221/main
fix(package-lock.json): fix for nixpkgs
2024-10-04 11:06:58 -04:00
wxt
4274096645 fix(package-lock.json): fix for nixpkgs 2024-10-03 16:18:31 +08:00
8 changed files with 5013 additions and 4246 deletions

View File

@@ -13,6 +13,8 @@ import (
type Agent struct { type Agent struct {
debug bool // true if LOG_LEVEL is set to debug debug bool // true if LOG_LEVEL is set to debug
zfs bool // true if system has arcstats
memCalc string // Memory calculation formula
fsNames []string // List of filesystem device names being monitored fsNames []string // List of filesystem device names being monitored
fsStats map[string]*system.FsStats // Keeps track of disk stats for each filesystem fsStats map[string]*system.FsStats // Keeps track of disk stats for each filesystem
netInterfaces map[string]struct{} // Stores all valid network interfaces netInterfaces map[string]struct{} // Stores all valid network interfaces
@@ -26,6 +28,7 @@ type Agent struct {
func NewAgent() *Agent { func NewAgent() *Agent {
return &Agent{ return &Agent{
sensorsContext: context.Background(), sensorsContext: context.Background(),
memCalc: os.Getenv("MEM_CALC"),
} }
} }

View File

@@ -3,9 +3,12 @@ package agent
import ( import (
"beszel" "beszel"
"beszel/internal/entities/system" "beszel/internal/entities/system"
"bufio"
"fmt"
"log/slog" "log/slog"
"os" "os"
"strconv" "strconv"
"strings"
"time" "time"
"github.com/shirou/gopsutil/v4/cpu" "github.com/shirou/gopsutil/v4/cpu"
@@ -36,6 +39,13 @@ func (a *Agent) initializeSystemInfo() {
a.systemInfo.Threads = threads a.systemInfo.Threads = threads
} }
} }
// zfs
if _, err := getARCSize(); err == nil {
a.zfs = true
} else {
slog.Debug("Not monitoring ZFS ARC", "err", err)
}
} }
// Returns current info, stats about the host system // Returns current info, stats about the host system
@@ -52,12 +62,30 @@ func (a *Agent) getSystemStats() system.Stats {
// memory // memory
if v, err := mem.VirtualMemory(); err == nil { if v, err := mem.VirtualMemory(); err == nil {
systemStats.Mem = bytesToGigabytes(v.Total) // swap
systemStats.MemUsed = bytesToGigabytes(v.Used)
systemStats.MemBuffCache = bytesToGigabytes(v.Total - v.Free - v.Used)
systemStats.MemPct = twoDecimals(v.UsedPercent)
systemStats.Swap = bytesToGigabytes(v.SwapTotal) systemStats.Swap = bytesToGigabytes(v.SwapTotal)
systemStats.SwapUsed = bytesToGigabytes(v.SwapTotal - v.SwapFree) systemStats.SwapUsed = bytesToGigabytes(v.SwapTotal - v.SwapFree - v.SwapCached)
// cache + buffers value for default mem calculation
cacheBuff := v.Total - v.Free - v.Used
// htop memory calculation overrides
if a.memCalc == "htop" {
// note: gopsutil automatically adds SReclaimable to v.Cached
cacheBuff = v.Cached + v.Buffers - v.Shared
v.Used = v.Total - (v.Free + cacheBuff)
v.UsedPercent = float64(v.Used) / float64(v.Total) * 100.0
}
// subtract ZFS ARC size from used memory and add as its own category
if a.zfs {
if arcSize, _ := getARCSize(); arcSize > 0 && arcSize < v.Used {
v.Used = v.Used - arcSize
v.UsedPercent = float64(v.Used) / float64(v.Total) * 100.0
systemStats.MemZfsArc = bytesToGigabytes(arcSize)
}
}
systemStats.Mem = bytesToGigabytes(v.Total)
systemStats.MemBuffCache = bytesToGigabytes(cacheBuff)
systemStats.MemUsed = bytesToGigabytes(v.Used)
systemStats.MemPct = twoDecimals(v.UsedPercent)
} }
// disk usage // disk usage
@@ -154,7 +182,7 @@ func (a *Agent) getSystemStats() system.Stats {
systemStats.Temperatures = make(map[string]float64, len(temps)) systemStats.Temperatures = make(map[string]float64, len(temps))
for i, sensor := range temps { for i, sensor := range temps {
// skip if temperature is 0 // skip if temperature is 0
if sensor.Temperature == 0 { if sensor.Temperature <= 0 || sensor.Temperature >= 200 {
continue continue
} }
if _, ok := systemStats.Temperatures[sensor.SensorKey]; ok { if _, ok := systemStats.Temperatures[sensor.SensorKey]; ok {
@@ -183,3 +211,29 @@ func (a *Agent) getSystemStats() system.Stats {
return systemStats return systemStats
} }
// Returns the size of the ZFS ARC memory cache in bytes
func getARCSize() (uint64, error) {
file, err := os.Open("/proc/spl/kstat/zfs/arcstats")
if err != nil {
return 0, err
}
defer file.Close()
// Scan the lines
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
if strings.HasPrefix(line, "size") {
// Example line: size 4 15032385536
fields := strings.Fields(line)
if len(fields) < 3 {
return 0, err
}
// Return the size as uint64
return strconv.ParseUint(fields[2], 10, 64)
}
}
return 0, fmt.Errorf("failed to parse size field")
}

View File

@@ -11,6 +11,7 @@ type Stats struct {
MemUsed float64 `json:"mu"` MemUsed float64 `json:"mu"`
MemPct float64 `json:"mp"` MemPct float64 `json:"mp"`
MemBuffCache float64 `json:"mb"` MemBuffCache float64 `json:"mb"`
MemZfsArc float64 `json:"mz,omitempty"` // ZFS ARC memory
Swap float64 `json:"s,omitempty"` Swap float64 `json:"s,omitempty"`
SwapUsed float64 `json:"su,omitempty"` SwapUsed float64 `json:"su,omitempty"`
DiskTotal float64 `json:"d"` DiskTotal float64 `json:"d"`

File diff suppressed because it is too large Load Diff

View File

@@ -1,16 +1,8 @@
import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from 'recharts' import { Area, AreaChart, CartesianGrid, XAxis, YAxis } from 'recharts'
import { ChartContainer, ChartTooltip, ChartTooltipContent } from '@/components/ui/chart' import { ChartContainer, ChartTooltip, ChartTooltipContent } from '@/components/ui/chart'
import { import { useYAxisWidth, chartTimeData, cn, toFixedFloat, twoDecimalString } from '@/lib/utils'
useYAxisWidth,
chartTimeData,
cn,
formatShortDate,
toFixedFloat,
twoDecimalString,
} from '@/lib/utils'
import { useMemo } from 'react' import { useMemo } from 'react'
// import Spinner from '../spinner'
import { useStore } from '@nanostores/react' import { useStore } from '@nanostores/react'
import { $chartTime } from '@/lib/stores' import { $chartTime } from '@/lib/stores'
import { SystemStatsRecord } from '@/types' import { SystemStatsRecord } from '@/types'
@@ -79,16 +71,16 @@ export default function MemChart({
content={ content={
<ChartTooltipContent <ChartTooltipContent
// @ts-ignore // @ts-ignore
itemSorter={(a, b) => a.name.localeCompare(b.name)} itemSorter={(a, b) => a.order - b.order}
labelFormatter={(_, data) => formatShortDate(data[0].payload.created)}
contentFormatter={(item) => twoDecimalString(item.value) + ' GB'} contentFormatter={(item) => twoDecimalString(item.value) + ' GB'}
indicator="line" indicator="line"
/> />
} }
/> />
<Area <Area
dataKey="stats.mu"
name="Used" name="Used"
order={3}
dataKey="stats.mu"
type="monotoneX" type="monotoneX"
fill="hsl(var(--chart-2))" fill="hsl(var(--chart-2))"
fillOpacity={0.4} fillOpacity={0.4}
@@ -96,9 +88,23 @@ export default function MemChart({
stackId="1" stackId="1"
isAnimationActive={false} isAnimationActive={false}
/> />
{systemData.at(-1)?.stats.mz && (
<Area
name="ZFS ARC"
order={2}
dataKey="stats.mz"
type="monotoneX"
fill="hsla(175 60% 45% / 0.8)"
fillOpacity={0.5}
stroke="hsla(175 60% 45% / 0.8)"
stackId="1"
isAnimationActive={false}
/>
)}
<Area <Area
dataKey="stats.mb"
name="Cache / Buffers" name="Cache / Buffers"
order={1}
dataKey="stats.mb"
type="monotoneX" type="monotoneX"
fill="hsla(160 60% 45% / 0.5)" fill="hsla(160 60% 45% / 0.5)"
fillOpacity={0.4} fillOpacity={0.4}

View File

@@ -43,6 +43,8 @@ export interface SystemStats {
mp: number mp: number
/** memory buffer + cache (gb) */ /** memory buffer + cache (gb) */
mb: number mb: number
/** zfs arc memory (gb) */
mz?: number
/** swap space (gb) */ /** swap space (gb) */
s: number s: number
/** swap used (gb) */ /** swap used (gb) */

View File

@@ -1,6 +1,6 @@
package beszel package beszel
const ( const (
Version = "0.5.1" Version = "0.5.2"
AppName = "beszel" AppName = "beszel"
) )

View File

@@ -105,20 +105,21 @@ Use `./beszel update` and `./beszel-agent update` to update to the latest versio
### Agent ### Agent
| Name | Default | Description | | Name | Default | Description |
| ------------------- | ------- | ---------------------------------------------------------------------------------------- | | ------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------- |
| `DOCKER_HOST` | unset | Overrides the docker host (docker.sock) if using a proxy.[^socket] | | `DOCKER_HOST` | unset | Overrides the docker host (docker.sock) if using a proxy.[^socket] |
| `EXTRA_FILESYSTEMS` | unset | See [Monitoring additional disks, partitions, or remote mounts](#monitoring-additional-disks-partitions-or-remote-mounts) | | `EXTRA_FILESYSTEMS` | unset | See [Monitoring additional disks, partitions, or remote mounts](#monitoring-additional-disks-partitions-or-remote-mounts) |
| `FILESYSTEM` | unset | Device, partition, or mount point to use for root disk stats. | | `FILESYSTEM` | unset | Device, partition, or mount point to use for root disk stats. |
| `KEY` | unset | Public SSH key to use for authentication. Provided in hub. | | `KEY` | unset | Public SSH key to use for authentication. Provided in hub. |
| `LOG_LEVEL` | info | Logging level. Valid values: "debug", "info", "warn", "error". | | `LOG_LEVEL` | info | Logging level. Valid values: "debug", "info", "warn", "error". |
| `NICS` | unset | Whitelist of network interfaces to monitor for bandwidth chart. | | `MEM_CALC` | unset | Overrides the default memory calculation.[^memcalc] |
| `PORT` | 45876 | Port or address:port to listen on. | | `NICS` | unset | Whitelist of network interfaces to monitor for bandwidth chart. |
| `SENSORS` | unset | Whitelist of temperature sensors to monitor. | | `PORT` | 45876 | Port or address:port to listen on. |
| `SENSORS` | unset | Whitelist of temperature sensors to monitor. |
<!-- | `SYS_SENSORS` | unset | Overrides the sys location for sensors. | --> | `SYS_SENSORS` | unset | Overrides sys path for sensors. See [#160](https://github.com/henrygd/beszel/discussions/160). |
[^socket]: Beszel only needs access to read container information. For [linuxserver/docker-socket-proxy](https://github.com/linuxserver/docker-socket-proxy) you would set `CONTAINERS=1`. [^socket]: Beszel only needs access to read container information. For [linuxserver/docker-socket-proxy](https://github.com/linuxserver/docker-socket-proxy) you would set `CONTAINERS=1`.
[^memcalc]: The default value for used memory is based on gopsutil's [Used](https://pkg.go.dev/github.com/shirou/gopsutil/v4@v4.24.6/mem#VirtualMemoryStat) calculation, which should align fairly closely with `free`. Set `MEM_CALC` to `htop` to align with htop's calculation.
## OAuth / OIDC Setup ## OAuth / OIDC Setup