From 93ed22b17fc844cf14c8e5110263916715a7ec66 Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Fri, 20 Mar 2026 12:39:18 +0100 Subject: [PATCH 1/4] chore: slog update, pre-release v6 Signed-off-by: Valery Piashchynski --- .github/workflows/linux.yml | 36 +++-- .gitignore | 3 +- config.go | 290 ++++++++++++++++-------------------- doc.go | 5 + encoder.go | 33 ---- go.mod | 14 +- go.sum | 23 --- go.work.sum | 66 ++++++++ handler.go | 75 ++++++++++ logger.go | 39 ++++- plugin.go | 45 ++++-- schema.json | 3 +- tests/go.mod | 50 +++---- tests/go.sum | 44 ++++++ tests/logger_test.go | 100 +++++-------- tests/pkgs.txt | 1 - tests/plugin.go | 56 +++---- 17 files changed, 496 insertions(+), 387 deletions(-) create mode 100644 doc.go delete mode 100644 encoder.go create mode 100644 handler.go delete mode 100644 tests/pkgs.txt diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index d9e10b0..5dc3d6e 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -17,9 +17,9 @@ jobs: timeout-minutes: 60 strategy: matrix: - php: [ "8.4" ] - go: [ stable ] - os: [ "ubuntu-latest" ] + php: ["8.5"] + go: [stable] + os: ["ubuntu-latest"] steps: - name: Set up Go ${{ matrix.go }} uses: actions/setup-go@v6 # action page: @@ -65,32 +65,42 @@ jobs: run: | cd tests mkdir ./coverage-ci - - go test -timeout 20m -v -race -cover -tags=debug -failfast -coverpkg=$(cat pkgs.txt) -coverprofile=./coverage-ci/logger.out -covermode=atomic ./... + go test -timeout 20m -v -race -cover -tags=debug -failfast -coverpkg=./... -coverprofile=./coverage-ci/logger.out -covermode=atomic ./... - name: Archive code coverage results - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 with: name: coverage - path: ./tests/coverage-ci/logger.out + path: ./tests/coverage-ci codecov: name: Upload codecov runs-on: ubuntu-latest needs: - logger_test - timeout-minutes: 60 steps: + - name: Check out code + uses: actions/checkout@v6 - name: Download code coverage results - uses: actions/download-artifact@v7 + uses: actions/download-artifact@v8 + with: + pattern: coverage* + path: coverage + merge-multiple: true - run: | echo 'mode: atomic' > summary.txt - tail -q -n +2 *.out >> summary.txt - sed -i '2,${/roadrunner/!d}' summary.txt - + tail -q -n +2 coverage/*.out >> summary.txt + awk ' + NR == 1 { print; next } + /^github\.com\/roadrunner-server\/logger\/v6\// { + sub(/^github\.com\/roadrunner-server\/logger\/v6\//, "", $0) + print + } + ' summary.txt > summary.filtered.txt + mv summary.filtered.txt summary.txt - name: upload to codecov - uses: codecov/codecov-action@v5 # Docs: + uses: codecov/codecov-action@v5 with: files: summary.txt fail_ci_if_error: false diff --git a/.gitignore b/.gitignore index 4c3946c..4df8fc3 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,5 @@ vendor/ .idea .vscode .DS_Store -**/composer.lock \ No newline at end of file +**/composer.lock +.claude/settings.local.json \ No newline at end of file diff --git a/config.go b/config.go index d9a0c53..df49827 100644 --- a/config.go +++ b/config.go @@ -1,12 +1,12 @@ package logger import ( + "io" + "log/slog" "os" "strings" - "time" - "go.uber.org/zap" - "go.uber.org/zap/zapcore" + "github.com/roadrunner-server/errors" "gopkg.in/natefinch/lumberjack.v2" ) @@ -16,11 +16,11 @@ type ChannelConfig struct { Channels map[string]*Config `mapstructure:"channels"` } -// FileLoggerConfig structure represents configuration for the file logger +// FileLoggerConfig represents configuration for the file logger backed by +// lumberjack for log rotation. type FileLoggerConfig struct { - // Filename is the file to write logs to. Backup log files will be retained - // in the same directory. It uses -lumberjack.log in - // os.TempDir() if empty. + // LogOutput is the file to write logs to. Uses -lumberjack.log + // in os.TempDir() if empty. LogOutput string `mapstructure:"log_output"` // MaxSize is the maximum size in megabytes of the log file before it gets @@ -34,209 +34,135 @@ type FileLoggerConfig struct { // based on age. MaxAge int `mapstructure:"max_age"` - // MaxBackups is the maximum number of old log files to retain. The default + // MaxBackups is the maximum number of old log files to retain. The default // is to retain all old log files (though MaxAge may still cause them to get - // deleted.) + // deleted). MaxBackups int `mapstructure:"max_backups"` - // Compress determines if the rotated log files should be compressed - // using gzip. The default is not to perform compression. + // Compress determines if the rotated log files should be compressed using + // gzip. The default is not to perform compression. Compress bool `mapstructure:"compress"` } +// InitDefaults fills zero-value fields with sensible defaults. func (fl *FileLoggerConfig) InitDefaults() *FileLoggerConfig { if fl.LogOutput == "" { fl.LogOutput = os.TempDir() } - if fl.MaxSize == 0 { fl.MaxSize = 100 } - if fl.MaxAge == 0 { fl.MaxAge = 24 } - if fl.MaxBackups == 0 { fl.MaxBackups = 10 } - return fl } +// Config holds the logger configuration for a single channel. type Config struct { - // Mode configures logger based on some default template (development, production, off). + // Mode configures logger based on some default template (development, + // production, raw, off, none). Mode Mode `mapstructure:"mode"` - // Level is the minimum enabled logging level. Note that this is a dynamic - // level, so calling ChannelConfig.Level.SetLevel will atomically change the log - // level of all loggers descended from this config. + // Level is the minimum enabled logging level. Level string `mapstructure:"level"` - // Logger line ending. Default: "\n" for the all modes except production + // LineEnding for log entries. Default: "\n". LineEnding string `mapstructure:"line_ending"` - // SkipLineEnding determines if the logger should skip appending the default line ending to each log entry. + // SkipLineEnding determines if the logger should skip appending the default + // line ending to each log entry. SkipLineEnding bool `mapstructure:"skip_line_ending"` - // Encoding sets the logger's encoding. InitDefault values are "json" and - // "console", as well as any third-party encodings registered via - // RegisterEncoder. + // Encoding sets the logger's encoding. Valid values are "json" and + // "console". Encoding string `mapstructure:"encoding"` // Output is a list of URLs or file paths to write logging output to. - // See Open for details. Output []string `mapstructure:"output"` - // ErrorOutput is a list of URLs to write internal logger errors to. - // The default is a standard error. - // - // Note that this setting only affects internal errors; for sample code that - // sends error-level logs to a different location from info- and debug-level - // logs, see the package-level AdvancedConfiguration example. + // ErrorOutput is a list of URLs to write internal logger errors to. The + // default is standard error. ErrorOutput []string `mapstructure:"err_output"` - // File logger options + // FileLogger options for lumberjack-based file rotation. FileLogger *FileLoggerConfig `mapstructure:"file_logger_options"` } -// BuildLogger converts config into Zap configuration. -func (cfg *Config) BuildLogger() (*zap.Logger, error) { - var zCfg zap.Config - switch Mode(strings.ToLower(string(cfg.Mode))) { - case off, none: - return zap.NewNop(), nil - case production: - zCfg = zap.Config{ - Level: zap.NewAtomicLevelAt(zap.InfoLevel), - Development: false, - Encoding: "json", - EncoderConfig: zapcore.EncoderConfig{ - TimeKey: "ts", - LevelKey: "level", - NameKey: "logger", - CallerKey: zapcore.OmitKey, - FunctionKey: zapcore.OmitKey, - MessageKey: "msg", - StacktraceKey: zapcore.OmitKey, - LineEnding: cfg.LineEnding, - SkipLineEnding: cfg.SkipLineEnding, - EncodeLevel: zapcore.LowercaseLevelEncoder, - EncodeTime: utcEpochTimeEncoder, - EncodeDuration: zapcore.SecondsDurationEncoder, - EncodeCaller: zapcore.ShortCallerEncoder, - }, - OutputPaths: []string{"stderr"}, - ErrorOutputPaths: []string{"stderr"}, - } - case development: - zCfg = zap.Config{ - Level: zap.NewAtomicLevelAt(zap.DebugLevel), - Development: true, - Encoding: "console", - EncoderConfig: zapcore.EncoderConfig{ - TimeKey: "ts", - LevelKey: "level", - NameKey: "logger", - CallerKey: zapcore.OmitKey, - FunctionKey: zapcore.OmitKey, - MessageKey: "msg", - StacktraceKey: zapcore.OmitKey, - LineEnding: cfg.LineEnding, - SkipLineEnding: cfg.SkipLineEnding, - EncodeLevel: ColoredLevelEncoder, - EncodeName: ColoredNameEncoder, - EncodeTime: utcISO8601TimeEncoder, - EncodeDuration: zapcore.StringDurationEncoder, - EncodeCaller: zapcore.ShortCallerEncoder, - }, - OutputPaths: []string{"stderr"}, - ErrorOutputPaths: []string{"stderr"}, - } - case raw: - zCfg = zap.Config{ - Level: zap.NewAtomicLevelAt(zap.InfoLevel), - Encoding: "console", - EncoderConfig: zapcore.EncoderConfig{ - MessageKey: "message", - LineEnding: cfg.LineEnding, - SkipLineEnding: cfg.SkipLineEnding, - }, - OutputPaths: []string{"stderr"}, - ErrorOutputPaths: []string{"stderr"}, - } - default: - zCfg = zap.Config{ - Level: zap.NewAtomicLevelAt(zap.DebugLevel), - Encoding: "console", - EncoderConfig: zapcore.EncoderConfig{ - TimeKey: "T", - LevelKey: "L", - NameKey: "N", - CallerKey: zapcore.OmitKey, - FunctionKey: zapcore.OmitKey, - MessageKey: "M", - StacktraceKey: zapcore.OmitKey, - LineEnding: cfg.LineEnding, - SkipLineEnding: cfg.SkipLineEnding, - EncodeLevel: ColoredLevelEncoder, - EncodeName: ColoredNameEncoder, - EncodeTime: utcISO8601TimeEncoder, - EncodeDuration: zapcore.StringDurationEncoder, - EncodeCaller: zapcore.ShortCallerEncoder, - }, - OutputPaths: []string{"stderr"}, - ErrorOutputPaths: []string{"stderr"}, - } - } +// BuildResult holds the logger and any resources that need cleanup. +type BuildResult struct { + Logger *slog.Logger + Closers []io.Closer +} - if cfg.Level != "" { - level := zap.NewAtomicLevel() - if err := level.UnmarshalText([]byte(cfg.Level)); err == nil { - zCfg.Level = level - } - } +// BuildLogger creates an [*slog.Logger] from the configuration. +func (cfg *Config) BuildLogger() (*BuildResult, error) { + const op = errors.Op("build_logger") - if cfg.Encoding != "" { - zCfg.Encoding = cfg.Encoding - } + level := parseLevel(cfg.Level) - if len(cfg.Output) != 0 { - zCfg.OutputPaths = cfg.Output + mode := Mode(strings.ToLower(string(cfg.Mode))) + + switch mode { + case off, none: + return &BuildResult{Logger: slog.New(slog.DiscardHandler)}, nil + case production, development, raw: + // handled below + default: + // Unknown mode — fall through to development-like behavior. } - if len(cfg.ErrorOutput) != 0 { - zCfg.ErrorOutputPaths = cfg.ErrorOutput + w, closers, err := cfg.resolveOutputWriter() + if err != nil { + return nil, errors.E(op, err) } - // if we also have a file logger specified in the config, init it - // otherwise - return standard config + // File Logger: use lumberjack writer with JSON handler. if cfg.FileLogger != nil { - // init absent options cfg.FileLogger.InitDefaults() - w := zapcore.AddSync( - &lumberjack.Logger{ - Filename: cfg.FileLogger.LogOutput, - MaxSize: cfg.FileLogger.MaxSize, - MaxAge: cfg.FileLogger.MaxAge, - MaxBackups: cfg.FileLogger.MaxBackups, - Compress: cfg.FileLogger.Compress, - }, - ) - - core := zapcore.NewCore( - zapcore.NewJSONEncoder(zCfg.EncoderConfig), - w, - zCfg.Level, - ) - return zap.New(core), nil + lj := &lumberjack.Logger{ + Filename: cfg.FileLogger.LogOutput, + MaxSize: cfg.FileLogger.MaxSize, + MaxAge: cfg.FileLogger.MaxAge, + MaxBackups: cfg.FileLogger.MaxBackups, + Compress: cfg.FileLogger.Compress, + } + + closers = append(closers, lj) + + return &BuildResult{ + Logger: slog.New(newJSONHandler(lj, level)), + Closers: closers, + }, nil } - return zCfg.Build() + var handler slog.Handler + switch mode { + case production: + handler = newJSONHandler(w, level) + case raw: + handler = NewRawHandler(w, level) + case off, none: + // Already handled above; included for exhaustive linter. + handler = slog.DiscardHandler + case development: + handler = slog.NewTextHandler(w, &slog.HandlerOptions{Level: level}) + default: + // Unknown mode — fall through to development-like behavior. + handler = slog.NewTextHandler(w, &slog.HandlerOptions{Level: level}) + } + + return &BuildResult{ + Logger: slog.New(handler), + Closers: closers, + }, nil } -// InitDefault Initialize default logger +// InitDefault sets default values for empty fields. func (cfg *Config) InitDefault() { if cfg.Mode == "" { cfg.Mode = development @@ -246,10 +172,56 @@ func (cfg *Config) InitDefault() { } } -func utcISO8601TimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) { - enc.AppendString(t.UTC().Format("2006-01-02T15:04:05-0700")) +// resolveOutputWriter builds an [io.Writer] from the Output configuration. +// Supported values: "stderr", "stdout", or a file path. Multiple outputs are +// combined with [io.MultiWriter]. Returns the writer and any closers that must +// be closed when the logger is shut down. +func (cfg *Config) resolveOutputWriter() (io.Writer, []io.Closer, error) { + if len(cfg.Output) == 0 { + return os.Stderr, nil, nil + } + + writers := make([]io.Writer, 0, len(cfg.Output)) + var closers []io.Closer + + for _, out := range cfg.Output { + switch strings.ToLower(strings.TrimSpace(out)) { + case "stderr": + writers = append(writers, os.Stderr) + case "stdout": + writers = append(writers, os.Stdout) + default: + f, err := os.OpenFile(out, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0o644) + if err != nil { + // Close any files we already opened before returning. + for _, c := range closers { + _ = c.Close() + } + return nil, nil, err + } + writers = append(writers, f) + closers = append(closers, f) + } + } + + if len(writers) == 1 { + return writers[0], closers, nil + } + return io.MultiWriter(writers...), closers, nil } -func utcEpochTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) { - enc.AppendInt64(t.UTC().UnixNano()) +// parseLevel converts a level string to the corresponding [slog.Level]. +func parseLevel(s string) slog.Level { + switch strings.ToLower(strings.TrimSpace(s)) { + case "debug": + return slog.LevelDebug + case "info": + return slog.LevelInfo + case "warn", "warning": + return slog.LevelWarn + case "error": + return slog.LevelError + default: + return slog.LevelDebug + } } diff --git a/doc.go b/doc.go new file mode 100644 index 0000000..7d6bfd6 --- /dev/null +++ b/doc.go @@ -0,0 +1,5 @@ +// Package logger provides the RoadRunner logger plugin backed by [log/slog]. +// +// It supports multiple output modes (development, production, raw, off/none), +// per-plugin channel overrides, and file-based log rotation via lumberjack. +package logger diff --git a/encoder.go b/encoder.go deleted file mode 100644 index 7090cbe..0000000 --- a/encoder.go +++ /dev/null @@ -1,33 +0,0 @@ -package logger - -import ( - "strings" - - "github.com/fatih/color" - "go.uber.org/zap/zapcore" -) - -// ColoredLevelEncoder colorizes log levels. -func ColoredLevelEncoder(level zapcore.Level, enc zapcore.PrimitiveArrayEncoder) { - switch level { - case zapcore.DebugLevel: - enc.AppendString(color.HiWhiteString(level.CapitalString())) - case zapcore.InfoLevel: - enc.AppendString(color.HiCyanString(level.CapitalString())) - case zapcore.WarnLevel: - enc.AppendString(color.HiYellowString(level.CapitalString())) - case zapcore.ErrorLevel, zapcore.DPanicLevel: - enc.AppendString(color.HiRedString(level.CapitalString())) - case zapcore.PanicLevel, zapcore.FatalLevel, zapcore.InvalidLevel: - enc.AppendString(color.HiMagentaString(level.CapitalString())) - } -} - -// ColoredNameEncoder colorizes service names. -func ColoredNameEncoder(s string, enc zapcore.PrimitiveArrayEncoder) { - if len(s) < 12 { - s += strings.Repeat(" ", 12-len(s)) - } - - enc.AppendString(color.HiGreenString(s)) -} diff --git a/go.mod b/go.mod index c27ec34..a0a12b6 100644 --- a/go.mod +++ b/go.mod @@ -1,23 +1,11 @@ -module github.com/roadrunner-server/logger/v5 +module github.com/roadrunner-server/logger/v6 go 1.26 toolchain go1.26.0 require ( - github.com/fatih/color v1.18.0 github.com/roadrunner-server/endure/v2 v2.6.2 github.com/roadrunner-server/errors v1.4.1 - go.uber.org/zap v1.27.1 gopkg.in/natefinch/lumberjack.v2 v2.2.1 ) - -require ( - github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/mattn/go-colorable v0.1.14 // indirect - github.com/mattn/go-isatty v0.0.20 // indirect - github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/stretchr/testify v1.11.1 // indirect - go.uber.org/multierr v1.11.0 // indirect - golang.org/x/sys v0.41.0 // indirect -) diff --git a/go.sum b/go.sum index 1c3a422..0fbcbcc 100644 --- a/go.sum +++ b/go.sum @@ -1,29 +1,6 @@ -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= -github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= -github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= -github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= -github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/roadrunner-server/endure/v2 v2.6.2 h1:sIB4kTyE7gtT3fDhuYWUYn6Vt/dcPtiA6FoNS1eS+84= github.com/roadrunner-server/endure/v2 v2.6.2/go.mod h1:t/2+xpNYgGBwhzn83y2MDhvhZ19UVq1REcvqn7j7RB8= github.com/roadrunner-server/errors v1.4.1 h1:LKNeaCGiwd3t8IaL840ZNF3UA9yDQlpvHnKddnh0YRQ= github.com/roadrunner-server/errors v1.4.1/go.mod h1:qeffnIKG0e4j1dzGpa+OGY5VKSfMphizvqWIw8s2lAo= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= -go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= -go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= -go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= -go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= -golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/go.work.sum b/go.work.sum index 1c104e0..d91ce77 100644 --- a/go.work.sum +++ b/go.work.sum @@ -7,14 +7,18 @@ cloud.google.com/go v0.112.1/go.mod h1:+Vbu+Y1UU+I1rjmzeMOb/8RfkKJK2Gyxi1X6jJCZL cloud.google.com/go/accessapproval v1.8.6/go.mod h1:FfmTs7Emex5UvfnnpMkhuNkRCP85URnBFt5ClLxhZaQ= cloud.google.com/go/accessapproval v1.8.7 h1:Sc9ZjxFBEM/PoAxNlUwVGDcv8DYyjLYWDxHlzPG0q5I= cloud.google.com/go/accessapproval v1.8.7/go.mod h1:BFvZOW4GJjJnl6aA/YDEg0TGViFHyusa/bMdcVFmh8A= +cloud.google.com/go/accessapproval v1.8.8/go.mod h1:RFwPY9JDKseP4gJrX1BlAVsP5O6kI8NdGlTmaeDefmk= cloud.google.com/go/accesscontextmanager v1.9.6 h1:2LnncRqfYB8NEdh9+FeYxAt9POTW/0zVboktnRlO11w= cloud.google.com/go/accesscontextmanager v1.9.6/go.mod h1:884XHwy1AQpCX5Cj2VqYse77gfLaq9f8emE2bYriilk= +cloud.google.com/go/accesscontextmanager v1.9.7/go.mod h1:i6e0nd5CPcrh7+YwGq4bKvju5YB9sgoAip+mXU73aMM= cloud.google.com/go/aiplatform v1.86.0/go.mod h1:xp3wFix8imliXkVpgMRkjnreJYTaNzLF44GOrnIENto= cloud.google.com/go/aiplatform v1.103.0 h1:1Y0mlsxh8STlIImjdZeeXg8Rrf5FDLRY0AOQBy3AUi8= cloud.google.com/go/aiplatform v1.103.0/go.mod h1:4rwKOMdubQOND81AlO3EckcskvEFCYSzXKfn42GMm8k= +cloud.google.com/go/aiplatform v1.115.0/go.mod h1:DwPJAxebOTy6BajSMjF7ah3QvlYO4jf2gpJw6/1z9gU= cloud.google.com/go/analytics v0.28.1/go.mod h1:iPaIVr5iXPB3JzkKPW1JddswksACRFl3NSHgVHsuYC4= cloud.google.com/go/analytics v0.30.0 h1:9PvoT9SvNIHRqTZye7+WudvO2vr4PiZ9mLhiOtEE7Eo= cloud.google.com/go/analytics v0.30.0/go.mod h1:dneJtsGmmK6EkEPg59vRlncKFWt3xzmKNOc9aKXCTrI= +cloud.google.com/go/analytics v0.30.1/go.mod h1:V/FnINU5kMOsttZnKPnXfKi6clJUHTEXUKQjHxcNK8A= cloud.google.com/go/apigateway v1.7.6/go.mod h1:SiBx36VPjShaOCk8Emf63M2t2c1yF+I7mYZaId7OHiA= cloud.google.com/go/apigateway v1.7.7 h1:ehKUTy+QFsb3n07fEi18S2dpDDjCV4UlRyrbwfZV3Zk= cloud.google.com/go/apigateway v1.7.7/go.mod h1:j1bCmrUK1BzVHpiIyTApxB7cRyhivKzltqLmp6j6i7U= @@ -23,6 +27,7 @@ cloud.google.com/go/apigeeconnect v1.7.7 h1:S6s2zojwMymx0fyZYKm0eK1TdDxrriIBAlNV cloud.google.com/go/apigeeconnect v1.7.7/go.mod h1:ftGK3nca0JePiVLl0A6alaMjKdOc5C+sAkFMyH2RH8U= cloud.google.com/go/apigeeregistry v0.9.6 h1:TgdjAoGoRY81DEc2LYsYvi/OqCFImMzAk/TVKiSRsQw= cloud.google.com/go/apigeeregistry v0.9.6/go.mod h1:AFEepJBKPtGDfgabG2HWaLH453VVWWFFs3P4W00jbPs= +cloud.google.com/go/apigeeregistry v0.10.0/go.mod h1:SAlF5OhKvyLDuwWAaFAIVJjrEqKRrGTPkJs+TWNnSqg= cloud.google.com/go/appengine v1.9.6/go.mod h1:jPp9T7Opvzl97qytaRGPwoH7pFI3GAcLDaui1K8PNjY= cloud.google.com/go/appengine v1.9.7 h1:IxGz6j5xv0nTJX285wu95Vn6KEi2CeV9vbyRgCSEAoU= cloud.google.com/go/appengine v1.9.7/go.mod h1:y1XpGVeAhbsNzHida79cHbr3pFRsym0ob8xnC8yphbo= @@ -31,38 +36,52 @@ cloud.google.com/go/area120 v0.9.7 h1:BbpzLwaIXVPorrrzTH+ni7P5mLemmPPfSZ7o39k7zQ cloud.google.com/go/area120 v0.9.7/go.mod h1:5nJ0yksmjOMfc4Zpk+okWfJ3A1004FvB82rfia+ZLaY= cloud.google.com/go/artifactregistry v1.17.1 h1:A20kj2S2HO9vlyBVyVFHPxArjxkXvLP5LjcdE7NhaPc= cloud.google.com/go/artifactregistry v1.17.1/go.mod h1:06gLv5QwQPWtaudI2fWO37gfwwRUHwxm3gA8Fe568Hc= +cloud.google.com/go/artifactregistry v1.19.0/go.mod h1:UEAPCgHDFC1q+A8nnVxXHPEy9KCVOeavFBF1fEChQvU= cloud.google.com/go/asset v1.21.0/go.mod h1:0lMJ0STdyImZDSCB8B3i/+lzIquLBpJ9KZ4pyRvzccM= cloud.google.com/go/asset v1.21.1 h1:i55wWC/EwVdHMyJgRfbLp/L6ez4nQuOpZwSxkuqN9ek= cloud.google.com/go/asset v1.21.1/go.mod h1:7AzY1GCC+s1O73yzLM1IpHFLHz3ws2OigmCpOQHwebk= +cloud.google.com/go/asset v1.22.0/go.mod h1:q80JP2TeWWzMCazYnrAfDf36aQKf1QiKzzpNLflJwf8= cloud.google.com/go/assuredworkloads v1.12.6 h1:ip/shfJYx6lrHBWYADjrrrubcm7uZzy50TTF5tPG7ek= cloud.google.com/go/assuredworkloads v1.12.6/go.mod h1:QyZHd7nH08fmZ+G4ElihV1zoZ7H0FQCpgS0YWtwjCKo= +cloud.google.com/go/assuredworkloads v1.13.0/go.mod h1:o/oHEOnUlribR+uJWTKQo8A5RhSl9K9FNeMOew4TJ3M= cloud.google.com/go/automl v1.14.7 h1:ZLj48Ur2Qcso4M3bgOtjsOmeV5Ee92N14wuOc8OW+L0= cloud.google.com/go/automl v1.14.7/go.mod h1:8a4XbIH5pdvrReOU72oB+H3pOw2JBxo9XTk39oljObE= +cloud.google.com/go/automl v1.15.0/go.mod h1:U9zOtQb8zVrFNGTuW3BfxeqmLyeleLgT9B12EaXfODg= cloud.google.com/go/baremetalsolution v1.3.6 h1:9bdGlpY1LgLONQjFsDwrkjLzdPTlROpfU+GhA97YpOk= cloud.google.com/go/baremetalsolution v1.3.6/go.mod h1:7/CS0LzpLccRGO0HL3q2Rofxas2JwjREKut414sE9iM= +cloud.google.com/go/baremetalsolution v1.4.0/go.mod h1:K6C6g4aS8LW95I0fEHZiBsBlh0UxwDLGf+S/vyfXbvg= cloud.google.com/go/batch v1.12.2 h1:gWQdvdPplptpvrkqF6ibtxZkOsYKLTFbxYawHa/TvCg= cloud.google.com/go/batch v1.12.2/go.mod h1:tbnuTN/Iw59/n1yjAYKV2aZUjvMM2VJqAgvUgft6UEU= +cloud.google.com/go/batch v1.14.0/go.mod h1:oeQveyG6NDS/ks2ilOP4LzKRmuIaI7GLe0CkR7WF6pk= cloud.google.com/go/beyondcorp v1.1.6 h1:4FcR+4QmcNGkhVij6TrYS4AQVNLBo7PBXKxNrKzpclQ= cloud.google.com/go/beyondcorp v1.1.6/go.mod h1:V1PigSWPGh5L/vRRmyutfnjAbkxLI2aWqJDdxKbwvsQ= +cloud.google.com/go/beyondcorp v1.2.0/go.mod h1:sszcgxpPPBEfLzbI0aYCTg6tT1tyt3CmKav3NZIUcvI= cloud.google.com/go/bigquery v1.8.0 h1:PQcPefKFdaIzjQFbiyOgAqyx8q5djaE7x9Sqe712DPA= cloud.google.com/go/bigquery v1.67.0/go.mod h1:HQeP1AHFuAz0Y55heDSb0cjZIhnEkuwFRBGo6EEKHug= cloud.google.com/go/bigquery v1.71.0 h1:NvSZvXU1Hyb+YiRVKQPuQXGeZaw/0NP6M/WOrBqSx3g= cloud.google.com/go/bigquery v1.71.0/go.mod h1:GUbRtmeCckOE85endLherHD9RsujY+gS7i++c1CqssQ= +cloud.google.com/go/bigquery v1.73.1/go.mod h1:KSLx1mKP/yGiA8U+ohSrqZM1WknUnjZAxHAQZ51/b1k= cloud.google.com/go/bigtable v1.37.0/go.mod h1:HXqddP6hduwzrtiTCqZPpj9ij4hGZb4Zy1WF/dT+yaU= cloud.google.com/go/bigtable v1.40.1 h1:k8HfpUOvn7sQwc6oNKqjvD/yjkwynf4qBuyKwh5cU08= cloud.google.com/go/bigtable v1.40.1/go.mod h1:LtPzCcrAFaGRZ82Hs8xMueUeYW9Jw12AmNdUTMfDnh4= +cloud.google.com/go/bigtable v1.42.0/go.mod h1:oZ30nofVB6/UYGg7lBwGLWSea7NZUvw/WvBBgLY07xU= cloud.google.com/go/billing v1.20.4 h1:pqM5/c9UGydB9H90IPCxSvfCNLUPazAOSMsZkz5q5P4= cloud.google.com/go/billing v1.20.4/go.mod h1:hBm7iUmGKGCnBm6Wp439YgEdt+OnefEq/Ib9SlJYxIU= +cloud.google.com/go/billing v1.21.0/go.mod h1:ZGairB3EVnb3i09E2SxFxo50p5unPaMTuo1jh6jW9js= cloud.google.com/go/binaryauthorization v1.9.5 h1:T0zYEroXT+y0O/x/yZd5SwQdFv4UbUINjvJyJKzDm0Q= cloud.google.com/go/binaryauthorization v1.9.5/go.mod h1:CV5GkS2eiY461Bzv+OH3r5/AsuB6zny+MruRju3ccB8= +cloud.google.com/go/binaryauthorization v1.10.0/go.mod h1:WOuiaQkI4PU/okwrcREjSAr2AUtjQgVe+PlrXKOmKKw= cloud.google.com/go/certificatemanager v1.9.5 h1:+ZPglfDurCcsv4azizDFpBucD1IkRjWjbnU7zceyjfY= cloud.google.com/go/certificatemanager v1.9.5/go.mod h1:kn7gxT/80oVGhjL8rurMUYD36AOimgtzSBPadtAeffs= +cloud.google.com/go/certificatemanager v1.9.6/go.mod h1:vWogV874jKZkSRDFCMM3r7wqybv8WXs3XhyNff6o/Zo= cloud.google.com/go/channel v1.19.5/go.mod h1:vevu+LK8Oy1Yuf7lcpDbkQQQm5I7oiY5fFTn3uwfQLY= cloud.google.com/go/channel v1.20.0 h1:EeUa6SnD3+EL9B06G6N9Ud5/p/NtT6PC7lv5kmaUiHs= cloud.google.com/go/channel v1.20.0/go.mod h1:nBR1Lz+/1TjSA16HTllvW9Y+QULODj3o3jEKrNNeOp4= +cloud.google.com/go/channel v1.21.0/go.mod h1:8v3TwHtgLmFxTpL2U+e10CLFOQN8u/Vr9RhYcJUS3y8= cloud.google.com/go/cloudbuild v1.22.2/go.mod h1:rPyXfINSgMqMZvuTk1DbZcbKYtvbYF/i9IXQ7eeEMIM= cloud.google.com/go/cloudbuild v1.23.0 h1:ycLO1q8CdpDqWKpcqlcP+RMbkUguXqwvO//Q0vC1/jE= cloud.google.com/go/cloudbuild v1.23.0/go.mod h1:BkxnZUIHUHkl+oNpEbwc7n9id4pZRDQRVKIa6sDCuJI= +cloud.google.com/go/cloudbuild v1.25.0/go.mod h1:lCu+T6IPkobPo2Nw+vCE7wuaAl9HbXLzdPx/tcF+oWo= cloud.google.com/go/clouddms v1.8.7/go.mod h1:DhWLd3nzHP8GoHkA6hOhso0R9Iou+IGggNqlVaq/KZ4= cloud.google.com/go/clouddms v1.8.8 h1:YWsmRXTyK6Ba0hm4qTBak5g1oLhryuM8rSBxHWC8iq4= cloud.google.com/go/clouddms v1.8.8/go.mod h1:QtCyw+a73dlkDb2q20aTAPvfaTZCepDDi6Gb1AKq0a4= @@ -78,6 +97,7 @@ cloud.google.com/go/compute v1.24.0/go.mod h1:kw1/T+h/+tK2LJK0wiPPx1intgdAM3j/g3 cloud.google.com/go/compute v1.37.0/go.mod h1:AsK4VqrSyXBo4SMbRtfAO1VfaMjUEjEwv1UB/AwVp5Q= cloud.google.com/go/compute v1.48.0 h1:/Mx0l7qFnJTxq9kXGU9TcLOsjjmyGANc23ed8LB3ZnA= cloud.google.com/go/compute v1.48.0/go.mod h1:1uoZvP8Avyfhe3Y4he7sMOR16ZiAm2Q+Rc2P5rrJM28= +cloud.google.com/go/compute v1.54.0/go.mod h1:RfBj0L1x/pIM84BrzNX2V21oEv16EKRPBiTcBRRH1Ww= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/contactcenterinsights v1.17.3/go.mod h1:7Uu2CpxS3f6XxhRdlEzYAkrChpR5P5QfcdGAFEdHOG8= @@ -86,13 +106,16 @@ cloud.google.com/go/contactcenterinsights v1.17.4/go.mod h1:kZe6yOnKDfpPz2GphDHy cloud.google.com/go/container v1.42.4/go.mod h1:wf9lKc3ayWVbbV/IxKIDzT7E+1KQgzkzdxEJpj1pebE= cloud.google.com/go/container v1.44.0 h1:JEHeW535svvNwJrjrlQ/cdjd15LCWrPKnHsulrufd3A= cloud.google.com/go/container v1.44.0/go.mod h1:tVK2o4UZUTkg9WpBcgj4qRzwGA1dSFdWA3mil3YkLIQ= +cloud.google.com/go/container v1.46.0/go.mod h1:A7gMqdQduTk46+zssWDTKbGS2z46UsJNXfKqvMI1ZO4= cloud.google.com/go/containeranalysis v0.14.1 h1:1SoHlNqL3XrhqcoozB+3eoHif2sRUFtp/JeASQTtGKo= cloud.google.com/go/containeranalysis v0.14.1/go.mod h1:28e+tlZgauWGHmEbnI5UfIsjMmrkoR1tFN0K2i71jBI= +cloud.google.com/go/containeranalysis v0.14.2/go.mod h1:FjppROiUtP9cyMegdWdY/TsBSGc6kqh1GjA2NOJXXL8= cloud.google.com/go/datacatalog v1.26.0/go.mod h1:bLN2HLBAwB3kLTFT5ZKLHVPj/weNz6bR0c7nYp0LE14= cloud.google.com/go/datacatalog v1.26.1 h1:bCRKA8uSQN8wGW3Tw0gwko4E9a64GRmbW1nCblhgC2k= cloud.google.com/go/datacatalog v1.26.1/go.mod h1:2Qcq8vsHNxMDgjgadRFmFG47Y+uuIVsyEGUrlrKEdrg= cloud.google.com/go/dataflow v0.11.0 h1:AdhB4cAkMOC9NtrHJxpKOVvO/VqBLaIyk0tEEhbGjYM= cloud.google.com/go/dataflow v0.11.0/go.mod h1:gNHC9fUjlV9miu0hd4oQaXibIuVYTQvZhMdPievKsPk= +cloud.google.com/go/dataflow v0.11.1/go.mod h1:3s6y/h5Qz7uuxTmKJKBifkYZ3zs63jS+6VGtSu8Cf7Y= cloud.google.com/go/dataform v0.11.2/go.mod h1:IMmueJPEKpptT2ZLWlvIYjw6P/mYHHxA7/SUBiXqZUY= cloud.google.com/go/dataform v0.12.1 h1:yf6Up6m1FUt+YB5CBgNtIZfz2OzjuNBdzZWV3SLSVNE= cloud.google.com/go/dataform v0.12.1/go.mod h1:atGS8ReRjfNDUQib0X/o/7Gi2bqHI2G7/J86LKiGimE= @@ -105,14 +128,18 @@ cloud.google.com/go/datalabeling v0.9.7/go.mod h1:EEUVn+wNn3jl19P2S13FqE1s9LsKzR cloud.google.com/go/dataplex v1.25.2/go.mod h1:AH2/a7eCYvFP58scJGR7YlSY9qEhM8jq5IeOA/32IZ0= cloud.google.com/go/dataplex v1.27.1 h1:renSEYTQZMQ3ag7lM0BDmSj4FWqaTGW60YQ/lvAE5iA= cloud.google.com/go/dataplex v1.27.1/go.mod h1:VB+xlYJiJ5kreonXsa2cHPj0A3CfPh/mgiHG4JFhbUA= +cloud.google.com/go/dataplex v1.28.0/go.mod h1:VB+xlYJiJ5kreonXsa2cHPj0A3CfPh/mgiHG4JFhbUA= cloud.google.com/go/dataproc/v2 v2.11.2/go.mod h1:xwukBjtfiO4vMEa1VdqyFLqJmcv7t3lo+PbLDcTEw+g= cloud.google.com/go/dataproc/v2 v2.14.1 h1:Kxq0iomU0H4MlVP4HYeYPNJnV+YxNctf/hFrprmGy5Y= cloud.google.com/go/dataproc/v2 v2.14.1/go.mod h1:tSdkodShfzrrUNPDVEL6MdH9/mIEvp/Z9s9PBdbsZg8= +cloud.google.com/go/dataproc/v2 v2.15.0/go.mod h1:tSdkodShfzrrUNPDVEL6MdH9/mIEvp/Z9s9PBdbsZg8= cloud.google.com/go/dataqna v0.9.7 h1:qTRAG/E3T63Xj1orefRlwupfwH9c9ERUAnWSRGp75so= cloud.google.com/go/dataqna v0.9.7/go.mod h1:4ac3r7zm7Wqm8NAc8sDIDM0v7Dz7d1e/1Ka1yMFanUM= +cloud.google.com/go/dataqna v0.9.8/go.mod h1:2lHKmGPOqzzuqCc5NI0+Xrd5om4ulxGwPpLB4AnFgpA= cloud.google.com/go/datastore v1.1.0 h1:/May9ojXjRkPBNVrq+oWLqmWCkr4OU5uRY29bu0mRyQ= cloud.google.com/go/datastore v1.20.0 h1:NNpXoyEqIJmZFc0ACcwBEaXnmscUpcG4NkKnbCePmiM= cloud.google.com/go/datastore v1.20.0/go.mod h1:uFo3e+aEpRfHgtp5pp0+6M0o147KoPaYNaPAKpfh8Ew= +cloud.google.com/go/datastore v1.22.0/go.mod h1:aopSX+Whx0lHspWWBj+AjWt68/zjYsPfDe3LjWtqZg8= cloud.google.com/go/datastream v1.14.1/go.mod h1:JqMKXq/e0OMkEgfYe0nP+lDye5G2IhIlmencWxmesMo= cloud.google.com/go/datastream v1.15.1 h1:7PKeDpksi8nbOR4gspmNokzsr0q/uRzDIt20bR3BtRs= cloud.google.com/go/datastream v1.15.1/go.mod h1:aV1Grr9LFon0YvqryE5/gF1XAhcau2uxN2OvQJPpqRw= @@ -122,12 +149,15 @@ cloud.google.com/go/deploy v1.27.3/go.mod h1:7LFIYYTSSdljYRqY3n+JSmIFdD4lv6aMD5x cloud.google.com/go/dialogflow v1.68.2/go.mod h1:E0Ocrhf5/nANZzBju8RX8rONf0PuIvz2fVj3XkbAhiY= cloud.google.com/go/dialogflow v1.69.1 h1:R69CCEgx9RMHWjS2eP7aw5sE1Ajo5buQTzTdBe2o13w= cloud.google.com/go/dialogflow v1.69.1/go.mod h1:mP4XrpgDvPYBP+cdLxFC1WJJlkwuy0H8L1Lada9No/M= +cloud.google.com/go/dialogflow v1.75.0/go.mod h1:z1W1ZogmigYVtP5YmyeUh+D219VCjdd3VJqY76PG3gA= cloud.google.com/go/dlp v1.22.1/go.mod h1:Gc7tGo1UJJTBRt4OvNQhm8XEQ0i9VidAiGXBVtsftjM= cloud.google.com/go/dlp v1.25.0 h1:283+PJFk72SIj3apDTT/cHnKPBtuBUnduLBpbz1diFw= cloud.google.com/go/dlp v1.25.0/go.mod h1:PY4DMzV7lqRC5JvpxL05fXNeL8dknxYpFp4WjxmE22M= +cloud.google.com/go/dlp v1.28.0/go.mod h1:C3od1fIK8lf7Kr62aU1Uh0z4OL5Z8s3do3znAiEupAw= cloud.google.com/go/documentai v1.37.0/go.mod h1:qAf3ewuIUJgvSHQmmUWvM3Ogsr5A16U2WPHmiJldvLA= cloud.google.com/go/documentai v1.38.1 h1:6uWDoi/wAT8D9belR9Q1n2fFtY6YJ+/3aj6rR9mAlbY= cloud.google.com/go/documentai v1.38.1/go.mod h1:KmlLO93F7GRU8dENXRxvt+7V8o7eCG6Y6WDitKbcYJs= +cloud.google.com/go/documentai v1.40.0/go.mod h1:oDTm0aoG8ldKucW/yzRrLbaTO0NvtgGAWm5KPAT5iNY= cloud.google.com/go/domains v0.10.6/go.mod h1:3xzG+hASKsVBA8dOPc4cIaoV3OdBHl1qgUpAvXK7pGY= cloud.google.com/go/domains v0.10.7 h1:G3kUq0vKBMhyOj5GqAfEYbVuez05U+ENHZUAtrEp/pI= cloud.google.com/go/domains v0.10.7/go.mod h1:T3WG/QUAO/52z4tUPooKS8AY7yXaFxPYn1V3F0/JbNQ= @@ -136,12 +166,14 @@ cloud.google.com/go/edgecontainer v1.4.4 h1:6KTQo6Qf0iEtfPVotlG7orazEO1I93Ham0PM cloud.google.com/go/edgecontainer v1.4.4/go.mod h1:yyNVHsCKtsX/0mqFdbljQw0Uo660q2dlMPaiqYiC2Tg= cloud.google.com/go/errorreporting v0.3.2 h1:isaoPwWX8kbAOea4qahcmttoS79+gQhvKsfg5L5AgH8= cloud.google.com/go/errorreporting v0.3.2/go.mod h1:s5kjs5r3l6A8UUyIsgvAhGq6tkqyBCUss0FRpsoVTww= +cloud.google.com/go/errorreporting v0.4.0/go.mod h1:dZGEhqzdHZSRxxWLVjC3Ue5CVaROzvP58D9rU6zbBfw= cloud.google.com/go/essentialcontacts v1.7.6/go.mod h1:/Ycn2egr4+XfmAfxpLYsJeJlVf9MVnq9V7OMQr9R4lA= cloud.google.com/go/essentialcontacts v1.7.7 h1:v9sO4IHFuwplaOuDnEXZFtfOrjw2bi11TSIVp5PnAU4= cloud.google.com/go/essentialcontacts v1.7.7/go.mod h1:ytycWAEn/aKUMRKQPMVgMrAtphEMgjbzL8vFwM3tqXs= cloud.google.com/go/eventarc v1.15.5/go.mod h1:vDCqGqyY7SRiickhEGt1Zhuj81Ya4F/NtwwL3OZNskg= cloud.google.com/go/eventarc v1.16.1 h1:1wIofFPViIgTABbIryIkx6uXJ0ima8UE2xIyo0nBesE= cloud.google.com/go/eventarc v1.16.1/go.mod h1:wB3NTIQ+l4QPirJiTMeU+YpSc5+iyoDYWV4n2/Vmh78= +cloud.google.com/go/eventarc v1.18.0/go.mod h1:/6SDoqh5+9QNUqCX4/oQcJVK16fG/snHBSXu7lrJtO8= cloud.google.com/go/filestore v1.10.2/go.mod h1:w0Pr8uQeSRQfCPRsL0sYKW6NKyooRgixCkV9yyLykR4= cloud.google.com/go/filestore v1.10.3 h1:3KZifUVTqGhNNv6MLeONYth1HjlVM4vDhaH+xrdPljU= cloud.google.com/go/filestore v1.10.3/go.mod h1:94ZGyLTx9j+aWKozPQ6Wbq1DuImie/L/HIdGMshtwac= @@ -153,6 +185,7 @@ cloud.google.com/go/firestore v1.15.0 h1:/k8ppuWOtNuDHt2tsRV42yI21uaGnKDEQnRFeBp cloud.google.com/go/firestore v1.15.0/go.mod h1:GWOxFXcv8GZUtYpWHw/w6IuYNux/BtmeVTMmjrm4yhk= cloud.google.com/go/firestore v1.18.0 h1:cuydCaLS7Vl2SatAeivXyhbhDEIR8BDmtn4egDhIn2s= cloud.google.com/go/firestore v1.18.0/go.mod h1:5ye0v48PhseZBdcl0qbl3uttu7FIEwEYVaWm0UIEOEU= +cloud.google.com/go/firestore v1.21.0/go.mod h1:1xH6HNcnkf/gGyR8udd6pFO4Z7GWJSwLKQMx/u6UrP4= cloud.google.com/go/functions v1.19.6/go.mod h1:0G0RnIlbM4MJEycfbPZlCzSf2lPOjL7toLDwl+r0ZBw= cloud.google.com/go/functions v1.19.7 h1:7LcOD18euIVGRUPaeCmgO6vfWSLNIsi6STWRQcdANG8= cloud.google.com/go/functions v1.19.7/go.mod h1:xbcKfS7GoIcaXr2FSwmtn9NXal1JR4TV6iYZlgXffwA= @@ -168,6 +201,7 @@ cloud.google.com/go/gkehub v0.16.0/go.mod h1:ADp27Ucor8v81wY+x/5pOxTorxkPj/xswH3 cloud.google.com/go/gkemulticloud v1.5.3/go.mod h1:KPFf+/RcfvmuScqwS9/2MF5exZAmXSuoSLPuaQ98Xlk= cloud.google.com/go/gkemulticloud v1.5.4 h1:AKuOmr5QBCPLJCyZhBABP3lIz+h3jxAy53LVMrEuvlg= cloud.google.com/go/gkemulticloud v1.5.4/go.mod h1:7l9+6Tp4jySSGj4PStO8CE6RrHFdcRARK4ScReHX1bU= +cloud.google.com/go/gkemulticloud v1.6.0/go.mod h1:bGpd4o/Z5Z/XFlaojkgdVisHRwb+fLJvUPzsmV0I9ok= cloud.google.com/go/gsuiteaddons v1.7.7/go.mod h1:zTGmmKG/GEBCONsvMOY2ckDiEsq3FN+lzWGUiXccF9o= cloud.google.com/go/gsuiteaddons v1.7.8 h1:Dayrv57XW8kZIvmQjAc89Tp7Kr3O9Am/hf6pXkTjYFY= cloud.google.com/go/gsuiteaddons v1.7.8/go.mod h1:DBKNHH4YXAdd/rd6zVvtOGAJNGo0ekOh+nIjTUDEJ5U= @@ -175,6 +209,7 @@ cloud.google.com/go/iam v1.1.5 h1:1jTsCu4bcsNsE4iiqNT5SHwrDRCfRmIaaaVFhRveTJI= cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8= cloud.google.com/go/iam v1.5.2 h1:qgFRAGEmd8z6dJ/qyEchAuL9jpswyODjA2lS+w234g8= cloud.google.com/go/iam v1.5.2/go.mod h1:SE1vg0N81zQqLzQEwxL2WI6yhetBdbNQuTvIKCSkUHE= +cloud.google.com/go/iam v1.5.3/go.mod h1:MR3v9oLkZCTlaqljW6Eb2d3HGDGK5/bDv93jhfISFvU= cloud.google.com/go/iap v1.11.1/go.mod h1:qFipMJ4nOIv4yDHZxn31PiS8QxJJH2FlxgH9aFauejw= cloud.google.com/go/iap v1.11.3 h1:Nheb77nO0/pECm/thoE3wHVAbkQSI+G8KBWviqBepiA= cloud.google.com/go/iap v1.11.3/go.mod h1:+gXO0ClH62k2LVlfhHzrpiHQNyINlEVmGAE3+DB4ShU= @@ -187,13 +222,16 @@ cloud.google.com/go/iot v1.8.7/go.mod h1:HvVcypV8LPv1yTXSLCNK+YCtqGHhq+p0F3BXETf cloud.google.com/go/kms v1.21.2/go.mod h1:8wkMtHV/9Z8mLXEXr1GK7xPSBdi6knuLXIhqjuWcI6w= cloud.google.com/go/kms v1.23.0 h1:WaqAZsUptyHwOo9II8rFC1Kd2I+yvNsNP2IJ14H2sUw= cloud.google.com/go/kms v1.23.0/go.mod h1:rZ5kK0I7Kn9W4erhYVoIRPtpizjunlrfU4fUkumUp8g= +cloud.google.com/go/kms v1.25.0/go.mod h1:XIdHkzfj0bUO3E+LvwPg+oc7s58/Ns8Nd8Sdtljihbk= cloud.google.com/go/language v1.14.5 h1:BVJ/POtlnJ55LElvnQY19UOxpMVtHoHHkFJW2uHJsVU= cloud.google.com/go/language v1.14.5/go.mod h1:nl2cyAVjcBct1Hk73tzxuKebk0t2eULFCaruhetdZIA= +cloud.google.com/go/language v1.14.6/go.mod h1:7y3J9OexQsfkWNGCxhT+7lb64pa60e12ZCoWDOHxJ1M= cloud.google.com/go/lifesciences v0.10.6/go.mod h1:1nnZwaZcBThDujs9wXzECnd1S5d+UiDkPuJWAmhRi7Q= cloud.google.com/go/lifesciences v0.10.7 h1:MO5aBahcYv7JeuCpHbg/11h7KL/BYt1+PpgHhleLDbI= cloud.google.com/go/lifesciences v0.10.7/go.mod h1:v3AbTki9iWttEls/Wf4ag3EqeLRHofploOcpsLnu7iY= cloud.google.com/go/logging v1.13.0 h1:7j0HgAp0B94o1YRDqiqm26w4q1rDMH7XNRU34lJXHYc= cloud.google.com/go/logging v1.13.0/go.mod h1:36CoKh6KA/M0PbhPKMq6/qety2DCAErbhXT62TuXALA= +cloud.google.com/go/logging v1.13.2/go.mod h1:zaybliM3yun1J8mU2dVQ1/qDzjbOqEijZCn6hSBtKak= cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM= cloud.google.com/go/longrunning v0.4.1/go.mod h1:4iWDqhBZ70CvZ6BfETbvam3T8FMvLK+eFj0E6AaRQTo= cloud.google.com/go/longrunning v0.5.1 h1:Fr7TXftcqTudoyRJa113hyaqlGdiBQkp0Gq7tErFDWI= @@ -202,12 +240,14 @@ cloud.google.com/go/longrunning v0.5.5 h1:GOE6pZFdSrTb4KAiKnXsJBtlE6mEyaW44oKyMI cloud.google.com/go/longrunning v0.5.5/go.mod h1:WV2LAxD8/rg5Z1cNW6FJ/ZpX4E4VnDnoTk0yawPBB7s= cloud.google.com/go/longrunning v0.6.7 h1:IGtfDWHhQCgCjwQjV9iiLnUta9LBCo8R9QmAFsS/PrE= cloud.google.com/go/longrunning v0.6.7/go.mod h1:EAFV3IZAKmM56TyiE6VAP3VoTzhZzySwI/YI1s/nRsY= +cloud.google.com/go/longrunning v0.8.0/go.mod h1:UmErU2Onzi+fKDg2gR7dusz11Pe26aknR4kHmJJqIfk= cloud.google.com/go/managedidentities v1.7.6/go.mod h1:pYCWPaI1AvR8Q027Vtp+SFSM/VOVgbjBF4rxp1/z5p4= cloud.google.com/go/managedidentities v1.7.7 h1:vC/q7D+97PZfb0UNf7r/+/clHauuaf1PqWwP7neuaeg= cloud.google.com/go/managedidentities v1.7.7/go.mod h1:nwNlMxtBo2YJMvsKXRtAD1bL41qiCI9npS7cbqrsJUs= cloud.google.com/go/maps v1.20.4/go.mod h1:Act0Ws4HffrECH+pL8YYy1scdSLegov7+0c6gvKqRzI= cloud.google.com/go/maps v1.23.0 h1:NGQM1vBXHZ7SgrlJ5q+KEoSw1B9pgYiFTNfuPa+2wOQ= cloud.google.com/go/maps v1.23.0/go.mod h1:8tjxLplMV7FEoR9FIwqoY7siDnaOdE7FBWnjaXK/xts= +cloud.google.com/go/maps v1.26.0/go.mod h1:+auempdONAP8emtm48aCfNo1ZC+3CJniRA1h8J4u7bY= cloud.google.com/go/mediatranslation v0.9.6/go.mod h1:WS3QmObhRtr2Xu5laJBQSsjnWFPPthsyetlOyT9fJvE= cloud.google.com/go/mediatranslation v0.9.7 h1:JXbjms+JxgaWkj/YuaQm1OeCzuF+IZCDV17uUcZgFOU= cloud.google.com/go/mediatranslation v0.9.7/go.mod h1:mz3v6PR7+Fd/1bYrRxNFGnd+p4wqdc/fyutqC5QHctw= @@ -219,15 +259,19 @@ cloud.google.com/go/metastore v1.14.8 h1:nfyUDD9AeKIs6btY5buQ1No0OVco20WpX9wIruL cloud.google.com/go/metastore v1.14.8/go.mod h1:h1XI2LpD4ohJhQYn9TwXqKb5sVt6KSo47ft96SiFF1s= cloud.google.com/go/monitoring v1.24.2 h1:5OTsoJ1dXYIiMiuL+sYscLc9BumrL3CarVLL7dd7lHM= cloud.google.com/go/monitoring v1.24.2/go.mod h1:x7yzPWcgDRnPEv3sI+jJGBkwl5qINf+6qY4eq0I9B4U= +cloud.google.com/go/monitoring v1.24.3/go.mod h1:nYP6W0tm3N9H/bOw8am7t62YTzZY+zUeQ+Bi6+2eonI= cloud.google.com/go/networkconnectivity v1.17.1/go.mod h1:DTZCq8POTkHgAlOAAEDQF3cMEr/B9k1ZbpklqvHEBtg= cloud.google.com/go/networkconnectivity v1.19.1 h1:n0IzhdgSNzIKQygWwDV8yKRXkZpX3FsjCYFbO9iNHPU= cloud.google.com/go/networkconnectivity v1.19.1/go.mod h1:Q5v6uNNNz8BP232uuXM66XgWML9m379xhwv58Y+8Kb0= +cloud.google.com/go/networkconnectivity v1.20.0/go.mod h1:9MzGwD4ljiq+Z2Pg3ue27OEewCuHz7IUfw1fITrIdSw= cloud.google.com/go/networkmanagement v1.19.1/go.mod h1:icgk265dNnilxQzpr6rO9WuAuuCmUOqq9H6WBeM2Af4= cloud.google.com/go/networkmanagement v1.20.1 h1:W5zdnH332yJFADTXlsHWLVkiXLKGWHjrsg7vXLGd+Ws= cloud.google.com/go/networkmanagement v1.20.1/go.mod h1:clG/5Yt0wQ57qSH6Yh7oehQYlobHw3F6nb3Pn4ig5hU= +cloud.google.com/go/networkmanagement v1.22.0/go.mod h1:RGR62aLOlm72C7DT/3yaMUK43oill6hj9wqktUQ8h6Q= cloud.google.com/go/networksecurity v0.10.6/go.mod h1:FTZvabFPvK2kR/MRIH3l/OoQ/i53eSix2KA1vhBMJec= cloud.google.com/go/networksecurity v0.10.7 h1:J5gdG7mHdRLrsyM7yy4nKFgbN8+geaOo/4Zpeh4DWrg= cloud.google.com/go/networksecurity v0.10.7/go.mod h1:FgoictpfaJkeBlM1o2m+ngPZi8mgJetbFDH4ws1i2fQ= +cloud.google.com/go/networksecurity v0.11.0/go.mod h1:JLgDsg4tOyJ3eMO8lypjqMftbfd60SJ+P7T+DUmWBsM= cloud.google.com/go/notebooks v1.12.6/go.mod h1:3Z4TMEqAKP3pu6DI/U+aEXrNJw9hGZIVbp+l3zw8EuA= cloud.google.com/go/notebooks v1.12.7 h1:g5LTI1LHa/86abDTWd8nrq7/4qq8oFhVx1SmnNpZLVg= cloud.google.com/go/notebooks v1.12.7/go.mod h1:uR9pxAkKmlNloibMr9Q1t8WhIu4P2JeqJs7c064/0Mo= @@ -243,6 +287,7 @@ cloud.google.com/go/orgpolicy v1.15.1/go.mod h1:bpvi9YIyU7wCW9WiXL/ZKT7pd2Ovegyr cloud.google.com/go/osconfig v1.14.6/go.mod h1:LS39HDBH0IJDFgOUkhSZUHFQzmcWaCpYXLrc3A4CVzI= cloud.google.com/go/osconfig v1.15.1 h1:QQzK5njfsfO2rdOWYVDyLQktqSq9gKf2ohRYeKUuA10= cloud.google.com/go/osconfig v1.15.1/go.mod h1:NegylQQl0+5m+I+4Ey/g3HGeQxKkncQ1q+Il4DZ8PME= +cloud.google.com/go/osconfig v1.16.0/go.mod h1:PRmLgZ1loD1hGaqnTBww1nETbqcqAvmTQOLYiIZ7Nvk= cloud.google.com/go/oslogin v1.14.6/go.mod h1:xEvcRZTkMXHfNSKdZ8adxD6wvRzeyAq3cQX3F3kbMRw= cloud.google.com/go/oslogin v1.14.7 h1:YQ8P/+MLwH0tpENYU9QOgwKQxe8DYfAKxIfm6y+OBtA= cloud.google.com/go/oslogin v1.14.7/go.mod h1:NB6NqBHfDMwznePdBVX+ILllc1oPCdNSGp5u/WIyndY= @@ -266,6 +311,7 @@ cloud.google.com/go/pubsublite v1.8.2/go.mod h1:4r8GSa9NznExjuLPEJlF1VjOPOpgf3IT cloud.google.com/go/recaptchaenterprise/v2 v2.20.4/go.mod h1:3H8nb8j8N7Ss2eJ+zr+/H7gyorfzcxiDEtVBDvDjwDQ= cloud.google.com/go/recaptchaenterprise/v2 v2.20.5 h1:Q2CcYGxcvnvng2q3o1SaOpV+rjE/AbFVYGTJomxlG4g= cloud.google.com/go/recaptchaenterprise/v2 v2.20.5/go.mod h1:TCHn8+vtwgygBOwwbUJgRi6R9qglIpTeImsWsWDr5Lo= +cloud.google.com/go/recaptchaenterprise/v2 v2.21.0/go.mod h1:HxQYqZC2/zl2CvKN7jJEv71vEdDi1GMGNUiZxnpiuVI= cloud.google.com/go/recommendationengine v0.9.6/go.mod h1:nZnjKJu1vvoxbmuRvLB5NwGuh6cDMMQdOLXTnkukUOE= cloud.google.com/go/recommendationengine v0.9.7 h1:NH89CyKQP8e98kpdKLwV0jXkQGzSEEZia0V867vkoy8= cloud.google.com/go/recommendationengine v0.9.7/go.mod h1:snZ/FL147u86Jqpv1j95R+CyU5NvL/UzYiyDo6UByTM= @@ -283,15 +329,18 @@ cloud.google.com/go/resourcesettings v1.8.3/go.mod h1:BzgfXFHIWOOmHe6ZV9+r3OWfpH cloud.google.com/go/retail v1.20.0/go.mod h1:1CXWDZDJTOsK6lPjkv67gValP9+h1TMadTC9NpFFr9s= cloud.google.com/go/retail v1.25.1 h1:S9Rl1sB6Dmdak5y9I2Pwn9hcWA5ubk33Vnevxw+CSrI= cloud.google.com/go/retail v1.25.1/go.mod h1:J75G8pd+DH0SHueL9IJw7Y5d2VhTsjFsk+F1t9f8jXc= +cloud.google.com/go/retail v1.26.0/go.mod h1:gMfh6s174Mvy1rK4g50J9TH5sRim8px+Krml25kdrqo= cloud.google.com/go/run v1.9.3/go.mod h1:Si9yDIkUGr5vsXE2QVSWFmAjJkv/O8s3tJ1eTxw3p1o= cloud.google.com/go/run v1.12.0 h1:l4tpqhzJ75uOugXl2BQ15uEM5gLamVH5M70tBv70ZCU= cloud.google.com/go/run v1.12.0/go.mod h1:/APJ89UqgGdIdaD1yaTiSYXozx3fNoqKR/cueDFRueI= +cloud.google.com/go/run v1.15.0/go.mod h1:rgFHMdAopLl++57vzeqA+a1o2x0/ILZnEacRD6nC0EA= cloud.google.com/go/scheduler v1.11.7/go.mod h1:gqYs8ndLx2M5D0oMJh48aGS630YYvC432tHCnVWN13s= cloud.google.com/go/scheduler v1.11.8 h1:BoXY2BvBsaRw3ggVMzC9tborZqJBu+NcJcD9PqeC5Kc= cloud.google.com/go/scheduler v1.11.8/go.mod h1:bNKU7/f04eoM6iKQpwVLvFNBgGyJNS87RiFN73mIPik= cloud.google.com/go/secretmanager v1.14.7/go.mod h1:uRuB4F6NTFbg0vLQ6HsT7PSsfbY7FqHbtJP1J94qxGc= cloud.google.com/go/secretmanager v1.15.0 h1:RtkCMgTpaBMbzozcRUGfZe46jb9a3qh5EdEtVRUATF8= cloud.google.com/go/secretmanager v1.15.0/go.mod h1:1hQSAhKK7FldiYw//wbR/XPfPc08eQ81oBsnRUHEvUc= +cloud.google.com/go/secretmanager v1.16.0/go.mod h1://C/e4I8D26SDTz1f3TQcddhcmiC3rMEl0S1Cakvs3Q= cloud.google.com/go/security v1.18.5/go.mod h1:D1wuUkDwGqTKD0Nv7d4Fn2Dc53POJSmO4tlg1K1iS7s= cloud.google.com/go/security v1.19.2 h1:cF3FkCRRbRC1oXuaGZFl3qU2sdu2gP3iOAHKzL5y04Y= cloud.google.com/go/security v1.19.2/go.mod h1:KXmf64mnOsLVKe8mk/bZpU1Rsvxqc0Ej0A6tgCeN93w= @@ -307,29 +356,35 @@ cloud.google.com/go/shell v1.8.7/go.mod h1:OTke7qc3laNEW5Jr5OV9VR3IwU5x5VqGOE670 cloud.google.com/go/spanner v1.81.0/go.mod h1:3yqzHZvK52zLw10mNLG8MefCEYp3iRFJryTLf5u+mJg= cloud.google.com/go/spanner v1.86.0 h1:jlNWusBol1Jxa9PmYGknUBzLwvD1cebuEenzqebZ9xs= cloud.google.com/go/spanner v1.86.0/go.mod h1:bbwCXbM+zljwSPLZ44wZOdzcdmy89hbUGmM/r9sD0ws= +cloud.google.com/go/spanner v1.87.0/go.mod h1:tcj735Y2aqphB6/l+X5MmwG4NnV+X1NJIbFSZGaHYXw= cloud.google.com/go/speech v1.27.1/go.mod h1:efCfklHFL4Flxcdt9gpEMEJh9MupaBzw3QiSOVeJ6ck= cloud.google.com/go/speech v1.28.0 h1:9AuiAxDTmh/aeREtw+/0e7aI27T5QN4fK5lhssc9MxA= cloud.google.com/go/speech v1.28.0/go.mod h1:hJf6oa+1rzCW/CeDE/qCXedV20B2TXEUje5iaGwW+JI= +cloud.google.com/go/speech v1.29.0/go.mod h1:wtUmIS/h0ZYU6cPA9klcyST3f6i2FdnvNDqENjrRDds= cloud.google.com/go/storage v1.14.0 h1:6RRlFMv1omScs6iq2hfE3IvgE+l6RfJPampq8UZc5TU= cloud.google.com/go/storage v1.35.1 h1:B59ahL//eDfx2IIKFBeT5Atm9wnNmj3+8xG/W4WB//w= cloud.google.com/go/storage v1.35.1/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= cloud.google.com/go/storagetransfer v1.12.4/go.mod h1:p1xLKvpt78aQFRJ8lZGYArgFuL4wljFzitPZoYjl/8A= cloud.google.com/go/storagetransfer v1.13.0 h1:uqKX3OgcYzR1W1YI943ZZ45id0RqA2eXXoCBSPstlbw= cloud.google.com/go/storagetransfer v1.13.0/go.mod h1:+aov7guRxXBYgR3WCqedkyibbTICdQOiXOdpPcJCKl8= +cloud.google.com/go/storagetransfer v1.13.1/go.mod h1:S858w5l383ffkdqAqrAA+BC7KlhCqeNieK3sFf5Bj4Y= cloud.google.com/go/talent v1.8.3/go.mod h1:oD3/BilJpJX8/ad8ZUAxlXHCslTg2YBbafFH3ciZSLQ= cloud.google.com/go/talent v1.8.4 h1:1kJJ+WCY5LZ1A4rCa32zKh3N2xT3I8koiS63+vV0WC4= cloud.google.com/go/talent v1.8.4/go.mod h1:3yukBXUTVFNyKcJpUExW/k5gqEy8qW6OCNj7WdN0MWo= cloud.google.com/go/texttospeech v1.13.0/go.mod h1:g/tW/m0VJnulGncDrAoad6WdELMTes8eb77Idz+4HCo= cloud.google.com/go/texttospeech v1.15.0 h1:8+fZQY8NBEhiMGp+psVK7YPm0sOTfi+d0Q0P+y/SN/4= cloud.google.com/go/texttospeech v1.15.0/go.mod h1:AeSkoH3ziPvapsuyI07TWY4oGxluAjntX+pF4PJ2jy0= +cloud.google.com/go/texttospeech v1.16.0/go.mod h1:AeSkoH3ziPvapsuyI07TWY4oGxluAjntX+pF4PJ2jy0= cloud.google.com/go/tpu v1.8.3/go.mod h1:Do6Gq+/Jx6Xs3LcY2WhHyGwKDKVw++9jIJp+X+0rxRE= cloud.google.com/go/tpu v1.8.4 h1:5DDheA1f7yZ/KUbVT/9lL+Yhgd3IqHDSVVrSqDVkAFY= cloud.google.com/go/tpu v1.8.4/go.mod h1:ul0cyWSHr6jHGZYElZe6HvQn35VY93RAlwpDiSBRnPA= cloud.google.com/go/trace v1.11.6 h1:2O2zjPzqPYAHrn3OKl029qlqG6W8ZdYaOWRyr8NgMT4= cloud.google.com/go/trace v1.11.6/go.mod h1:GA855OeDEBiBMzcckLPE2kDunIpC72N+Pq8WFieFjnI= +cloud.google.com/go/trace v1.11.7/go.mod h1:TNn9d5V3fQVf6s4SCveVMIBS2LJUqo73GACmq/Tky0s= cloud.google.com/go/translate v1.12.5/go.mod h1:o/v+QG/bdtBV1d1edmtau0PwTfActvxPk/gtqdSDBi4= cloud.google.com/go/translate v1.12.6 h1:QHcszWZvBLEZHM2WJ6IDg2BUTWzEPMiHhbJAd15yKGU= cloud.google.com/go/translate v1.12.6/go.mod h1:nB3AXuX+iHbV8ZURmElcW85qkEDWZw68sf4kqMT/E5o= +cloud.google.com/go/translate v1.12.7/go.mod h1:wwJp14NZyWvcrFANhIXutXj0pOBkYciBHwSlUOykcjI= cloud.google.com/go/video v1.23.5/go.mod h1:ZSpGFCpfTOTmb1IkmHNGC/9yI3TjIa/vkkOKBDo0Vpo= cloud.google.com/go/video v1.27.1 h1:Hp+2AeM7b3AagdHcyh2820UTzSbGyqpFJVMu0nHbBcw= cloud.google.com/go/video v1.27.1/go.mod h1:xzfAC77B4vtnbi/TT3UUxEjCa/+Ehy5EA8w470ytOig= @@ -338,9 +393,11 @@ cloud.google.com/go/videointelligence v1.12.7 h1:FisUrSZ+y3oLuGdlFQQgZoNTDm7FAfb cloud.google.com/go/videointelligence v1.12.7/go.mod h1:XAk5hCMY+GihxJ55jNoMdwdXSNZnCl3wGs2+94gK7MA= cloud.google.com/go/vision/v2 v2.9.5 h1:UJZ0H6UlOaYKgCn6lWG2iMAOJIsJZLnseEfzBR8yIqQ= cloud.google.com/go/vision/v2 v2.9.5/go.mod h1:1SiNZPpypqZDbOzU052ZYRiyKjwOcyqgGgqQCI/nlx8= +cloud.google.com/go/vision/v2 v2.9.6/go.mod h1:lJC+vP15D5znJvHQYjEoTKnpToX1L93BUlvBmzM0gyg= cloud.google.com/go/vmmigration v1.8.6/go.mod h1:uZ6/KXmekwK3JmC8PzBM/cKQmq404TTfWtThF6bbf0U= cloud.google.com/go/vmmigration v1.9.1 h1:7IED5P1BmZIDy0U9Co0OYGQkFK/19/7Y6xlCaitmIcs= cloud.google.com/go/vmmigration v1.9.1/go.mod h1:jI3lBlhQn9+BKIWE/MmMsOzGekCXCc34b1M0CihL3zY= +cloud.google.com/go/vmmigration v1.10.0/go.mod h1:LDztCWEb+RwS1bPg4Xzt0fcJS9kVrFxa3ejhH7OW9vg= cloud.google.com/go/vmwareengine v1.3.5/go.mod h1:QuVu2/b/eo8zcIkxBYY5QSwiyEcAy6dInI7N+keI+Jg= cloud.google.com/go/vmwareengine v1.3.6 h1:TKvULKbk44QrIx674cnoVjcZueXhyCAm2sNAJu/S1ds= cloud.google.com/go/vmwareengine v1.3.6/go.mod h1:ps0rb+Skgpt9ppHYC0o5DqtJ5ld2FyS8sAqtbHH8t9s= @@ -400,6 +457,7 @@ github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEe github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -461,6 +519,7 @@ github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 h1:mV02weK github.com/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465 h1:KwWnWVWCNtNq/ewIX7HIKnELmEx2nDP42yskD/pi7QE= github.com/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= github.com/ianlancetaylor/demangle v0.0.0-20250417193237-f615e6bd150b/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= +github.com/jordanlewis/gcassert v0.0.0-20250430164644-389ef753e22e/go.mod h1:ZybsQk6DWyN5t7An1MuPm1gtSZ1xDaTXS9ZjIOxvQrk= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= @@ -584,6 +643,7 @@ go.temporal.io/api v1.35.0/go.mod h1:OYkuupuCw6s/5TkcKHMb9EcIrOI+vTsbf/CGaprbzb0 go.temporal.io/api v1.49.0/go.mod h1:iaxoP/9OXMJcQkETTECfwYq4cw/bj4nwov8b3ZLVnXM= go.temporal.io/api v1.52.0 h1:Tn69z2nhQeXtofa1/j/MbwPHnFRM9+13xqYmFl/KFjM= go.temporal.io/api v1.52.0/go.mod h1:iaxoP/9OXMJcQkETTECfwYq4cw/bj4nwov8b3ZLVnXM= +go.temporal.io/api v1.57.0/go.mod h1:iaxoP/9OXMJcQkETTECfwYq4cw/bj4nwov8b3ZLVnXM= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= @@ -613,6 +673,7 @@ golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbht golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= golang.org/x/oauth2 v0.31.0 h1:8Fq0yVZLh4j4YA47vHKFTa9Ew5XIrCP8LC6UeNZnLxo= golang.org/x/oauth2 v0.31.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= @@ -621,6 +682,7 @@ golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFK golang.org/x/telemetry v0.0.0-20250710130107-8d8967aff50b/go.mod h1:4ZwOYna0/zsOKwuR5X/m0QFOJpSZvAxFfkQT+Erd9D4= golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8 h1:LvzTn0GQhWuvKH/kVRS3R3bVAsdQWI7hvfLHGgh9+lU= golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8/go.mod h1:Pi4ztBfryZoJEkyFTI5/Ocsu2jXyDr6iSdgJiYE/uwE= +golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4/go.mod h1:g5NllXBEermZrmR51cJDQxmJUHUOfRAaNyWBM+R+548= golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= @@ -632,6 +694,7 @@ golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= +golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= @@ -672,6 +735,7 @@ google.golang.org/genproto/googleapis/api v0.0.0-20240624140628-dc46fd24d27d/go. google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:pKLAc5OolXC3ViWGI62vvC0n10CpwAtRcTNCFwTKBEw= google.golang.org/genproto/googleapis/api v0.0.0-20251002232023-7c0ddcbb5797 h1:D/zZ8knc/wLq9imidPFpHsGuRUYTCWWCwemZ2dxACGs= google.golang.org/genproto/googleapis/api v0.0.0-20251002232023-7c0ddcbb5797/go.mod h1:NnuHhy+bxcg30o7FnVAZbXsPHUDQ9qKWAQKCD7VxFtk= +google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20/go.mod h1:ZdbssH/1SOVnjnDlXzxDHK2MCidiqXtbYccJNzNYPEE= google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4= google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97 h1:6GQBEOdGkX6MMTLT9V+TjtIRZCw9VPD5Z+yHY9wMgS0= @@ -683,6 +747,7 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20240624140628-dc46fd24d27d/go. google.golang.org/genproto/googleapis/rpc v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= google.golang.org/genproto/googleapis/rpc v0.0.0-20251002232023-7c0ddcbb5797 h1:CirRxTOwnRWVLKzDNrs0CXAaVozJoR4G9xvdRecrdpk= google.golang.org/genproto/googleapis/rpc v0.0.0-20251002232023-7c0ddcbb5797/go.mod h1:HSkG/KdJWusxU1F6CNrwNDjBMgisKxGnc5dAZfT0mjQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= google.golang.org/grpc v1.58.0 h1:32JY8YpPMSR45K+c3o6b8VL73V+rR8k+DeMIr4vRH8o= google.golang.org/grpc v1.58.0/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I= @@ -694,6 +759,7 @@ google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLp google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM= google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI= google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= +google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= diff --git a/handler.go b/handler.go new file mode 100644 index 0000000..b5891f1 --- /dev/null +++ b/handler.go @@ -0,0 +1,75 @@ +package logger + +import ( + "context" + "io" + "log/slog" + "strings" + "sync" +) + +// RawHandler is an [slog.Handler] that outputs only the log message followed by +// a newline. Structured attributes and groups are discarded. This is used for +// the "raw" logger mode where callers want plain, undecorated output. +type RawHandler struct { + w io.Writer + level slog.Leveler + mu sync.Mutex +} + +// NewRawHandler returns a [RawHandler] writing to w. Only records at or above +// the given level are emitted. +func NewRawHandler(w io.Writer, level slog.Leveler) *RawHandler { + return &RawHandler{ + w: w, + level: level, + } +} + +func (h *RawHandler) Enabled(_ context.Context, level slog.Level) bool { + return level >= h.level.Level() +} + +func (h *RawHandler) Handle(_ context.Context, r slog.Record) error { + h.mu.Lock() + defer h.mu.Unlock() + + if _, err := io.WriteString(h.w, r.Message); err != nil { + return err + } + + if !strings.HasSuffix(r.Message, "\n") { + if _, err := io.WriteString(h.w, "\n"); err != nil { + return err + } + } + + return nil +} + +// WithAttrs returns the same handler — raw mode discards attributes. +func (h *RawHandler) WithAttrs([]slog.Attr) slog.Handler { return h } + +// WithGroup returns the same handler — raw mode discards groups. +func (h *RawHandler) WithGroup(string) slog.Handler { return h } + +// newJSONHandler creates a [slog.JSONHandler] configured for production output. +// Keys are remapped: time → ts (Unix epoch nanoseconds), level stays lowercase, +// message → msg. +func newJSONHandler(w io.Writer, level slog.Leveler) slog.Handler { + return slog.NewJSONHandler(w, &slog.HandlerOptions{ + Level: level, + ReplaceAttr: func(_ []string, a slog.Attr) slog.Attr { + switch a.Key { + case slog.TimeKey: + a.Key = "ts" + a.Value = slog.Int64Value(a.Value.Time().UnixNano()) + case slog.LevelKey: + a.Value = slog.StringValue(strings.ToLower(a.Value.String())) + case slog.MessageKey: + a.Key = "msg" + } + return a + }, + }) +} diff --git a/logger.go b/logger.go index 1114d04..936b355 100644 --- a/logger.go +++ b/logger.go @@ -1,29 +1,54 @@ package logger import ( - "go.uber.org/zap" + "log/slog" + "sync" ) +// Log implements the [Logger] interface, dispatching named loggers to +// per-channel configurations when available, falling back to the base logger. type Log struct { - base *zap.Logger + base *slog.Logger channels ChannelConfig + + mu sync.Mutex + cache map[string]*slog.Logger } -func NewLogger(channels ChannelConfig, base *zap.Logger) *Log { +// NewLogger creates a new [Log] with the given channel overrides and base +// logger. +func NewLogger(channels ChannelConfig, base *slog.Logger) *Log { return &Log{ channels: channels, base: base, + cache: make(map[string]*slog.Logger), } } -func (l *Log) NamedLogger(name string) *zap.Logger { +// NamedLogger returns a logger for the given name. If a channel-specific +// configuration exists, a dedicated logger is built from it; otherwise the base +// logger is returned with a "logger" attribute set to name. Channel loggers are +// cached so that repeated calls for the same name reuse the same logger and +// underlying writers. +func (l *Log) NamedLogger(name string) *slog.Logger { + l.mu.Lock() + defer l.mu.Unlock() + + if cached, ok := l.cache[name]; ok { + return cached + } + + var result *slog.Logger if cfg, ok := l.channels.Channels[name]; ok { - ll, err := cfg.BuildLogger() + res, err := cfg.BuildLogger() if err != nil { panic(err) } - return ll.Named(name) + result = res.Logger.With("logger", name) + } else { + result = l.base.With("logger", name) } - return l.base.Named(name) + l.cache[name] = result + return result } diff --git a/plugin.go b/plugin.go index da5cf26..57ed6cf 100644 --- a/plugin.go +++ b/plugin.go @@ -2,15 +2,17 @@ package logger import ( "context" + "io" + "log/slog" "github.com/roadrunner-server/endure/v2/dep" "github.com/roadrunner-server/errors" - "go.uber.org/zap" ) // PluginName declares plugin name. const PluginName = "logs" +// Configurer provides access to the application configuration. type Configurer interface { // UnmarshalKey takes a single key and unmarshal it into a Struct. UnmarshalKey(name string, out any) error @@ -18,32 +20,37 @@ type Configurer interface { Has(name string) bool } -// Logger is the main logger interface to provide a named (per-plugin) logger +// Logger is the main logger interface to provide a named (per-plugin) logger. type Logger interface { - NamedLogger(name string) *zap.Logger + NamedLogger(name string) *slog.Logger } -// Plugin manages zap logger. +// Plugin manages the slog-based logger. type Plugin struct { - base *zap.Logger + base *slog.Logger cfg *Config channels ChannelConfig + closers []io.Closer } // Init logger service. func (p *Plugin) Init(cfg Configurer) error { const op = errors.Op("config_plugin_init") var err error + // if not configured, configure with default params if !cfg.Has(PluginName) { p.cfg = &Config{} p.cfg.InitDefault() - p.base, err = p.cfg.BuildLogger() - if err != nil { - return errors.E(op, err) + res, buildErr := p.cfg.BuildLogger() + if buildErr != nil { + return errors.E(op, buildErr) } + p.base = res.Logger + p.closers = res.Closers + return nil } @@ -58,35 +65,45 @@ func (p *Plugin) Init(cfg Configurer) error { } p.cfg.InitDefault() - p.base, err = p.cfg.BuildLogger() - if err != nil { - return errors.E(op, err) + res, buildErr := p.cfg.BuildLogger() + if buildErr != nil { + return errors.E(op, buildErr) } + + p.base = res.Logger + p.closers = res.Closers + return nil } +// Serve starts the plugin (no-op for the logger). func (p *Plugin) Serve() chan error { return make(chan error, 1) } +// Stop gracefully shuts down the plugin, closing any file handles opened for +// log output. func (p *Plugin) Stop(context.Context) error { - _ = p.base.Sync() + for _, c := range p.closers { + _ = c.Close() + } return nil } +// Provides declares the services this plugin exports. func (p *Plugin) Provides() []*dep.Out { return []*dep.Out{ dep.Bind((*Logger)(nil), p.ServiceLogger), } } -// ServiceLogger returns a logger dedicated to the specific channel. Similar to Named() but also reads the core params. +// ServiceLogger returns a logger dedicated to the specific channel. func (p *Plugin) ServiceLogger() *Log { return NewLogger(p.channels, p.base) } -// Name returns a user-friendly plugin name +// Name returns a user-friendly plugin name. func (p *Plugin) Name() string { return PluginName } diff --git a/schema.json b/schema.json index b185208..6e86758 100644 --- a/schema.json +++ b/schema.json @@ -121,8 +121,7 @@ "debug", "info", "warn", - "error", - "panic" + "error" ] }, "LogEncoding": { diff --git a/tests/go.mod b/tests/go.mod index d7a6c8d..d601996 100644 --- a/tests/go.mod +++ b/tests/go.mod @@ -2,36 +2,33 @@ module tests go 1.26 -toolchain go1.26.0 - require ( github.com/roadrunner-server/config/v5 v5.1.9 github.com/roadrunner-server/endure/v2 v2.6.2 github.com/roadrunner-server/errors v1.4.1 github.com/roadrunner-server/http/v5 v5.3.0 - github.com/roadrunner-server/logger/v5 v5.1.9 + github.com/roadrunner-server/logger/v6 v6.0.0 github.com/roadrunner-server/rpc/v5 v5.1.9 github.com/roadrunner-server/server/v5 v5.2.10 github.com/stretchr/testify v1.11.1 - go.uber.org/zap v1.27.1 ) -replace github.com/roadrunner-server/logger/v5 => ../ +replace github.com/roadrunner-server/logger/v6 => ../ require ( github.com/beorn7/perks v1.0.1 // indirect - github.com/caddyserver/certmagic v0.25.1 // indirect - github.com/caddyserver/zerossl v0.1.4 // indirect + github.com/caddyserver/certmagic v0.25.2 // indirect + github.com/caddyserver/zerossl v0.1.5 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/fatih/color v1.18.0 // indirect + github.com/fatih/color v1.19.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.9.0 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-viper/mapstructure/v2 v2.5.0 // indirect - github.com/goccy/go-json v0.10.5 // indirect + github.com/goccy/go-json v0.10.6 // indirect github.com/google/uuid v1.6.0 // indirect github.com/joho/godotenv v1.5.1 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect @@ -39,7 +36,7 @@ require ( github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mholt/acmez v1.2.0 // indirect - github.com/mholt/acmez/v3 v3.1.4 // indirect + github.com/mholt/acmez/v3 v3.1.6 // indirect github.com/miekg/dns v1.1.72 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect @@ -47,11 +44,11 @@ require ( github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.67.5 // indirect - github.com/prometheus/procfs v0.19.2 // indirect + github.com/prometheus/procfs v0.20.1 // indirect github.com/quic-go/qpack v0.6.0 // indirect github.com/quic-go/quic-go v0.59.0 // indirect github.com/roadrunner-server/api/v4 v4.23.0 // indirect - github.com/roadrunner-server/context v1.2.0 // indirect + github.com/roadrunner-server/context v1.3.0 // indirect github.com/roadrunner-server/events v1.0.1 // indirect github.com/roadrunner-server/goridge/v3 v3.8.3 // indirect github.com/roadrunner-server/pool v1.1.3 // indirect @@ -71,23 +68,24 @@ require ( github.com/zeebo/assert v1.3.1 // indirect github.com/zeebo/blake3 v0.2.4 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 // indirect - go.opentelemetry.io/contrib/propagators/jaeger v1.40.0 // indirect - go.opentelemetry.io/otel v1.40.0 // indirect - go.opentelemetry.io/otel/metric v1.40.0 // indirect - go.opentelemetry.io/otel/trace v1.40.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 // indirect + go.opentelemetry.io/contrib/propagators/jaeger v1.42.0 // indirect + go.opentelemetry.io/otel v1.42.0 // indirect + go.opentelemetry.io/otel/metric v1.42.0 // indirect + go.opentelemetry.io/otel/trace v1.42.0 // indirect go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.1 // indirect go.uber.org/zap/exp v0.3.0 // indirect - go.yaml.in/yaml/v2 v2.4.3 // indirect + go.yaml.in/yaml/v2 v2.4.4 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/crypto v0.48.0 // indirect - golang.org/x/mod v0.33.0 // indirect - golang.org/x/net v0.50.0 // indirect - golang.org/x/sync v0.19.0 // indirect - golang.org/x/sys v0.41.0 // indirect - golang.org/x/text v0.34.0 // indirect - golang.org/x/tools v0.42.0 // indirect - google.golang.org/genproto v0.0.0-20260209200024-4cfbd4190f57 // indirect + golang.org/x/crypto v0.49.0 // indirect + golang.org/x/mod v0.34.0 // indirect + golang.org/x/net v0.52.0 // indirect + golang.org/x/sync v0.20.0 // indirect + golang.org/x/sys v0.42.0 // indirect + golang.org/x/text v0.35.0 // indirect + golang.org/x/tools v0.43.0 // indirect + google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 // indirect google.golang.org/protobuf v1.36.11 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/tests/go.sum b/tests/go.sum index ad29bb6..2e79f9b 100644 --- a/tests/go.sum +++ b/tests/go.sum @@ -2,14 +2,20 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/caddyserver/certmagic v0.25.1 h1:4sIKKbOt5pg6+sL7tEwymE1x2bj6CHr80da1CRRIPbY= github.com/caddyserver/certmagic v0.25.1/go.mod h1:VhyvndxtVton/Fo/wKhRoC46Rbw1fmjvQ3GjHYSQTEY= +github.com/caddyserver/certmagic v0.25.2 h1:D7xcS7ggX/WEY54x0czj7ioTkmDWKIgxtIi2OcQclUc= +github.com/caddyserver/certmagic v0.25.2/go.mod h1:llW/CvsNmza8S6hmsuggsZeiX+uS27dkqY27wDIuBWg= github.com/caddyserver/zerossl v0.1.4 h1:CVJOE3MZeFisCERZjkxIcsqIH4fnFdlYWnPYeFtBHRw= github.com/caddyserver/zerossl v0.1.4/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4= +github.com/caddyserver/zerossl v0.1.5 h1:dkvOjBAEEtY6LIGAHei7sw2UgqSD6TrWweXpV7lvEvE= +github.com/caddyserver/zerossl v0.1.5/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= +github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w= +github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -28,6 +34,8 @@ github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPE github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= +github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU= +github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= @@ -50,6 +58,8 @@ github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30= github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE= github.com/mholt/acmez/v3 v3.1.4 h1:DyzZe/RnAzT3rpZj/2Ii5xZpiEvvYk3cQEN/RmqxwFQ= github.com/mholt/acmez/v3 v3.1.4/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ= +github.com/mholt/acmez/v3 v3.1.6 h1:eGVQNObP0pBN4sxqrXeg7MYqTOWyoiYpQqITVWlrevk= +github.com/mholt/acmez/v3 v3.1.6/go.mod h1:5nTPosTGosLxF3+LU4ygbgMRFDhbAVpqMI4+a4aHLBY= github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI= github.com/miekg/dns v1.1.72/go.mod h1:+EuEPhdHOsfk6Wk5TT2CzssZdqkmFhf8r+aVyDEToIs= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= @@ -66,6 +76,8 @@ github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTU github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws= github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw= +github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= +github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII= github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw= @@ -76,6 +88,8 @@ github.com/roadrunner-server/config/v5 v5.1.9 h1:ReWwts/prEvuC4yVJ0BRDmY5sxw/1c+ github.com/roadrunner-server/config/v5 v5.1.9/go.mod h1:R6YyTWahW61tWHOI2BfdkQU/0Zc/2d6/JbJ/KEvq8F8= github.com/roadrunner-server/context v1.2.0 h1:QKSd6ZypPbtHlxaSUULsRYHPO1IlIm0H6hm6Fdu5Ge4= github.com/roadrunner-server/context v1.2.0/go.mod h1:BSjiuIBLbA5qxlLIIDS/tFDruikZ/AElS5wKtqZiltw= +github.com/roadrunner-server/context v1.3.0 h1:iyTXVORhPU2/26z7kdzEaggwG5P8yhIKUDLiePjylFQ= +github.com/roadrunner-server/context v1.3.0/go.mod h1:KPAzAlnErXekQazW9t4h55U1S42Q2bk0WCaPQrezJw4= github.com/roadrunner-server/endure/v2 v2.6.2 h1:sIB4kTyE7gtT3fDhuYWUYn6Vt/dcPtiA6FoNS1eS+84= github.com/roadrunner-server/endure/v2 v2.6.2/go.mod h1:t/2+xpNYgGBwhzn83y2MDhvhZ19UVq1REcvqn7j7RB8= github.com/roadrunner-server/errors v1.4.1 h1:LKNeaCGiwd3t8IaL840ZNF3UA9yDQlpvHnKddnh0YRQ= @@ -132,18 +146,30 @@ go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 h1:7iP2uCb7sGddAr30RRS6xjKy7AZ2JtTOPA3oolgVSw8= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0/go.mod h1:c7hN3ddxs/z6q9xwvfLPk+UHlWRQyaeR1LdgfL/66l0= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 h1:OyrsyzuttWTSur2qN/Lm0m2a8yqyIjUVBZcxFPuXq2o= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0/go.mod h1:C2NGBr+kAB4bk3xtMXfZ94gqFDtg/GkI7e9zqGh5Beg= go.opentelemetry.io/contrib/propagators/jaeger v1.40.0 h1:aXl9uobjJs5vquMLt9ZkI/3zIuz8XQ3TqOKSWx0/xdU= go.opentelemetry.io/contrib/propagators/jaeger v1.40.0/go.mod h1:ioMePqe6k6c/ovXSkmkMr1mbN5qRBGJxNTVop7/2XO0= +go.opentelemetry.io/contrib/propagators/jaeger v1.42.0 h1:jP8unWI6q5kcb3gpGLjKDGaUa+JW+nHKWvpS/q+YuWA= +go.opentelemetry.io/contrib/propagators/jaeger v1.42.0/go.mod h1:xd89e/pUyPatUP1C4z1UknD9jHptESO99tWyvd4mWD4= go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms= go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g= +go.opentelemetry.io/otel v1.42.0 h1:lSQGzTgVR3+sgJDAU/7/ZMjN9Z+vUip7leaqBKy4sho= +go.opentelemetry.io/otel v1.42.0/go.mod h1:lJNsdRMxCUIWuMlVJWzecSMuNjE7dOYyWlqOXWkdqCc= go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g= go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc= +go.opentelemetry.io/otel/metric v1.42.0 h1:2jXG+3oZLNXEPfNmnpxKDeZsFI5o4J+nz6xUlaFdF/4= +go.opentelemetry.io/otel/metric v1.42.0/go.mod h1:RlUN/7vTU7Ao/diDkEpQpnz3/92J9ko05BIwxYa2SSI= go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8= go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE= +go.opentelemetry.io/otel/sdk v1.42.0 h1:LyC8+jqk6UJwdrI/8VydAq/hvkFKNHZVIWuslJXYsDo= go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw= go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg= +go.opentelemetry.io/otel/sdk/metric v1.42.0 h1:D/1QR46Clz6ajyZ3G8SgNlTJKBdGp84q9RKCAZ3YGuA= go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw= go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA= +go.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY= +go.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= @@ -156,27 +182,45 @@ go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U= go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= +go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= +go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= +golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= +golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= +golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= +golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60= golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM= +golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= +golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= +golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= +golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= +golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s= +golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0= google.golang.org/genproto v0.0.0-20260209200024-4cfbd4190f57 h1:uZSB/r2MjH9IsqpG2vRNSV1Juteix90oHe8oTcLW9tk= google.golang.org/genproto v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:nGuPfp0lnDJcJD0J47StV0Skgnw3qMSQhjsLKiejq5Y= +google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 h1:XzmzkmB14QhVhgnawEVsOn6OFsnpyxNPRY9QV01dNB0= +google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:L43LFes82YgSonw6iTXTxXUX1OlULt4AQtkik4ULL/I= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/tests/logger_test.go b/tests/logger_test.go index 34eab36..0ae8173 100644 --- a/tests/logger_test.go +++ b/tests/logger_test.go @@ -15,20 +15,21 @@ import ( "github.com/roadrunner-server/config/v5" "github.com/roadrunner-server/endure/v2" httpPlugin "github.com/roadrunner-server/http/v5" - "github.com/roadrunner-server/logger/v5" + "github.com/roadrunner-server/logger/v6" "github.com/roadrunner-server/rpc/v5" "github.com/roadrunner-server/server/v5" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) +const cfgPrefix = "rr" + func TestLogger(t *testing.T) { container := endure.New(slog.LevelDebug) - // config plugin vp := &config.Plugin{} vp.Path = "configs/.rr.yaml" - vp.Prefix = "rr" + vp.Prefix = cfgPrefix //nolint:staticcheck // Prefix is deprecated but still required by config/v5 err := container.RegisterAll( vp, @@ -47,17 +48,13 @@ func TestLogger(t *testing.T) { t.Fatal(err) } - // stop by CTRL+C c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) stopCh := make(chan struct{}, 1) - wg := &sync.WaitGroup{} - wg.Add(1) - - go func() { - defer wg.Done() + var wg sync.WaitGroup + wg.Go(func() { for { select { case e := <-errCh: @@ -73,7 +70,7 @@ func TestLogger(t *testing.T) { return } } - }() + }) stopCh <- struct{}{} wg.Wait() @@ -82,12 +79,11 @@ func TestLogger(t *testing.T) { func TestLoggerRawErr(t *testing.T) { cont := endure.New(slog.LevelDebug) - // config plugin cfg := &config.Plugin{ - Version: "2.9.0", + Version: "2025.1.11", Path: "configs/.rr-raw-mode.yaml", - Prefix: "rr", } + cfg.Prefix = cfgPrefix //nolint:staticcheck // Prefix is deprecated but still required by config/v5 err := cont.RegisterAll( cfg, @@ -108,13 +104,10 @@ func TestLoggerRawErr(t *testing.T) { sig := make(chan os.Signal, 1) signal.Notify(sig, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) - wg := &sync.WaitGroup{} - wg.Add(1) - stopCh := make(chan struct{}, 1) - go func() { - defer wg.Done() + var wg sync.WaitGroup + wg.Go(func() { for { select { case e := <-ch: @@ -130,7 +123,6 @@ func TestLoggerRawErr(t *testing.T) { } return case <-stopCh: - // timeout err = cont.Stop() if err != nil { assert.FailNow(t, "error", err.Error()) @@ -138,10 +130,14 @@ func TestLoggerRawErr(t *testing.T) { return } } - }() + }) time.Sleep(time.Second) - resp, err := http.Get("http://127.0.0.1:34999") + + req, err := http.NewRequestWithContext(t.Context(), http.MethodGet, "http://127.0.0.1:34999", nil) + assert.NoError(t, err) + + resp, err := http.DefaultClient.Do(req) assert.NoError(t, err) require.NotNil(t, resp) @@ -157,10 +153,9 @@ func TestLoggerRawErr(t *testing.T) { func TestLoggerNoConfig(t *testing.T) { container := endure.New(slog.LevelDebug) - // config plugin vp := &config.Plugin{} vp.Path = "configs/.rr-no-logger.yaml" - vp.Prefix = "rr" + vp.Prefix = cfgPrefix //nolint:staticcheck // Prefix is deprecated but still required by config/v5 err := container.RegisterAll( vp, @@ -179,17 +174,13 @@ func TestLoggerNoConfig(t *testing.T) { t.Fatal(err) } - // stop by CTRL+C c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) stopCh := make(chan struct{}, 1) - wg := &sync.WaitGroup{} - wg.Add(1) - - go func() { - defer wg.Done() + var wg sync.WaitGroup + wg.Go(func() { for { select { case e := <-errCh: @@ -205,20 +196,20 @@ func TestLoggerNoConfig(t *testing.T) { return } } - }() + }) stopCh <- struct{}{} wg.Wait() } -// Should no panic +// TestLoggerNoConfig2 verifies no panic when plugins are disabled due to +// missing dependencies. func TestLoggerNoConfig2(t *testing.T) { container := endure.New(slog.LevelDebug) - // config plugin vp := &config.Plugin{} vp.Path = "configs/.rr-no-logger2.yaml" - vp.Prefix = "rr" + vp.Prefix = cfgPrefix //nolint:staticcheck // Prefix is deprecated but still required by config/v5 err := container.RegisterAll( vp, @@ -239,17 +230,13 @@ func TestLoggerNoConfig2(t *testing.T) { t.Fatal(err) } - // stop by CTRL+C c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) stopCh := make(chan struct{}, 1) - wg := &sync.WaitGroup{} - wg.Add(1) - - go func() { - defer wg.Done() + var wg sync.WaitGroup + wg.Go(func() { for { select { case e := <-errCh: @@ -265,7 +252,7 @@ func TestLoggerNoConfig2(t *testing.T) { return } } - }() + }) stopCh <- struct{}{} wg.Wait() @@ -274,10 +261,9 @@ func TestLoggerNoConfig2(t *testing.T) { func TestFileLogger(t *testing.T) { container := endure.New(slog.LevelDebug) - // config plugin vp := &config.Plugin{} vp.Path = "configs/.rr-file-logger.yaml" - vp.Prefix = "rr" + vp.Prefix = cfgPrefix //nolint:staticcheck // Prefix is deprecated but still required by config/v5 err := container.RegisterAll( vp, @@ -298,17 +284,13 @@ func TestFileLogger(t *testing.T) { t.Fatal(err) } - // stop by CTRL+C c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) stopCh := make(chan struct{}, 1) - wg := &sync.WaitGroup{} - wg.Add(1) - - go func() { - defer wg.Done() + var wg sync.WaitGroup + wg.Go(func() { for { select { case e := <-errCh: @@ -324,7 +306,7 @@ func TestFileLogger(t *testing.T) { return } } - }() + }) time.Sleep(time.Second * 2) t.Run("HTTPEchoReq", httpEcho) @@ -334,8 +316,8 @@ func TestFileLogger(t *testing.T) { t.Fatal(err) } - strings.Contains(string(f), "worker constructed") - strings.Contains(string(f), "201 GET") + assert.Contains(t, string(f), "worker constructed") + assert.Contains(t, string(f), "201 GET") _ = os.Remove("test.log") @@ -344,11 +326,12 @@ func TestFileLogger(t *testing.T) { } func httpEcho(t *testing.T) { - req, err := http.NewRequest(http.MethodGet, "http://127.0.0.1:54224?hello=world", nil) + req, err := http.NewRequestWithContext(t.Context(), http.MethodGet, "http://127.0.0.1:54224?hello=world", nil) assert.NoError(t, err) r, err := http.DefaultClient.Do(req) assert.NoError(t, err) + require.NotNil(t, r) assert.Equal(t, http.StatusCreated, r.StatusCode) err = r.Body.Close() @@ -358,10 +341,9 @@ func httpEcho(t *testing.T) { func TestMarshalObjectLogging(t *testing.T) { container := endure.New(slog.LevelDebug) - // config plugin vp := &config.Plugin{} vp.Path = "configs/.rr-file-logger.yaml" - vp.Prefix = "rr" + vp.Prefix = cfgPrefix //nolint:staticcheck // Prefix is deprecated but still required by config/v5 err := container.RegisterAll( vp, @@ -380,17 +362,13 @@ func TestMarshalObjectLogging(t *testing.T) { t.Fatal(err) } - // stop by CTRL+C c := make(chan os.Signal, 1) signal.Notify(c, os.Interrupt) stopCh := make(chan struct{}, 1) - wg := &sync.WaitGroup{} - wg.Add(1) - - go func() { - defer wg.Done() + var wg sync.WaitGroup + wg.Go(func() { for { select { case e := <-errCh: @@ -406,7 +384,7 @@ func TestMarshalObjectLogging(t *testing.T) { return } } - }() + }) time.Sleep(time.Second * 2) diff --git a/tests/pkgs.txt b/tests/pkgs.txt deleted file mode 100644 index b53e695..0000000 --- a/tests/pkgs.txt +++ /dev/null @@ -1 +0,0 @@ -github.com/roadrunner-server/amqp/v5,github.com/roadrunner-server/app-logger/v5,github.com/roadrunner-server/beanstalk/v5,github.com/roadrunner-server/boltdb/v5,github.com/roadrunner-server/centrifuge/v5,github.com/roadrunner-server/config/v5,github.com/roadrunner-server/fileserver/v5,github.com/roadrunner-server/grpc/v5,github.com/roadrunner-server/gzip/v5,github.com/roadrunner-server/headers/v5,github.com/roadrunner-server/http/v5,github.com/roadrunner-server/informer/v5,github.com/roadrunner-server/jobs/v5,github.com/roadrunner-server/kafka/v5,github.com/roadrunner-server/kv/v5,github.com/roadrunner-server/logger/v5,github.com/roadrunner-server/lock/v5,github.com/roadrunner-server/memcached/v5,github.com/roadrunner-server/memory/v5,github.com/roadrunner-server/metrics/v5,github.com/roadrunner-server/nats/v5,github.com/roadrunner-server/otel/v5,github.com/roadrunner-server/prometheus/v5,github.com/roadrunner-server/proxy_ip_parser/v5,github.com/roadrunner-server/redis/v5,github.com/roadrunner-server/resetter/v5,github.com/roadrunner-server/rpc/v5,github.com/roadrunner-server/send/v5,github.com/roadrunner-server/server/v5,github.com/roadrunner-server/service/v5,github.com/roadrunner-server/sqs/v5,github.com/roadrunner-server/static/v5,github.com/roadrunner-server/status/v5,github.com/roadrunner-server/tcp/v5 diff --git a/tests/plugin.go b/tests/plugin.go index ad72d86..ef3427d 100644 --- a/tests/plugin.go +++ b/tests/plugin.go @@ -2,11 +2,10 @@ package logger import ( "context" + "log/slog" "strings" "github.com/roadrunner-server/errors" - "go.uber.org/zap" - "go.uber.org/zap/zapcore" ) type Configurer interface { @@ -17,19 +16,12 @@ type Configurer interface { } type Logger interface { - NamedLogger(name string) *zap.Logger + NamedLogger(name string) *slog.Logger } type TestPlugin struct { config Configurer - log *zap.Logger -} - -type Loggable struct{} - -func (l *Loggable) MarshalLogObject(encoder zapcore.ObjectEncoder) error { - encoder.AddString("error", "Example marshaller error") - return nil + log *slog.Logger } func (p1 *TestPlugin) Init(cfg Configurer, log Logger) error { @@ -40,29 +32,25 @@ func (p1 *TestPlugin) Init(cfg Configurer, log Logger) error { func (p1 *TestPlugin) Serve() chan error { errCh := make(chan error, 1) - p1.log.Error("error", zap.Error(errors.E(errors.Str("test")))) - p1.log.Info("error", zap.Error(errors.E(errors.Str("test")))) - p1.log.Debug("error", zap.Error(errors.E(errors.Str("test")))) - p1.log.Warn("error", zap.Error(errors.E(errors.Str("test")))) - - field := zap.String("error", "Example field error") - - p1.log.Error("error", field) - p1.log.Info("error", field) - p1.log.Debug("error", field) - p1.log.Warn("error", field) - - marshalledObject := &Loggable{} - - p1.log.Error("error", zap.Any("object", marshalledObject)) - p1.log.Info("error", zap.Any("object", marshalledObject)) - p1.log.Debug("error", zap.Any("object", marshalledObject)) - p1.log.Warn("error", zap.Any("object", marshalledObject)) - - p1.log.Error("error", zap.String("test", "")) - p1.log.Info("error", zap.String("test", "")) - p1.log.Debug("error", zap.String("test", "")) - p1.log.Warn("error", zap.String("test", "")) + p1.log.Error("error", slog.Any("error", errors.E(errors.Str("test")))) + p1.log.Info("error", slog.Any("error", errors.E(errors.Str("test")))) + p1.log.Debug("error", slog.Any("error", errors.E(errors.Str("test")))) + p1.log.Warn("error", slog.Any("error", errors.E(errors.Str("test")))) + + p1.log.Error("error", slog.String("error", "Example field error")) + p1.log.Info("error", slog.String("error", "Example field error")) + p1.log.Debug("error", slog.String("error", "Example field error")) + p1.log.Warn("error", slog.String("error", "Example field error")) + + p1.log.Error("error", slog.Any("object", map[string]string{"error": "Example marshaller error"})) + p1.log.Info("error", slog.Any("object", map[string]string{"error": "Example marshaller error"})) + p1.log.Debug("error", slog.Any("object", map[string]string{"error": "Example marshaller error"})) + p1.log.Warn("error", slog.Any("object", map[string]string{"error": "Example marshaller error"})) + + p1.log.Error("error", slog.String("test", "")) + p1.log.Info("error", slog.String("test", "")) + p1.log.Debug("error", slog.String("test", "")) + p1.log.Warn("error", slog.String("test", "")) // test the `raw` mode messageJSON := []byte(`{"field": "value"}`) From 83883bd8a025476789dfc0f365e40298a3af2ebf Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Fri, 20 Mar 2026 12:53:22 +0100 Subject: [PATCH 2/4] chore: review fixes Signed-off-by: Valery Piashchynski --- config.go | 4 ++-- go.work.sum | 26 +++++++++++++++++++++++ handler.go | 21 ------------------- logger.go | 29 ++++--------------------- tests/go.sum | 50 ++++---------------------------------------- tests/logger_test.go | 8 ------- 6 files changed, 36 insertions(+), 102 deletions(-) diff --git a/config.go b/config.go index df49827..ed3f92d 100644 --- a/config.go +++ b/config.go @@ -135,7 +135,7 @@ func (cfg *Config) BuildLogger() (*BuildResult, error) { closers = append(closers, lj) return &BuildResult{ - Logger: slog.New(newJSONHandler(lj, level)), + Logger: slog.New(slog.NewJSONHandler(lj, &slog.HandlerOptions{Level: level})), Closers: closers, }, nil } @@ -143,7 +143,7 @@ func (cfg *Config) BuildLogger() (*BuildResult, error) { var handler slog.Handler switch mode { case production: - handler = newJSONHandler(w, level) + handler = slog.NewJSONHandler(w, &slog.HandlerOptions{Level: level}) case raw: handler = NewRawHandler(w, level) case off, none: diff --git a/go.work.sum b/go.work.sum index d91ce77..6be8fdf 100644 --- a/go.work.sum +++ b/go.work.sum @@ -15,6 +15,7 @@ cloud.google.com/go/aiplatform v1.86.0/go.mod h1:xp3wFix8imliXkVpgMRkjnreJYTaNzL cloud.google.com/go/aiplatform v1.103.0 h1:1Y0mlsxh8STlIImjdZeeXg8Rrf5FDLRY0AOQBy3AUi8= cloud.google.com/go/aiplatform v1.103.0/go.mod h1:4rwKOMdubQOND81AlO3EckcskvEFCYSzXKfn42GMm8k= cloud.google.com/go/aiplatform v1.115.0/go.mod h1:DwPJAxebOTy6BajSMjF7ah3QvlYO4jf2gpJw6/1z9gU= +cloud.google.com/go/aiplatform v1.120.0/go.mod h1:6mDthfmy0oS1EQhVFdijoxkVdI2+HIZkpuGTBpedeCg= cloud.google.com/go/analytics v0.28.1/go.mod h1:iPaIVr5iXPB3JzkKPW1JddswksACRFl3NSHgVHsuYC4= cloud.google.com/go/analytics v0.30.0 h1:9PvoT9SvNIHRqTZye7+WudvO2vr4PiZ9mLhiOtEE7Eo= cloud.google.com/go/analytics v0.30.0/go.mod h1:dneJtsGmmK6EkEPg59vRlncKFWt3xzmKNOc9aKXCTrI= @@ -34,13 +35,16 @@ cloud.google.com/go/appengine v1.9.7/go.mod h1:y1XpGVeAhbsNzHida79cHbr3pFRsym0ob cloud.google.com/go/area120 v0.9.6/go.mod h1:qKSokqe0iTmwBDA3tbLWonMEnh0pMAH4YxiceiHUed4= cloud.google.com/go/area120 v0.9.7 h1:BbpzLwaIXVPorrrzTH+ni7P5mLemmPPfSZ7o39k7zQc= cloud.google.com/go/area120 v0.9.7/go.mod h1:5nJ0yksmjOMfc4Zpk+okWfJ3A1004FvB82rfia+ZLaY= +cloud.google.com/go/area120 v0.10.0/go.mod h1:Xg3fKl4xU3UVai9wsI1FXwNU8wSCDYT7dFZfwJKViAM= cloud.google.com/go/artifactregistry v1.17.1 h1:A20kj2S2HO9vlyBVyVFHPxArjxkXvLP5LjcdE7NhaPc= cloud.google.com/go/artifactregistry v1.17.1/go.mod h1:06gLv5QwQPWtaudI2fWO37gfwwRUHwxm3gA8Fe568Hc= cloud.google.com/go/artifactregistry v1.19.0/go.mod h1:UEAPCgHDFC1q+A8nnVxXHPEy9KCVOeavFBF1fEChQvU= +cloud.google.com/go/artifactregistry v1.20.0/go.mod h1:0G9wdbGyDFkvrYH+2AlQs9MuTJdbY8Vg45M8VjlI8rc= cloud.google.com/go/asset v1.21.0/go.mod h1:0lMJ0STdyImZDSCB8B3i/+lzIquLBpJ9KZ4pyRvzccM= cloud.google.com/go/asset v1.21.1 h1:i55wWC/EwVdHMyJgRfbLp/L6ez4nQuOpZwSxkuqN9ek= cloud.google.com/go/asset v1.21.1/go.mod h1:7AzY1GCC+s1O73yzLM1IpHFLHz3ws2OigmCpOQHwebk= cloud.google.com/go/asset v1.22.0/go.mod h1:q80JP2TeWWzMCazYnrAfDf36aQKf1QiKzzpNLflJwf8= +cloud.google.com/go/asset v1.22.1/go.mod h1:NlvWwmca7CX6BIBEdRNxOocH6DowmBghAAHucOHuHng= cloud.google.com/go/assuredworkloads v1.12.6 h1:ip/shfJYx6lrHBWYADjrrrubcm7uZzy50TTF5tPG7ek= cloud.google.com/go/assuredworkloads v1.12.6/go.mod h1:QyZHd7nH08fmZ+G4ElihV1zoZ7H0FQCpgS0YWtwjCKo= cloud.google.com/go/assuredworkloads v1.13.0/go.mod h1:o/oHEOnUlribR+uJWTKQo8A5RhSl9K9FNeMOew4TJ3M= @@ -61,6 +65,7 @@ cloud.google.com/go/bigquery v1.67.0/go.mod h1:HQeP1AHFuAz0Y55heDSb0cjZIhnEkuwFR cloud.google.com/go/bigquery v1.71.0 h1:NvSZvXU1Hyb+YiRVKQPuQXGeZaw/0NP6M/WOrBqSx3g= cloud.google.com/go/bigquery v1.71.0/go.mod h1:GUbRtmeCckOE85endLherHD9RsujY+gS7i++c1CqssQ= cloud.google.com/go/bigquery v1.73.1/go.mod h1:KSLx1mKP/yGiA8U+ohSrqZM1WknUnjZAxHAQZ51/b1k= +cloud.google.com/go/bigquery v1.74.0/go.mod h1:iViO7Cx3A/cRKcHNRsHB3yqGAMInFBswrE9Pxazsc90= cloud.google.com/go/bigtable v1.37.0/go.mod h1:HXqddP6hduwzrtiTCqZPpj9ij4hGZb4Zy1WF/dT+yaU= cloud.google.com/go/bigtable v1.40.1 h1:k8HfpUOvn7sQwc6oNKqjvD/yjkwynf4qBuyKwh5cU08= cloud.google.com/go/bigtable v1.40.1/go.mod h1:LtPzCcrAFaGRZ82Hs8xMueUeYW9Jw12AmNdUTMfDnh4= @@ -119,6 +124,7 @@ cloud.google.com/go/dataflow v0.11.1/go.mod h1:3s6y/h5Qz7uuxTmKJKBifkYZ3zs63jS+6 cloud.google.com/go/dataform v0.11.2/go.mod h1:IMmueJPEKpptT2ZLWlvIYjw6P/mYHHxA7/SUBiXqZUY= cloud.google.com/go/dataform v0.12.1 h1:yf6Up6m1FUt+YB5CBgNtIZfz2OzjuNBdzZWV3SLSVNE= cloud.google.com/go/dataform v0.12.1/go.mod h1:atGS8ReRjfNDUQib0X/o/7Gi2bqHI2G7/J86LKiGimE= +cloud.google.com/go/dataform v0.13.0/go.mod h1:U3fqrPY5jAcFh1a8rQb4a+PQ7zKlc5qfgotFZ+luKPo= cloud.google.com/go/datafusion v1.8.6/go.mod h1:fCyKJF2zUKC+O3hc2F9ja5EUCAbT4zcH692z8HiFZFw= cloud.google.com/go/datafusion v1.8.7 h1:tLCV+xYuOrSjdrRTkc9Cqsb5mBSQEsNfFmuTNYl5/rA= cloud.google.com/go/datafusion v1.8.7/go.mod h1:4dkFb1la41qCEXh1AzYtFwl842bu2ikTUXyKhjvFCb0= @@ -133,6 +139,7 @@ cloud.google.com/go/dataproc/v2 v2.11.2/go.mod h1:xwukBjtfiO4vMEa1VdqyFLqJmcv7t3 cloud.google.com/go/dataproc/v2 v2.14.1 h1:Kxq0iomU0H4MlVP4HYeYPNJnV+YxNctf/hFrprmGy5Y= cloud.google.com/go/dataproc/v2 v2.14.1/go.mod h1:tSdkodShfzrrUNPDVEL6MdH9/mIEvp/Z9s9PBdbsZg8= cloud.google.com/go/dataproc/v2 v2.15.0/go.mod h1:tSdkodShfzrrUNPDVEL6MdH9/mIEvp/Z9s9PBdbsZg8= +cloud.google.com/go/dataproc/v2 v2.16.0/go.mod h1:HlzFg8k1SK+bJN3Zsy2z5g6OZS1D4DYiDUgJtF0gJnE= cloud.google.com/go/dataqna v0.9.7 h1:qTRAG/E3T63Xj1orefRlwupfwH9c9ERUAnWSRGp75so= cloud.google.com/go/dataqna v0.9.7/go.mod h1:4ac3r7zm7Wqm8NAc8sDIDM0v7Dz7d1e/1Ka1yMFanUM= cloud.google.com/go/dataqna v0.9.8/go.mod h1:2lHKmGPOqzzuqCc5NI0+Xrd5om4ulxGwPpLB4AnFgpA= @@ -150,6 +157,7 @@ cloud.google.com/go/dialogflow v1.68.2/go.mod h1:E0Ocrhf5/nANZzBju8RX8rONf0PuIvz cloud.google.com/go/dialogflow v1.69.1 h1:R69CCEgx9RMHWjS2eP7aw5sE1Ajo5buQTzTdBe2o13w= cloud.google.com/go/dialogflow v1.69.1/go.mod h1:mP4XrpgDvPYBP+cdLxFC1WJJlkwuy0H8L1Lada9No/M= cloud.google.com/go/dialogflow v1.75.0/go.mod h1:z1W1ZogmigYVtP5YmyeUh+D219VCjdd3VJqY76PG3gA= +cloud.google.com/go/dialogflow v1.76.0/go.mod h1:mdLkMmSCghfcP85X9dFBlirC1OssS65KE5hrrSz2GXY= cloud.google.com/go/dlp v1.22.1/go.mod h1:Gc7tGo1UJJTBRt4OvNQhm8XEQ0i9VidAiGXBVtsftjM= cloud.google.com/go/dlp v1.25.0 h1:283+PJFk72SIj3apDTT/cHnKPBtuBUnduLBpbz1diFw= cloud.google.com/go/dlp v1.25.0/go.mod h1:PY4DMzV7lqRC5JvpxL05fXNeL8dknxYpFp4WjxmE22M= @@ -158,6 +166,7 @@ cloud.google.com/go/documentai v1.37.0/go.mod h1:qAf3ewuIUJgvSHQmmUWvM3Ogsr5A16U cloud.google.com/go/documentai v1.38.1 h1:6uWDoi/wAT8D9belR9Q1n2fFtY6YJ+/3aj6rR9mAlbY= cloud.google.com/go/documentai v1.38.1/go.mod h1:KmlLO93F7GRU8dENXRxvt+7V8o7eCG6Y6WDitKbcYJs= cloud.google.com/go/documentai v1.40.0/go.mod h1:oDTm0aoG8ldKucW/yzRrLbaTO0NvtgGAWm5KPAT5iNY= +cloud.google.com/go/documentai v1.42.0/go.mod h1:CABOUzRNOuvb/QwJS2LS80Hpqbu3UW2afyRKTYuW7bo= cloud.google.com/go/domains v0.10.6/go.mod h1:3xzG+hASKsVBA8dOPc4cIaoV3OdBHl1qgUpAvXK7pGY= cloud.google.com/go/domains v0.10.7 h1:G3kUq0vKBMhyOj5GqAfEYbVuez05U+ENHZUAtrEp/pI= cloud.google.com/go/domains v0.10.7/go.mod h1:T3WG/QUAO/52z4tUPooKS8AY7yXaFxPYn1V3F0/JbNQ= @@ -223,6 +232,7 @@ cloud.google.com/go/kms v1.21.2/go.mod h1:8wkMtHV/9Z8mLXEXr1GK7xPSBdi6knuLXIhqju cloud.google.com/go/kms v1.23.0 h1:WaqAZsUptyHwOo9II8rFC1Kd2I+yvNsNP2IJ14H2sUw= cloud.google.com/go/kms v1.23.0/go.mod h1:rZ5kK0I7Kn9W4erhYVoIRPtpizjunlrfU4fUkumUp8g= cloud.google.com/go/kms v1.25.0/go.mod h1:XIdHkzfj0bUO3E+LvwPg+oc7s58/Ns8Nd8Sdtljihbk= +cloud.google.com/go/kms v1.26.0/go.mod h1:pHKOdFJm63hxBsiPkYtowZPltu9dW0MWvBa6IA4HM58= cloud.google.com/go/language v1.14.5 h1:BVJ/POtlnJ55LElvnQY19UOxpMVtHoHHkFJW2uHJsVU= cloud.google.com/go/language v1.14.5/go.mod h1:nl2cyAVjcBct1Hk73tzxuKebk0t2eULFCaruhetdZIA= cloud.google.com/go/language v1.14.6/go.mod h1:7y3J9OexQsfkWNGCxhT+7lb64pa60e12ZCoWDOHxJ1M= @@ -248,6 +258,7 @@ cloud.google.com/go/maps v1.20.4/go.mod h1:Act0Ws4HffrECH+pL8YYy1scdSLegov7+0c6g cloud.google.com/go/maps v1.23.0 h1:NGQM1vBXHZ7SgrlJ5q+KEoSw1B9pgYiFTNfuPa+2wOQ= cloud.google.com/go/maps v1.23.0/go.mod h1:8tjxLplMV7FEoR9FIwqoY7siDnaOdE7FBWnjaXK/xts= cloud.google.com/go/maps v1.26.0/go.mod h1:+auempdONAP8emtm48aCfNo1ZC+3CJniRA1h8J4u7bY= +cloud.google.com/go/maps v1.29.0/go.mod h1:FNATcM5ziB2TDE2IVWH4f/yeXc+SbUk1X+bmKjR8HEA= cloud.google.com/go/mediatranslation v0.9.6/go.mod h1:WS3QmObhRtr2Xu5laJBQSsjnWFPPthsyetlOyT9fJvE= cloud.google.com/go/mediatranslation v0.9.7 h1:JXbjms+JxgaWkj/YuaQm1OeCzuF+IZCDV17uUcZgFOU= cloud.google.com/go/mediatranslation v0.9.7/go.mod h1:mz3v6PR7+Fd/1bYrRxNFGnd+p4wqdc/fyutqC5QHctw= @@ -264,10 +275,12 @@ cloud.google.com/go/networkconnectivity v1.17.1/go.mod h1:DTZCq8POTkHgAlOAAEDQF3 cloud.google.com/go/networkconnectivity v1.19.1 h1:n0IzhdgSNzIKQygWwDV8yKRXkZpX3FsjCYFbO9iNHPU= cloud.google.com/go/networkconnectivity v1.19.1/go.mod h1:Q5v6uNNNz8BP232uuXM66XgWML9m379xhwv58Y+8Kb0= cloud.google.com/go/networkconnectivity v1.20.0/go.mod h1:9MzGwD4ljiq+Z2Pg3ue27OEewCuHz7IUfw1fITrIdSw= +cloud.google.com/go/networkconnectivity v1.21.0/go.mod h1:XC1UJ+tqBsLWz73dqrMc7kUvdTv0FIxtDGv6YntTBO0= cloud.google.com/go/networkmanagement v1.19.1/go.mod h1:icgk265dNnilxQzpr6rO9WuAuuCmUOqq9H6WBeM2Af4= cloud.google.com/go/networkmanagement v1.20.1 h1:W5zdnH332yJFADTXlsHWLVkiXLKGWHjrsg7vXLGd+Ws= cloud.google.com/go/networkmanagement v1.20.1/go.mod h1:clG/5Yt0wQ57qSH6Yh7oehQYlobHw3F6nb3Pn4ig5hU= cloud.google.com/go/networkmanagement v1.22.0/go.mod h1:RGR62aLOlm72C7DT/3yaMUK43oill6hj9wqktUQ8h6Q= +cloud.google.com/go/networkmanagement v1.23.0/go.mod h1:QTYCWp5UxUnU280SqF7AX/mf6NhsqKblmLeCALQmx5c= cloud.google.com/go/networksecurity v0.10.6/go.mod h1:FTZvabFPvK2kR/MRIH3l/OoQ/i53eSix2KA1vhBMJec= cloud.google.com/go/networksecurity v0.10.7 h1:J5gdG7mHdRLrsyM7yy4nKFgbN8+geaOo/4Zpeh4DWrg= cloud.google.com/go/networksecurity v0.10.7/go.mod h1:FgoictpfaJkeBlM1o2m+ngPZi8mgJetbFDH4ws1i2fQ= @@ -357,10 +370,12 @@ cloud.google.com/go/spanner v1.81.0/go.mod h1:3yqzHZvK52zLw10mNLG8MefCEYp3iRFJry cloud.google.com/go/spanner v1.86.0 h1:jlNWusBol1Jxa9PmYGknUBzLwvD1cebuEenzqebZ9xs= cloud.google.com/go/spanner v1.86.0/go.mod h1:bbwCXbM+zljwSPLZ44wZOdzcdmy89hbUGmM/r9sD0ws= cloud.google.com/go/spanner v1.87.0/go.mod h1:tcj735Y2aqphB6/l+X5MmwG4NnV+X1NJIbFSZGaHYXw= +cloud.google.com/go/spanner v1.88.0/go.mod h1:MzulBwuuYwQUVdkZXBBFapmXee3N+sQrj2T/yup6uEE= cloud.google.com/go/speech v1.27.1/go.mod h1:efCfklHFL4Flxcdt9gpEMEJh9MupaBzw3QiSOVeJ6ck= cloud.google.com/go/speech v1.28.0 h1:9AuiAxDTmh/aeREtw+/0e7aI27T5QN4fK5lhssc9MxA= cloud.google.com/go/speech v1.28.0/go.mod h1:hJf6oa+1rzCW/CeDE/qCXedV20B2TXEUje5iaGwW+JI= cloud.google.com/go/speech v1.29.0/go.mod h1:wtUmIS/h0ZYU6cPA9klcyST3f6i2FdnvNDqENjrRDds= +cloud.google.com/go/speech v1.30.0/go.mod h1:F2+NJujR8uzDLd6bwy5kgtVycxvEq06nzvzz5eQ/gMo= cloud.google.com/go/storage v1.14.0 h1:6RRlFMv1omScs6iq2hfE3IvgE+l6RfJPampq8UZc5TU= cloud.google.com/go/storage v1.35.1 h1:B59ahL//eDfx2IIKFBeT5Atm9wnNmj3+8xG/W4WB//w= cloud.google.com/go/storage v1.35.1/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= @@ -413,6 +428,7 @@ cloud.google.com/go/websecurityscanner v1.7.7/go.mod h1:ng/PzARaus3Bj4Os4LpUnyYH cloud.google.com/go/workflows v1.14.2/go.mod h1:5nqKjMD+MsJs41sJhdVrETgvD5cOK3hUcAs8ygqYvXQ= cloud.google.com/go/workflows v1.14.3 h1:FGF6QEl3rtOSIHPOMZofWRVy3KNx26jDdgoYzJZ6ZhY= cloud.google.com/go/workflows v1.14.3/go.mod h1:CC9+YdVI2Kvp0L58WajHpEfKJxhrtRh3uQ0SYWcmAk4= +code.pfad.fr/check v1.1.0/go.mod h1:NiUH13DtYsb7xp5wll0U4SXx7KhXQVCtRgdC96IPfoM= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY= github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= @@ -449,6 +465,7 @@ github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJn github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe6ZudgnXrq0barxBImvnnJoMEhXAzcbM0I= +github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= @@ -544,6 +561,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/letsencrypt/challtestsrv v1.4.2/go.mod h1:GhqMqcSoeGpYd5zX5TgwA6er/1MbWzx/o7yuuVya+Wk= +github.com/letsencrypt/pebble/v2 v2.10.0/go.mod h1:Sk8cmUIPcIdv2nINo+9PB4L+ZBhzY+F9A1a/h/xmWiQ= github.com/libdns/libdns v1.0.0-beta.1 h1:KIf4wLfsrEpXpZ3vmc/poM8zCATXT2klbdPe6hyOBjQ= github.com/libdns/libdns v1.0.0-beta.1/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= @@ -632,6 +651,8 @@ go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg= go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E= go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE= go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs= +go.opentelemetry.io/otel/sdk v1.42.0/go.mod h1:rGHCAxd9DAph0joO4W6OPwxjNTYWghRWmkHuGbayMts= +go.opentelemetry.io/otel/sdk/metric v1.42.0/go.mod h1:Ua6AAlDKdZ7tdvaQKfSmnFTdHx37+J4ba8MwVCYM5hc= go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w= go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA= go.temporal.io/api v1.24.0 h1:WWjMYSXNh4+T4Y4jq1e/d9yCNnWoHhq4bIwflHY6fic= @@ -683,6 +704,7 @@ golang.org/x/telemetry v0.0.0-20250710130107-8d8967aff50b/go.mod h1:4ZwOYna0/zsO golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8 h1:LvzTn0GQhWuvKH/kVRS3R3bVAsdQWI7hvfLHGgh9+lU= golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8/go.mod h1:Pi4ztBfryZoJEkyFTI5/Ocsu2jXyDr6iSdgJiYE/uwE= golang.org/x/telemetry v0.0.0-20260209163413-e7419c687ee4/go.mod h1:g5NllXBEermZrmR51cJDQxmJUHUOfRAaNyWBM+R+548= +golang.org/x/telemetry v0.0.0-20260311193753-579e4da9a98c/go.mod h1:TpUTTEp9frx7rTdLpC9gFG9kdI7zVLFTFFlqaH2Cncw= golang.org/x/term v0.12.0 h1:/ZfYdc3zq+q02Rv9vGqTeSItdzZTSNDmfTi0mBAuidU= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= @@ -695,6 +717,7 @@ golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ= golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= +golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A= golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= @@ -736,6 +759,7 @@ google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2/go. google.golang.org/genproto/googleapis/api v0.0.0-20251002232023-7c0ddcbb5797 h1:D/zZ8knc/wLq9imidPFpHsGuRUYTCWWCwemZ2dxACGs= google.golang.org/genproto/googleapis/api v0.0.0-20251002232023-7c0ddcbb5797/go.mod h1:NnuHhy+bxcg30o7FnVAZbXsPHUDQ9qKWAQKCD7VxFtk= google.golang.org/genproto/googleapis/api v0.0.0-20260203192932-546029d2fa20/go.mod h1:ZdbssH/1SOVnjnDlXzxDHK2MCidiqXtbYccJNzNYPEE= +google.golang.org/genproto/googleapis/api v0.0.0-20260316172706-e463d84ca32d/go.mod h1:X2gu9Qwng7Nn009s/r3RUxqkzQNqOrAy79bluY7ojIg= google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4= google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97 h1:6GQBEOdGkX6MMTLT9V+TjtIRZCw9VPD5Z+yHY9wMgS0= @@ -748,6 +772,7 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20250505200425-f936aa4a68b2/go. google.golang.org/genproto/googleapis/rpc v0.0.0-20251002232023-7c0ddcbb5797 h1:CirRxTOwnRWVLKzDNrs0CXAaVozJoR4G9xvdRecrdpk= google.golang.org/genproto/googleapis/rpc v0.0.0-20251002232023-7c0ddcbb5797/go.mod h1:HSkG/KdJWusxU1F6CNrwNDjBMgisKxGnc5dAZfT0mjQ= google.golang.org/genproto/googleapis/rpc v0.0.0-20260203192932-546029d2fa20/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260311181403-84a4fc48630c/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= google.golang.org/grpc v1.58.0 h1:32JY8YpPMSR45K+c3o6b8VL73V+rR8k+DeMIr4vRH8o= google.golang.org/grpc v1.58.0/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I= @@ -760,6 +785,7 @@ google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3i google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI= google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= +google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= diff --git a/handler.go b/handler.go index b5891f1..9cf2d54 100644 --- a/handler.go +++ b/handler.go @@ -52,24 +52,3 @@ func (h *RawHandler) WithAttrs([]slog.Attr) slog.Handler { return h } // WithGroup returns the same handler — raw mode discards groups. func (h *RawHandler) WithGroup(string) slog.Handler { return h } - -// newJSONHandler creates a [slog.JSONHandler] configured for production output. -// Keys are remapped: time → ts (Unix epoch nanoseconds), level stays lowercase, -// message → msg. -func newJSONHandler(w io.Writer, level slog.Leveler) slog.Handler { - return slog.NewJSONHandler(w, &slog.HandlerOptions{ - Level: level, - ReplaceAttr: func(_ []string, a slog.Attr) slog.Attr { - switch a.Key { - case slog.TimeKey: - a.Key = "ts" - a.Value = slog.Int64Value(a.Value.Time().UnixNano()) - case slog.LevelKey: - a.Value = slog.StringValue(strings.ToLower(a.Value.String())) - case slog.MessageKey: - a.Key = "msg" - } - return a - }, - }) -} diff --git a/logger.go b/logger.go index 936b355..cdd8483 100644 --- a/logger.go +++ b/logger.go @@ -1,18 +1,12 @@ package logger -import ( - "log/slog" - "sync" -) +import "log/slog" // Log implements the [Logger] interface, dispatching named loggers to // per-channel configurations when available, falling back to the base logger. type Log struct { base *slog.Logger channels ChannelConfig - - mu sync.Mutex - cache map[string]*slog.Logger } // NewLogger creates a new [Log] with the given channel overrides and base @@ -21,34 +15,19 @@ func NewLogger(channels ChannelConfig, base *slog.Logger) *Log { return &Log{ channels: channels, base: base, - cache: make(map[string]*slog.Logger), } } // NamedLogger returns a logger for the given name. If a channel-specific // configuration exists, a dedicated logger is built from it; otherwise the base -// logger is returned with a "logger" attribute set to name. Channel loggers are -// cached so that repeated calls for the same name reuse the same logger and -// underlying writers. +// logger is returned with a "logger" attribute set to name. func (l *Log) NamedLogger(name string) *slog.Logger { - l.mu.Lock() - defer l.mu.Unlock() - - if cached, ok := l.cache[name]; ok { - return cached - } - - var result *slog.Logger if cfg, ok := l.channels.Channels[name]; ok { res, err := cfg.BuildLogger() if err != nil { panic(err) } - result = res.Logger.With("logger", name) - } else { - result = l.base.With("logger", name) + return res.Logger.With("logger", name) } - - l.cache[name] = result - return result + return l.base.With("logger", name) } diff --git a/tests/go.sum b/tests/go.sum index 2e79f9b..f714362 100644 --- a/tests/go.sum +++ b/tests/go.sum @@ -1,19 +1,14 @@ +code.pfad.fr/check v1.1.0 h1:GWvjdzhSEgHvEHe2uJujDcpmZoySKuHQNrZMfzfO0bE= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/caddyserver/certmagic v0.25.1 h1:4sIKKbOt5pg6+sL7tEwymE1x2bj6CHr80da1CRRIPbY= -github.com/caddyserver/certmagic v0.25.1/go.mod h1:VhyvndxtVton/Fo/wKhRoC46Rbw1fmjvQ3GjHYSQTEY= github.com/caddyserver/certmagic v0.25.2 h1:D7xcS7ggX/WEY54x0czj7ioTkmDWKIgxtIi2OcQclUc= github.com/caddyserver/certmagic v0.25.2/go.mod h1:llW/CvsNmza8S6hmsuggsZeiX+uS27dkqY27wDIuBWg= -github.com/caddyserver/zerossl v0.1.4 h1:CVJOE3MZeFisCERZjkxIcsqIH4fnFdlYWnPYeFtBHRw= -github.com/caddyserver/zerossl v0.1.4/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4= github.com/caddyserver/zerossl v0.1.5 h1:dkvOjBAEEtY6LIGAHei7sw2UgqSD6TrWweXpV7lvEvE= github.com/caddyserver/zerossl v0.1.5/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= -github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w= github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= @@ -22,6 +17,7 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -32,8 +28,6 @@ github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= -github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= -github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU= github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= @@ -48,6 +42,8 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/letsencrypt/challtestsrv v1.4.2 h1:0ON3ldMhZyWlfVNYYpFuWRTmZNnyfiL9Hh5YzC3JVwU= +github.com/letsencrypt/pebble/v2 v2.10.0 h1:Wq6gYXlsY6ubqI3hhxsTzdyotvfdjFBxuwYqCLCnj/U= github.com/libdns/libdns v1.1.1 h1:wPrHrXILoSHKWJKGd0EiAVmiJbFShguILTg9leS/P/U= github.com/libdns/libdns v1.1.1/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= @@ -56,8 +52,6 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30= github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE= -github.com/mholt/acmez/v3 v3.1.4 h1:DyzZe/RnAzT3rpZj/2Ii5xZpiEvvYk3cQEN/RmqxwFQ= -github.com/mholt/acmez/v3 v3.1.4/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ= github.com/mholt/acmez/v3 v3.1.6 h1:eGVQNObP0pBN4sxqrXeg7MYqTOWyoiYpQqITVWlrevk= github.com/mholt/acmez/v3 v3.1.6/go.mod h1:5nTPosTGosLxF3+LU4ygbgMRFDhbAVpqMI4+a4aHLBY= github.com/miekg/dns v1.1.72 h1:vhmr+TF2A3tuoGNkLDFK9zi36F2LS+hKTRW0Uf8kbzI= @@ -74,8 +68,6 @@ github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNw github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4= github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= -github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws= -github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw= github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc= github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8= @@ -86,8 +78,6 @@ github.com/roadrunner-server/api/v4 v4.23.0 h1:lrVXgP4ozD/H5DrIdT181ldVhD1R9QT5q github.com/roadrunner-server/api/v4 v4.23.0/go.mod h1:AlHuVVOklb7XF33Cf7IfmwOn3j4gGg37on9Xi6j08Bg= github.com/roadrunner-server/config/v5 v5.1.9 h1:ReWwts/prEvuC4yVJ0BRDmY5sxw/1c+hGTSdJ71hIQU= github.com/roadrunner-server/config/v5 v5.1.9/go.mod h1:R6YyTWahW61tWHOI2BfdkQU/0Zc/2d6/JbJ/KEvq8F8= -github.com/roadrunner-server/context v1.2.0 h1:QKSd6ZypPbtHlxaSUULsRYHPO1IlIm0H6hm6Fdu5Ge4= -github.com/roadrunner-server/context v1.2.0/go.mod h1:BSjiuIBLbA5qxlLIIDS/tFDruikZ/AElS5wKtqZiltw= github.com/roadrunner-server/context v1.3.0 h1:iyTXVORhPU2/26z7kdzEaggwG5P8yhIKUDLiePjylFQ= github.com/roadrunner-server/context v1.3.0/go.mod h1:KPAzAlnErXekQazW9t4h55U1S42Q2bk0WCaPQrezJw4= github.com/roadrunner-server/endure/v2 v2.6.2 h1:sIB4kTyE7gtT3fDhuYWUYn6Vt/dcPtiA6FoNS1eS+84= @@ -144,30 +134,16 @@ github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 h1:7iP2uCb7sGddAr30RRS6xjKy7AZ2JtTOPA3oolgVSw8= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0/go.mod h1:c7hN3ddxs/z6q9xwvfLPk+UHlWRQyaeR1LdgfL/66l0= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 h1:OyrsyzuttWTSur2qN/Lm0m2a8yqyIjUVBZcxFPuXq2o= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0/go.mod h1:C2NGBr+kAB4bk3xtMXfZ94gqFDtg/GkI7e9zqGh5Beg= -go.opentelemetry.io/contrib/propagators/jaeger v1.40.0 h1:aXl9uobjJs5vquMLt9ZkI/3zIuz8XQ3TqOKSWx0/xdU= -go.opentelemetry.io/contrib/propagators/jaeger v1.40.0/go.mod h1:ioMePqe6k6c/ovXSkmkMr1mbN5qRBGJxNTVop7/2XO0= go.opentelemetry.io/contrib/propagators/jaeger v1.42.0 h1:jP8unWI6q5kcb3gpGLjKDGaUa+JW+nHKWvpS/q+YuWA= go.opentelemetry.io/contrib/propagators/jaeger v1.42.0/go.mod h1:xd89e/pUyPatUP1C4z1UknD9jHptESO99tWyvd4mWD4= -go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms= -go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g= go.opentelemetry.io/otel v1.42.0 h1:lSQGzTgVR3+sgJDAU/7/ZMjN9Z+vUip7leaqBKy4sho= go.opentelemetry.io/otel v1.42.0/go.mod h1:lJNsdRMxCUIWuMlVJWzecSMuNjE7dOYyWlqOXWkdqCc= -go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g= -go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc= go.opentelemetry.io/otel/metric v1.42.0 h1:2jXG+3oZLNXEPfNmnpxKDeZsFI5o4J+nz6xUlaFdF/4= go.opentelemetry.io/otel/metric v1.42.0/go.mod h1:RlUN/7vTU7Ao/diDkEpQpnz3/92J9ko05BIwxYa2SSI= -go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8= -go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE= go.opentelemetry.io/otel/sdk v1.42.0 h1:LyC8+jqk6UJwdrI/8VydAq/hvkFKNHZVIWuslJXYsDo= -go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw= -go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg= go.opentelemetry.io/otel/sdk/metric v1.42.0 h1:D/1QR46Clz6ajyZ3G8SgNlTJKBdGp84q9RKCAZ3YGuA= -go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw= -go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA= go.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY= go.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -180,45 +156,27 @@ go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U= go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ= -go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= -go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ= go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= -golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= -golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= -golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= -golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= -golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60= -golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM= golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= -golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= -golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= -golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= -golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= -golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= -golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s= golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0= -google.golang.org/genproto v0.0.0-20260209200024-4cfbd4190f57 h1:uZSB/r2MjH9IsqpG2vRNSV1Juteix90oHe8oTcLW9tk= -google.golang.org/genproto v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:nGuPfp0lnDJcJD0J47StV0Skgnw3qMSQhjsLKiejq5Y= google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 h1:XzmzkmB14QhVhgnawEVsOn6OFsnpyxNPRY9QV01dNB0= google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:L43LFes82YgSonw6iTXTxXUX1OlULt4AQtkik4ULL/I= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= diff --git a/tests/logger_test.go b/tests/logger_test.go index 0ae8173..5194abb 100644 --- a/tests/logger_test.go +++ b/tests/logger_test.go @@ -22,14 +22,11 @@ import ( "github.com/stretchr/testify/require" ) -const cfgPrefix = "rr" - func TestLogger(t *testing.T) { container := endure.New(slog.LevelDebug) vp := &config.Plugin{} vp.Path = "configs/.rr.yaml" - vp.Prefix = cfgPrefix //nolint:staticcheck // Prefix is deprecated but still required by config/v5 err := container.RegisterAll( vp, @@ -83,7 +80,6 @@ func TestLoggerRawErr(t *testing.T) { Version: "2025.1.11", Path: "configs/.rr-raw-mode.yaml", } - cfg.Prefix = cfgPrefix //nolint:staticcheck // Prefix is deprecated but still required by config/v5 err := cont.RegisterAll( cfg, @@ -155,7 +151,6 @@ func TestLoggerNoConfig(t *testing.T) { vp := &config.Plugin{} vp.Path = "configs/.rr-no-logger.yaml" - vp.Prefix = cfgPrefix //nolint:staticcheck // Prefix is deprecated but still required by config/v5 err := container.RegisterAll( vp, @@ -209,7 +204,6 @@ func TestLoggerNoConfig2(t *testing.T) { vp := &config.Plugin{} vp.Path = "configs/.rr-no-logger2.yaml" - vp.Prefix = cfgPrefix //nolint:staticcheck // Prefix is deprecated but still required by config/v5 err := container.RegisterAll( vp, @@ -263,7 +257,6 @@ func TestFileLogger(t *testing.T) { vp := &config.Plugin{} vp.Path = "configs/.rr-file-logger.yaml" - vp.Prefix = cfgPrefix //nolint:staticcheck // Prefix is deprecated but still required by config/v5 err := container.RegisterAll( vp, @@ -343,7 +336,6 @@ func TestMarshalObjectLogging(t *testing.T) { vp := &config.Plugin{} vp.Path = "configs/.rr-file-logger.yaml" - vp.Prefix = cfgPrefix //nolint:staticcheck // Prefix is deprecated but still required by config/v5 err := container.RegisterAll( vp, From e8320421992fa91e19d59bd6aa08fb2eab68d38f Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Fri, 20 Mar 2026 13:29:54 +0100 Subject: [PATCH 3/4] feature: logger custom format Signed-off-by: Valery Piashchynski --- config.go | 65 +++- format_handler.go | 237 ++++++++++++ format_handler_test.go | 552 +++++++++++++++++++++++++++ schema.json | 24 ++ tests/configs/.rr-custom-format.yaml | 6 + tests/logger_test.go | 51 +++ tests/php_test_files/composer.json | 9 +- 7 files changed, 929 insertions(+), 15 deletions(-) create mode 100644 format_handler.go create mode 100644 format_handler_test.go create mode 100644 tests/configs/.rr-custom-format.yaml diff --git a/config.go b/config.go index ed3f92d..43b15ea 100644 --- a/config.go +++ b/config.go @@ -44,6 +44,19 @@ type FileLoggerConfig struct { Compress bool `mapstructure:"compress"` } +// NewLumberjack creates a configured [lumberjack.Logger] from the file logger +// settings, calling [InitDefaults] first to fill zero-value fields. +func (fl *FileLoggerConfig) NewLumberjack() *lumberjack.Logger { + fl.InitDefaults() + return &lumberjack.Logger{ + Filename: fl.LogOutput, + MaxSize: fl.MaxSize, + MaxAge: fl.MaxAge, + MaxBackups: fl.MaxBackups, + Compress: fl.Compress, + } +} + // InitDefaults fills zero-value fields with sensible defaults. func (fl *FileLoggerConfig) InitDefaults() *FileLoggerConfig { if fl.LogOutput == "" { @@ -70,6 +83,15 @@ type Config struct { // Level is the minimum enabled logging level. Level string `mapstructure:"level"` + // Format is a custom format string with %placeholder% tokens (e.g. + // "%time% [%level%] %message% %attrs%"). When set, it overrides Mode for + // handler selection. + Format string `mapstructure:"format"` + + // TimeFormat is a Go time layout used for the %time% placeholder in a + // custom format string. Defaults to [time.RFC3339]. + TimeFormat string `mapstructure:"time_format"` + // LineEnding for log entries. Default: "\n". LineEnding string `mapstructure:"line_ending"` @@ -120,18 +142,29 @@ func (cfg *Config) BuildLogger() (*BuildResult, error) { return nil, errors.E(op, err) } - // File Logger: use lumberjack writer with JSON handler. - if cfg.FileLogger != nil { - cfg.FileLogger.InitDefaults() - - lj := &lumberjack.Logger{ - Filename: cfg.FileLogger.LogOutput, - MaxSize: cfg.FileLogger.MaxSize, - MaxAge: cfg.FileLogger.MaxAge, - MaxBackups: cfg.FileLogger.MaxBackups, - Compress: cfg.FileLogger.Compress, + // Custom format: build a FormatHandler instead of mode-based handlers. + if cfg.Format != "" { + targetWriter := w + if cfg.FileLogger != nil { + lj := cfg.FileLogger.NewLumberjack() + closers = append(closers, lj) + targetWriter = lj } + return &BuildResult{ + Logger: slog.New(NewFormatHandler(targetWriter, &FormatHandlerOptions{ + Level: level, + Format: cfg.Format, + TimeLayout: cfg.TimeFormat, + LineEnding: new(cfg.lineEnding()), + })), + Closers: closers, + }, nil + } + + // File Logger: use lumberjack writer with JSON handler. + if cfg.FileLogger != nil { + lj := cfg.FileLogger.NewLumberjack() closers = append(closers, lj) return &BuildResult{ @@ -210,6 +243,18 @@ func (cfg *Config) resolveOutputWriter() (io.Writer, []io.Closer, error) { return io.MultiWriter(writers...), closers, nil } +// lineEnding returns the effective line ending for log entries, respecting the +// SkipLineEnding and LineEnding configuration fields. +func (cfg *Config) lineEnding() string { + if cfg.SkipLineEnding { + return "" + } + if cfg.LineEnding != "" { + return cfg.LineEnding + } + return "\n" +} + // parseLevel converts a level string to the corresponding [slog.Level]. func parseLevel(s string) slog.Level { switch strings.ToLower(strings.TrimSpace(s)) { diff --git a/format_handler.go b/format_handler.go new file mode 100644 index 0000000..0375432 --- /dev/null +++ b/format_handler.go @@ -0,0 +1,237 @@ +package logger + +import ( + "context" + "io" + "log/slog" + "runtime" + "strconv" + "strings" + "sync" + "time" +) + +// FormatHandlerOptions configures a [FormatHandler]. +type FormatHandlerOptions struct { + // Level is the minimum enabled logging level. + Level slog.Leveler + // Format is the format string with %placeholder% tokens. + Format string + // TimeLayout is the Go time layout used for the %time% placeholder. + // Defaults to [time.RFC3339] when empty. + TimeLayout string + // LineEnding is appended after each formatted record. Defaults to "\n". + // Set to a non-nil pointer to an empty string to suppress the line ending. + LineEnding *string +} + +// FormatHandler is an [slog.Handler] that renders log records according to a +// user-defined format string containing %placeholder% tokens such as %time%, +// %level%, %message%, %attrs%, %logger%, %source_file%, %source_line%, and +// %source_func%. +// +// Unknown placeholders are left in the output verbatim. +type FormatHandler struct { + w io.Writer + level slog.Leveler + format string + timeLayout string + lineEnding string + preAttrs []slog.Attr + groups []string + grpPfx string // cached dot-joined prefix from groups ("g1.g2." or "") + mu *sync.Mutex + + // Optimization flags set once at construction to avoid scanning the format + // string on every Handle call. + needsSource bool + needsAttrs bool + needsLogger bool +} + +// NewFormatHandler returns a [FormatHandler] that writes to w using the given +// options. The format string is scanned once to determine which placeholders +// are present so that expensive operations (source lookup, attr rendering) can +// be skipped when not needed. +func NewFormatHandler(w io.Writer, opts *FormatHandlerOptions) *FormatHandler { + le := "\n" + if opts.LineEnding != nil { + le = *opts.LineEnding + } + + tl := time.RFC3339 + if opts.TimeLayout != "" { + tl = opts.TimeLayout + } + + f := opts.Format + + return &FormatHandler{ + w: w, + level: opts.Level, + format: f, + timeLayout: tl, + lineEnding: le, + mu: &sync.Mutex{}, + needsSource: strings.Contains(f, "%source_file%") || strings.Contains(f, "%source_line%") || strings.Contains(f, "%source_func%"), + needsAttrs: strings.Contains(f, "%attrs%"), + needsLogger: strings.Contains(f, "%logger%"), + } +} + +// Enabled reports whether the handler is enabled for the given level. +func (h *FormatHandler) Enabled(_ context.Context, level slog.Level) bool { + return level >= h.level.Level() +} + +// Handle formats and writes a single log record. +func (h *FormatHandler) Handle(_ context.Context, r slog.Record) error { + // Time + var timeStr string + if !r.Time.IsZero() { + timeStr = r.Time.Format(h.timeLayout) + } + + // Source (only resolved when needed). + var sourceFile, sourceLine, sourceFunc string + if h.needsSource && r.PC != 0 { + frame, _ := runtime.CallersFrames([]uintptr{r.PC}).Next() + sourceFile = frame.File + sourceLine = strconv.Itoa(frame.Line) + if idx := strings.LastIndex(frame.Function, "."); idx >= 0 { + sourceFunc = frame.Function[idx+1:] + } else { + sourceFunc = frame.Function + } + } + + // Attrs — collect pre-attached attrs and record attrs. + var loggerName string + var attrsStr string + + if h.needsAttrs || h.needsLogger { + var sb strings.Builder + first := true + + appendAttr := func(key string, v slog.Value) { + // Extract logger name and exclude from %attrs%. + if h.needsLogger && key == "logger" { + loggerName = v.String() + return + } + + if h.needsAttrs { + if !first { + sb.WriteByte(' ') + } + sb.WriteString(key) + sb.WriteByte('=') + sb.WriteString(formatAttrValue(v)) + first = false + } + } + + // Pre-attached attrs already have their group prefix baked in. + for _, a := range h.preAttrs { + a.Value = a.Value.Resolve() + if a.Equal(slog.Attr{}) { + continue + } + appendAttr(a.Key, a.Value) + } + + // Record attrs get the current full group prefix. + r.Attrs(func(a slog.Attr) bool { + a.Value = a.Value.Resolve() + if !a.Equal(slog.Attr{}) { + appendAttr(h.grpPfx+a.Key, a.Value) + } + return true + }) + + attrsStr = sb.String() + } + + line := strings.NewReplacer( + "%time%", timeStr, + "%level%", r.Level.String(), + "%message%", r.Message, + "%attrs%", attrsStr, + "%logger%", loggerName, + "%source_file%", sourceFile, + "%source_line%", sourceLine, + "%source_func%", sourceFunc, + ).Replace(h.format) + h.lineEnding + + h.mu.Lock() + defer h.mu.Unlock() + + _, err := io.WriteString(h.w, line) + return err +} + +// WithAttrs returns a new [FormatHandler] that includes the given attributes in +// every subsequent record. The current group prefix is baked into attr keys so +// that groups added later do not retroactively affect them. +func (h *FormatHandler) WithAttrs(attrs []slog.Attr) slog.Handler { + if len(attrs) == 0 { + return h + } + c := h.clone() + for _, a := range attrs { + c.preAttrs = append(c.preAttrs, slog.Attr{ + Key: h.grpPfx + a.Key, + Value: a.Value, + }) + } + return c +} + +// WithGroup returns a new [FormatHandler] that qualifies subsequent attributes +// with the given group name using dot-separated keys. +func (h *FormatHandler) WithGroup(name string) slog.Handler { + if name == "" { + return h + } + c := h.clone() + c.groups = append(c.groups, name) + c.grpPfx = strings.Join(c.groups, ".") + "." + return c +} + +// clone creates a shallow copy of the handler, sharing the mutex and writer but +// with independent slices for preAttrs and groups. +func (h *FormatHandler) clone() *FormatHandler { + return &FormatHandler{ + w: h.w, + level: h.level, + format: h.format, + timeLayout: h.timeLayout, + lineEnding: h.lineEnding, + preAttrs: append([]slog.Attr(nil), h.preAttrs...), + groups: append([]string(nil), h.groups...), + grpPfx: h.grpPfx, + mu: h.mu, // shared across clones + needsSource: h.needsSource, + needsAttrs: h.needsAttrs, + needsLogger: h.needsLogger, + } +} + +// formatAttrValue converts an [slog.Value] to its string representation. +// Group values are rendered as nested dot-separated key=value pairs. +func formatAttrValue(v slog.Value) string { + if v.Kind() == slog.KindGroup { + var sb strings.Builder + for i, a := range v.Group() { + if i > 0 { + sb.WriteByte(' ') + } + sb.WriteString(a.Key) + sb.WriteByte('=') + sb.WriteString(formatAttrValue(a.Value)) + } + return sb.String() + } + return v.String() +} diff --git a/format_handler_test.go b/format_handler_test.go new file mode 100644 index 0000000..88020e4 --- /dev/null +++ b/format_handler_test.go @@ -0,0 +1,552 @@ +package logger + +import ( + "bytes" + "context" + "log/slog" + "strings" + "sync" + "testing" + "time" +) + +func TestFormatHandler_BasicPlaceholders(t *testing.T) { + var buf bytes.Buffer + h := NewFormatHandler(&buf, &FormatHandlerOptions{ + Level: slog.LevelDebug, + Format: "%time% [%level%] %message%", + TimeLayout: "15:04:05", + }) + + ts := time.Date(2026, 3, 20, 12, 0, 0, 0, time.UTC) + r := slog.NewRecord(ts, slog.LevelInfo, "hello world", 0) + + err := h.Handle(t.Context(), r) + if err != nil { + t.Fatal(err) + } + + got := buf.String() + want := "12:00:00 [INFO] hello world\n" + if got != want { + t.Errorf("got %q, want %q", got, want) + } +} + +func TestFormatHandler_Attrs(t *testing.T) { + var buf bytes.Buffer + h := NewFormatHandler(&buf, &FormatHandlerOptions{ + Level: slog.LevelDebug, + Format: "%message% %attrs%", + }) + + r := slog.NewRecord(time.Time{}, slog.LevelInfo, "req", 0) + r.AddAttrs(slog.String("method", "GET"), slog.Int("status", 200)) + + err := h.Handle(t.Context(), r) + if err != nil { + t.Fatal(err) + } + + got := strings.TrimSpace(buf.String()) + want := "req method=GET status=200" + if got != want { + t.Errorf("got %q, want %q", got, want) + } +} + +func TestFormatHandler_WithAttrs(t *testing.T) { + var buf bytes.Buffer + h := NewFormatHandler(&buf, &FormatHandlerOptions{ + Level: slog.LevelDebug, + Format: "%message% %attrs%", + }) + + child := h.WithAttrs([]slog.Attr{slog.String("pid", "1234")}) + + r := slog.NewRecord(time.Time{}, slog.LevelInfo, "start", 0) + r.AddAttrs(slog.String("worker", "w1")) + + err := child.Handle(t.Context(), r) + if err != nil { + t.Fatal(err) + } + + got := strings.TrimSpace(buf.String()) + want := "start pid=1234 worker=w1" + if got != want { + t.Errorf("got %q, want %q", got, want) + } + + // Verify parent handler does not have child's pre-attached attrs. + buf.Reset() + r2 := slog.NewRecord(time.Time{}, slog.LevelInfo, "parent", 0) + err = h.Handle(t.Context(), r2) + if err != nil { + t.Fatal(err) + } + + got = strings.TrimSpace(buf.String()) + want = "parent" + if got != want { + t.Errorf("parent got %q, want %q", got, want) + } +} + +func TestFormatHandler_WithAttrsEmpty(t *testing.T) { + var buf bytes.Buffer + h := NewFormatHandler(&buf, &FormatHandlerOptions{ + Level: slog.LevelDebug, + Format: "%message%", + }) + + // WithAttrs with empty slice should return the same handler. + child := h.WithAttrs(nil) + if child != h { + t.Error("WithAttrs(nil) should return the same handler") + } +} + +func TestFormatHandler_WithGroup(t *testing.T) { + var buf bytes.Buffer + h := NewFormatHandler(&buf, &FormatHandlerOptions{ + Level: slog.LevelDebug, + Format: "%message% %attrs%", + }) + + child := h.WithGroup("http") + + r := slog.NewRecord(time.Time{}, slog.LevelInfo, "req", 0) + r.AddAttrs(slog.String("method", "POST")) + + err := child.Handle(t.Context(), r) + if err != nil { + t.Fatal(err) + } + + got := strings.TrimSpace(buf.String()) + want := "req http.method=POST" + if got != want { + t.Errorf("got %q, want %q", got, want) + } +} + +func TestFormatHandler_WithGroupEmpty(t *testing.T) { + var buf bytes.Buffer + h := NewFormatHandler(&buf, &FormatHandlerOptions{ + Level: slog.LevelDebug, + Format: "%message%", + }) + + child := h.WithGroup("") + if child != h { + t.Error("WithGroup(\"\") should return the same handler") + } +} + +func TestFormatHandler_NestedGroups(t *testing.T) { + var buf bytes.Buffer + h := NewFormatHandler(&buf, &FormatHandlerOptions{ + Level: slog.LevelDebug, + Format: "%message% %attrs%", + }) + + child := h.WithGroup("http").WithGroup("request") + + r := slog.NewRecord(time.Time{}, slog.LevelInfo, "req", 0) + r.AddAttrs(slog.String("method", "GET")) + + err := child.Handle(t.Context(), r) + if err != nil { + t.Fatal(err) + } + + got := strings.TrimSpace(buf.String()) + want := "req http.request.method=GET" + if got != want { + t.Errorf("got %q, want %q", got, want) + } +} + +func TestFormatHandler_SourcePlaceholders(t *testing.T) { + var buf bytes.Buffer + h := NewFormatHandler(&buf, &FormatHandlerOptions{ + Level: slog.LevelDebug, + Format: "%source_file%:%source_line% %source_func% %message%", + }) + + // Use slog.Logger so PC is populated. + l := slog.New(h) + l.Info("test source") + + got := buf.String() + if !strings.Contains(got, "format_handler_test.go") { + t.Errorf("expected source file, got %q", got) + } + if !strings.Contains(got, "test source") { + t.Errorf("expected message, got %q", got) + } + // The function name should be present. + if !strings.Contains(got, "TestFormatHandler_SourcePlaceholders") { + t.Errorf("expected function name, got %q", got) + } +} + +func TestFormatHandler_LoggerPlaceholder(t *testing.T) { + var buf bytes.Buffer + h := NewFormatHandler(&buf, &FormatHandlerOptions{ + Level: slog.LevelDebug, + Format: "[%logger%] %message% %attrs%", + }) + + // Simulate NamedLogger attaching the "logger" attr. + l := slog.New(h).With("logger", "http") + l.Info("request", "status", 200) + + got := strings.TrimSpace(buf.String()) + // %logger% should be "http", and "logger" should NOT appear in %attrs%. + if !strings.HasPrefix(got, "[http] request") { + t.Errorf("expected logger prefix, got %q", got) + } + if strings.Contains(got, "logger=http") { + t.Error("logger attr should be excluded from %attrs%") + } + if !strings.Contains(got, "status=200") { + t.Errorf("expected status attr, got %q", got) + } +} + +func TestFormatHandler_CustomTimeFormat(t *testing.T) { + var buf bytes.Buffer + h := NewFormatHandler(&buf, &FormatHandlerOptions{ + Level: slog.LevelDebug, + Format: "%time% %message%", + TimeLayout: "2006-01-02", + }) + + ts := time.Date(2026, 3, 20, 12, 0, 0, 0, time.UTC) + r := slog.NewRecord(ts, slog.LevelInfo, "test", 0) + + err := h.Handle(t.Context(), r) + if err != nil { + t.Fatal(err) + } + + got := strings.TrimSpace(buf.String()) + want := "2026-03-20 test" + if got != want { + t.Errorf("got %q, want %q", got, want) + } +} + +func TestFormatHandler_DefaultTimeLayout(t *testing.T) { + var buf bytes.Buffer + h := NewFormatHandler(&buf, &FormatHandlerOptions{ + Level: slog.LevelDebug, + Format: "%time%", + }) + + ts := time.Date(2026, 3, 20, 12, 0, 0, 0, time.UTC) + r := slog.NewRecord(ts, slog.LevelInfo, "", 0) + + err := h.Handle(t.Context(), r) + if err != nil { + t.Fatal(err) + } + + got := strings.TrimSpace(buf.String()) + want := ts.Format(time.RFC3339) + if got != want { + t.Errorf("got %q, want %q", got, want) + } +} + +func TestFormatHandler_CustomLineEnding(t *testing.T) { + var buf bytes.Buffer + h := NewFormatHandler(&buf, &FormatHandlerOptions{ + Level: slog.LevelDebug, + Format: "%message%", + LineEnding: new("\r\n"), + }) + + r := slog.NewRecord(time.Time{}, slog.LevelInfo, "test", 0) + err := h.Handle(t.Context(), r) + if err != nil { + t.Fatal(err) + } + + got := buf.String() + want := "test\r\n" + if got != want { + t.Errorf("got %q, want %q", got, want) + } +} + +func TestFormatHandler_SkipLineEnding(t *testing.T) { + // Build via Config with SkipLineEnding to test the end-to-end path + // from config through to handler output. + var buf bytes.Buffer + cfg := &Config{ + Format: "%message%", + SkipLineEnding: true, + Level: "debug", + } + cfg.InitDefault() + + h := NewFormatHandler(&buf, &FormatHandlerOptions{ + Level: slog.LevelDebug, + Format: cfg.Format, + LineEnding: new(cfg.lineEnding()), + }) + + r := slog.NewRecord(time.Time{}, slog.LevelInfo, "test", 0) + err := h.Handle(t.Context(), r) + if err != nil { + t.Fatal(err) + } + + got := buf.String() + want := "test" + if got != want { + t.Errorf("got %q, want %q (skip_line_ending should produce no trailing newline)", got, want) + } +} + +func TestFormatHandler_LevelFiltering(t *testing.T) { + var buf bytes.Buffer + h := NewFormatHandler(&buf, &FormatHandlerOptions{ + Level: slog.LevelWarn, + Format: "%message%", + }) + + if h.Enabled(t.Context(), slog.LevelDebug) { + t.Error("debug should not be enabled at warn level") + } + if h.Enabled(t.Context(), slog.LevelInfo) { + t.Error("info should not be enabled at warn level") + } + if !h.Enabled(t.Context(), slog.LevelWarn) { + t.Error("warn should be enabled at warn level") + } + if !h.Enabled(t.Context(), slog.LevelError) { + t.Error("error should be enabled at warn level") + } +} + +func TestFormatHandler_ConcurrentWrites(t *testing.T) { + var buf bytes.Buffer + h := NewFormatHandler(&buf, &FormatHandlerOptions{ + Level: slog.LevelDebug, + Format: "%message% %attrs%", + }) + + const n = 100 + var wg sync.WaitGroup + + for i := range n { + wg.Go(func() { + r := slog.NewRecord(time.Time{}, slog.LevelInfo, "msg", 0) + r.AddAttrs(slog.Int("i", i)) + _ = h.Handle(t.Context(), r) + }) + } + + wg.Wait() + + lines := strings.Split(strings.TrimSpace(buf.String()), "\n") + if len(lines) != n { + t.Errorf("expected %d lines, got %d", n, len(lines)) + } +} + +func TestFormatHandler_EmptyFormat(t *testing.T) { + var buf bytes.Buffer + h := NewFormatHandler(&buf, &FormatHandlerOptions{ + Level: slog.LevelDebug, + Format: "", + }) + + r := slog.NewRecord(time.Time{}, slog.LevelInfo, "test", 0) + err := h.Handle(t.Context(), r) + if err != nil { + t.Fatal(err) + } + + // Empty format produces just a line ending. + got := buf.String() + if got != "\n" { + t.Errorf("got %q, want %q", got, "\n") + } +} + +func TestFormatHandler_NoPlaceholders(t *testing.T) { + var buf bytes.Buffer + h := NewFormatHandler(&buf, &FormatHandlerOptions{ + Level: slog.LevelDebug, + Format: "static text", + }) + + r := slog.NewRecord(time.Time{}, slog.LevelInfo, "ignored", 0) + err := h.Handle(t.Context(), r) + if err != nil { + t.Fatal(err) + } + + got := strings.TrimSpace(buf.String()) + if got != "static text" { + t.Errorf("got %q, want %q", got, "static text") + } +} + +func TestFormatHandler_UnknownPlaceholders(t *testing.T) { + var buf bytes.Buffer + h := NewFormatHandler(&buf, &FormatHandlerOptions{ + Level: slog.LevelDebug, + Format: "%message% %unknown%", + }) + + r := slog.NewRecord(time.Time{}, slog.LevelInfo, "test", 0) + err := h.Handle(t.Context(), r) + if err != nil { + t.Fatal(err) + } + + got := strings.TrimSpace(buf.String()) + want := "test %unknown%" + if got != want { + t.Errorf("got %q, want %q", got, want) + } +} + +func TestFormatHandler_ZeroTime(t *testing.T) { + var buf bytes.Buffer + h := NewFormatHandler(&buf, &FormatHandlerOptions{ + Level: slog.LevelDebug, + Format: "%time% %message%", + }) + + r := slog.NewRecord(time.Time{}, slog.LevelInfo, "test", 0) + err := h.Handle(t.Context(), r) + if err != nil { + t.Fatal(err) + } + + got := strings.TrimSpace(buf.String()) + want := "test" + if got != want { + t.Errorf("got %q, want %q (zero time should produce empty string)", got, want) + } +} + +func TestFormatHandler_ConfigLineEnding(t *testing.T) { + cfg := &Config{ + Format: "%message%", + LineEnding: "\r\n", + Level: "info", + } + cfg.InitDefault() + + res, err := cfg.BuildLogger() + if err != nil { + t.Fatal(err) + } + + // The logger should be usable. + res.Logger.Info("test") + + for _, c := range res.Closers { + _ = c.Close() + } +} + +func TestFormatHandler_ConfigSkipLineEnding(t *testing.T) { + cfg := &Config{ + Format: "%message%", + SkipLineEnding: true, + Level: "info", + } + cfg.InitDefault() + + le := cfg.lineEnding() + if le != "" { + t.Errorf("expected empty line ending with skip, got %q", le) + } +} + +func TestFormatHandler_ConfigFormatOverridesMode(t *testing.T) { + cfg := &Config{ + Mode: production, + Format: "%level% %message%", + Level: "info", + } + cfg.InitDefault() + + res, err := cfg.BuildLogger() + if err != nil { + t.Fatal(err) + } + + // Verify the handler is a FormatHandler, not JSON. + _, ok := res.Logger.Handler().(*FormatHandler) + if !ok { + t.Errorf("expected *FormatHandler, got %T", res.Logger.Handler()) + } +} + +func TestFormatHandler_AllPlaceholders(t *testing.T) { + var buf bytes.Buffer + h := NewFormatHandler(&buf, &FormatHandlerOptions{ + Level: slog.LevelDebug, + Format: "%time% %level% %message% %logger% %attrs% %source_file% %source_line% %source_func%", + TimeLayout: "15:04", + }) + + l := slog.New(h).With("logger", "test-ch") + l.Info("hello", "key", "val") + + got := buf.String() + if !strings.Contains(got, "INFO") { + t.Errorf("expected level, got %q", got) + } + if !strings.Contains(got, "hello") { + t.Errorf("expected message, got %q", got) + } + if !strings.Contains(got, "test-ch") { + t.Errorf("expected logger name, got %q", got) + } + if !strings.Contains(got, "key=val") { + t.Errorf("expected attrs, got %q", got) + } + if !strings.Contains(got, "format_handler_test.go") { + t.Errorf("expected source file, got %q", got) + } +} + +// Verify that the handler satisfies the slog.Handler interface. +var _ slog.Handler = (*FormatHandler)(nil) + +func TestFormatHandler_GroupWithPreAttrs(t *testing.T) { + var buf bytes.Buffer + h := NewFormatHandler(&buf, &FormatHandlerOptions{ + Level: slog.LevelDebug, + Format: "%message% %attrs%", + }) + + // Pre-attach an attr, then add a group. + child := h.WithAttrs([]slog.Attr{slog.String("pid", "1")}).WithGroup("http") + + r := slog.NewRecord(time.Time{}, slog.LevelInfo, "req", 0) + r.AddAttrs(slog.String("method", "GET")) + + err := child.Handle(context.Background(), r) + if err != nil { + t.Fatal(err) + } + + got := strings.TrimSpace(buf.String()) + // pid is pre-group so gets prefixed by the group; method is in the group. + want := "req pid=1 http.method=GET" + if got != want { + t.Errorf("got %q, want %q", got, want) + } +} diff --git a/schema.json b/schema.json index 6e86758..43cc48d 100644 --- a/schema.json +++ b/schema.json @@ -25,6 +25,12 @@ "err_output": { "$ref": "#/$defs/LogOutput" }, + "format": { + "$ref": "#/$defs/LogFormat" + }, + "time_format": { + "$ref": "#/$defs/LogTimeFormat" + }, "file_logger_options": { "$ref": "#/$defs/FileLoggerOptions" }, @@ -58,6 +64,12 @@ "err_output": { "$ref": "#/$defs/LogOutput" }, + "format": { + "$ref": "#/$defs/LogFormat" + }, + "time_format": { + "$ref": "#/$defs/LogTimeFormat" + }, "file_logger_options": { "$ref": "#/$defs/FileLoggerOptions" } @@ -148,6 +160,18 @@ "description": "Line-ending to use for logging.", "type": "string", "default": "\n" + }, + "LogFormat": { + "description": "Custom log format with %placeholder% tokens (e.g. \"%time% [%level%] %message% %attrs%\"). Overrides mode when set. Supported: %time%, %level%, %message%, %attrs%, %logger%, %source_file%, %source_line%, %source_func%.", + "type": "string" + }, + "LogTimeFormat": { + "description": "Go time layout for the %time% placeholder in a custom format string. Defaults to RFC3339.", + "type": "string", + "examples": [ + "2006-01-02 15:04:05", + "15:04:05" + ] } } } diff --git a/tests/configs/.rr-custom-format.yaml b/tests/configs/.rr-custom-format.yaml new file mode 100644 index 0000000..afac439 --- /dev/null +++ b/tests/configs/.rr-custom-format.yaml @@ -0,0 +1,6 @@ +version: '3' + +logs: + format: "%time% [%level%] %message% %attrs%" + time_format: "2006-01-02 15:04:05" + level: info diff --git a/tests/logger_test.go b/tests/logger_test.go index 5194abb..01ac2e1 100644 --- a/tests/logger_test.go +++ b/tests/logger_test.go @@ -318,6 +318,57 @@ func TestFileLogger(t *testing.T) { wg.Wait() } +func TestLoggerCustomFormat(t *testing.T) { + container := endure.New(slog.LevelDebug) + + vp := &config.Plugin{} + vp.Path = "configs/.rr-custom-format.yaml" + + err := container.RegisterAll( + vp, + &TestPlugin{}, + &logger.Plugin{}, + ) + assert.NoError(t, err) + + err = container.Init() + if err != nil { + t.Fatal(err) + } + + errCh, err := container.Serve() + if err != nil { + t.Fatal(err) + } + + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt) + + stopCh := make(chan struct{}, 1) + + var wg sync.WaitGroup + wg.Go(func() { + for { + select { + case e := <-errCh: + assert.NoError(t, e.Error) + assert.NoError(t, container.Stop()) + return + case <-c: + err = container.Stop() + assert.NoError(t, err) + return + case <-stopCh: + assert.NoError(t, container.Stop()) + return + } + } + }) + + stopCh <- struct{}{} + wg.Wait() +} + func httpEcho(t *testing.T) { req, err := http.NewRequestWithContext(t.Context(), http.MethodGet, "http://127.0.0.1:54224?hello=world", nil) assert.NoError(t, err) diff --git a/tests/php_test_files/composer.json b/tests/php_test_files/composer.json index 0f29363..3685613 100644 --- a/tests/php_test_files/composer.json +++ b/tests/php_test_files/composer.json @@ -3,7 +3,7 @@ "prefer-stable": true, "require": { "ext-curl": "*", - "guzzlehttp/guzzle": "^6.5", + "guzzlehttp/guzzle": "^7.0", "nyholm/psr7": "^1.5", "spiral/roadrunner-http": "^3.0", "spiral/roadrunner-worker": "^3.0", @@ -13,15 +13,14 @@ "spiral/roadrunner-tcp": "^4.0", "spiral/roadrunner-jobs": "^4.0", "monolog/monolog": "^3.1", - "php-http/guzzle6-adapter": "~2", "spiral/goridge": "^4.0", "roadrunner-php/centrifugo": "^2.0", "roadrunner-php/app-logger": "^1.0", "open-telemetry/sdk": "^0.0.10", "psr/http-factory": "^1.0", - "php-http/message-factory": "^1.1", "spiral/roadrunner-kv": "^4.0", - "spiral/roadrunner-services": "^2.0" + "spiral/roadrunner-services": "^2.0", + "php-http/guzzle7-adapter": "^1.1" }, "name": "test/test", "description": "test", @@ -30,4 +29,4 @@ "php-http/discovery": true } } -} +} \ No newline at end of file From 3b17eaaa21855f0001e7e7b6f15dc1d56678f338 Mon Sep 17 00:00:00 2001 From: Valery Piashchynski Date: Fri, 20 Mar 2026 14:04:43 +0100 Subject: [PATCH 4/4] chore: much simpler approach w/o filelogger Signed-off-by: Valery Piashchynski --- config.go | 82 +----------------------------- doc.go | 2 +- go.mod | 1 - go.sum | 2 - logger.go | 35 ++++++++++++- plugin.go | 10 +++- schema.json | 40 --------------- tests/configs/.rr-file-logger.yaml | 5 +- tests/go.mod | 1 - tests/go.sum | 8 ++- tests/logger_test.go | 21 ++++---- 11 files changed, 61 insertions(+), 146 deletions(-) diff --git a/config.go b/config.go index 43b15ea..acd547b 100644 --- a/config.go +++ b/config.go @@ -7,7 +7,6 @@ import ( "strings" "github.com/roadrunner-server/errors" - "gopkg.in/natefinch/lumberjack.v2" ) // ChannelConfig configures loggers per channel. @@ -16,64 +15,6 @@ type ChannelConfig struct { Channels map[string]*Config `mapstructure:"channels"` } -// FileLoggerConfig represents configuration for the file logger backed by -// lumberjack for log rotation. -type FileLoggerConfig struct { - // LogOutput is the file to write logs to. Uses -lumberjack.log - // in os.TempDir() if empty. - LogOutput string `mapstructure:"log_output"` - - // MaxSize is the maximum size in megabytes of the log file before it gets - // rotated. It defaults to 100 megabytes. - MaxSize int `mapstructure:"max_size"` - - // MaxAge is the maximum number of days to retain old log files based on the - // timestamp encoded in their filename. Note that a day is defined as 24 - // hours and may not exactly correspond to calendar days due to daylight - // savings, leap seconds, etc. The default is not to remove old log files - // based on age. - MaxAge int `mapstructure:"max_age"` - - // MaxBackups is the maximum number of old log files to retain. The default - // is to retain all old log files (though MaxAge may still cause them to get - // deleted). - MaxBackups int `mapstructure:"max_backups"` - - // Compress determines if the rotated log files should be compressed using - // gzip. The default is not to perform compression. - Compress bool `mapstructure:"compress"` -} - -// NewLumberjack creates a configured [lumberjack.Logger] from the file logger -// settings, calling [InitDefaults] first to fill zero-value fields. -func (fl *FileLoggerConfig) NewLumberjack() *lumberjack.Logger { - fl.InitDefaults() - return &lumberjack.Logger{ - Filename: fl.LogOutput, - MaxSize: fl.MaxSize, - MaxAge: fl.MaxAge, - MaxBackups: fl.MaxBackups, - Compress: fl.Compress, - } -} - -// InitDefaults fills zero-value fields with sensible defaults. -func (fl *FileLoggerConfig) InitDefaults() *FileLoggerConfig { - if fl.LogOutput == "" { - fl.LogOutput = os.TempDir() - } - if fl.MaxSize == 0 { - fl.MaxSize = 100 - } - if fl.MaxAge == 0 { - fl.MaxAge = 24 - } - if fl.MaxBackups == 0 { - fl.MaxBackups = 10 - } - return fl -} - // Config holds the logger configuration for a single channel. type Config struct { // Mode configures logger based on some default template (development, @@ -109,9 +50,6 @@ type Config struct { // ErrorOutput is a list of URLs to write internal logger errors to. The // default is standard error. ErrorOutput []string `mapstructure:"err_output"` - - // FileLogger options for lumberjack-based file rotation. - FileLogger *FileLoggerConfig `mapstructure:"file_logger_options"` } // BuildResult holds the logger and any resources that need cleanup. @@ -144,15 +82,8 @@ func (cfg *Config) BuildLogger() (*BuildResult, error) { // Custom format: build a FormatHandler instead of mode-based handlers. if cfg.Format != "" { - targetWriter := w - if cfg.FileLogger != nil { - lj := cfg.FileLogger.NewLumberjack() - closers = append(closers, lj) - targetWriter = lj - } - return &BuildResult{ - Logger: slog.New(NewFormatHandler(targetWriter, &FormatHandlerOptions{ + Logger: slog.New(NewFormatHandler(w, &FormatHandlerOptions{ Level: level, Format: cfg.Format, TimeLayout: cfg.TimeFormat, @@ -162,17 +93,6 @@ func (cfg *Config) BuildLogger() (*BuildResult, error) { }, nil } - // File Logger: use lumberjack writer with JSON handler. - if cfg.FileLogger != nil { - lj := cfg.FileLogger.NewLumberjack() - closers = append(closers, lj) - - return &BuildResult{ - Logger: slog.New(slog.NewJSONHandler(lj, &slog.HandlerOptions{Level: level})), - Closers: closers, - }, nil - } - var handler slog.Handler switch mode { case production: diff --git a/doc.go b/doc.go index 7d6bfd6..3089ae0 100644 --- a/doc.go +++ b/doc.go @@ -1,5 +1,5 @@ // Package logger provides the RoadRunner logger plugin backed by [log/slog]. // // It supports multiple output modes (development, production, raw, off/none), -// per-plugin channel overrides, and file-based log rotation via lumberjack. +// per-plugin channel overrides, and file-based output. package logger diff --git a/go.mod b/go.mod index a0a12b6..f028b29 100644 --- a/go.mod +++ b/go.mod @@ -7,5 +7,4 @@ toolchain go1.26.0 require ( github.com/roadrunner-server/endure/v2 v2.6.2 github.com/roadrunner-server/errors v1.4.1 - gopkg.in/natefinch/lumberjack.v2 v2.2.1 ) diff --git a/go.sum b/go.sum index 0fbcbcc..ef55a93 100644 --- a/go.sum +++ b/go.sum @@ -2,5 +2,3 @@ github.com/roadrunner-server/endure/v2 v2.6.2 h1:sIB4kTyE7gtT3fDhuYWUYn6Vt/dcPti github.com/roadrunner-server/endure/v2 v2.6.2/go.mod h1:t/2+xpNYgGBwhzn83y2MDhvhZ19UVq1REcvqn7j7RB8= github.com/roadrunner-server/errors v1.4.1 h1:LKNeaCGiwd3t8IaL840ZNF3UA9yDQlpvHnKddnh0YRQ= github.com/roadrunner-server/errors v1.4.1/go.mod h1:qeffnIKG0e4j1dzGpa+OGY5VKSfMphizvqWIw8s2lAo= -gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= -gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= diff --git a/logger.go b/logger.go index cdd8483..9f0bd95 100644 --- a/logger.go +++ b/logger.go @@ -1,12 +1,20 @@ package logger -import "log/slog" +import ( + "errors" + "io" + "log/slog" + "sync" +) // Log implements the [Logger] interface, dispatching named loggers to // per-channel configurations when available, falling back to the base logger. type Log struct { base *slog.Logger channels ChannelConfig + + mu sync.Mutex + closers []io.Closer } // NewLogger creates a new [Log] with the given channel overrides and base @@ -25,9 +33,32 @@ func (l *Log) NamedLogger(name string) *slog.Logger { if cfg, ok := l.channels.Channels[name]; ok { res, err := cfg.BuildLogger() if err != nil { - panic(err) + l.base.Error("failed to build channel logger, falling back to base", "channel", name, "error", err) + return l.base.With("logger", name) } + + l.mu.Lock() + l.closers = append(l.closers, res.Closers...) + l.mu.Unlock() + return res.Logger.With("logger", name) } return l.base.With("logger", name) } + +// Close releases all resources (file handles, file writers) opened by +// channel-specific loggers created through [NamedLogger]. +func (l *Log) Close() error { + l.mu.Lock() + defer l.mu.Unlock() + + var errs []error + for _, c := range l.closers { + if err := c.Close(); err != nil { + errs = append(errs, err) + } + } + l.closers = nil + + return errors.Join(errs...) +} diff --git a/plugin.go b/plugin.go index 57ed6cf..8b59008 100644 --- a/plugin.go +++ b/plugin.go @@ -31,6 +31,7 @@ type Plugin struct { cfg *Config channels ChannelConfig closers []io.Closer + logs []*Log } // Init logger service. @@ -83,8 +84,11 @@ func (p *Plugin) Serve() chan error { } // Stop gracefully shuts down the plugin, closing any file handles opened for -// log output. +// log output — both root-level and per-channel closers. func (p *Plugin) Stop(context.Context) error { + for _, l := range p.logs { + _ = l.Close() + } for _, c := range p.closers { _ = c.Close() } @@ -100,7 +104,9 @@ func (p *Plugin) Provides() []*dep.Out { // ServiceLogger returns a logger dedicated to the specific channel. func (p *Plugin) ServiceLogger() *Log { - return NewLogger(p.channels, p.base) + l := NewLogger(p.channels, p.base) + p.logs = append(p.logs, l) + return l } // Name returns a user-friendly plugin name. diff --git a/schema.json b/schema.json index 43cc48d..d6a6fe9 100644 --- a/schema.json +++ b/schema.json @@ -31,9 +31,6 @@ "time_format": { "$ref": "#/$defs/LogTimeFormat" }, - "file_logger_options": { - "$ref": "#/$defs/FileLoggerOptions" - }, "channels": { "description": "You can configure logging for each plugin individually. The key is the plugin name and the value is logging options in same format as the parent.", "type": "object", @@ -70,49 +67,12 @@ "time_format": { "$ref": "#/$defs/LogTimeFormat" }, - "file_logger_options": { - "$ref": "#/$defs/FileLoggerOptions" - } } } } } }, "$defs": { - "FileLoggerOptions": { - "description": "File logger options.", - "type": "object", - "additionalProperties": false, - "properties": { - "log_output": { - "type": "string", - "description": "Path to the log file. Uses -lumberjack.log and the OS temp (i.e. `/tmp`) directory if empty." - }, - "max_size": { - "type": "integer", - "description": "Maximum file size in MB.", - "minimum": 0, - "default": 100 - }, - "max_age": { - "type": "integer", - "description": "The maximum number of days to retain old log files based on the timestamp encoded in their filename. Empty or zero defaults to 24 days.", - "minimum": 0, - "default": 24 - }, - "max_backups": { - "type": "integer", - "description": "The maximum number of old log files to retain. Empty or zero defaults to 10.", - "minimum": 0, - "default": 10 - }, - "compress": { - "type": "boolean", - "description": "Whether to compress log files.", - "default": false - } - } - }, "LogMode": { "description": "Logging mode", "type": "string", diff --git a/tests/configs/.rr-file-logger.yaml b/tests/configs/.rr-file-logger.yaml index d7fb8a5..17ad12d 100644 --- a/tests/configs/.rr-file-logger.yaml +++ b/tests/configs/.rr-file-logger.yaml @@ -19,7 +19,6 @@ http: destroy_timeout: 60s logs: - mode: development + mode: production level: debug - file_logger_options: - log_output: "test.log" + output: ["test.log"] diff --git a/tests/go.mod b/tests/go.mod index d601996..0a8dd4e 100644 --- a/tests/go.mod +++ b/tests/go.mod @@ -87,6 +87,5 @@ require ( golang.org/x/tools v0.43.0 // indirect google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 // indirect google.golang.org/protobuf v1.36.11 // indirect - gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/tests/go.sum b/tests/go.sum index f714362..0586edc 100644 --- a/tests/go.sum +++ b/tests/go.sum @@ -1,4 +1,5 @@ code.pfad.fr/check v1.1.0 h1:GWvjdzhSEgHvEHe2uJujDcpmZoySKuHQNrZMfzfO0bE= +code.pfad.fr/check v1.1.0/go.mod h1:NiUH13DtYsb7xp5wll0U4SXx7KhXQVCtRgdC96IPfoM= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/caddyserver/certmagic v0.25.2 h1:D7xcS7ggX/WEY54x0czj7ioTkmDWKIgxtIi2OcQclUc= @@ -18,6 +19,7 @@ github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7z github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs= +github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -43,7 +45,9 @@ github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3x github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/letsencrypt/challtestsrv v1.4.2 h1:0ON3ldMhZyWlfVNYYpFuWRTmZNnyfiL9Hh5YzC3JVwU= +github.com/letsencrypt/challtestsrv v1.4.2/go.mod h1:GhqMqcSoeGpYd5zX5TgwA6er/1MbWzx/o7yuuVya+Wk= github.com/letsencrypt/pebble/v2 v2.10.0 h1:Wq6gYXlsY6ubqI3hhxsTzdyotvfdjFBxuwYqCLCnj/U= +github.com/letsencrypt/pebble/v2 v2.10.0/go.mod h1:Sk8cmUIPcIdv2nINo+9PB4L+ZBhzY+F9A1a/h/xmWiQ= github.com/libdns/libdns v1.1.1 h1:wPrHrXILoSHKWJKGd0EiAVmiJbFShguILTg9leS/P/U= github.com/libdns/libdns v1.1.1/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= @@ -143,7 +147,9 @@ go.opentelemetry.io/otel v1.42.0/go.mod h1:lJNsdRMxCUIWuMlVJWzecSMuNjE7dOYyWlqOX go.opentelemetry.io/otel/metric v1.42.0 h1:2jXG+3oZLNXEPfNmnpxKDeZsFI5o4J+nz6xUlaFdF/4= go.opentelemetry.io/otel/metric v1.42.0/go.mod h1:RlUN/7vTU7Ao/diDkEpQpnz3/92J9ko05BIwxYa2SSI= go.opentelemetry.io/otel/sdk v1.42.0 h1:LyC8+jqk6UJwdrI/8VydAq/hvkFKNHZVIWuslJXYsDo= +go.opentelemetry.io/otel/sdk v1.42.0/go.mod h1:rGHCAxd9DAph0joO4W6OPwxjNTYWghRWmkHuGbayMts= go.opentelemetry.io/otel/sdk/metric v1.42.0 h1:D/1QR46Clz6ajyZ3G8SgNlTJKBdGp84q9RKCAZ3YGuA= +go.opentelemetry.io/otel/sdk/metric v1.42.0/go.mod h1:Ua6AAlDKdZ7tdvaQKfSmnFTdHx37+J4ba8MwVCYM5hc= go.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY= go.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -184,7 +190,5 @@ google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= -gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/tests/logger_test.go b/tests/logger_test.go index 01ac2e1..96c90bf 100644 --- a/tests/logger_test.go +++ b/tests/logger_test.go @@ -128,19 +128,18 @@ func TestLoggerRawErr(t *testing.T) { } }) - time.Sleep(time.Second) + assert.EventuallyWithT(t, func(ct *assert.CollectT) { + req, reqErr := http.NewRequestWithContext(t.Context(), http.MethodGet, "http://127.0.0.1:34999", nil) + assert.NoError(ct, reqErr) - req, err := http.NewRequestWithContext(t.Context(), http.MethodGet, "http://127.0.0.1:34999", nil) - assert.NoError(t, err) - - resp, err := http.DefaultClient.Do(req) - assert.NoError(t, err) - require.NotNil(t, resp) + resp, doErr := http.DefaultClient.Do(req) + assert.NoError(ct, doErr) - _, _ = io.Copy(io.Discard, resp.Body) - _ = resp.Body.Close() - - time.Sleep(time.Second) + if resp != nil { + _, _ = io.Copy(io.Discard, resp.Body) + _ = resp.Body.Close() + } + }, 10*time.Second, 100*time.Millisecond) stopCh <- struct{}{} wg.Wait()