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.
This commit is contained in:
Felix Lange 2022-11-11 11:33:18 +01:00 committed by GitHub
parent 0ad2014026
commit 5b4c149f97
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -54,6 +54,11 @@ var (
Usage: "Format logs with JSON", Usage: "Format logs with JSON",
Category: flags.LoggingCategory, Category: flags.LoggingCategory,
} }
logFileFlag = &cli.StringFlag{
Name: "log.file",
Usage: "Write logs to a file",
Category: flags.LoggingCategory,
}
backtraceAtFlag = &cli.StringFlag{ backtraceAtFlag = &cli.StringFlag{
Name: "log.backtrace", Name: "log.backtrace",
Usage: "Request a stack trace at a specific logging statement (e.g. \"block.go:271\")", Usage: "Request a stack trace at a specific logging statement (e.g. \"block.go:271\")",
@ -110,6 +115,7 @@ var Flags = []cli.Flag{
verbosityFlag, verbosityFlag,
vmoduleFlag, vmoduleFlag,
logjsonFlag, logjsonFlag,
logFileFlag,
backtraceAtFlag, backtraceAtFlag,
debugFlag, debugFlag,
pprofFlag, pprofFlag,
@ -121,7 +127,10 @@ var Flags = []cli.Flag{
traceFlag, traceFlag,
} }
var glogger *log.GlogHandler var (
glogger *log.GlogHandler
logOutputStream log.Handler
)
func init() { func init() {
glogger = log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false))) 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. // Setup initializes profiling and logging based on the CLI flags.
// It should be called as early as possible in the program. // It should be called as early as possible in the program.
func Setup(ctx *cli.Context) error { func Setup(ctx *cli.Context) error {
var ostream log.Handler logFile := ctx.String(logFileFlag.Name)
output := io.Writer(os.Stderr) 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) { if ctx.Bool(logjsonFlag.Name) {
ostream = log.StreamHandler(output, log.JSONFormat()) logfmt = log.JSONFormat()
} else { } else {
usecolor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb" logfmt = log.TerminalFormat(useColor)
if 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() output = colorable.NewColorableStderr()
} }
ostream = log.StreamHandler(output, log.TerminalFormat(usecolor)) logOutputStream = log.StreamHandler(output, logfmt)
} }
glogger.SetHandler(ostream) glogger.SetHandler(logOutputStream)
// logging // logging
verbosity := ctx.Int(verbosityFlag.Name) verbosity := ctx.Int(verbosityFlag.Name)
@ -217,4 +238,7 @@ func StartPProf(address string, withMetrics bool) {
func Exit() { func Exit() {
Handler.StopCPUProfile() Handler.StopCPUProfile()
Handler.StopGoTrace() Handler.StopGoTrace()
if closer, ok := logOutputStream.(io.Closer); ok {
closer.Close()
}
} }