You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
153 lines
4.5 KiB
153 lines
4.5 KiB
package build |
|
|
|
import ( |
|
"fmt" |
|
"io" |
|
"os" |
|
"path/filepath" |
|
"sort" |
|
|
|
"github.com/btcsuite/btclog" |
|
"github.com/jrick/logrotate/rotator" |
|
) |
|
|
|
// RotatingLogWriter is a wrapper around the LogWriter that supports log file |
|
// rotation. |
|
type RotatingLogWriter struct { |
|
logWriter *LogWriter |
|
|
|
backendLog *btclog.Backend |
|
|
|
logRotator *rotator.Rotator |
|
|
|
subsystemLoggers SubLoggers |
|
} |
|
|
|
// A compile time check to ensure RotatingLogWriter implements the |
|
// LeveledSubLogger interface. |
|
var _ LeveledSubLogger = (*RotatingLogWriter)(nil) |
|
|
|
// NewRotatingLogWriter creates a new file rotating log writer. |
|
// |
|
// NOTE: `InitLogRotator` must be called to set up log rotation after creating |
|
// the writer. |
|
func NewRotatingLogWriter() *RotatingLogWriter { |
|
logWriter := &LogWriter{} |
|
backendLog := btclog.NewBackend(logWriter) |
|
return &RotatingLogWriter{ |
|
logWriter: logWriter, |
|
backendLog: backendLog, |
|
subsystemLoggers: SubLoggers{}, |
|
} |
|
} |
|
|
|
// GenSubLogger creates a new sublogger. A shutdown callback function |
|
// is provided to be able to shutdown in case of a critical error. |
|
func (r *RotatingLogWriter) GenSubLogger(tag string, shutdown func()) btclog.Logger { |
|
logger := r.backendLog.Logger(tag) |
|
return NewShutdownLogger(logger, shutdown) |
|
} |
|
|
|
// RegisterSubLogger registers a new subsystem logger. |
|
func (r *RotatingLogWriter) RegisterSubLogger(subsystem string, |
|
logger btclog.Logger) { |
|
|
|
r.subsystemLoggers[subsystem] = logger |
|
} |
|
|
|
// InitLogRotator initializes the log file rotator to write logs to logFile and |
|
// create roll files in the same directory. It should be called as early on |
|
// startup and possible and must be closed on shutdown by calling `Close`. |
|
func (r *RotatingLogWriter) InitLogRotator(logFile string, maxLogFileSize int, |
|
maxLogFiles int) error { |
|
|
|
logDir, _ := filepath.Split(logFile) |
|
err := os.MkdirAll(logDir, 0700) |
|
if err != nil { |
|
return fmt.Errorf("failed to create log directory: %v", err) |
|
} |
|
r.logRotator, err = rotator.New( |
|
logFile, int64(maxLogFileSize*1024), false, maxLogFiles, |
|
) |
|
if err != nil { |
|
return fmt.Errorf("failed to create file rotator: %v", err) |
|
} |
|
|
|
// Run rotator as a goroutine now but make sure we catch any errors |
|
// that happen in case something with the rotation goes wrong during |
|
// runtime (like running out of disk space or not being allowed to |
|
// create a new logfile for whatever reason). |
|
pr, pw := io.Pipe() |
|
go func() { |
|
err := r.logRotator.Run(pr) |
|
if err != nil { |
|
_, _ = fmt.Fprintf(os.Stderr, |
|
"failed to run file rotator: %v\n", err) |
|
} |
|
}() |
|
|
|
r.logWriter.RotatorPipe = pw |
|
return nil |
|
} |
|
|
|
// Close closes the underlying log rotator if it has already been created. |
|
func (r *RotatingLogWriter) Close() error { |
|
if r.logRotator != nil { |
|
return r.logRotator.Close() |
|
} |
|
return nil |
|
} |
|
|
|
// SubLoggers returns all currently registered subsystem loggers for this log |
|
// writer. |
|
// |
|
// NOTE: This is part of the LeveledSubLogger interface. |
|
func (r *RotatingLogWriter) SubLoggers() SubLoggers { |
|
return r.subsystemLoggers |
|
} |
|
|
|
// SupportedSubsystems returns a sorted string slice of all keys in the |
|
// subsystems map, corresponding to the names of the subsystems. |
|
// |
|
// NOTE: This is part of the LeveledSubLogger interface. |
|
func (r *RotatingLogWriter) SupportedSubsystems() []string { |
|
// Convert the subsystemLoggers map keys to a string slice. |
|
subsystems := make([]string, 0, len(r.subsystemLoggers)) |
|
for subsysID := range r.subsystemLoggers { |
|
subsystems = append(subsystems, subsysID) |
|
} |
|
|
|
// Sort the subsystems for stable display. |
|
sort.Strings(subsystems) |
|
return subsystems |
|
} |
|
|
|
// SetLogLevel sets the logging level for provided subsystem. Invalid |
|
// subsystems are ignored. Uninitialized subsystems are dynamically created as |
|
// needed. |
|
// |
|
// NOTE: This is part of the LeveledSubLogger interface. |
|
func (r *RotatingLogWriter) SetLogLevel(subsystemID string, logLevel string) { |
|
// Ignore invalid subsystems. |
|
logger, ok := r.subsystemLoggers[subsystemID] |
|
if !ok { |
|
return |
|
} |
|
|
|
// Defaults to info if the log level is invalid. |
|
level, _ := btclog.LevelFromString(logLevel) |
|
logger.SetLevel(level) |
|
} |
|
|
|
// SetLogLevels sets the log level for all subsystem loggers to the passed |
|
// level. It also dynamically creates the subsystem loggers as needed, so it |
|
// can be used to initialize the logging system. |
|
// |
|
// NOTE: This is part of the LeveledSubLogger interface. |
|
func (r *RotatingLogWriter) SetLogLevels(logLevel string) { |
|
// Configure all sub-systems with the new logging level. Dynamically |
|
// create loggers as needed. |
|
for subsystemID := range r.subsystemLoggers { |
|
r.SetLogLevel(subsystemID, logLevel) |
|
} |
|
}
|
|
|