cmd/geth: enable log rotation (#26843)
This change enables log rotation, which can be activated using the flag --log.rotate. Additional parameters that can be given are: - log.maxsize to set maximum size before files are rotated, - log.maxbackups to set how many files are retailed, - log.maxage to configure max age of rotated files, - log.compress whether to compress rotated files The way to configure location of the logfile(s) is left unchanged, via the `log.logfile` parameter. --------- Co-authored-by: Martin Holst Swende <martin@swende.se>
This commit is contained in:
parent
2c5798464e
commit
7076ae00aa
1
.gitignore
vendored
1
.gitignore
vendored
@ -47,3 +47,4 @@ profile.cov
|
|||||||
/dashboard/assets/package-lock.json
|
/dashboard/assets/package-lock.json
|
||||||
|
|
||||||
**/yarn-error.log
|
**/yarn-error.log
|
||||||
|
logs/
|
1
go.mod
1
go.mod
@ -65,6 +65,7 @@ require (
|
|||||||
golang.org/x/text v0.8.0
|
golang.org/x/text v0.8.0
|
||||||
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af
|
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af
|
||||||
golang.org/x/tools v0.7.0
|
golang.org/x/tools v0.7.0
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce
|
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce
|
||||||
)
|
)
|
||||||
|
|
||||||
|
3
go.sum
3
go.sum
@ -7,6 +7,7 @@ github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3/go.mod h1:KLF4gFr6DcKFZwSu
|
|||||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0 h1:Px2UA+2RvSSvv+RvJNuUB6n7rs5Wsel4dXLe90Um2n4=
|
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0 h1:Px2UA+2RvSSvv+RvJNuUB6n7rs5Wsel4dXLe90Um2n4=
|
||||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0/go.mod h1:tPaiy8S5bQ+S5sOiDlINkp7+Ef339+Nz5L5XO+cnOHo=
|
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0/go.mod h1:tPaiy8S5bQ+S5sOiDlINkp7+Ef339+Nz5L5XO+cnOHo=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0=
|
||||||
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=
|
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=
|
||||||
github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo=
|
github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo=
|
||||||
github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8=
|
github.com/DataDog/zstd v1.5.2 h1:vUG4lAyuPCXO0TLbXvPv7EB7cNK1QV/luu55UHLrrn8=
|
||||||
@ -617,6 +618,8 @@ gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8
|
|||||||
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
|
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
|
||||||
gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
|
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||||
|
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU=
|
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU=
|
||||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
|
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
|
@ -20,8 +20,9 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
_ "net/http/pprof" // nolint: gosec
|
_ "net/http/pprof"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/internal/flags"
|
"github.com/ethereum/go-ethereum/internal/flags"
|
||||||
@ -32,6 +33,7 @@ import (
|
|||||||
"github.com/mattn/go-colorable"
|
"github.com/mattn/go-colorable"
|
||||||
"github.com/mattn/go-isatty"
|
"github.com/mattn/go-isatty"
|
||||||
"github.com/urfave/cli/v2"
|
"github.com/urfave/cli/v2"
|
||||||
|
"gopkg.in/natefinch/lumberjack.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Memsize memsizeui.Handler
|
var Memsize memsizeui.Handler
|
||||||
@ -76,6 +78,34 @@ var (
|
|||||||
Usage: "Prepends log messages with call-site location (file and line number)",
|
Usage: "Prepends log messages with call-site location (file and line number)",
|
||||||
Category: flags.LoggingCategory,
|
Category: flags.LoggingCategory,
|
||||||
}
|
}
|
||||||
|
logRotateFlag = &cli.BoolFlag{
|
||||||
|
Name: "log.rotate",
|
||||||
|
Usage: "Enables log file rotation",
|
||||||
|
}
|
||||||
|
logMaxSizeMBsFlag = &cli.IntFlag{
|
||||||
|
Name: "log.maxsize",
|
||||||
|
Usage: "Maximum size in MBs of a single log file",
|
||||||
|
Value: 100,
|
||||||
|
Category: flags.LoggingCategory,
|
||||||
|
}
|
||||||
|
logMaxBackupsFlag = &cli.IntFlag{
|
||||||
|
Name: "log.maxbackups",
|
||||||
|
Usage: "Maximum number of log files to retain",
|
||||||
|
Value: 10,
|
||||||
|
Category: flags.LoggingCategory,
|
||||||
|
}
|
||||||
|
logMaxAgeFlag = &cli.IntFlag{
|
||||||
|
Name: "log.maxage",
|
||||||
|
Usage: "Maximum number of days to retain a log file",
|
||||||
|
Value: 30,
|
||||||
|
Category: flags.LoggingCategory,
|
||||||
|
}
|
||||||
|
logCompressFlag = &cli.BoolFlag{
|
||||||
|
Name: "log.compress",
|
||||||
|
Usage: "Compress the log files",
|
||||||
|
Value: false,
|
||||||
|
Category: flags.LoggingCategory,
|
||||||
|
}
|
||||||
pprofFlag = &cli.BoolFlag{
|
pprofFlag = &cli.BoolFlag{
|
||||||
Name: "pprof",
|
Name: "pprof",
|
||||||
Usage: "Enable the pprof HTTP server",
|
Usage: "Enable the pprof HTTP server",
|
||||||
@ -120,11 +150,16 @@ var (
|
|||||||
var Flags = []cli.Flag{
|
var Flags = []cli.Flag{
|
||||||
verbosityFlag,
|
verbosityFlag,
|
||||||
vmoduleFlag,
|
vmoduleFlag,
|
||||||
|
backtraceAtFlag,
|
||||||
|
debugFlag,
|
||||||
logjsonFlag,
|
logjsonFlag,
|
||||||
logFormatFlag,
|
logFormatFlag,
|
||||||
logFileFlag,
|
logFileFlag,
|
||||||
backtraceAtFlag,
|
logRotateFlag,
|
||||||
debugFlag,
|
logMaxSizeMBsFlag,
|
||||||
|
logMaxBackupsFlag,
|
||||||
|
logMaxAgeFlag,
|
||||||
|
logCompressFlag,
|
||||||
pprofFlag,
|
pprofFlag,
|
||||||
pprofAddrFlag,
|
pprofAddrFlag,
|
||||||
pprofPortFlag,
|
pprofPortFlag,
|
||||||
@ -148,44 +183,71 @@ 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 {
|
||||||
logFile := ctx.String(logFileFlag.Name)
|
var (
|
||||||
useColor := logFile == "" && os.Getenv("TERM") != "dumb" && (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd()))
|
logfmt log.Format
|
||||||
|
output = io.Writer(os.Stderr)
|
||||||
var logfmt log.Format
|
logFmtFlag = ctx.String(logFormatFlag.Name)
|
||||||
switch ctx.String(logFormatFlag.Name) {
|
)
|
||||||
case "json":
|
switch {
|
||||||
logfmt = log.JSONFormat()
|
case ctx.Bool(logjsonFlag.Name):
|
||||||
case "logfmt":
|
|
||||||
logfmt = log.LogfmtFormat()
|
|
||||||
case "terminal":
|
|
||||||
logfmt = log.TerminalFormat(useColor)
|
|
||||||
case "":
|
|
||||||
// Retain backwards compatibility with `--log.json` flag if `--log.format` not set
|
// Retain backwards compatibility with `--log.json` flag if `--log.format` not set
|
||||||
if ctx.Bool(logjsonFlag.Name) {
|
|
||||||
defer log.Warn("The flag '--log.json' is deprecated, please use '--log.format=json' instead")
|
defer log.Warn("The flag '--log.json' is deprecated, please use '--log.format=json' instead")
|
||||||
logfmt = log.JSONFormat()
|
logfmt = log.JSONFormat()
|
||||||
} else {
|
case logFmtFlag == "json":
|
||||||
logfmt = log.TerminalFormat(useColor)
|
logfmt = log.JSONFormat()
|
||||||
|
case logFmtFlag == "logfmt":
|
||||||
|
logfmt = log.LogfmtFormat()
|
||||||
|
case logFmtFlag == "", logFmtFlag == "terminal":
|
||||||
|
useColor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb"
|
||||||
|
if useColor {
|
||||||
|
output = colorable.NewColorableStderr()
|
||||||
}
|
}
|
||||||
|
logfmt = log.TerminalFormat(useColor)
|
||||||
default:
|
default:
|
||||||
// Unknown log format specified
|
// Unknown log format specified
|
||||||
return fmt.Errorf("unknown log format: %v", ctx.String(logFormatFlag.Name))
|
return fmt.Errorf("unknown log format: %v", ctx.String(logFormatFlag.Name))
|
||||||
}
|
}
|
||||||
|
var (
|
||||||
if logFile != "" {
|
stdHandler = log.StreamHandler(output, logfmt)
|
||||||
var err error
|
ostream = stdHandler
|
||||||
logOutputStream, err = log.FileHandler(logFile, logfmt)
|
logFile = ctx.String(logFileFlag.Name)
|
||||||
if err != nil {
|
rotation = ctx.Bool(logRotateFlag.Name)
|
||||||
return err
|
)
|
||||||
|
if len(logFile) > 0 {
|
||||||
|
if err := validateLogLocation(filepath.Dir(logFile)); err != nil {
|
||||||
|
return fmt.Errorf("failed to initiatilize file logger: %v", err)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
context := []interface{}{"rotate", rotation}
|
||||||
|
if len(logFmtFlag) > 0 {
|
||||||
|
context = append(context, "format", logFmtFlag)
|
||||||
} else {
|
} else {
|
||||||
output := io.Writer(os.Stderr)
|
context = append(context, "format", "terminal")
|
||||||
if useColor {
|
|
||||||
output = colorable.NewColorableStderr()
|
|
||||||
}
|
}
|
||||||
logOutputStream = log.StreamHandler(output, logfmt)
|
if rotation {
|
||||||
|
// Lumberjack uses <processname>-lumberjack.log in is.TempDir() if empty.
|
||||||
|
// so typically /tmp/geth-lumberjack.log on linux
|
||||||
|
if len(logFile) > 0 {
|
||||||
|
context = append(context, "location", logFile)
|
||||||
|
} else {
|
||||||
|
context = append(context, "location", filepath.Join(os.TempDir(), "geth-lumberjack.log"))
|
||||||
}
|
}
|
||||||
glogger.SetHandler(logOutputStream)
|
ostream = log.MultiHandler(log.StreamHandler(&lumberjack.Logger{
|
||||||
|
Filename: logFile,
|
||||||
|
MaxSize: ctx.Int(logMaxSizeMBsFlag.Name),
|
||||||
|
MaxBackups: ctx.Int(logMaxBackupsFlag.Name),
|
||||||
|
MaxAge: ctx.Int(logMaxAgeFlag.Name),
|
||||||
|
Compress: ctx.Bool(logCompressFlag.Name),
|
||||||
|
}, logfmt), stdHandler)
|
||||||
|
} else if logFile != "" {
|
||||||
|
if logOutputStream, err := log.FileHandler(logFile, logfmt); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
ostream = log.MultiHandler(logOutputStream, stdHandler)
|
||||||
|
context = append(context, "location", logFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
glogger.SetHandler(ostream)
|
||||||
|
|
||||||
// logging
|
// logging
|
||||||
verbosity := ctx.Int(verbosityFlag.Name)
|
verbosity := ctx.Int(verbosityFlag.Name)
|
||||||
@ -236,6 +298,9 @@ func Setup(ctx *cli.Context) error {
|
|||||||
// It cannot be imported because it will cause a cyclical dependency.
|
// It cannot be imported because it will cause a cyclical dependency.
|
||||||
StartPProf(address, !ctx.IsSet("metrics.addr"))
|
StartPProf(address, !ctx.IsSet("metrics.addr"))
|
||||||
}
|
}
|
||||||
|
if len(logFile) > 0 || rotation {
|
||||||
|
log.Info("Logging configured", context...)
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -263,3 +328,17 @@ func Exit() {
|
|||||||
closer.Close()
|
closer.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateLogLocation(path string) error {
|
||||||
|
if err := os.MkdirAll(path, os.ModePerm); err != nil {
|
||||||
|
return fmt.Errorf("error creating the directory: %w", err)
|
||||||
|
}
|
||||||
|
// Check if the path is writable by trying to create a temporary file
|
||||||
|
tmp := filepath.Join(path, "tmp")
|
||||||
|
if f, err := os.Create(tmp); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
f.Close()
|
||||||
|
}
|
||||||
|
return os.Remove(tmp)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user