Files
Cyrene_v2/backend/services/ai-core/trace/logger.go
T
Aska e8b5679cf4 feat(admin): add full-chain trace and performance monitoring
- Backend: trace.Logger ring-buffer for request chain events
- Backend: perf.Monitor with runtime.MemStats sampling every 10s
- Backend: /api/v1/admin/trace and /api/v1/admin/perf endpoints
- Frontend: TracePage — timeline with hop icons and status dots
- Frontend: PerformancePage — memory/GC bar charts + live stats
- Admin nav expanded to 11 items
2026-08-12 17:16:33 +08:00

83 lines
2.0 KiB
Go

// Package trace provides a ring-buffer for full-chain tracing events.
package trace
import (
"sync"
"time"
)
// Event represents a single event in a request trace chain.
type Event struct {
ID string `json:"id"`
Timestamp time.Time `json:"timestamp"`
Hop string `json:"hop"` // "msg_received" | "intent" | "llm_call" | "tool_call" | "response"
Label string `json:"label"`
Status string `json:"status"` // "success" | "error" | "pending"
DurationMs int64 `json:"duration_ms"`
Detail string `json:"detail"`
Data map[string]any `json:"data,omitempty"`
}
// Logger is a ring-buffer that stores the last N trace events.
type Logger struct {
mu sync.RWMutex
events []Event
max int
index int
}
// NewLogger creates a trace logger with the given capacity.
func NewLogger(capacity int) *Logger {
return &Logger{events: make([]Event, capacity), max: capacity}
}
// Record adds a trace event.
func (l *Logger) Record(event Event) {
l.mu.Lock()
defer l.mu.Unlock()
if event.ID == "" {
event.ID = time.Now().Format("150405") + "-" + event.Hop
}
l.events[l.index%l.max] = event
l.index++
}
// Recent returns the most recent N events.
func (l *Logger) Recent(n int) []Event {
l.mu.RLock()
defer l.mu.RUnlock()
if n > l.max { n = l.max }
count := l.index
if count > l.max { count = l.max }
if n > count { n = count }
result := make([]Event, n)
for i := 0; i < n; i++ {
idx := (l.index - 1 - i + l.max) % l.max
if idx < l.max {
result[n-1-i] = l.events[idx]
}
}
return result
}
// Hops returns unique hop names seen in the buffer.
func (l *Logger) Hops() []string {
l.mu.RLock()
defer l.mu.RUnlock()
seen := make(map[string]bool)
var result []string
count := l.index
if count > l.max { count = l.max }
for i := 0; i < count; i++ {
ev := l.events[i]
if !seen[ev.Hop] && ev.Hop != "" {
seen[ev.Hop] = true
result = append(result, ev.Hop)
}
}
return result
}