mirror of
https://github.com/rclone/rclone.git
synced 2026-05-12 10:03:35 -04:00
log: fix side effects when importing rclone as a library
Avoid side effects by using own logger instance - Importing fs/log only sets rclone's private logger via fs.SetLogger, so internal rclone logging works from the moment the package is imported but the process-wide slog default is left untouched. - slog.SetDefault and slog.SetLogLoggerLevel move into InitLogging, which is called explicitly from the CLI (cmd/cmd.go), the librclone wrapper and the integration test framework. So rclone-as-a-program keeps capturing log.Print/log.Fatal and slog.Default() output as before. Library consumers that import fs/log without calling InitLogging now keep their own slog default and can safely route rclone output back into it via log.Handler.SetOutput without recursing. Fixes #8907 Co-authored-by: Nick Craig-Wood <nick@craig-wood.com>
This commit is contained in:
11
fs/log.go
11
fs/log.go
@@ -12,6 +12,10 @@ import (
|
||||
"github.com/rclone/rclone/lib/caller"
|
||||
)
|
||||
|
||||
// logger represents the slog logging facility and should be overridden by
|
||||
// the fs/log handling code.
|
||||
var logger *slog.Logger = slog.Default()
|
||||
|
||||
// LogLevel describes rclone's logs. These are a subset of the syslog log levels.
|
||||
type LogLevel = Enum[logLevelChoices]
|
||||
|
||||
@@ -137,7 +141,7 @@ func LogLevelToSlog(level LogLevel) slog.Level {
|
||||
}
|
||||
|
||||
func logSlog(level LogLevel, text string, attrs []any) {
|
||||
slog.Log(context.Background(), LogLevelToSlog(level), text, attrs...)
|
||||
logger.Log(context.Background(), LogLevelToSlog(level), text, attrs...)
|
||||
}
|
||||
|
||||
func logSlogWithObject(level LogLevel, o any, text string, attrs []any) {
|
||||
@@ -337,3 +341,8 @@ func PrettyPrint(in any, label string, level LogLevel) {
|
||||
}
|
||||
LogPrintf(level, label, "\n%s\n", string(inBytes))
|
||||
}
|
||||
|
||||
// SetLogger overrides the slog logger using the specified handler
|
||||
func SetLogger(h slog.Handler) {
|
||||
logger = slog.New(h)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"os"
|
||||
"reflect"
|
||||
"runtime"
|
||||
@@ -201,7 +202,21 @@ func init() {
|
||||
}
|
||||
|
||||
// InitLogging start the logging as per the command line flags
|
||||
//
|
||||
// This is called explicitly from the CLI, the librclone wrapper and
|
||||
// the test framework, but not from package init, so that importing
|
||||
// rclone as a library has no side effects on the process-wide default
|
||||
// slog logger.
|
||||
func InitLogging() {
|
||||
// Redirect the process-wide default logger through rclone's
|
||||
// handler so that log.Print/log.Fatal and slog.Default() (used by
|
||||
// some standard library and third party code) end up in rclone's
|
||||
// log output.
|
||||
slog.SetDefault(slog.New(Handler))
|
||||
|
||||
// Make log.Printf logs at level Notice
|
||||
slog.SetLogLoggerLevel(fs.SlogLevelNotice)
|
||||
|
||||
// Note that ci only has the defaults in at this point
|
||||
// We set real values in logReload
|
||||
ci := fs.GetConfig(context.Background())
|
||||
|
||||
@@ -27,6 +27,12 @@ var Handler = defaultHandler()
|
||||
// This will be adjusted by InitLogging to be the configured levels
|
||||
// but it is important we have a logger running regardless of whether
|
||||
// InitLogging has been called yet or not.
|
||||
//
|
||||
// Note that this only sets rclone's private logger via fs.SetLogger
|
||||
// so that importing this package has no side effects on the
|
||||
// process-wide default slog logger. The CLI and other rclone-as-a-
|
||||
// program entry points pick up the default logger redirection in
|
||||
// InitLogging instead.
|
||||
func defaultHandler() *OutputHandler {
|
||||
// Default options for default handler
|
||||
opts := &slog.HandlerOptions{
|
||||
@@ -36,11 +42,8 @@ func defaultHandler() *OutputHandler {
|
||||
// Create our handler
|
||||
h := NewOutputHandler(os.Stderr, opts, logFormatDate|logFormatTime)
|
||||
|
||||
// Set the slog default handler
|
||||
slog.SetDefault(slog.New(h))
|
||||
|
||||
// Make log.Printf logs at level Notice
|
||||
slog.SetLogLoggerLevel(fs.SlogLevelNotice)
|
||||
// Set rclone's internal logger so rclone logging works
|
||||
fs.SetLogger(h)
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user