mirror of
				https://github.com/rclone/rclone.git
				synced 2025-10-30 23:17:59 +02:00 
			
		
		
		
	Add --syslog flag to optionally log to syslog on capable platforms
This commit is contained in:
		
							
								
								
									
										17
									
								
								cmd/cmd.go
									
									
									
									
									
								
							
							
						
						
									
										17
									
								
								cmd/cmd.go
									
									
									
									
									
								
							| @@ -31,7 +31,6 @@ var ( | ||||
| 	statsInterval = fs.DurationP("stats", "", time.Minute*1, "Interval between printing stats, e.g 500ms, 60s, 5m. (0 to disable)") | ||||
| 	dataRateUnit  = fs.StringP("stats-unit", "", "bytes", "Show data rate in stats as either 'bits' or 'bytes'/s") | ||||
| 	version       bool | ||||
| 	logFile       = fs.StringP("log-file", "", "", "Log everything to this file") | ||||
| 	retries       = fs.IntP("retries", "", 3, "Retry operations this many times if they fail") | ||||
| ) | ||||
|  | ||||
| @@ -320,20 +319,8 @@ func StartStats() chan struct{} { | ||||
|  | ||||
| // initConfig is run by cobra after initialising the flags | ||||
| func initConfig() { | ||||
| 	// Log file output | ||||
| 	if *logFile != "" { | ||||
| 		f, err := os.OpenFile(*logFile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0640) | ||||
| 		if err != nil { | ||||
| 			log.Fatalf("Failed to open log file: %v", err) | ||||
| 		} | ||||
| 		_, err = f.Seek(0, os.SEEK_END) | ||||
| 		if err != nil { | ||||
| 			fs.Errorf(nil, "Failed to seek log file to end: %v", err) | ||||
| 		} | ||||
| 		log.SetOutput(f) | ||||
| 		fs.DebugLogger.SetOutput(f) | ||||
| 		redirectStderr(f) | ||||
| 	} | ||||
| 	// Start the logger | ||||
| 	fs.InitLogging() | ||||
|  | ||||
| 	// Load the rest of the config now we have started the logger | ||||
| 	fs.LoadConfig() | ||||
|   | ||||
| @@ -502,6 +502,18 @@ then the files will have SUFFIX added on to them. | ||||
|  | ||||
| See `--backup-dir` for more info. | ||||
|  | ||||
| ### --syslog ### | ||||
|  | ||||
| On capable OSes (not Windows or Plan9) send all log output to syslog. | ||||
|  | ||||
| This can be useful for running rclone in script or `rclone mount`. | ||||
|  | ||||
| ### -syslog-facility string ### | ||||
|  | ||||
| If using `--syslog` this sets the syslog facility (eg `KERN`, `USER`). | ||||
| See `man syslog` for a list of possible facilities.  The default | ||||
| facility is `DAEMON`. | ||||
|  | ||||
| ### --track-renames ### | ||||
|  | ||||
| By default rclone doesn't not keep track of renamed files, so if you | ||||
|   | ||||
| @@ -254,7 +254,7 @@ func setDefaultFromEnv(name string) { | ||||
| 		if err != nil { | ||||
| 			log.Fatalf("Invalid value for environment variable %q: %v", key, err) | ||||
| 		} | ||||
| 		// log.Printf("Set default for %q from %q to %q (%v)", name, key, newValue, flag.Value) | ||||
| 		Debugf(nil, "Set default for %q from %q to %q (%v)", name, key, newValue, flag.Value) | ||||
| 		flag.DefValue = newValue | ||||
| 	} | ||||
| } | ||||
|   | ||||
							
								
								
									
										96
									
								
								fs/log.go
									
									
									
									
									
								
							
							
						
						
									
										96
									
								
								fs/log.go
									
									
									
									
									
								
							| @@ -11,9 +11,17 @@ import ( | ||||
| // LogLevel describes rclone's logs.  These are a subset of the syslog log levels. | ||||
| type LogLevel byte | ||||
|  | ||||
| //go:generate stringer -type=LogLevel | ||||
|  | ||||
| // Log levels - a subset of the syslog logs | ||||
| // Log levels.  These are the syslog levels of which we only use a | ||||
| // subset. | ||||
| // | ||||
| //    LOG_EMERG      system is unusable | ||||
| //    LOG_ALERT      action must be taken immediately | ||||
| //    LOG_CRIT       critical conditions | ||||
| //    LOG_ERR        error conditions | ||||
| //    LOG_WARNING    warning conditions | ||||
| //    LOG_NOTICE     normal, but significant, condition | ||||
| //    LOG_INFO       informational message | ||||
| //    LOG_DEBUG      debug-level message | ||||
| const ( | ||||
| 	LogLevelEmergency LogLevel = iota | ||||
| 	LogLevelAlert | ||||
| @@ -25,25 +33,52 @@ const ( | ||||
| 	LogLevelDebug  // Debug level, needs -vv | ||||
| ) | ||||
|  | ||||
| // Outside world interface | ||||
| var logLevelToString = []string{ | ||||
| 	LogLevelEmergency: "EMERGENCY", | ||||
| 	LogLevelAlert:     "ALERT", | ||||
| 	LogLevelCritical:  "CRITICAL", | ||||
| 	LogLevelError:     "ERROR", | ||||
| 	LogLevelWarning:   "WARNING", | ||||
| 	LogLevelNotice:    "NOTICE", | ||||
| 	LogLevelInfo:      "INFO", | ||||
| 	LogLevelDebug:     "DEBUG", | ||||
| } | ||||
|  | ||||
| // DebugLogger - logs to Stdout | ||||
| var DebugLogger = log.New(os.Stdout, "", log.LstdFlags) | ||||
|  | ||||
| // makeLog produces a log string from the arguments passed in | ||||
| func makeLog(o interface{}, text string, args ...interface{}) string { | ||||
| 	out := fmt.Sprintf(text, args...) | ||||
| 	if o == nil { | ||||
| 		return out | ||||
| // String turns a LogLevel into a string | ||||
| func (l LogLevel) String() string { | ||||
| 	if l >= LogLevel(len(logLevelToString)) { | ||||
| 		return fmt.Sprintf("LogLevel(%d)", l) | ||||
| 	} | ||||
| 	return fmt.Sprintf("%v: %s", o, out) | ||||
| 	return logLevelToString[l] | ||||
| } | ||||
|  | ||||
| // Flags | ||||
| var ( | ||||
| 	logFile        = StringP("log-file", "", "", "Log everything to this file") | ||||
| 	useSyslog      = BoolP("syslog", "", false, "Use Syslog for logging") | ||||
| 	syslogFacility = StringP("syslog-facility", "", "DAEMON", "Facility for syslog, eg KERN,USER,...") | ||||
| ) | ||||
|  | ||||
| // logPrint sends the text to the logger of level | ||||
| var logPrint = func(level LogLevel, text string) { | ||||
| 	text = fmt.Sprintf("%-6s: %s", level, text) | ||||
| 	log.Print(text) | ||||
| } | ||||
|  | ||||
| // logPrintf produces a log string from the arguments passed in | ||||
| func logPrintf(level LogLevel, o interface{}, text string, args ...interface{}) { | ||||
| 	out := fmt.Sprintf(text, args...) | ||||
| 	if o != nil { | ||||
| 		out = fmt.Sprintf("%v: %s", o, out) | ||||
| 	} | ||||
| 	logPrint(level, out) | ||||
| } | ||||
|  | ||||
| // Errorf writes error log output for this Object or Fs.  It | ||||
| // unconditionally logs a message regardless of Config.LogLevel | ||||
| // should always be seen by the user. | ||||
| func Errorf(o interface{}, text string, args ...interface{}) { | ||||
| 	if Config.LogLevel >= LogLevelError { | ||||
| 		log.Print(makeLog(o, text, args...)) | ||||
| 		logPrintf(LogLevelError, o, text, args...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -54,7 +89,7 @@ func Errorf(o interface{}, text string, args ...interface{}) { | ||||
| // out with the -q flag. | ||||
| func Logf(o interface{}, text string, args ...interface{}) { | ||||
| 	if Config.LogLevel >= LogLevelNotice { | ||||
| 		log.Print(makeLog(o, text, args...)) | ||||
| 		logPrintf(LogLevelNotice, o, text, args...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -63,7 +98,7 @@ func Logf(o interface{}, text string, args ...interface{}) { | ||||
| // appear with the -v flag. | ||||
| func Infof(o interface{}, text string, args ...interface{}) { | ||||
| 	if Config.LogLevel >= LogLevelInfo { | ||||
| 		DebugLogger.Print(makeLog(o, text, args...)) | ||||
| 		logPrintf(LogLevelInfo, o, text, args...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| @@ -71,6 +106,31 @@ func Infof(o interface{}, text string, args ...interface{}) { | ||||
| // debug only.  The user must have to specify -vv to see this. | ||||
| func Debugf(o interface{}, text string, args ...interface{}) { | ||||
| 	if Config.LogLevel >= LogLevelDebug { | ||||
| 		DebugLogger.Print(makeLog(o, text, args...)) | ||||
| 		logPrintf(LogLevelDebug, o, text, args...) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // InitLogging start the logging as per the command line flags | ||||
| func InitLogging() { | ||||
| 	// Log file output | ||||
| 	if *logFile != "" { | ||||
| 		f, err := os.OpenFile(*logFile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0640) | ||||
| 		if err != nil { | ||||
| 			log.Fatalf("Failed to open log file: %v", err) | ||||
| 		} | ||||
| 		_, err = f.Seek(0, os.SEEK_END) | ||||
| 		if err != nil { | ||||
| 			Errorf(nil, "Failed to seek log file to end: %v", err) | ||||
| 		} | ||||
| 		log.SetOutput(f) | ||||
| 		redirectStderr(f) | ||||
| 	} | ||||
|  | ||||
| 	// Syslog output | ||||
| 	if *useSyslog { | ||||
| 		if *logFile != "" { | ||||
| 			log.Fatalf("Can't use --syslog and --log-file together") | ||||
| 		} | ||||
| 		startSysLog() | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -1,16 +0,0 @@ | ||||
| // Code generated by "stringer -type=LogLevel"; DO NOT EDIT | ||||
|  | ||||
| package fs | ||||
|  | ||||
| import "fmt" | ||||
|  | ||||
| const _LogLevel_name = "LogLevelEmergencyLogLevelAlertLogLevelCriticalLogLevelErrorLogLevelWarningLogLevelNoticeLogLevelInfoLogLevelDebug" | ||||
|  | ||||
| var _LogLevel_index = [...]uint8{0, 17, 30, 46, 59, 74, 88, 100, 113} | ||||
|  | ||||
| func (i LogLevel) String() string { | ||||
| 	if i >= LogLevel(len(_LogLevel_index)-1) { | ||||
| 		return fmt.Sprintf("LogLevel(%d)", i) | ||||
| 	} | ||||
| 	return _LogLevel_name[_LogLevel_index[i]:_LogLevel_index[i+1]] | ||||
| } | ||||
| @@ -2,15 +2,11 @@ | ||||
| 
 | ||||
| // +build !windows,!darwin,!dragonfly,!freebsd,!linux,!nacl,!netbsd,!openbsd | ||||
| 
 | ||||
| package cmd | ||||
| package fs | ||||
| 
 | ||||
| import ( | ||||
| 	"os" | ||||
| 
 | ||||
| 	"github.com/ncw/rclone/fs" | ||||
| ) | ||||
| import "os" | ||||
| 
 | ||||
| // redirectStderr to the file passed in | ||||
| func redirectStderr(f *os.File) { | ||||
| 	fs.Errorf(nil, "Can't redirect stderr to file") | ||||
| 	Errorf(nil, "Can't redirect stderr to file") | ||||
| } | ||||
| @@ -2,7 +2,7 @@ | ||||
| 
 | ||||
| // +build !windows,!solaris,!plan9 | ||||
| 
 | ||||
| package cmd | ||||
| package fs | ||||
| 
 | ||||
| import ( | ||||
| 	"log" | ||||
| @@ -6,7 +6,7 @@ | ||||
| 
 | ||||
| // +build windows | ||||
| 
 | ||||
| package cmd | ||||
| package fs | ||||
| 
 | ||||
| import ( | ||||
| 	"log" | ||||
							
								
								
									
										16
									
								
								fs/syslog.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								fs/syslog.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| // Syslog interface for non-Unix variants only | ||||
|  | ||||
| // +build windows nacl plan9 | ||||
|  | ||||
| package fs | ||||
|  | ||||
| import ( | ||||
| 	"log" | ||||
| 	"runtime" | ||||
| ) | ||||
|  | ||||
| // Starts syslog if configured, returns true if it was started | ||||
| func startSysLog() bool { | ||||
| 	log.Fatalf("--syslog not supported on %s platform", runtime.GOOS) | ||||
| 	return false | ||||
| } | ||||
							
								
								
									
										65
									
								
								fs/syslog_unix.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								fs/syslog_unix.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | ||||
| // Syslog interface for Unix variants only | ||||
|  | ||||
| // +build !windows,!nacl,!plan9 | ||||
|  | ||||
| package fs | ||||
|  | ||||
| import ( | ||||
| 	"log" | ||||
| 	"log/syslog" | ||||
| 	"os" | ||||
| 	"path" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	syslogFacilityMap = map[string]syslog.Priority{ | ||||
| 		"KERN":     syslog.LOG_KERN, | ||||
| 		"USER":     syslog.LOG_USER, | ||||
| 		"MAIL":     syslog.LOG_MAIL, | ||||
| 		"DAEMON":   syslog.LOG_DAEMON, | ||||
| 		"AUTH":     syslog.LOG_AUTH, | ||||
| 		"SYSLOG":   syslog.LOG_SYSLOG, | ||||
| 		"LPR":      syslog.LOG_LPR, | ||||
| 		"NEWS":     syslog.LOG_NEWS, | ||||
| 		"UUCP":     syslog.LOG_UUCP, | ||||
| 		"CRON":     syslog.LOG_CRON, | ||||
| 		"AUTHPRIV": syslog.LOG_AUTHPRIV, | ||||
| 		"FTP":      syslog.LOG_FTP, | ||||
| 	} | ||||
| ) | ||||
|  | ||||
| // Starts syslog | ||||
| func startSysLog() bool { | ||||
| 	facility, ok := syslogFacilityMap[*syslogFacility] | ||||
| 	if !ok { | ||||
| 		log.Fatalf("Unknown syslog facility %q - man syslog for list", *syslogFacility) | ||||
| 	} | ||||
| 	Me := path.Base(os.Args[0]) | ||||
| 	w, err := syslog.New(syslog.LOG_NOTICE|facility, Me) | ||||
| 	if err != nil { | ||||
| 		log.Fatalf("Failed to start syslog: %v", err) | ||||
| 	} | ||||
| 	log.SetFlags(0) | ||||
| 	log.SetOutput(w) | ||||
| 	logPrint = func(level LogLevel, text string) { | ||||
| 		switch level { | ||||
| 		case LogLevelEmergency: | ||||
| 			_ = w.Emerg(text) | ||||
| 		case LogLevelAlert: | ||||
| 			_ = w.Alert(text) | ||||
| 		case LogLevelCritical: | ||||
| 			_ = w.Crit(text) | ||||
| 		case LogLevelError: | ||||
| 			_ = w.Err(text) | ||||
| 		case LogLevelWarning: | ||||
| 			_ = w.Warning(text) | ||||
| 		case LogLevelNotice: | ||||
| 			_ = w.Notice(text) | ||||
| 		case LogLevelInfo: | ||||
| 			_ = w.Info(text) | ||||
| 		case LogLevelDebug: | ||||
| 			_ = w.Debug(text) | ||||
| 		} | ||||
| 	} | ||||
| 	return true | ||||
| } | ||||
		Reference in New Issue
	
	Block a user