Skip to content

Commit 7076ae0

Browse files
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 <[email protected]>
1 parent 2c57984 commit 7076ae0

File tree

4 files changed

+114
-30
lines changed

4 files changed

+114
-30
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,3 +47,4 @@ profile.cov
4747
/dashboard/assets/package-lock.json
4848

4949
**/yarn-error.log
50+
logs/

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ require (
6565
golang.org/x/text v0.8.0
6666
golang.org/x/time v0.0.0-20220922220347-f3bd1da661af
6767
golang.org/x/tools v0.7.0
68+
gopkg.in/natefinch/lumberjack.v2 v2.0.0
6869
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce
6970
)
7071

go.sum

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.3/go.mod h1:KLF4gFr6DcKFZwSu
77
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0 h1:Px2UA+2RvSSvv+RvJNuUB6n7rs5Wsel4dXLe90Um2n4=
88
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.3.0/go.mod h1:tPaiy8S5bQ+S5sOiDlINkp7+Ef339+Nz5L5XO+cnOHo=
99
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
10+
github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0=
1011
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=
1112
github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo=
1213
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
617618
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
618619
gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
619620
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
621+
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
622+
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
620623
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU=
621624
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
622625
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=

internal/debug/flags.go

Lines changed: 109 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ import (
2020
"fmt"
2121
"io"
2222
"net/http"
23-
_ "net/http/pprof" // nolint: gosec
23+
_ "net/http/pprof"
2424
"os"
25+
"path/filepath"
2526
"runtime"
2627

2728
"github.com/ethereum/go-ethereum/internal/flags"
@@ -32,6 +33,7 @@ import (
3233
"github.com/mattn/go-colorable"
3334
"github.com/mattn/go-isatty"
3435
"github.com/urfave/cli/v2"
36+
"gopkg.in/natefinch/lumberjack.v2"
3537
)
3638

3739
var Memsize memsizeui.Handler
@@ -76,6 +78,34 @@ var (
7678
Usage: "Prepends log messages with call-site location (file and line number)",
7779
Category: flags.LoggingCategory,
7880
}
81+
logRotateFlag = &cli.BoolFlag{
82+
Name: "log.rotate",
83+
Usage: "Enables log file rotation",
84+
}
85+
logMaxSizeMBsFlag = &cli.IntFlag{
86+
Name: "log.maxsize",
87+
Usage: "Maximum size in MBs of a single log file",
88+
Value: 100,
89+
Category: flags.LoggingCategory,
90+
}
91+
logMaxBackupsFlag = &cli.IntFlag{
92+
Name: "log.maxbackups",
93+
Usage: "Maximum number of log files to retain",
94+
Value: 10,
95+
Category: flags.LoggingCategory,
96+
}
97+
logMaxAgeFlag = &cli.IntFlag{
98+
Name: "log.maxage",
99+
Usage: "Maximum number of days to retain a log file",
100+
Value: 30,
101+
Category: flags.LoggingCategory,
102+
}
103+
logCompressFlag = &cli.BoolFlag{
104+
Name: "log.compress",
105+
Usage: "Compress the log files",
106+
Value: false,
107+
Category: flags.LoggingCategory,
108+
}
79109
pprofFlag = &cli.BoolFlag{
80110
Name: "pprof",
81111
Usage: "Enable the pprof HTTP server",
@@ -120,11 +150,16 @@ var (
120150
var Flags = []cli.Flag{
121151
verbosityFlag,
122152
vmoduleFlag,
153+
backtraceAtFlag,
154+
debugFlag,
123155
logjsonFlag,
124156
logFormatFlag,
125157
logFileFlag,
126-
backtraceAtFlag,
127-
debugFlag,
158+
logRotateFlag,
159+
logMaxSizeMBsFlag,
160+
logMaxBackupsFlag,
161+
logMaxAgeFlag,
162+
logCompressFlag,
128163
pprofFlag,
129164
pprofAddrFlag,
130165
pprofPortFlag,
@@ -148,44 +183,71 @@ func init() {
148183
// Setup initializes profiling and logging based on the CLI flags.
149184
// It should be called as early as possible in the program.
150185
func Setup(ctx *cli.Context) error {
151-
logFile := ctx.String(logFileFlag.Name)
152-
useColor := logFile == "" && os.Getenv("TERM") != "dumb" && (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd()))
153-
154-
var logfmt log.Format
155-
switch ctx.String(logFormatFlag.Name) {
156-
case "json":
186+
var (
187+
logfmt log.Format
188+
output = io.Writer(os.Stderr)
189+
logFmtFlag = ctx.String(logFormatFlag.Name)
190+
)
191+
switch {
192+
case ctx.Bool(logjsonFlag.Name):
193+
// Retain backwards compatibility with `--log.json` flag if `--log.format` not set
194+
defer log.Warn("The flag '--log.json' is deprecated, please use '--log.format=json' instead")
157195
logfmt = log.JSONFormat()
158-
case "logfmt":
196+
case logFmtFlag == "json":
197+
logfmt = log.JSONFormat()
198+
case logFmtFlag == "logfmt":
159199
logfmt = log.LogfmtFormat()
160-
case "terminal":
161-
logfmt = log.TerminalFormat(useColor)
162-
case "":
163-
// Retain backwards compatibility with `--log.json` flag if `--log.format` not set
164-
if ctx.Bool(logjsonFlag.Name) {
165-
defer log.Warn("The flag '--log.json' is deprecated, please use '--log.format=json' instead")
166-
logfmt = log.JSONFormat()
167-
} else {
168-
logfmt = log.TerminalFormat(useColor)
200+
case logFmtFlag == "", logFmtFlag == "terminal":
201+
useColor := (isatty.IsTerminal(os.Stderr.Fd()) || isatty.IsCygwinTerminal(os.Stderr.Fd())) && os.Getenv("TERM") != "dumb"
202+
if useColor {
203+
output = colorable.NewColorableStderr()
169204
}
205+
logfmt = log.TerminalFormat(useColor)
170206
default:
171207
// Unknown log format specified
172208
return fmt.Errorf("unknown log format: %v", ctx.String(logFormatFlag.Name))
173209
}
174-
175-
if logFile != "" {
176-
var err error
177-
logOutputStream, err = log.FileHandler(logFile, logfmt)
178-
if err != nil {
179-
return err
210+
var (
211+
stdHandler = log.StreamHandler(output, logfmt)
212+
ostream = stdHandler
213+
logFile = ctx.String(logFileFlag.Name)
214+
rotation = ctx.Bool(logRotateFlag.Name)
215+
)
216+
if len(logFile) > 0 {
217+
if err := validateLogLocation(filepath.Dir(logFile)); err != nil {
218+
return fmt.Errorf("failed to initiatilize file logger: %v", err)
180219
}
220+
}
221+
context := []interface{}{"rotate", rotation}
222+
if len(logFmtFlag) > 0 {
223+
context = append(context, "format", logFmtFlag)
181224
} else {
182-
output := io.Writer(os.Stderr)
183-
if useColor {
184-
output = colorable.NewColorableStderr()
225+
context = append(context, "format", "terminal")
226+
}
227+
if rotation {
228+
// Lumberjack uses <processname>-lumberjack.log in is.TempDir() if empty.
229+
// so typically /tmp/geth-lumberjack.log on linux
230+
if len(logFile) > 0 {
231+
context = append(context, "location", logFile)
232+
} else {
233+
context = append(context, "location", filepath.Join(os.TempDir(), "geth-lumberjack.log"))
234+
}
235+
ostream = log.MultiHandler(log.StreamHandler(&lumberjack.Logger{
236+
Filename: logFile,
237+
MaxSize: ctx.Int(logMaxSizeMBsFlag.Name),
238+
MaxBackups: ctx.Int(logMaxBackupsFlag.Name),
239+
MaxAge: ctx.Int(logMaxAgeFlag.Name),
240+
Compress: ctx.Bool(logCompressFlag.Name),
241+
}, logfmt), stdHandler)
242+
} else if logFile != "" {
243+
if logOutputStream, err := log.FileHandler(logFile, logfmt); err != nil {
244+
return err
245+
} else {
246+
ostream = log.MultiHandler(logOutputStream, stdHandler)
247+
context = append(context, "location", logFile)
185248
}
186-
logOutputStream = log.StreamHandler(output, logfmt)
187249
}
188-
glogger.SetHandler(logOutputStream)
250+
glogger.SetHandler(ostream)
189251

190252
// logging
191253
verbosity := ctx.Int(verbosityFlag.Name)
@@ -236,6 +298,9 @@ func Setup(ctx *cli.Context) error {
236298
// It cannot be imported because it will cause a cyclical dependency.
237299
StartPProf(address, !ctx.IsSet("metrics.addr"))
238300
}
301+
if len(logFile) > 0 || rotation {
302+
log.Info("Logging configured", context...)
303+
}
239304
return nil
240305
}
241306

@@ -263,3 +328,17 @@ func Exit() {
263328
closer.Close()
264329
}
265330
}
331+
332+
func validateLogLocation(path string) error {
333+
if err := os.MkdirAll(path, os.ModePerm); err != nil {
334+
return fmt.Errorf("error creating the directory: %w", err)
335+
}
336+
// Check if the path is writable by trying to create a temporary file
337+
tmp := filepath.Join(path, "tmp")
338+
if f, err := os.Create(tmp); err != nil {
339+
return err
340+
} else {
341+
f.Close()
342+
}
343+
return os.Remove(tmp)
344+
}

0 commit comments

Comments
 (0)