package logx import ( "context" "log" "log/slog" "strings" "time" "git.yuntuo.hk/k2safe/go-omni/internal/configdiag" ) // Validate checks configuration without opening files or creating resources. func (c Config) Validate() error { _, err := c.validate(); return err } func (c Config) validate() (*time.Location, error) { c = normalizeConfig(c) if _, err := parseLevel(c.Level); err != nil { return nil, configdiag.New("level", "must be debug, info, warn, warning or error") } if c.Format != "json" && c.Format != "text" { return nil, configdiag.New("format", "must be json or text") } switch c.Output { case "stdout", "stderr", "discard", "none": default: return nil, configdiag.New("output", "must be stdout, stderr, discard or none") } if err := c.Rotation.validate(c); err != nil { return nil, err } return logLocation(c.TimeZone) } type loggerContextKey struct{} // WithLogger borrows a logger for a call tree; it does not alter the global // default and does not transfer ownership. nil preserves the existing context. func WithLogger(ctx context.Context, logger *Logger) context.Context { ctx = normalizeContext(ctx) if logger == nil { return ctx } return context.WithValue(ctx, loggerContextKey{}, logger) } // FromContext returns a call-scoped logger, falling back to package logging. func FromContext(ctx context.Context) *Logger { if ctx != nil { if l, ok := ctx.Value(loggerContextKey{}).(*Logger); ok && l != nil { return l } } return defaultLogger.Load() } // Standard adapts this instance to the standard logger (for http.Server etc). // It borrows the writer and never closes it. Each Print call becomes one event. func (l *Logger) Standard(level slog.Level) *log.Logger { if l == nil { l = defaultLogger.Load() } return log.New(standardWriter{logger: l, level: level}, "", 0) } type standardWriter struct { logger *Logger level slog.Level } func (w standardWriter) Write(p []byte) (int, error) { w.logger.logger.Log(context.Background(), w.level, strings.TrimRight(string(p), "\r\n")) return len(p), nil }