Files
admin b19682a767 fix(monitoring): hostname, tooltip timestamps, default range (v0.5.1)
- Bug 1: Read hostname from /host/etc/hostname instead of os.Hostname()
  which returns the container ID inside Docker. Added volume mount.
- Bug 2: Tooltip callback used parsed.x (category index) instead of
  label (actual timestamp), showing 1970 dates.
- Bug 3+4: Default range changed from 24h to 1h so charts show data
  immediately on new deployments with limited history.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 11:01:08 +01:00

115 lines
2.7 KiB
Go

//go:build linux
package metrics
import (
"bufio"
"fmt"
"os"
"runtime"
"strings"
"time"
)
// GetStaticInfo reads host-level static system information.
// Reads from /proc and /etc (which reflect the host in Docker containers).
func GetStaticInfo() StaticSystemInfo {
info := StaticSystemInfo{}
// Hostname — try host mount first, fall back to os.Hostname()
if data, err := os.ReadFile("/host/etc/hostname"); err == nil {
info.Hostname = strings.TrimSpace(string(data))
} else {
info.Hostname, _ = os.Hostname()
}
// OS — try host mount first, fall back to container's
info.OS = readOSRelease("/host/etc/os-release")
if info.OS == "" {
info.OS = readOSRelease("/etc/os-release")
}
// Kernel version
if data, err := os.ReadFile("/proc/sys/kernel/osrelease"); err == nil {
info.Kernel = strings.TrimSpace(string(data))
}
// Architecture
if data, err := os.ReadFile("/proc/sys/kernel/arch"); err == nil {
info.Architecture = strings.TrimSpace(string(data))
}
if info.Architecture == "" {
// Fallback: use uname -m equivalent from /proc/cpuinfo or runtime
info.Architecture = runtime.GOARCH
// Try to get the actual host arch
if data, err := os.ReadFile("/proc/version"); err == nil {
v := string(data)
if strings.Contains(v, "x86_64") {
info.Architecture = "x86_64"
} else if strings.Contains(v, "aarch64") {
info.Architecture = "aarch64"
}
}
}
// CPU model and cores
info.CPUModel, info.CPUCores = readCPUInfo()
if info.CPUCores == 0 {
info.CPUCores = runtime.NumCPU()
}
// Uptime
if data, err := os.ReadFile("/proc/uptime"); err == nil {
var uptimeSec float64
if _, err := fmt.Sscanf(string(data), "%f", &uptimeSec); err == nil {
info.UptimeSeconds = int64(uptimeSec)
info.BootTime = time.Now().Add(-time.Duration(info.UptimeSeconds) * time.Second)
}
}
return info
}
// readOSRelease reads PRETTY_NAME from an os-release file.
func readOSRelease(path string) string {
f, err := os.Open(path)
if err != nil {
return ""
}
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := scanner.Text()
if strings.HasPrefix(line, "PRETTY_NAME=") {
val := strings.TrimPrefix(line, "PRETTY_NAME=")
val = strings.Trim(val, `"`)
return val
}
}
return ""
}
// readCPUInfo reads the CPU model name and number of cores from /proc/cpuinfo.
func readCPUInfo() (model string, cores int) {
f, err := os.Open("/proc/cpuinfo")
if err != nil {
return "", 0
}
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := scanner.Text()
if strings.HasPrefix(line, "model name") {
if idx := strings.Index(line, ":"); idx >= 0 {
model = strings.TrimSpace(line[idx+1:])
}
}
if strings.HasPrefix(line, "processor") {
cores++
}
}
return model, cores
}