feat: complete API-driven projects with infrastructure and business acceptance
This commit is contained in:
43
logx/logx.go
43
logx/logx.go
@@ -10,6 +10,7 @@ import (
|
||||
"io"
|
||||
"log/slog"
|
||||
"os"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
@@ -21,14 +22,16 @@ import (
|
||||
// standard stream. File takes precedence over Output and appends to the named
|
||||
// file.
|
||||
type Config struct {
|
||||
Level string `json:"level"`
|
||||
Format string `json:"format"`
|
||||
Output string `json:"output"`
|
||||
File string `json:"file"`
|
||||
Service string `json:"service"`
|
||||
Environment string `json:"environment"`
|
||||
AddSource bool `json:"addSource"`
|
||||
Writer io.Writer `json:"-"`
|
||||
Level string `json:"level"`
|
||||
Format string `json:"format"`
|
||||
Output string `json:"output"`
|
||||
File string `json:"file"`
|
||||
Service string `json:"service"`
|
||||
Environment string `json:"environment"`
|
||||
AddSource bool `json:"addSource"`
|
||||
Writer io.Writer `json:"-"`
|
||||
Rotation RotationConfig `json:"rotation"`
|
||||
RedactKeys []string `json:"redactKeys"`
|
||||
}
|
||||
|
||||
// Default returns the default production-friendly logger configuration.
|
||||
@@ -62,13 +65,17 @@ var (
|
||||
func init() {
|
||||
// Keep package-level logging useful without requiring every service to
|
||||
// repeat setup. Applications can replace this logger with Setup.
|
||||
defaultLogger.Store(&Logger{logger: slog.New(slog.NewJSONHandler(os.Stderr, nil))})
|
||||
logger, _ := New(Default())
|
||||
defaultLogger.Store(logger)
|
||||
}
|
||||
|
||||
// New creates an independent logger. The caller owns its lifecycle and should
|
||||
// call Close when the process is shutting down.
|
||||
func New(cfg Config) (*Logger, error) {
|
||||
cfg = normalizeConfig(cfg)
|
||||
if err := cfg.Rotation.validate(cfg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
level, err := parseLevel(cfg.Level)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -81,7 +88,7 @@ func New(cfg Config) (*Logger, error) {
|
||||
|
||||
dynamicLevel := new(slog.LevelVar)
|
||||
dynamicLevel.Set(level)
|
||||
opts := &slog.HandlerOptions{Level: dynamicLevel, AddSource: cfg.AddSource}
|
||||
opts := &slog.HandlerOptions{Level: dynamicLevel, AddSource: cfg.AddSource, ReplaceAttr: redactor(cfg.RedactKeys)}
|
||||
var handler slog.Handler
|
||||
if cfg.Format == "text" {
|
||||
handler = slog.NewTextHandler(writer, opts)
|
||||
@@ -208,6 +215,19 @@ func (l *Logger) ErrorContext(ctx context.Context, msg string, args ...any) {
|
||||
l.logger.Log(ctx, slog.LevelError, msg, contextAttrs(ctx, args)...)
|
||||
}
|
||||
|
||||
// ErrorStackContext records the current goroutine's stack at the logging call,
|
||||
// not the historical location where an error was originally constructed.
|
||||
func (l *Logger) ErrorStackContext(ctx context.Context, msg string, args ...any) {
|
||||
attrs := make([]any, 0, len(args)+2)
|
||||
attrs = append(attrs, args...)
|
||||
attrs = append(attrs, "stack", string(debug.Stack()))
|
||||
l.ErrorContext(ctx, msg, attrs...)
|
||||
}
|
||||
|
||||
func ErrorStackContext(ctx context.Context, msg string, args ...any) {
|
||||
defaultLogger.Load().ErrorStackContext(ctx, msg, args...)
|
||||
}
|
||||
|
||||
func (l *Logger) Debug(msg string, args ...any) { l.DebugContext(context.Background(), msg, args...) }
|
||||
func (l *Logger) Info(msg string, args ...any) { l.InfoContext(context.Background(), msg, args...) }
|
||||
func (l *Logger) Warn(msg string, args ...any) { l.WarnContext(context.Background(), msg, args...) }
|
||||
@@ -324,6 +344,9 @@ func openWriter(cfg Config) (io.Writer, func() error, error) {
|
||||
return cfg.Writer, nil, nil
|
||||
}
|
||||
if cfg.File != "" {
|
||||
if cfg.Rotation.Enabled {
|
||||
return rotatingWriter(cfg)
|
||||
}
|
||||
file, err := os.OpenFile(cfg.File, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
||||
Reference in New Issue
Block a user