初始提交:边缘AI算力机统一AI通讯层
This commit is contained in:
@@ -0,0 +1,319 @@
|
||||
package observability
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// LogLevel represents logging severity levels.
|
||||
type LogLevel int
|
||||
|
||||
const (
|
||||
LevelDebug LogLevel = iota
|
||||
LevelInfo
|
||||
LevelWarn
|
||||
LevelError
|
||||
)
|
||||
|
||||
func (l LogLevel) String() string {
|
||||
switch l {
|
||||
case LevelDebug:
|
||||
return "DEBUG"
|
||||
case LevelInfo:
|
||||
return "INFO"
|
||||
case LevelWarn:
|
||||
return "WARN"
|
||||
case LevelError:
|
||||
return "ERROR"
|
||||
default:
|
||||
return "INFO"
|
||||
}
|
||||
}
|
||||
|
||||
// ParseLogLevel parses a string to LogLevel.
|
||||
func ParseLogLevel(s string) LogLevel {
|
||||
switch strings.ToLower(s) {
|
||||
case "debug":
|
||||
return LevelDebug
|
||||
case "info":
|
||||
return LevelInfo
|
||||
case "warn", "warning":
|
||||
return LevelWarn
|
||||
case "error":
|
||||
return LevelError
|
||||
default:
|
||||
return LevelInfo
|
||||
}
|
||||
}
|
||||
|
||||
// LogEntry is a structured JSON log entry.
|
||||
type LogEntry struct {
|
||||
Timestamp string `json:"timestamp"`
|
||||
Level string `json:"level"`
|
||||
Event string `json:"event,omitempty"`
|
||||
Message string `json:"message,omitempty"`
|
||||
RequestID string `json:"request_id,omitempty"`
|
||||
TaskID string `json:"task_id,omitempty"`
|
||||
SessionID string `json:"session_id,omitempty"`
|
||||
TraceID string `json:"trace_id,omitempty"`
|
||||
Application string `json:"application,omitempty"`
|
||||
TenantID string `json:"tenant_id,omitempty"`
|
||||
UserID string `json:"user_id,omitempty"`
|
||||
FromState string `json:"from_state,omitempty"`
|
||||
ToState string `json:"to_state,omitempty"`
|
||||
Reason string `json:"reason,omitempty"`
|
||||
LogicalModel string `json:"logical_model,omitempty"`
|
||||
ActualModel string `json:"actual_model,omitempty"`
|
||||
NodeID string `json:"node_id,omitempty"`
|
||||
Degraded bool `json:"degraded,omitempty"`
|
||||
Extra map[string]any `json:"extra,omitempty"`
|
||||
}
|
||||
|
||||
// Logger is a structured JSON logger with sensitive field masking.
|
||||
type Logger struct {
|
||||
mu sync.RWMutex
|
||||
level LogLevel
|
||||
output *os.File
|
||||
maskFields []string
|
||||
promptLogging string
|
||||
}
|
||||
|
||||
var defaultLogger *Logger
|
||||
|
||||
func init() {
|
||||
defaultLogger = NewLogger(LevelInfo, os.Stdout, "metadata_only")
|
||||
}
|
||||
|
||||
// NewLogger creates a new Logger instance.
|
||||
func NewLogger(level LogLevel, out *os.File, promptLogging string) *Logger {
|
||||
return &Logger{
|
||||
level: level,
|
||||
output: out,
|
||||
maskFields: []string{"api_key", "apikey", "authorization", "jwt", "secret", "password", "token"},
|
||||
promptLogging: promptLogging,
|
||||
}
|
||||
}
|
||||
|
||||
// GetLogger returns the default logger.
|
||||
func GetLogger() *Logger {
|
||||
return defaultLogger
|
||||
}
|
||||
|
||||
// SetLevel updates the log level (thread-safe).
|
||||
func (l *Logger) SetLevel(level LogLevel) {
|
||||
l.mu.Lock()
|
||||
l.level = level
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// SetPromptLogging updates the prompt logging policy.
|
||||
func (l *Logger) SetPromptLogging(policy string) {
|
||||
l.mu.Lock()
|
||||
l.promptLogging = policy
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
func (l *Logger) shouldLog(level LogLevel) bool {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
return level >= l.level
|
||||
}
|
||||
|
||||
func (l *Logger) maskSensitive(data map[string]any) map[string]any {
|
||||
if data == nil {
|
||||
return nil
|
||||
}
|
||||
masked := make(map[string]any, len(data))
|
||||
for k, v := range data {
|
||||
if l.isSensitive(k) {
|
||||
masked[k] = "***REDACTED***"
|
||||
} else if sub, ok := v.(map[string]any); ok {
|
||||
masked[k] = l.maskSensitive(sub)
|
||||
} else {
|
||||
masked[k] = v
|
||||
}
|
||||
}
|
||||
return masked
|
||||
}
|
||||
|
||||
func (l *Logger) isSensitive(key string) bool {
|
||||
lk := strings.ToLower(key)
|
||||
for _, s := range l.maskFields {
|
||||
if strings.Contains(lk, s) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (l *Logger) write(entry LogEntry) {
|
||||
if !l.shouldLog(parseLevelFromString(entry.Level)) {
|
||||
return
|
||||
}
|
||||
if entry.Extra != nil {
|
||||
entry.Extra = l.maskSensitive(entry.Extra)
|
||||
}
|
||||
if entry.Timestamp == "" {
|
||||
entry.Timestamp = time.Now().UTC().Format(time.RFC3339Nano)
|
||||
}
|
||||
data, err := json.Marshal(entry)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "log marshal error: %v\n", err)
|
||||
return
|
||||
}
|
||||
l.mu.Lock()
|
||||
fmt.Fprintln(l.output, string(data))
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
func parseLevelFromString(s string) LogLevel {
|
||||
switch strings.ToUpper(s) {
|
||||
case "DEBUG":
|
||||
return LevelDebug
|
||||
case "INFO":
|
||||
return LevelInfo
|
||||
case "WARN", "WARNING":
|
||||
return LevelWarn
|
||||
case "ERROR":
|
||||
return LevelError
|
||||
default:
|
||||
return LevelInfo
|
||||
}
|
||||
}
|
||||
|
||||
// LogFields is a builder for structured log fields.
|
||||
type LogFields struct {
|
||||
fields map[string]any
|
||||
}
|
||||
|
||||
func F() *LogFields {
|
||||
return &LogFields{fields: make(map[string]any)}
|
||||
}
|
||||
|
||||
func (f *LogFields) Set(key string, value any) *LogFields {
|
||||
f.fields[key] = value
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *LogFields) RequestID(id string) *LogFields { f.fields["request_id"] = id; return f }
|
||||
func (f *LogFields) TaskID(id string) *LogFields { f.fields["task_id"] = id; return f }
|
||||
func (f *LogFields) SessionID(id string) *LogFields { f.fields["session_id"] = id; return f }
|
||||
func (f *LogFields) TraceID(id string) *LogFields { f.fields["trace_id"] = id; return f }
|
||||
func (f *LogFields) Application(app string) *LogFields { f.fields["application"] = app; return f }
|
||||
func (f *LogFields) TenantID(id string) *LogFields { f.fields["tenant_id"] = id; return f }
|
||||
func (f *LogFields) UserID(id string) *LogFields { f.fields["user_id"] = id; return f }
|
||||
func (f *LogFields) Event(e string) *LogFields { f.fields["event"] = e; return f }
|
||||
func (f *LogFields) Reason(r string) *LogFields { f.fields["reason"] = r; return f }
|
||||
|
||||
func (l *Logger) Debug(msg string, fields *LogFields) {
|
||||
entry := l.buildEntry("DEBUG", msg, fields)
|
||||
l.write(entry)
|
||||
}
|
||||
|
||||
func (l *Logger) Info(msg string, fields *LogFields) {
|
||||
entry := l.buildEntry("INFO", msg, fields)
|
||||
l.write(entry)
|
||||
}
|
||||
|
||||
func (l *Logger) Warn(msg string, fields *LogFields) {
|
||||
entry := l.buildEntry("WARN", msg, fields)
|
||||
l.write(entry)
|
||||
}
|
||||
|
||||
func (l *Logger) Error(msg string, fields *LogFields) {
|
||||
entry := l.buildEntry("ERROR", msg, fields)
|
||||
l.write(entry)
|
||||
}
|
||||
|
||||
func (l *Logger) buildEntry(level, msg string, fields *LogFields) LogEntry {
|
||||
entry := LogEntry{
|
||||
Level: level,
|
||||
Message: msg,
|
||||
}
|
||||
if fields != nil {
|
||||
for k, v := range fields.fields {
|
||||
switch k {
|
||||
case "event":
|
||||
if s, ok := v.(string); ok {
|
||||
entry.Event = s
|
||||
}
|
||||
case "request_id":
|
||||
if s, ok := v.(string); ok {
|
||||
entry.RequestID = s
|
||||
}
|
||||
case "task_id":
|
||||
if s, ok := v.(string); ok {
|
||||
entry.TaskID = s
|
||||
}
|
||||
case "session_id":
|
||||
if s, ok := v.(string); ok {
|
||||
entry.SessionID = s
|
||||
}
|
||||
case "trace_id":
|
||||
if s, ok := v.(string); ok {
|
||||
entry.TraceID = s
|
||||
}
|
||||
case "application":
|
||||
if s, ok := v.(string); ok {
|
||||
entry.Application = s
|
||||
}
|
||||
case "tenant_id":
|
||||
if s, ok := v.(string); ok {
|
||||
entry.TenantID = s
|
||||
}
|
||||
case "user_id":
|
||||
if s, ok := v.(string); ok {
|
||||
entry.UserID = s
|
||||
}
|
||||
case "reason":
|
||||
if s, ok := v.(string); ok {
|
||||
entry.Reason = s
|
||||
}
|
||||
case "from_state":
|
||||
if s, ok := v.(string); ok {
|
||||
entry.FromState = s
|
||||
}
|
||||
case "to_state":
|
||||
if s, ok := v.(string); ok {
|
||||
entry.ToState = s
|
||||
}
|
||||
case "logical_model":
|
||||
if s, ok := v.(string); ok {
|
||||
entry.LogicalModel = s
|
||||
}
|
||||
case "actual_model":
|
||||
if s, ok := v.(string); ok {
|
||||
entry.ActualModel = s
|
||||
}
|
||||
case "node_id":
|
||||
if s, ok := v.(string); ok {
|
||||
entry.NodeID = s
|
||||
}
|
||||
case "degraded":
|
||||
if b, ok := v.(bool); ok {
|
||||
entry.Degraded = b
|
||||
}
|
||||
default:
|
||||
if entry.Extra == nil {
|
||||
entry.Extra = make(map[string]any)
|
||||
}
|
||||
entry.Extra[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
return entry
|
||||
}
|
||||
|
||||
// SetLogLevel updates the global log level.
|
||||
func SetLogLevel(level string) {
|
||||
defaultLogger.SetLevel(ParseLogLevel(level))
|
||||
}
|
||||
|
||||
// SetPromptLoggingPolicy updates the global prompt logging policy.
|
||||
func SetPromptLoggingPolicy(policy string) {
|
||||
defaultLogger.SetPromptLogging(policy)
|
||||
}
|
||||
Reference in New Issue
Block a user