This commit is contained in:
2026-08-15 01:12:04 +08:00
parent f63acae353
commit 6698e59c3a
45 changed files with 725 additions and 415 deletions
+98 -3
View File
@@ -1,9 +1,11 @@
package logging
import (
"context"
"log/slog"
"os"
"strings"
"sync"
"time"
"github.com/go-kratos/kratos/contrib/otel/v3/tracing"
@@ -20,6 +22,85 @@ type Options struct {
FileOnlyModules []string
}
type handlerOperation struct {
attrs []slog.Attr
group string
}
type reloadableHandlerState struct {
mu sync.RWMutex
handler slog.Handler
cleanup func()
}
type reloadableHandler struct {
state *reloadableHandlerState
ops []handlerOperation
}
func (h *reloadableHandler) resolved() slog.Handler {
current := h.state.handler
for _, operation := range h.ops {
if operation.group != "" {
current = current.WithGroup(operation.group)
} else {
current = current.WithAttrs(operation.attrs)
}
}
return current
}
func (h *reloadableHandler) Enabled(ctx context.Context, level slog.Level) bool {
h.state.mu.RLock()
defer h.state.mu.RUnlock()
return h.resolved().Enabled(ctx, level)
}
func (h *reloadableHandler) Handle(ctx context.Context, record slog.Record) error {
h.state.mu.RLock()
defer h.state.mu.RUnlock()
return h.resolved().Handle(ctx, record)
}
func (h *reloadableHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
next := append([]handlerOperation(nil), h.ops...)
next = append(next, handlerOperation{attrs: append([]slog.Attr(nil), attrs...)})
return &reloadableHandler{state: h.state, ops: next}
}
func (h *reloadableHandler) WithGroup(name string) slog.Handler {
next := append([]handlerOperation(nil), h.ops...)
next = append(next, handlerOperation{group: name})
return &reloadableHandler{state: h.state, ops: next}
}
type ReloadableLogger struct {
state *reloadableHandlerState
filename string
}
func (l *ReloadableLogger) Reload(root string, options Options) {
handler, cleanup := newZapHandler(root, l.filename, options)
l.state.mu.Lock()
previous := l.state.cleanup
l.state.handler = handler
l.state.cleanup = cleanup
l.state.mu.Unlock()
if previous != nil {
previous()
}
}
func (l *ReloadableLogger) Close() {
l.state.mu.Lock()
cleanup := l.state.cleanup
l.state.cleanup = nil
l.state.mu.Unlock()
if cleanup != nil {
cleanup()
}
}
type moduleFilterCore struct {
zapcore.Core
fileOnly map[string]struct{}
@@ -63,7 +144,7 @@ func moduleField(fields []zapcore.Field) string {
// NewZapLogger adapts a Zap core to the slog logger used by Kratos v3.
// The file layout remains compatible with the administration log viewer.
func NewZapLogger(root, filename string, options Options, attrs ...any) (*slog.Logger, func()) {
func newZapHandler(root, filename string, options Options) (slog.Handler, func()) {
file := NewDailyWriter(root, filename, options.RetentionDay)
encoder := zap.NewProductionEncoderConfig()
encoder.EncodeTime = zapcore.RFC3339NanoTimeEncoder
@@ -114,10 +195,24 @@ func NewZapLogger(root, filename string, options Options, attrs ...any) (*slog.L
handlerOptions = append(handlerOptions, zapslog.WithCaller(true))
}
handler := zapslog.NewHandler(zapLogger.Core(), handlerOptions...)
logger := kratoslog.NewLogger(handler, kratoslog.WithExtractor(tracing.TraceAttrs)).With(attrs...)
cleanup := func() {
_ = zapLogger.Sync()
_ = file.Close()
}
return logger, cleanup
return handler, cleanup
}
// NewReloadableZapLogger keeps the slog/Kratos adapter stable while replacing
// the underlying Zap core when the runtime configuration changes.
func NewReloadableZapLogger(root, filename string, options Options, attrs ...any) (*slog.Logger, *ReloadableLogger) {
handler, cleanup := newZapHandler(root, filename, options)
state := &reloadableHandlerState{handler: handler, cleanup: cleanup}
control := &ReloadableLogger{state: state, filename: filename}
logger := kratoslog.NewLogger(&reloadableHandler{state: state}, kratoslog.WithExtractor(tracing.TraceAttrs)).With(attrs...)
return logger, control
}
func NewZapLogger(root, filename string, options Options, attrs ...any) (*slog.Logger, func()) {
logger, control := NewReloadableZapLogger(root, filename, options, attrs...)
return logger, control.Close
}