zai-proxy/dashboard/logger/logger.go
jedarden e7c24a0c08 feat: initial zai-proxy ecosystem repo
Extracted from ardenone-cluster/containers/zai-proxy and
ardenone-cluster/containers/zai-proxy-dashboard.

- proxy/: OpenAI-compatible ZAI reverse proxy (Go, v1.10.0)
  - Token counting, rate limiting, Prometheus metrics, canary support
- dashboard/: Metrics dashboard backend + React frontend (Go, v1.0.0)
  - Prometheus collector, SQLite storage, SSE live updates
- docs/: Operational notes, research, and plan subdirs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 15:53:52 -04:00

137 lines
3 KiB
Go

// Package logger provides structured JSON logging for the zai-proxy-dashboard.
package logger
import (
"context"
"log/slog"
"os"
"sync"
)
var (
defaultLogger *slog.Logger
once sync.Once
)
// Config holds logger configuration.
type Config struct {
// Format: "json" or "text" (default: json)
Format string
// Level: "debug", "info", "warn", "error" (default: info)
Level string
// AddSource adds source file/line to log entries (default: false)
AddSource bool
}
// DefaultConfig returns the default logger configuration.
func DefaultConfig() Config {
return Config{
Format: getEnvOrDefault("LOG_FORMAT", "json"),
Level: getEnvOrDefault("LOG_LEVEL", "info"),
AddSource: getEnvOrDefault("LOG_ADD_SOURCE", "false") == "true",
}
}
func getEnvOrDefault(key, def string) string {
if v := os.Getenv(key); v != "" {
return v
}
return def
}
// Init initializes the default logger with the given configuration.
func Init(cfg Config) {
once.Do(func() {
var level slog.Level
switch cfg.Level {
case "debug":
level = slog.LevelDebug
case "info":
level = slog.LevelInfo
case "warn":
level = slog.LevelWarn
case "error":
level = slog.LevelError
default:
level = slog.LevelInfo
}
opts := &slog.HandlerOptions{
Level: level,
AddSource: cfg.AddSource,
}
var handler slog.Handler
if cfg.Format == "text" {
handler = slog.NewTextHandler(os.Stdout, opts)
} else {
handler = slog.NewJSONHandler(os.Stdout, opts)
}
defaultLogger = slog.New(handler)
slog.SetDefault(defaultLogger)
})
}
// Get returns the default logger.
func Get() *slog.Logger {
if defaultLogger == nil {
Init(DefaultConfig())
}
return defaultLogger
}
// Debug logs at debug level.
func Debug(msg string, args ...any) {
Get().Debug(msg, args...)
}
// DebugCtx logs at debug level with context.
func DebugCtx(ctx context.Context, msg string, args ...any) {
Get().DebugContext(ctx, msg, args...)
}
// Info logs at info level.
func Info(msg string, args ...any) {
Get().Info(msg, args...)
}
// InfoCtx logs at info level with context.
func InfoCtx(ctx context.Context, msg string, args ...any) {
Get().InfoContext(ctx, msg, args...)
}
// Warn logs at warn level.
func Warn(msg string, args ...any) {
Get().Warn(msg, args...)
}
// WarnCtx logs at warn level with context.
func WarnCtx(ctx context.Context, msg string, args ...any) {
Get().WarnContext(ctx, msg, args...)
}
// Error logs at error level.
func Error(msg string, args ...any) {
Get().Error(msg, args...)
}
// ErrorCtx logs at error level with context.
func ErrorCtx(ctx context.Context, msg string, args ...any) {
Get().ErrorContext(ctx, msg, args...)
}
// With returns a logger with additional context.
func With(args ...any) *slog.Logger {
return Get().With(args...)
}
// WithGroup returns a logger with a group.
func WithGroup(name string) *slog.Logger {
return Get().WithGroup(name)
}
// Component returns a logger with a component label.
func Component(name string) *slog.Logger {
return Get().With("component", name)
}