From 729628beefddf8afdde2aa6cfd39932ecb4d6616 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 6 Feb 2026 17:27:35 +0000 Subject: [PATCH 1/2] feat: add logger middleware config migrator for Output -> Stream rename Add v3 migration that renames the `Output` config field to `Stream` in logger.Config blocks, matching the Fiber v3 API change. https://claude.ai/code/session_01TAwsCfCgeoqqyf3gQuG2Xa --- cmd/internal/migrations/lists.go | 1 + cmd/internal/migrations/v3/logger_config.go | 30 +++++++ .../migrations/v3/logger_config_test.go | 86 +++++++++++++++++++ 3 files changed, 117 insertions(+) create mode 100644 cmd/internal/migrations/v3/logger_config.go create mode 100644 cmd/internal/migrations/v3/logger_config_test.go diff --git a/cmd/internal/migrations/lists.go b/cmd/internal/migrations/lists.go index 584fd9b..ef36344 100644 --- a/cmd/internal/migrations/lists.go +++ b/cmd/internal/migrations/lists.go @@ -45,6 +45,7 @@ var Migrations = []Migration{ v3migrations.MigrateMimeConstants, v3migrations.MigrateLoggerTags, v3migrations.MigrateLoggerGenerics, + v3migrations.MigrateLoggerConfig, v3migrations.MigrateStaticRoutes, v3migrations.MigrateTrustedProxyConfig, v3migrations.MigrateMount, diff --git a/cmd/internal/migrations/v3/logger_config.go b/cmd/internal/migrations/v3/logger_config.go new file mode 100644 index 0000000..344a82d --- /dev/null +++ b/cmd/internal/migrations/v3/logger_config.go @@ -0,0 +1,30 @@ +package v3 + +import ( + "fmt" + "regexp" + "strings" + + semver "github.com/Masterminds/semver/v3" + "github.com/spf13/cobra" + + "github.com/gofiber/cli/cmd/internal" +) + +func MigrateLoggerConfig(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { + changed, err := internal.ChangeFileContent(cwd, func(content string) string { + reConfig := regexp.MustCompile(`logger\.Config{`) + return IterateConfigBlocks(content, reConfig, func(s string) string { + return strings.ReplaceAll(s, "Output:", "Stream:") + }) + }) + if err != nil { + return fmt.Errorf("failed to migrate logger configs: %w", err) + } + if !changed { + return nil + } + + cmd.Println("Migrating logger middleware configs") + return nil +} diff --git a/cmd/internal/migrations/v3/logger_config_test.go b/cmd/internal/migrations/v3/logger_config_test.go new file mode 100644 index 0000000..8c9f055 --- /dev/null +++ b/cmd/internal/migrations/v3/logger_config_test.go @@ -0,0 +1,86 @@ +package v3_test + +import ( + "bytes" + "os" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/gofiber/cli/cmd/internal/migrations/v3" +) + +func Test_MigrateLoggerConfig(t *testing.T) { + t.Parallel() + + dir, err := os.MkdirTemp("", "mlogger") + require.NoError(t, err) + defer func() { require.NoError(t, os.RemoveAll(dir)) }() + + file := writeTempFile(t, dir, `package main +import ( + "os" + "github.com/gofiber/fiber/v2/middleware/logger" +) +var _ = logger.New(logger.Config{ + Output: os.Stdout, +})`) + + var buf bytes.Buffer + cmd := newCmd(&buf) + require.NoError(t, v3.MigrateLoggerConfig(cmd, dir, nil, nil)) + + content := readFile(t, file) + assert.Contains(t, content, "Stream:") + assert.NotContains(t, content, "Output:") + assert.Contains(t, buf.String(), "Migrating logger middleware configs") +} + +func Test_MigrateLoggerConfig_NoChange(t *testing.T) { + t.Parallel() + + dir, err := os.MkdirTemp("", "mlogger_noop") + require.NoError(t, err) + defer func() { require.NoError(t, os.RemoveAll(dir)) }() + + writeTempFile(t, dir, `package main +import "fmt" +func main() { fmt.Println("hello") } +`) + + var buf bytes.Buffer + cmd := newCmd(&buf) + require.NoError(t, v3.MigrateLoggerConfig(cmd, dir, nil, nil)) + + assert.Empty(t, buf.String()) +} + +func Test_MigrateLoggerConfig_MultipleFields(t *testing.T) { + t.Parallel() + + dir, err := os.MkdirTemp("", "mlogger_multi") + require.NoError(t, err) + defer func() { require.NoError(t, os.RemoveAll(dir)) }() + + file := writeTempFile(t, dir, `package main +import ( + "os" + "github.com/gofiber/fiber/v2/middleware/logger" +) +var _ = logger.New(logger.Config{ + Format: "[${time}] ${status}\n", + Output: os.Stderr, + TimeFormat: "2006-01-02", +})`) + + var buf bytes.Buffer + cmd := newCmd(&buf) + require.NoError(t, v3.MigrateLoggerConfig(cmd, dir, nil, nil)) + + content := readFile(t, file) + assert.Contains(t, content, "Stream:") + assert.NotContains(t, content, "Output:") + assert.Contains(t, content, "Format:") + assert.Contains(t, content, "TimeFormat:") +} From e218d5c738e28eefa1b30757f43ed65a2a5f0669 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 7 Feb 2026 18:32:14 +0000 Subject: [PATCH 2/2] fix: harden logger config migrator against edge cases - Use package-level compiled regexes instead of per-call compilation - Support aliased logger imports via collectAliases helper - Handle whitespace between Config and opening brace - Use positional regex to only rename Output field assignments, not Output: occurrences inside string literals (e.g. Format field) - Add tests for alias imports, string literal safety, whitespace, and single-line config blocks https://claude.ai/code/session_01TAwsCfCgeoqqyf3gQuG2Xa --- cmd/internal/migrations/v3/logger_config.go | 23 +++- .../migrations/v3/logger_config_test.go | 100 ++++++++++++++++++ 2 files changed, 118 insertions(+), 5 deletions(-) diff --git a/cmd/internal/migrations/v3/logger_config.go b/cmd/internal/migrations/v3/logger_config.go index 344a82d..b9509c9 100644 --- a/cmd/internal/migrations/v3/logger_config.go +++ b/cmd/internal/migrations/v3/logger_config.go @@ -3,7 +3,6 @@ package v3 import ( "fmt" "regexp" - "strings" semver "github.com/Masterminds/semver/v3" "github.com/spf13/cobra" @@ -11,12 +10,26 @@ import ( "github.com/gofiber/cli/cmd/internal" ) +var ( + reLoggerImport = regexp.MustCompile(`(?m)^\s*(?:import\s+)?(?:([\w.]+)\s+)?"github\.com/gofiber/fiber/(?:v2|v3)/middleware/logger"`) + reLoggerOutputField = regexp.MustCompile(`((?:^|[{\n])\s*)Output(\s*:)`) +) + func MigrateLoggerConfig(cmd *cobra.Command, cwd string, _, _ *semver.Version) error { changed, err := internal.ChangeFileContent(cwd, func(content string) string { - reConfig := regexp.MustCompile(`logger\.Config{`) - return IterateConfigBlocks(content, reConfig, func(s string) string { - return strings.ReplaceAll(s, "Output:", "Stream:") - }) + aliases := collectAliases(content, reLoggerImport, []string{"logger"}) + if len(aliases) == 0 { + return content + } + + for _, alias := range aliases { + reConfig := regexp.MustCompile(regexp.QuoteMeta(alias) + `\.Config\s*\{`) + content = IterateConfigBlocks(content, reConfig, func(s string) string { + return reLoggerOutputField.ReplaceAllString(s, "${1}Stream${2}") + }) + } + + return content }) if err != nil { return fmt.Errorf("failed to migrate logger configs: %w", err) diff --git a/cmd/internal/migrations/v3/logger_config_test.go b/cmd/internal/migrations/v3/logger_config_test.go index 8c9f055..dfedfb2 100644 --- a/cmd/internal/migrations/v3/logger_config_test.go +++ b/cmd/internal/migrations/v3/logger_config_test.go @@ -84,3 +84,103 @@ var _ = logger.New(logger.Config{ assert.Contains(t, content, "Format:") assert.Contains(t, content, "TimeFormat:") } + +func Test_MigrateLoggerConfig_OutputInStringLiteral(t *testing.T) { + t.Parallel() + + dir, err := os.MkdirTemp("", "mlogger_str") + require.NoError(t, err) + defer func() { require.NoError(t, os.RemoveAll(dir)) }() + + file := writeTempFile(t, dir, `package main +import ( + "os" + "github.com/gofiber/fiber/v2/middleware/logger" +) +var _ = logger.New(logger.Config{ + Format: "Output: ${status}\n", + Output: os.Stdout, +})`) + + var buf bytes.Buffer + cmd := newCmd(&buf) + require.NoError(t, v3.MigrateLoggerConfig(cmd, dir, nil, nil)) + + content := readFile(t, file) + assert.Contains(t, content, "Stream:") + assert.Contains(t, content, `"Output: ${status}\n"`, "string literal should not be modified") + assert.Contains(t, buf.String(), "Migrating logger middleware configs") +} + +func Test_MigrateLoggerConfig_WhitespaceBeforeBrace(t *testing.T) { + t.Parallel() + + dir, err := os.MkdirTemp("", "mlogger_ws") + require.NoError(t, err) + defer func() { require.NoError(t, os.RemoveAll(dir)) }() + + file := writeTempFile(t, dir, `package main +import ( + "os" + "github.com/gofiber/fiber/v2/middleware/logger" +) +var _ = logger.New(logger.Config { + Output: os.Stdout, +})`) + + var buf bytes.Buffer + cmd := newCmd(&buf) + require.NoError(t, v3.MigrateLoggerConfig(cmd, dir, nil, nil)) + + content := readFile(t, file) + assert.Contains(t, content, "Stream:") + assert.NotContains(t, content, "Output:") +} + +func Test_MigrateLoggerConfig_Alias(t *testing.T) { + t.Parallel() + + dir, err := os.MkdirTemp("", "mlogger_alias") + require.NoError(t, err) + defer func() { require.NoError(t, os.RemoveAll(dir)) }() + + file := writeTempFile(t, dir, `package main +import ( + "os" + fiberlog "github.com/gofiber/fiber/v2/middleware/logger" +) +var _ = fiberlog.New(fiberlog.Config{ + Output: os.Stdout, +})`) + + var buf bytes.Buffer + cmd := newCmd(&buf) + require.NoError(t, v3.MigrateLoggerConfig(cmd, dir, nil, nil)) + + content := readFile(t, file) + assert.Contains(t, content, "Stream:") + assert.NotContains(t, content, "Output:") +} + +func Test_MigrateLoggerConfig_SingleLine(t *testing.T) { + t.Parallel() + + dir, err := os.MkdirTemp("", "mlogger_single") + require.NoError(t, err) + defer func() { require.NoError(t, os.RemoveAll(dir)) }() + + file := writeTempFile(t, dir, `package main +import ( + "os" + "github.com/gofiber/fiber/v2/middleware/logger" +) +var _ = logger.New(logger.Config{Output: os.Stdout})`) + + var buf bytes.Buffer + cmd := newCmd(&buf) + require.NoError(t, v3.MigrateLoggerConfig(cmd, dir, nil, nil)) + + content := readFile(t, file) + assert.Contains(t, content, "Stream:") + assert.NotContains(t, content, "Output:") +}