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)")
|
||||
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
|
||||
}
|
Loading…
Reference in a new issue