mirror of
https://github.com/fankes/beszel.git
synced 2025-10-19 01:39:34 +08:00
add ZFS ARC memory accounting
This commit is contained in:
@@ -13,6 +13,7 @@ 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
|
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
|
||||||
|
@@ -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,6 +62,9 @@ func (a *Agent) getSystemStats() system.Stats {
|
|||||||
|
|
||||||
// memory
|
// memory
|
||||||
if v, err := mem.VirtualMemory(); err == nil {
|
if v, err := mem.VirtualMemory(); err == nil {
|
||||||
|
// swap
|
||||||
|
systemStats.Swap = bytesToGigabytes(v.SwapTotal)
|
||||||
|
systemStats.SwapUsed = bytesToGigabytes(v.SwapTotal - v.SwapFree - v.SwapCached)
|
||||||
// cache + buffers value for default mem calculation
|
// cache + buffers value for default mem calculation
|
||||||
cacheBuff := v.Total - v.Free - v.Used
|
cacheBuff := v.Total - v.Free - v.Used
|
||||||
// htop memory calculation overrides
|
// htop memory calculation overrides
|
||||||
@@ -61,9 +74,15 @@ func (a *Agent) getSystemStats() system.Stats {
|
|||||||
v.Used = v.Total - (v.Free + cacheBuff)
|
v.Used = v.Total - (v.Free + cacheBuff)
|
||||||
v.UsedPercent = float64(v.Used) / float64(v.Total) * 100.0
|
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.Mem = bytesToGigabytes(v.Total)
|
||||||
systemStats.Swap = bytesToGigabytes(v.SwapTotal)
|
|
||||||
systemStats.SwapUsed = bytesToGigabytes(v.SwapTotal - v.SwapFree - v.SwapCached)
|
|
||||||
systemStats.MemBuffCache = bytesToGigabytes(cacheBuff)
|
systemStats.MemBuffCache = bytesToGigabytes(cacheBuff)
|
||||||
systemStats.MemUsed = bytesToGigabytes(v.Used)
|
systemStats.MemUsed = bytesToGigabytes(v.Used)
|
||||||
systemStats.MemPct = twoDecimals(v.UsedPercent)
|
systemStats.MemPct = twoDecimals(v.UsedPercent)
|
||||||
@@ -192,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")
|
||||||
|
}
|
||||||
|
@@ -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"`
|
||||||
|
@@ -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}
|
||||||
|
2
beszel/site/src/types.d.ts
vendored
2
beszel/site/src/types.d.ts
vendored
@@ -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) */
|
||||||
|
Reference in New Issue
Block a user