mirror of
https://github.com/fankes/beszel.git
synced 2025-10-19 17:59:28 +08:00
Add 5m and 10m load avg alerts and table values (#816)
Co-authored-by: henrygd <hank@henrygd.me>
This commit is contained in:
@@ -14,6 +14,7 @@ import (
|
|||||||
"github.com/shirou/gopsutil/v4/cpu"
|
"github.com/shirou/gopsutil/v4/cpu"
|
||||||
"github.com/shirou/gopsutil/v4/disk"
|
"github.com/shirou/gopsutil/v4/disk"
|
||||||
"github.com/shirou/gopsutil/v4/host"
|
"github.com/shirou/gopsutil/v4/host"
|
||||||
|
"github.com/shirou/gopsutil/v4/load"
|
||||||
"github.com/shirou/gopsutil/v4/mem"
|
"github.com/shirou/gopsutil/v4/mem"
|
||||||
psutilNet "github.com/shirou/gopsutil/v4/net"
|
psutilNet "github.com/shirou/gopsutil/v4/net"
|
||||||
)
|
)
|
||||||
@@ -77,6 +78,16 @@ func (a *Agent) getSystemStats() system.Stats {
|
|||||||
systemStats.Cpu = twoDecimals(cpuPct[0])
|
systemStats.Cpu = twoDecimals(cpuPct[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// load average
|
||||||
|
if avgstat, err := load.Avg(); err == nil {
|
||||||
|
systemStats.LoadAvg1 = twoDecimals(avgstat.Load1)
|
||||||
|
systemStats.LoadAvg5 = twoDecimals(avgstat.Load5)
|
||||||
|
systemStats.LoadAvg15 = twoDecimals(avgstat.Load15)
|
||||||
|
slog.Debug("Load average", "5m", systemStats.LoadAvg5, "15m", systemStats.LoadAvg15)
|
||||||
|
} else {
|
||||||
|
slog.Error("Error getting load average", "err", err)
|
||||||
|
}
|
||||||
|
|
||||||
// memory
|
// memory
|
||||||
if v, err := mem.VirtualMemory(); err == nil {
|
if v, err := mem.VirtualMemory(); err == nil {
|
||||||
// swap
|
// swap
|
||||||
@@ -240,6 +251,8 @@ func (a *Agent) getSystemStats() system.Stats {
|
|||||||
|
|
||||||
// update base system info
|
// update base system info
|
||||||
a.systemInfo.Cpu = systemStats.Cpu
|
a.systemInfo.Cpu = systemStats.Cpu
|
||||||
|
a.systemInfo.LoadAvg5 = systemStats.LoadAvg5
|
||||||
|
a.systemInfo.LoadAvg15 = systemStats.LoadAvg15
|
||||||
a.systemInfo.MemPct = systemStats.MemPct
|
a.systemInfo.MemPct = systemStats.MemPct
|
||||||
a.systemInfo.DiskPct = systemStats.DiskPct
|
a.systemInfo.DiskPct = systemStats.DiskPct
|
||||||
a.systemInfo.Uptime, _ = host.Uptime()
|
a.systemInfo.Uptime, _ = host.Uptime()
|
||||||
|
@@ -47,6 +47,8 @@ type SystemAlertStats struct {
|
|||||||
NetSent float64 `json:"ns"`
|
NetSent float64 `json:"ns"`
|
||||||
NetRecv float64 `json:"nr"`
|
NetRecv float64 `json:"nr"`
|
||||||
Temperatures map[string]float32 `json:"t"`
|
Temperatures map[string]float32 `json:"t"`
|
||||||
|
LoadAvg5 float64 `json:"l5"`
|
||||||
|
LoadAvg15 float64 `json:"l15"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SystemAlertData struct {
|
type SystemAlertData struct {
|
||||||
|
@@ -54,6 +54,12 @@ func (am *AlertManager) HandleSystemAlerts(systemRecord *core.Record, data *syst
|
|||||||
}
|
}
|
||||||
val = data.Info.DashboardTemp
|
val = data.Info.DashboardTemp
|
||||||
unit = "°C"
|
unit = "°C"
|
||||||
|
case "LoadAvg5":
|
||||||
|
val = data.Info.LoadAvg5
|
||||||
|
unit = ""
|
||||||
|
case "LoadAvg15":
|
||||||
|
val = data.Info.LoadAvg15
|
||||||
|
unit = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
triggered := alertRecord.GetBool("triggered")
|
triggered := alertRecord.GetBool("triggered")
|
||||||
@@ -190,6 +196,10 @@ func (am *AlertManager) HandleSystemAlerts(systemRecord *core.Record, data *syst
|
|||||||
}
|
}
|
||||||
alert.mapSums[key] += temp
|
alert.mapSums[key] += temp
|
||||||
}
|
}
|
||||||
|
case "LoadAvg5":
|
||||||
|
alert.val += stats.LoadAvg5
|
||||||
|
case "LoadAvg15":
|
||||||
|
alert.val += stats.LoadAvg15
|
||||||
default:
|
default:
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@@ -247,6 +257,10 @@ func (am *AlertManager) sendSystemAlert(alert SystemAlertData) {
|
|||||||
if alert.name == "Disk" {
|
if alert.name == "Disk" {
|
||||||
alert.name += " usage"
|
alert.name += " usage"
|
||||||
}
|
}
|
||||||
|
// format LoadAvg5 and LoadAvg15
|
||||||
|
if after, ok := strings.CutPrefix(alert.name, "LoadAvg"); ok {
|
||||||
|
alert.name = after + "m Load"
|
||||||
|
}
|
||||||
|
|
||||||
// make title alert name lowercase if not CPU
|
// make title alert name lowercase if not CPU
|
||||||
titleAlertName := alert.name
|
titleAlertName := alert.name
|
||||||
|
@@ -31,6 +31,9 @@ type Stats struct {
|
|||||||
Temperatures map[string]float64 `json:"t,omitempty" cbor:"20,keyasint,omitempty"`
|
Temperatures map[string]float64 `json:"t,omitempty" cbor:"20,keyasint,omitempty"`
|
||||||
ExtraFs map[string]*FsStats `json:"efs,omitempty" cbor:"21,keyasint,omitempty"`
|
ExtraFs map[string]*FsStats `json:"efs,omitempty" cbor:"21,keyasint,omitempty"`
|
||||||
GPUData map[string]GPUData `json:"g,omitempty" cbor:"22,keyasint,omitempty"`
|
GPUData map[string]GPUData `json:"g,omitempty" cbor:"22,keyasint,omitempty"`
|
||||||
|
LoadAvg1 float64 `json:"l1,omitempty" cbor:"23,keyasint,omitempty,omitzero"`
|
||||||
|
LoadAvg5 float64 `json:"l5,omitempty" cbor:"24,keyasint,omitempty,omitzero"`
|
||||||
|
LoadAvg15 float64 `json:"l15,omitempty" cbor:"25,keyasint,omitempty,omitzero"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type GPUData struct {
|
type GPUData struct {
|
||||||
@@ -89,6 +92,8 @@ type Info struct {
|
|||||||
GpuPct float64 `json:"g,omitempty" cbor:"12,keyasint,omitempty"`
|
GpuPct float64 `json:"g,omitempty" cbor:"12,keyasint,omitempty"`
|
||||||
DashboardTemp float64 `json:"dt,omitempty" cbor:"13,keyasint,omitempty"`
|
DashboardTemp float64 `json:"dt,omitempty" cbor:"13,keyasint,omitempty"`
|
||||||
Os Os `json:"os" cbor:"14,keyasint"`
|
Os Os `json:"os" cbor:"14,keyasint"`
|
||||||
|
LoadAvg5 float64 `json:"l5,omitempty" cbor:"15,keyasint,omitempty,omitzero"`
|
||||||
|
LoadAvg15 float64 `json:"l15,omitempty" cbor:"16,keyasint,omitempty,omitzero"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Final data structure to return to the hub
|
// Final data structure to return to the hub
|
||||||
|
@@ -75,7 +75,9 @@ func init() {
|
|||||||
"Memory",
|
"Memory",
|
||||||
"Disk",
|
"Disk",
|
||||||
"Temperature",
|
"Temperature",
|
||||||
"Bandwidth"
|
"Bandwidth",
|
||||||
|
"LoadAvg5",
|
||||||
|
"LoadAvg15"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
@@ -5,7 +5,7 @@ import (
|
|||||||
m "github.com/pocketbase/pocketbase/migrations"
|
m "github.com/pocketbase/pocketbase/migrations"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
const (
|
||||||
TempAdminEmail = "_@b.b"
|
TempAdminEmail = "_@b.b"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -217,7 +217,7 @@ function AlertContent({ data }: { data: AlertData }) {
|
|||||||
|
|
||||||
const [checked, setChecked] = useState(data.checked || false)
|
const [checked, setChecked] = useState(data.checked || false)
|
||||||
const [min, setMin] = useState(data.min || 10)
|
const [min, setMin] = useState(data.min || 10)
|
||||||
const [value, setValue] = useState(data.val || (singleDescription ? 0 : 80))
|
const [value, setValue] = useState(data.val || (singleDescription ? 0 : data.alert.start ?? 80))
|
||||||
|
|
||||||
const Icon = alertInfo[name].icon
|
const Icon = alertInfo[name].icon
|
||||||
|
|
||||||
@@ -268,7 +268,8 @@ function AlertContent({ data }: { data: AlertData }) {
|
|||||||
onValueChange={(val) => {
|
onValueChange={(val) => {
|
||||||
setValue(val[0])
|
setValue(val[0])
|
||||||
}}
|
}}
|
||||||
min={1}
|
step={data.alert.step ?? 1}
|
||||||
|
min={data.alert.min ?? 1}
|
||||||
max={alertInfo[name].max ?? 99}
|
max={alertInfo[name].max ?? 99}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -68,7 +68,7 @@ import { useStore } from "@nanostores/react"
|
|||||||
import { cn, copyToClipboard, decimalString, isReadOnlyUser, useLocalStorage } from "@/lib/utils"
|
import { cn, copyToClipboard, decimalString, isReadOnlyUser, useLocalStorage } from "@/lib/utils"
|
||||||
import AlertsButton from "../alerts/alert-button"
|
import AlertsButton from "../alerts/alert-button"
|
||||||
import { $router, Link, navigate } from "../router"
|
import { $router, Link, navigate } from "../router"
|
||||||
import { EthernetIcon, GpuIcon, ThermometerIcon } from "../ui/icons"
|
import { EthernetIcon, GpuIcon, HourglassIcon, ThermometerIcon } from "../ui/icons"
|
||||||
import { useLingui, Trans } from "@lingui/react/macro"
|
import { useLingui, Trans } from "@lingui/react/macro"
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../ui/card"
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../ui/card"
|
||||||
import { Input } from "../ui/input"
|
import { Input } from "../ui/input"
|
||||||
@@ -83,8 +83,8 @@ function CellFormatter(info: CellContext<SystemRecord, unknown>) {
|
|||||||
const val = (info.getValue() as number) || 0
|
const val = (info.getValue() as number) || 0
|
||||||
return (
|
return (
|
||||||
<div className="flex gap-2 items-center tabular-nums tracking-tight">
|
<div className="flex gap-2 items-center tabular-nums tracking-tight">
|
||||||
<span className="min-w-[3.3em]">{decimalString(val, 1)}%</span>
|
<span className="min-w-8">{decimalString(val, 1)}%</span>
|
||||||
<span className="grow min-w-10 block bg-muted h-[1em] relative rounded-sm overflow-hidden">
|
<span className="grow min-w-8 block bg-muted h-[1em] relative rounded-sm overflow-hidden">
|
||||||
<span
|
<span
|
||||||
className={cn(
|
className={cn(
|
||||||
"absolute inset-0 w-full h-full origin-left",
|
"absolute inset-0 w-full h-full origin-left",
|
||||||
@@ -144,7 +144,6 @@ export default function SystemsTable() {
|
|||||||
}
|
}
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
// size: 200,
|
|
||||||
size: 200,
|
size: 200,
|
||||||
minSize: 0,
|
minSize: 0,
|
||||||
accessorKey: "name",
|
accessorKey: "name",
|
||||||
@@ -163,6 +162,7 @@ export default function SystemsTable() {
|
|||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
enableHiding: false,
|
enableHiding: false,
|
||||||
|
invertSorting: false,
|
||||||
Icon: ServerIcon,
|
Icon: ServerIcon,
|
||||||
cell: (info) => (
|
cell: (info) => (
|
||||||
<span className="flex gap-0.5 items-center text-base md:pe-5">
|
<span className="flex gap-0.5 items-center text-base md:pe-5">
|
||||||
@@ -181,28 +181,26 @@ export default function SystemsTable() {
|
|||||||
header: sortableHeader,
|
header: sortableHeader,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "info.cpu",
|
accessorFn: (originalRow) => originalRow.info.cpu,
|
||||||
id: "cpu",
|
id: "cpu",
|
||||||
name: () => t`CPU`,
|
name: () => t`CPU`,
|
||||||
invertSorting: true,
|
|
||||||
cell: CellFormatter,
|
cell: CellFormatter,
|
||||||
Icon: CpuIcon,
|
Icon: CpuIcon,
|
||||||
header: sortableHeader,
|
header: sortableHeader,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "info.mp",
|
// accessorKey: "info.mp",
|
||||||
|
accessorFn: (originalRow) => originalRow.info.mp,
|
||||||
id: "memory",
|
id: "memory",
|
||||||
name: () => t`Memory`,
|
name: () => t`Memory`,
|
||||||
invertSorting: true,
|
|
||||||
cell: CellFormatter,
|
cell: CellFormatter,
|
||||||
Icon: MemoryStickIcon,
|
Icon: MemoryStickIcon,
|
||||||
header: sortableHeader,
|
header: sortableHeader,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "info.dp",
|
accessorFn: (originalRow) => originalRow.info.dp,
|
||||||
id: "disk",
|
id: "disk",
|
||||||
name: () => t`Disk`,
|
name: () => t`Disk`,
|
||||||
invertSorting: true,
|
|
||||||
cell: CellFormatter,
|
cell: CellFormatter,
|
||||||
Icon: HardDriveIcon,
|
Icon: HardDriveIcon,
|
||||||
header: sortableHeader,
|
header: sortableHeader,
|
||||||
@@ -211,8 +209,6 @@ export default function SystemsTable() {
|
|||||||
accessorFn: (originalRow) => originalRow.info.g,
|
accessorFn: (originalRow) => originalRow.info.g,
|
||||||
id: "gpu",
|
id: "gpu",
|
||||||
name: () => "GPU",
|
name: () => "GPU",
|
||||||
invertSorting: true,
|
|
||||||
sortUndefined: -1,
|
|
||||||
cell: CellFormatter,
|
cell: CellFormatter,
|
||||||
Icon: GpuIcon,
|
Icon: GpuIcon,
|
||||||
header: sortableHeader,
|
header: sortableHeader,
|
||||||
@@ -221,19 +217,50 @@ export default function SystemsTable() {
|
|||||||
accessorFn: (originalRow) => originalRow.info.b || 0,
|
accessorFn: (originalRow) => originalRow.info.b || 0,
|
||||||
id: "net",
|
id: "net",
|
||||||
name: () => t`Net`,
|
name: () => t`Net`,
|
||||||
invertSorting: true,
|
|
||||||
size: 50,
|
size: 50,
|
||||||
Icon: EthernetIcon,
|
Icon: EthernetIcon,
|
||||||
header: sortableHeader,
|
header: sortableHeader,
|
||||||
cell(info) {
|
cell(info) {
|
||||||
const val = info.getValue() as number
|
const val = info.getValue() as number
|
||||||
|
return <span className="tabular-nums whitespace-nowrap">{decimalString(val, val >= 100 ? 1 : 2)} MB/s</span>
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "info.l5",
|
||||||
|
id: "l5",
|
||||||
|
name: () => t({ message: "L5", comment: "Load average 5 minutes" }),
|
||||||
|
size: 0,
|
||||||
|
hideSort: true,
|
||||||
|
Icon: HourglassIcon,
|
||||||
|
header: sortableHeader,
|
||||||
|
cell(info) {
|
||||||
|
const val = info.getValue() as number
|
||||||
|
if (!val) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<span
|
<span className={cn("tabular-nums whitespace-nowrap", viewMode === "table" && "ps-1")}>
|
||||||
className={cn("tabular-nums whitespace-nowrap", {
|
{decimalString(val)}
|
||||||
"ps-1": viewMode === "table",
|
</span>
|
||||||
})}
|
)
|
||||||
>
|
},
|
||||||
{decimalString(val, val >= 100 ? 1 : 2)} MB/s
|
},
|
||||||
|
{
|
||||||
|
accessorKey: "info.l15",
|
||||||
|
id: "l15",
|
||||||
|
name: () => t({ message: "L15", comment: "Load average 15 minutes" }),
|
||||||
|
size: 0,
|
||||||
|
hideSort: true,
|
||||||
|
Icon: HourglassIcon,
|
||||||
|
header: sortableHeader,
|
||||||
|
cell(info) {
|
||||||
|
const val = info.getValue() as number
|
||||||
|
if (!val) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<span className={cn("tabular-nums whitespace-nowrap", viewMode === "table" && "ps-1")}>
|
||||||
|
{decimalString(val)}
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@@ -242,8 +269,6 @@ export default function SystemsTable() {
|
|||||||
accessorFn: (originalRow) => originalRow.info.dt,
|
accessorFn: (originalRow) => originalRow.info.dt,
|
||||||
id: "temp",
|
id: "temp",
|
||||||
name: () => t({ message: "Temp", comment: "Temperature label in systems table" }),
|
name: () => t({ message: "Temp", comment: "Temperature label in systems table" }),
|
||||||
invertSorting: true,
|
|
||||||
sortUndefined: -1,
|
|
||||||
size: 50,
|
size: 50,
|
||||||
hideSort: true,
|
hideSort: true,
|
||||||
Icon: ThermometerIcon,
|
Icon: ThermometerIcon,
|
||||||
@@ -254,21 +279,17 @@ export default function SystemsTable() {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<span
|
<span className={cn("tabular-nums whitespace-nowrap", viewMode === "table" && "ps-0.5")}>
|
||||||
className={cn("tabular-nums whitespace-nowrap", {
|
|
||||||
"ps-1.5": viewMode === "table",
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{decimalString(val)} °C
|
{decimalString(val)} °C
|
||||||
</span>
|
</span>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "info.v",
|
accessorFn: (originalRow) => originalRow.info.v,
|
||||||
id: "agent",
|
id: "agent",
|
||||||
name: () => t`Agent`,
|
name: () => t`Agent`,
|
||||||
invertSorting: true,
|
// invertSorting: true,
|
||||||
size: 50,
|
size: 50,
|
||||||
Icon: WifiIcon,
|
Icon: WifiIcon,
|
||||||
hideSort: true,
|
hideSort: true,
|
||||||
@@ -280,11 +301,7 @@ export default function SystemsTable() {
|
|||||||
}
|
}
|
||||||
const system = info.row.original
|
const system = info.row.original
|
||||||
return (
|
return (
|
||||||
<span
|
<span className={cn("flex gap-2 items-center md:pe-5 tabular-nums", viewMode === "table" && "ps-0.5")}>
|
||||||
className={cn("flex gap-2 items-center md:pe-5 tabular-nums", {
|
|
||||||
"ps-1": viewMode === "table",
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<IndicatorDot
|
<IndicatorDot
|
||||||
system={system}
|
system={system}
|
||||||
className={
|
className={
|
||||||
@@ -304,7 +321,7 @@ export default function SystemsTable() {
|
|||||||
name: () => t({ message: "Actions", comment: "Table column" }),
|
name: () => t({ message: "Actions", comment: "Table column" }),
|
||||||
size: 50,
|
size: 50,
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<div className="flex justify-end items-center gap-1">
|
<div className="flex justify-end items-center gap-1 -ms-3">
|
||||||
<AlertsButton system={row.original} />
|
<AlertsButton system={row.original} />
|
||||||
<ActionsButton system={row.original} />
|
<ActionsButton system={row.original} />
|
||||||
</div>
|
</div>
|
||||||
@@ -328,6 +345,9 @@ export default function SystemsTable() {
|
|||||||
columnVisibility,
|
columnVisibility,
|
||||||
},
|
},
|
||||||
defaultColumn: {
|
defaultColumn: {
|
||||||
|
// sortDescFirst: true,
|
||||||
|
invertSorting: true,
|
||||||
|
sortUndefined: "last",
|
||||||
minSize: 0,
|
minSize: 0,
|
||||||
size: 900,
|
size: 900,
|
||||||
maxSize: 900,
|
maxSize: 900,
|
||||||
@@ -511,7 +531,7 @@ function SystemsTableHead({ table, colLength }: { table: TableType<SystemRecord>
|
|||||||
<TableRow key={headerGroup.id}>
|
<TableRow key={headerGroup.id}>
|
||||||
{headerGroup.headers.map((header) => {
|
{headerGroup.headers.map((header) => {
|
||||||
return (
|
return (
|
||||||
<TableHead className="px-2" key={header.id}>
|
<TableHead className="px-1" key={header.id}>
|
||||||
{flexRender(header.column.columnDef.header, header.getContext())}
|
{flexRender(header.column.columnDef.header, header.getContext())}
|
||||||
</TableHead>
|
</TableHead>
|
||||||
)
|
)
|
||||||
|
@@ -121,3 +121,12 @@ export function GpuIcon(props: SVGProps<SVGSVGElement>) {
|
|||||||
</svg>
|
</svg>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remix icons (Apache 2.0) https://github.com/Remix-Design/RemixIcon/blob/master/License
|
||||||
|
export function HourglassIcon(props: SVGProps<SVGSVGElement>) {
|
||||||
|
return (
|
||||||
|
<svg viewBox="0 0 24 24" {...props} fill="currentColor">
|
||||||
|
<path d="M4 2h16v4.5L13.5 12l6.5 5.5V22H4v-4.5l6.5-5.5L4 6.5zm12.3 5L18 5.5V4H6v1.5L7.7 7zM12 13.3l-6 5.2V20h1l5-3 5 3h1v-1.5z" />
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
@@ -9,7 +9,7 @@ import { WritableAtom } from "nanostores"
|
|||||||
import { timeDay, timeHour } from "d3-time"
|
import { timeDay, timeHour } from "d3-time"
|
||||||
import { useEffect, useState } from "react"
|
import { useEffect, useState } from "react"
|
||||||
import { CpuIcon, HardDriveIcon, MemoryStickIcon, ServerIcon } from "lucide-react"
|
import { CpuIcon, HardDriveIcon, MemoryStickIcon, ServerIcon } from "lucide-react"
|
||||||
import { EthernetIcon, ThermometerIcon } from "@/components/ui/icons"
|
import { EthernetIcon, HourglassIcon, ThermometerIcon } from "@/components/ui/icons"
|
||||||
import { prependBasePath } from "@/components/router"
|
import { prependBasePath } from "@/components/router"
|
||||||
|
|
||||||
export function cn(...inputs: ClassValue[]) {
|
export function cn(...inputs: ClassValue[]) {
|
||||||
@@ -342,6 +342,26 @@ export const alertInfo: Record<string, AlertInfo> = {
|
|||||||
icon: ThermometerIcon,
|
icon: ThermometerIcon,
|
||||||
desc: () => t`Triggers when any sensor exceeds a threshold`,
|
desc: () => t`Triggers when any sensor exceeds a threshold`,
|
||||||
},
|
},
|
||||||
|
LoadAvg5: {
|
||||||
|
name: () => t`Load Average 5m`,
|
||||||
|
unit: "",
|
||||||
|
icon: HourglassIcon,
|
||||||
|
max: 100,
|
||||||
|
min: 0.1,
|
||||||
|
start: 10,
|
||||||
|
step: 0.1,
|
||||||
|
desc: () => t`Triggers when 5 minute load average exceeds a threshold`,
|
||||||
|
},
|
||||||
|
LoadAvg15: {
|
||||||
|
name: () => t`Load Average 15m`,
|
||||||
|
unit: "",
|
||||||
|
icon: HourglassIcon,
|
||||||
|
min: 0.1,
|
||||||
|
max: 100,
|
||||||
|
start: 10,
|
||||||
|
step: 0.1,
|
||||||
|
desc: () => t`Triggers when 15 minute load average exceeds a threshold`,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
13
beszel/site/src/types.d.ts
vendored
13
beszel/site/src/types.d.ts
vendored
@@ -44,6 +44,10 @@ export interface SystemInfo {
|
|||||||
c: number
|
c: number
|
||||||
/** cpu model */
|
/** cpu model */
|
||||||
m: string
|
m: string
|
||||||
|
/** load average 5 minutes */
|
||||||
|
l5?: number
|
||||||
|
/** load average 15 minutes */
|
||||||
|
l15?: number
|
||||||
/** operating system */
|
/** operating system */
|
||||||
o?: string
|
o?: string
|
||||||
/** uptime */
|
/** uptime */
|
||||||
@@ -71,6 +75,12 @@ export interface SystemStats {
|
|||||||
cpu: number
|
cpu: number
|
||||||
/** peak cpu */
|
/** peak cpu */
|
||||||
cpum?: number
|
cpum?: number
|
||||||
|
/** load average 1 minute */
|
||||||
|
l1?: number
|
||||||
|
/** load average 5 minutes */
|
||||||
|
l5?: number
|
||||||
|
/** load average 15 minutes */
|
||||||
|
l15?: number
|
||||||
/** total memory (gb) */
|
/** total memory (gb) */
|
||||||
m: number
|
m: number
|
||||||
/** memory used (gb) */
|
/** memory used (gb) */
|
||||||
@@ -218,6 +228,9 @@ interface AlertInfo {
|
|||||||
icon: any
|
icon: any
|
||||||
desc: () => string
|
desc: () => string
|
||||||
max?: number
|
max?: number
|
||||||
|
min?: number
|
||||||
|
step?: number
|
||||||
|
start?: number
|
||||||
/** Single value description (when there's only one value, like status) */
|
/** Single value description (when there's only one value, like status) */
|
||||||
singleDesc?: () => string
|
singleDesc?: () => string
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user