From f049668c210d23e8177b2bb9f0ac98163cbb42ab Mon Sep 17 00:00:00 2001 From: ThomasL Date: Sat, 2 May 2026 17:45:31 +0200 Subject: [PATCH] fix: redirect early CLI console logger to stderr (#37507) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When running `gitea dump` with output routed to stdout (--file -), deprecation warnings from loadAvatarsFrom were written to stdout, corrupting the archive stream. Root cause: PrepareConsoleLoggerLevel (called in app.Before) sets up a console logger via SetConsoleLogger, which used WriterConsoleOption{} defaulting Stderr to false (i.e. stdout). This logger is installed before the dump subcommand can redirect logging to stderr in runDump. Fix: use WriterConsoleOption{Stderr: true} in SetConsoleLogger so all early CLI diagnostic output goes to stderr from the start. This is correct for all subcommands — diagnostic/log output should never pollute stdout. --------- Signed-off-by: wxiaoguang Co-authored-by: Claude Sonnet 4.6 Co-authored-by: wxiaoguang Co-authored-by: Nicolas --- cmd/helper.go | 2 +- modules/log/logger_global.go | 21 ++++++++++++++++----- modules/setting/log.go | 2 +- modules/setting/setting.go | 4 ++-- 4 files changed, 20 insertions(+), 9 deletions(-) diff --git a/cmd/helper.go b/cmd/helper.go index 9d70b057015..ca4cddb49d7 100644 --- a/cmd/helper.go +++ b/cmd/helper.go @@ -134,7 +134,7 @@ func PrepareConsoleLoggerLevel(defaultLevel log.Level) func(context.Context, *cl if globalBool(c, "debug") || globalBool(c, "verbose") { level = log.TRACE } - log.SetConsoleLogger(log.DEFAULT, "console-default", level) + log.SetupStderrLogger(log.DEFAULT, "console-stderr", level) return ctx, nil } } diff --git a/modules/log/logger_global.go b/modules/log/logger_global.go index 2bc8c4f4497..6dacf583a7f 100644 --- a/modules/log/logger_global.go +++ b/modules/log/logger_global.go @@ -75,12 +75,23 @@ func IsLoggerEnabled(name string) bool { return GetManager().GetLogger(name).IsEnabled() } -func SetConsoleLogger(loggerName, writerName string, level Level) { +func SetupStderrLogger(loggerName, writerName string, level Level) { writer := NewEventWriterConsole(writerName, WriterMode{ - Level: level, - Flags: FlagsFromBits(LstdFlags), - Colorize: CanColorStdout, - WriterOption: WriterConsoleOption{}, + Level: level, + Flags: FlagsFromBits(LstdFlags), + Colorize: CanColorStderr, + + // For most CLI commands, it's better to use Stderr as log output: + // this logger is installed early (app.Before), before subcommands like "dump" redirect logging to stderr. + // If Stdout, early log output (e.g.: warning during config loading) goes to stdout + // and corrupts any command that writes data to stdout (e.g. "gitea dump --file -"). + // + // It is inconsistent with the web server's default console logger from config + // (which will be initialized later and use Stdout by default), but there is no other way at the moment: + // many existing users depend on such behavior to collect web logs (e.g. fail2ban). + // + // Maybe need to refactor the logger system again in the future. + WriterOption: WriterConsoleOption{Stderr: true}, }) GetManager().GetLogger(loggerName).ReplaceAllWriters(writer) } diff --git a/modules/setting/log.go b/modules/setting/log.go index 59866c76055..764c2a82f4f 100644 --- a/modules/setting/log.go +++ b/modules/setting/log.go @@ -256,7 +256,7 @@ func initLoggerByName(manager *log.LoggerManager, rootCfg ConfigProvider, logger } func InitSQLLoggersForCli(level log.Level) { - log.SetConsoleLogger("xorm", "console", level) + log.SetupStderrLogger("xorm", "console-stderr", level) } func IsAccessLogEnabled() bool { diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 3c1ad144282..5bfe68697af 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -41,9 +41,9 @@ func init() { AppVer = "dev" } - // We can rely on log.CanColorStdout being set properly because modules/log/console_windows.go comes before modules/setting/setting.go lexicographically + // FIXME: the logger shouldn't be initialized here, the app entry should initialize the logger // By default set this logger at Info - we'll change it later, but we need to start with something. - log.SetConsoleLogger(log.DEFAULT, "console", log.INFO) + log.SetupStderrLogger(log.DEFAULT, "console-stderr", log.INFO) } // IsRunUserMatchCurrentUser returns false if configured run user does not match