feat: add slog structured logging
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
8f2f3cf271
commit
ea754a85c5
+42
-4
@@ -1,11 +1,49 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"log"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"codespace/pkg/config"
|
||||
)
|
||||
|
||||
// New returns a standard library logger writing to stdout with a codespace prefix.
|
||||
func New() *log.Logger {
|
||||
return log.New(os.Stdout, "codespace ", log.LstdFlags|log.Lshortfile)
|
||||
// New creates a *slog.Logger from the given log configuration.
|
||||
// It also sets the logger as the default slog logger.
|
||||
func New(cfg config.LogConfig) (*slog.Logger, error) {
|
||||
level, err := parseLevel(cfg.Level)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
opts := &slog.HandlerOptions{Level: level}
|
||||
var handler slog.Handler
|
||||
switch strings.ToLower(cfg.Format) {
|
||||
case "json", "":
|
||||
handler = slog.NewJSONHandler(os.Stdout, opts)
|
||||
case "text":
|
||||
handler = slog.NewTextHandler(os.Stdout, opts)
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid log format %q", cfg.Format)
|
||||
}
|
||||
|
||||
lg := slog.New(handler)
|
||||
slog.SetDefault(lg)
|
||||
return lg, nil
|
||||
}
|
||||
|
||||
func parseLevel(value string) (slog.Level, error) {
|
||||
switch strings.ToLower(value) {
|
||||
case "debug":
|
||||
return slog.LevelDebug, nil
|
||||
case "info", "":
|
||||
return slog.LevelInfo, nil
|
||||
case "warn":
|
||||
return slog.LevelWarn, nil
|
||||
case "error":
|
||||
return slog.LevelError, nil
|
||||
default:
|
||||
return slog.LevelInfo, fmt.Errorf("invalid log level %q", value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"codespace/pkg/config"
|
||||
)
|
||||
|
||||
func TestNewJSONInfoLogger(t *testing.T) {
|
||||
lg, err := New(config.LogConfig{Level: "info", Format: "json"})
|
||||
if err != nil {
|
||||
t.Fatalf("New returned error: %v", err)
|
||||
}
|
||||
if lg == nil {
|
||||
t.Fatal("logger is nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewTextDebugLogger(t *testing.T) {
|
||||
lg, err := New(config.LogConfig{Level: "debug", Format: "text"})
|
||||
if err != nil {
|
||||
t.Fatalf("New returned error: %v", err)
|
||||
}
|
||||
if lg == nil {
|
||||
t.Fatal("logger is nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewRejectsInvalidLevel(t *testing.T) {
|
||||
if _, err := New(config.LogConfig{Level: "verbose", Format: "json"}); err == nil {
|
||||
t.Fatal("expected invalid level error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewRejectsInvalidFormat(t *testing.T) {
|
||||
if _, err := New(config.LogConfig{Level: "info", Format: "xml"}); err == nil {
|
||||
t.Fatal("expected invalid format error")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user