Add --syslog flag to optionally log to syslog on capable platforms

This commit is contained in:
Nick Craig-Wood 2017-02-10 13:28:06 +00:00
parent ac1c041377
commit 666dae4229
10 changed files with 179 additions and 59 deletions

View file

@ -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()

View file

@ -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

View file

@ -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
}
}

View file

@ -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()
}
}

View file

@ -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]]
}

View file

@ -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")
}

View file

@ -2,7 +2,7 @@
// +build !windows,!solaris,!plan9
package cmd
package fs
import (
"log"

View file

@ -6,7 +6,7 @@
// +build windows
package cmd
package fs
import (
"log"

16
fs/syslog.go Normal file
View 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
View 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
}