From 5b4c149f97408ecefc7f440e86c12a30c4342620 Mon Sep 17 00:00:00 2001 From: Felix Lange Date: Fri, 11 Nov 2022 11:33:18 +0100 Subject: [PATCH] internal/debug: add --log.file option (#26149) This adds an option to direct log output to a file. This feature has been requested a lot. It's sometimes useful to have this available when running geth in an environment that doesn't easily allow redirecting the output. Notably, there is no support for log rotation with this change. The --log.file option opens the file once on startup and then keeps writing to the file handle. This can become an issue when external log rotation tools are involved, so it's best not to use them with this option for now. --- internal/debug/flags.go | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/internal/debug/flags.go b/internal/debug/flags.go index 2082d60df..e014a85d7 100644 --- a/internal/debug/flags.go +++ b/internal/debug/flags.go @@ -54,6 +54,11 @@ var ( Usage: "Format logs with JSON", Category: flags.LoggingCategory, } + logFileFlag = &cli.StringFlag{ + Name: "log.file", + Usage: "Write logs to a file", + Category: flags.LoggingCategory, + } backtraceAtFlag = &cli.StringFlag{ Name: "log.backtrace", Usage: "Request a stack trace at a specific logging statement (e.g. \"block.go:271\")", @@ -110,6 +115,7 @@ var Flags = []cli.Flag{ verbosityFlag, vmoduleFlag, logjsonFlag, + logFileFlag, backtraceAtFlag, debugFlag, pprofFlag, @@ -121,7 +127,10 @@ var Flags = []cli.Flag{ traceFlag, } -var glogger *log.GlogHandler +var ( + glogger *log.GlogHandler + logOutputStream log.Handler +) func init() { glogger = log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) @@ -132,18 +141,30 @@ func init() { // Setup initializes profiling and logging based on the CLI flags. // It should be called as early as possible in the program. func Setup(ctx *cli.Context) error { - var ostream log.Handler - output := io.Writer(os.Stderr) + logFile := ctx.String(logFileFlag.Name) + useColor := logFile == "" && os.Getenv("TERM") != "dumb" && (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) + + var logfmt log.Format if ctx.Bool(logjsonFlag.Name) { - ostream = log.StreamHandler(output, log.JSONFormat()) + logfmt = log.JSONFormat() } else { - usecolor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb" - if usecolor { + logfmt = log.TerminalFormat(useColor) + } + + if logFile != "" { + var err error + logOutputStream, err = log.FileHandler(logFile, logfmt) + if err != nil { + return err + } + } else { + output := io.Writer(os.Stderr) + if useColor { output = colorable.NewColorableStderr() } - ostream = log.StreamHandler(output, log.TerminalFormat(usecolor)) + logOutputStream = log.StreamHandler(output, logfmt) } - glogger.SetHandler(ostream) + glogger.SetHandler(logOutputStream) // logging verbosity := ctx.Int(verbosityFlag.Name) @@ -217,4 +238,7 @@ func StartPProf(address string, withMetrics bool) { func Exit() { Handler.StopCPUProfile() Handler.StopGoTrace() + if closer, ok := logOutputStream.(io.Closer); ok { + closer.Close() + } }