监控网页实现
This commit is contained in:
7
backend/internal/monitor/disk_other.go
Normal file
7
backend/internal/monitor/disk_other.go
Normal file
@@ -0,0 +1,7 @@
|
||||
//go:build !windows
|
||||
|
||||
package monitor
|
||||
|
||||
func getDiskSpaceGB() (float64, float64) {
|
||||
return 0, 0
|
||||
}
|
||||
38
backend/internal/monitor/disk_windows.go
Normal file
38
backend/internal/monitor/disk_windows.go
Normal file
@@ -0,0 +1,38 @@
|
||||
//go:build windows
|
||||
|
||||
package monitor
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func getDiskSpaceGB() (float64, float64) {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
vol := filepath.VolumeName(wd)
|
||||
if vol == "" {
|
||||
return 0, 0
|
||||
}
|
||||
root := vol + `\\`
|
||||
|
||||
pathPtr, err := windows.UTF16PtrFromString(root)
|
||||
if err != nil {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
var freeBytesAvailable uint64
|
||||
var totalBytes uint64
|
||||
var totalFreeBytes uint64
|
||||
if err := windows.GetDiskFreeSpaceEx(pathPtr, &freeBytesAvailable, &totalBytes, &totalFreeBytes); err != nil {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
const gb = 1024.0 * 1024.0 * 1024.0
|
||||
return float64(totalBytes) / gb, float64(totalFreeBytes) / gb
|
||||
}
|
||||
130
backend/internal/monitor/logs.go
Normal file
130
backend/internal/monitor/logs.go
Normal file
@@ -0,0 +1,130 @@
|
||||
package monitor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type LogEntry struct {
|
||||
Time string `json:"time"`
|
||||
Level string `json:"level"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type logHub struct {
|
||||
mutex sync.RWMutex
|
||||
entries []LogEntry
|
||||
maxEntries int
|
||||
subscribers map[chan LogEntry]struct{}
|
||||
}
|
||||
|
||||
var hub = &logHub{
|
||||
maxEntries: 1000,
|
||||
subscribers: make(map[chan LogEntry]struct{}),
|
||||
}
|
||||
|
||||
func Init(maxEntries int) {
|
||||
if maxEntries > 0 {
|
||||
hub.maxEntries = maxEntries
|
||||
}
|
||||
}
|
||||
|
||||
func Infof(format string, args ...interface{}) {
|
||||
appendEntry("info", fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
func Warnf(format string, args ...interface{}) {
|
||||
appendEntry("warn", fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
func Errorf(format string, args ...interface{}) {
|
||||
appendEntry("error", fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
func Auditf(format string, args ...interface{}) {
|
||||
appendEntry("audit", fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
func appendEntry(level, message string) {
|
||||
entry := LogEntry{
|
||||
Time: time.Now().Format(time.RFC3339),
|
||||
Level: strings.ToLower(level),
|
||||
Message: message,
|
||||
}
|
||||
|
||||
log.Printf("[%s] %s", strings.ToUpper(entry.Level), entry.Message)
|
||||
|
||||
hub.mutex.Lock()
|
||||
hub.entries = append(hub.entries, entry)
|
||||
if len(hub.entries) > hub.maxEntries {
|
||||
hub.entries = hub.entries[len(hub.entries)-hub.maxEntries:]
|
||||
}
|
||||
|
||||
for ch := range hub.subscribers {
|
||||
select {
|
||||
case ch <- entry:
|
||||
default:
|
||||
}
|
||||
}
|
||||
hub.mutex.Unlock()
|
||||
}
|
||||
|
||||
func Subscribe() chan LogEntry {
|
||||
ch := make(chan LogEntry, 100)
|
||||
hub.mutex.Lock()
|
||||
hub.subscribers[ch] = struct{}{}
|
||||
hub.mutex.Unlock()
|
||||
return ch
|
||||
}
|
||||
|
||||
func Unsubscribe(ch chan LogEntry) {
|
||||
hub.mutex.Lock()
|
||||
if _, ok := hub.subscribers[ch]; ok {
|
||||
delete(hub.subscribers, ch)
|
||||
close(ch)
|
||||
}
|
||||
hub.mutex.Unlock()
|
||||
}
|
||||
|
||||
func Query(level, keyword string, page, pageSize int) ([]LogEntry, int) {
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize < 1 {
|
||||
pageSize = 20
|
||||
}
|
||||
if pageSize > 200 {
|
||||
pageSize = 200
|
||||
}
|
||||
|
||||
level = strings.TrimSpace(strings.ToLower(level))
|
||||
keyword = strings.TrimSpace(strings.ToLower(keyword))
|
||||
|
||||
hub.mutex.RLock()
|
||||
defer hub.mutex.RUnlock()
|
||||
|
||||
filtered := make([]LogEntry, 0, len(hub.entries))
|
||||
for _, e := range hub.entries {
|
||||
if level != "" && e.Level != level {
|
||||
continue
|
||||
}
|
||||
if keyword != "" && !strings.Contains(strings.ToLower(e.Message), keyword) {
|
||||
continue
|
||||
}
|
||||
filtered = append(filtered, e)
|
||||
}
|
||||
|
||||
total := len(filtered)
|
||||
start := (page - 1) * pageSize
|
||||
if start >= total {
|
||||
return []LogEntry{}, total
|
||||
}
|
||||
end := start + pageSize
|
||||
if end > total {
|
||||
end = total
|
||||
}
|
||||
return filtered[start:end], total
|
||||
}
|
||||
55
backend/internal/monitor/metrics.go
Normal file
55
backend/internal/monitor/metrics.go
Normal file
@@ -0,0 +1,55 @@
|
||||
package monitor
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
var startedAt = time.Now()
|
||||
|
||||
var totalRequests uint64
|
||||
var totalErrors uint64
|
||||
|
||||
type Snapshot struct {
|
||||
UptimeSeconds int64 `json:"uptime_seconds"`
|
||||
Goroutines int `json:"goroutines"`
|
||||
MemoryAllocMB float64 `json:"memory_alloc_mb"`
|
||||
MemorySysMB float64 `json:"memory_sys_mb"`
|
||||
CPUCores int `json:"cpu_cores"`
|
||||
DiskTotalGB float64 `json:"disk_total_gb"`
|
||||
DiskFreeGB float64 `json:"disk_free_gb"`
|
||||
RequestsTotal uint64 `json:"requests_total"`
|
||||
ErrorsTotal uint64 `json:"errors_total"`
|
||||
}
|
||||
|
||||
func IncrementRequestCount() {
|
||||
atomic.AddUint64(&totalRequests, 1)
|
||||
}
|
||||
|
||||
func IncrementErrorCount() {
|
||||
atomic.AddUint64(&totalErrors, 1)
|
||||
}
|
||||
|
||||
func GetSnapshot() Snapshot {
|
||||
var mem runtime.MemStats
|
||||
runtime.ReadMemStats(&mem)
|
||||
|
||||
diskTotal, diskFree := getDiskSpaceGB()
|
||||
|
||||
return Snapshot{
|
||||
UptimeSeconds: int64(time.Since(startedAt).Seconds()),
|
||||
Goroutines: runtime.NumGoroutine(),
|
||||
MemoryAllocMB: bytesToMB(mem.Alloc),
|
||||
MemorySysMB: bytesToMB(mem.Sys),
|
||||
CPUCores: runtime.NumCPU(),
|
||||
DiskTotalGB: diskTotal,
|
||||
DiskFreeGB: diskFree,
|
||||
RequestsTotal: atomic.LoadUint64(&totalRequests),
|
||||
ErrorsTotal: atomic.LoadUint64(&totalErrors),
|
||||
}
|
||||
}
|
||||
|
||||
func bytesToMB(v uint64) float64 {
|
||||
return float64(v) / 1024.0 / 1024.0
|
||||
}
|
||||
Reference in New Issue
Block a user