@@ -7,13 +7,15 @@ import (
77 "strings"
88 "time"
99
10+ "github.com/hashicorp/go-multierror"
1011 "github.com/rs/zerolog"
1112)
1213
1314var _ Logger = (* defaultLogger )(nil )
1415
1516type defaultLogger struct {
1617 zerolog.Logger
18+ closeFuncs []func () error
1719}
1820
1921// NewDefaultLogger returns a default logger that can be used within Tendermint
@@ -25,29 +27,47 @@ type defaultLogger struct {
2527// that in a generic interface, all logging methods accept a series of key/value
2628// pair tuples, where the key must be a string.
2729func NewDefaultLogger (format , level string ) (Logger , error ) {
28- var logWriter io.Writer
29- switch strings .ToLower (format ) {
30- case LogFormatPlain , LogFormatText :
31- logWriter = zerolog.ConsoleWriter {
32- Out : os .Stderr ,
33- NoColor : true ,
34- TimeFormat : time .RFC3339Nano ,
35- FormatLevel : func (i interface {}) string {
36- if ll , ok := i .(string ); ok {
37- return strings .ToUpper (ll )
38- }
39- return "????"
40- },
41- }
42-
43- case LogFormatJSON :
44- logWriter = os .Stderr
30+ return NewMultiLogger (format , level , "" )
31+ }
4532
46- default :
47- return nil , fmt .Errorf ("unsupported log format: %s" , format )
33+ // NewMultiLogger creates a new logger that writes to os.Stderr and an additional log file if provided.
34+ // It takes in three parameters: format, level, and additionalLogPath.
35+ // The format parameter specifies the format of the log message.
36+ // The level parameter specifies the minimum log level to write.
37+ // The additionalLogPath parameter specifies the path to the additional log file.
38+ // If additionalLogPath is not empty, the logger writes to both os.Stderr and the additional log file.
39+ // The function returns a Logger interface and an error if any.
40+ //
41+ // See NewDefaultLogger for more details.
42+ func NewMultiLogger (format , level , additionalLogPath string ) (Logger , error ) {
43+ var (
44+ writer io.Writer = os .Stderr
45+ closeFunc = func () error { return nil }
46+ err error
47+ )
48+ if additionalLogPath != "" {
49+ file , err := os .OpenFile (additionalLogPath , os .O_WRONLY | os .O_CREATE | os .O_APPEND , 0644 )
50+ if err != nil {
51+ return nil , fmt .Errorf ("failed to create log writer: %w" , err )
52+ }
53+ closeFunc = func () error {
54+ return file .Close ()
55+ }
56+ writer = io .MultiWriter (writer , file )
57+ }
58+ writer , err = NewFormatter (format , writer )
59+ if err != nil {
60+ _ = closeFunc ()
61+ return nil , fmt .Errorf ("failed to create log formatter: %w" , err )
62+ }
63+ logger , err := NewLogger (level , writer )
64+ if err != nil {
65+ _ = closeFunc ()
66+ return nil , err
4867 }
68+ logger .(* defaultLogger ).closeFuncs = append (logger .(* defaultLogger ).closeFuncs , closeFunc )
4969
50- return NewLogger ( level , logWriter )
70+ return logger , nil
5171}
5272
5373func NewLogger (level string , logWriter io.Writer ) (Logger , error ) {
@@ -59,7 +79,9 @@ func NewLogger(level string, logWriter io.Writer) (Logger, error) {
5979 // make the writer thread-safe
6080 logWriter = newSyncWriter (logWriter )
6181
62- return & defaultLogger {Logger : zerolog .New (logWriter ).Level (logLevel ).With ().Timestamp ().Logger ()}, nil
82+ return & defaultLogger {
83+ Logger : zerolog .New (logWriter ).Level (logLevel ).With ().Timestamp ().Logger (),
84+ }, nil
6385}
6486
6587func (l defaultLogger ) Info (msg string , keyVals ... interface {}) {
@@ -81,26 +103,48 @@ func (l defaultLogger) Trace(msg string, keyVals ...interface{}) {
81103func (l defaultLogger ) With (keyVals ... interface {}) Logger {
82104 return & defaultLogger {Logger : l .Logger .With ().Fields (getLogFields (keyVals ... )).Logger ()}
83105}
106+ func (l * defaultLogger ) Close () (err error ) {
107+ if l == nil {
108+ return nil
109+ }
110+ l .Debug ("Closing logger" )
111+ for _ , f := range l .closeFuncs {
112+ if e := f (); e != nil {
113+ err = multierror .Append (err , e )
114+ }
115+ }
116+
117+ l .closeFuncs = nil
118+
119+ return err
120+ }
84121
85122// OverrideWithNewLogger replaces an existing logger's internal with
86123// a new logger, and makes it possible to reconfigure an existing
87124// logger that has already been propagated to callers.
88- func OverrideWithNewLogger (logger Logger , format , level string ) error {
125+ func OverrideWithNewLogger (logger Logger , format , level , additionalLogFilePath string ) error {
89126 ol , ok := logger .(* defaultLogger )
90127 if ! ok {
91128 return fmt .Errorf ("logger %T cannot be overridden" , logger )
92129 }
93130
94- newLogger , err := NewDefaultLogger (format , level )
131+ newLogger , err := NewMultiLogger (format , level , additionalLogFilePath )
95132 if err != nil {
96133 return err
97134 }
98135 nl , ok := newLogger .(* defaultLogger )
99136 if ! ok {
137+ newLogger .Close ()
100138 return fmt .Errorf ("logger %T cannot be overridden by %T" , logger , newLogger )
101139 }
102140
141+ if err := ol .Close (); err != nil {
142+ return err
143+ }
144+
103145 ol .Logger = nl .Logger
146+ ol .closeFuncs = nl .closeFuncs
147+
104148 return nil
105149}
106150
0 commit comments