forked from TrueCloudLab/rclone
Add --syslog flag to optionally log to syslog on capable platforms
This commit is contained in:
parent
ac1c041377
commit
666dae4229
10 changed files with 179 additions and 59 deletions
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)")
|
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")
|
dataRateUnit = fs.StringP("stats-unit", "", "bytes", "Show data rate in stats as either 'bits' or 'bytes'/s")
|
||||||
version bool
|
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")
|
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
|
// initConfig is run by cobra after initialising the flags
|
||||||
func initConfig() {
|
func initConfig() {
|
||||||
// Log file output
|
// Start the logger
|
||||||
if *logFile != "" {
|
fs.InitLogging()
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load the rest of the config now we have started the logger
|
// Load the rest of the config now we have started the logger
|
||||||
fs.LoadConfig()
|
fs.LoadConfig()
|
||||||
|
|
|
@ -502,6 +502,18 @@ then the files will have SUFFIX added on to them.
|
||||||
|
|
||||||
See `--backup-dir` for more info.
|
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 ###
|
### --track-renames ###
|
||||||
|
|
||||||
By default rclone doesn't not keep track of renamed files, so if you
|
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 {
|
if err != nil {
|
||||||
log.Fatalf("Invalid value for environment variable %q: %v", key, err)
|
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
|
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.
|
// LogLevel describes rclone's logs. These are a subset of the syslog log levels.
|
||||||
type LogLevel byte
|
type LogLevel byte
|
||||||
|
|
||||||
//go:generate stringer -type=LogLevel
|
// Log levels. These are the syslog levels of which we only use a
|
||||||
|
// subset.
|
||||||
// Log levels - a subset of the syslog logs
|
//
|
||||||
|
// 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 (
|
const (
|
||||||
LogLevelEmergency LogLevel = iota
|
LogLevelEmergency LogLevel = iota
|
||||||
LogLevelAlert
|
LogLevelAlert
|
||||||
|
@ -25,25 +33,52 @@ const (
|
||||||
LogLevelDebug // Debug level, needs -vv
|
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
|
// String turns a LogLevel into a string
|
||||||
var DebugLogger = log.New(os.Stdout, "", log.LstdFlags)
|
func (l LogLevel) String() string {
|
||||||
|
if l >= LogLevel(len(logLevelToString)) {
|
||||||
// makeLog produces a log string from the arguments passed in
|
return fmt.Sprintf("LogLevel(%d)", l)
|
||||||
func makeLog(o interface{}, text string, args ...interface{}) string {
|
|
||||||
out := fmt.Sprintf(text, args...)
|
|
||||||
if o == nil {
|
|
||||||
return out
|
|
||||||
}
|
}
|
||||||
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
|
// 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{}) {
|
func Errorf(o interface{}, text string, args ...interface{}) {
|
||||||
if Config.LogLevel >= LogLevelError {
|
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.
|
// out with the -q flag.
|
||||||
func Logf(o interface{}, text string, args ...interface{}) {
|
func Logf(o interface{}, text string, args ...interface{}) {
|
||||||
if Config.LogLevel >= LogLevelNotice {
|
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.
|
// appear with the -v flag.
|
||||||
func Infof(o interface{}, text string, args ...interface{}) {
|
func Infof(o interface{}, text string, args ...interface{}) {
|
||||||
if Config.LogLevel >= LogLevelInfo {
|
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.
|
// debug only. The user must have to specify -vv to see this.
|
||||||
func Debugf(o interface{}, text string, args ...interface{}) {
|
func Debugf(o interface{}, text string, args ...interface{}) {
|
||||||
if Config.LogLevel >= LogLevelDebug {
|
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
|
// +build !windows,!darwin,!dragonfly,!freebsd,!linux,!nacl,!netbsd,!openbsd
|
||||||
|
|
||||||
package cmd
|
package fs
|
||||||
|
|
||||||
import (
|
import "os"
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/ncw/rclone/fs"
|
|
||||||
)
|
|
||||||
|
|
||||||
// redirectStderr to the file passed in
|
// redirectStderr to the file passed in
|
||||||
func redirectStderr(f *os.File) {
|
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
|
// +build !windows,!solaris,!plan9
|
||||||
|
|
||||||
package cmd
|
package fs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
// +build windows
|
// +build windows
|
||||||
|
|
||||||
package cmd
|
package fs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"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
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue