Redo log level flags

* -vv or --log-level DEBUG
  * -v or --log-level INFO
  * --log-level NOTICE (default)
  * -q --log-level ERROR

Replace Config.Verbose and Config.Quiet with Config.LogLevel

Fixes #739 Fixes #1108 Fixes #1000
This commit is contained in:
Nick Craig-Wood 2017-02-09 21:22:46 +00:00
parent 0366ea39c5
commit ac1c041377
9 changed files with 137 additions and 40 deletions

View file

@ -274,12 +274,10 @@ func Run(Retry bool, showStats bool, cmd *cobra.Command, f func() error) {
if err != nil { if err != nil {
log.Fatalf("Failed to %s: %v", cmd.Name(), err) log.Fatalf("Failed to %s: %v", cmd.Name(), err)
} }
if showStats && (!fs.Config.Quiet || fs.Stats.Errored() || *statsInterval > 0) { if showStats && (fs.Stats.Errored() || *statsInterval > 0) {
fs.Infof(nil, "%s", fs.Stats) fs.Infof(nil, "%s", fs.Stats)
} }
if fs.Config.Verbose { fs.Debugf(nil, "Go routines at exit %d\n", runtime.NumGoroutine())
fs.Debugf(nil, "Go routines at exit %d\n", runtime.NumGoroutine())
}
if fs.Stats.Errored() { if fs.Stats.Errored() {
os.Exit(1) os.Exit(1)
} }

View file

@ -71,8 +71,9 @@ func newRun() *Run {
// "RCLONE_CONFIG_PASS=hunter2" (or your password) // "RCLONE_CONFIG_PASS=hunter2" (or your password)
*fs.AskPassword = false *fs.AskPassword = false
fs.LoadConfig() fs.LoadConfig()
fs.Config.Verbose = *Verbose if *Verbose {
fs.Config.Quiet = !*Verbose fs.Config.LogLevel = fs.LogLevelDebug
}
fs.Config.DumpHeaders = *DumpHeaders fs.Config.DumpHeaders = *DumpHeaders
fs.Config.DumpBodies = *DumpBodies fs.Config.DumpBodies = *DumpBodies
fs.Config.LowLevelRetries = *LowLevelRetries fs.Config.LowLevelRetries = *LowLevelRetries

View file

@ -362,6 +362,22 @@ This can be useful for tracking down problems with syncs in
combination with the `-v` flag. See the Logging section for more combination with the `-v` flag. See the Logging section for more
info. info.
### --log-level LEVEL ###
This sets the log level for rclone. The default log level is `INFO`.
`DEBUG` is equivalent to `-vv`. It outputs lots of debug info - useful
for bug reports and really finding out what rclone is doing.
`INFO` is equivalent to `-v`. It outputs information about each transfer
and prints stats once a minute by default.
`NOTICE` is the default log level if no logging flags are supplied. It
outputs very little when things are working normally. It outputs
warnings and significant events.
`ERROR` is equivalent to `-q`. It only output error messages.
### --low-level-retries NUMBER ### ### --low-level-retries NUMBER ###
This controls the number of low level retries rclone does. This controls the number of low level retries rclone does.
@ -568,12 +584,14 @@ This can be useful when transferring to a remote which doesn't support
mod times directly as it is more accurate than a `--size-only` check mod times directly as it is more accurate than a `--size-only` check
and faster than using `--checksum`. and faster than using `--checksum`.
### -v, --verbose ### ### -v, -vv, --verbose ###
If you set this flag, rclone will become very verbose telling you With `-v` rclone will tell you about each file that is transferred and
about every file it considers and transfers. a small number of significant events.
Very useful for debugging. With `-vv` rclone will become very verbose telling you about every
file it considers and transfers. Please send bug reports with a log
with this setting.
### -V, --version ### ### -V, --version ###
@ -762,22 +780,34 @@ See the [filtering section](/filtering/).
Logging Logging
------- -------
rclone has 3 levels of logging, `Error`, `Info` and `Debug`. rclone has 4 levels of logging, `Error`, `Notice`, `Info` and `Debug`.
By default rclone logs `Error` and `Info` to standard error and `Debug` By default rclone logs to standard error. This means you can redirect
to standard output. This means you can redirect standard output and standard error and still see the normal output of rclone commands (eg
standard error to different places. `rclone ls`).
By default rclone will produce `Error` and `Info` level messages. By default rclone will produce `Error` and `Notice` level messages.
If you use the `-q` flag, rclone will only produce `Error` messages. If you use the `-q` flag, rclone will only produce `Error` messages.
If you use the `-v` flag, rclone will produce `Error`, `Info` and If you use the `-v` flag, rclone will produce `Error`, `Notice` and
`Debug` messages. `Info` messages.
If you use the `-vv` flag, rclone will produce `Error`, `Notice`,
`Info` and `Debug` messages.
You can also control the log levels with the `--log-level` flag.
If you use the `--log-file=FILE` option, rclone will redirect `Error`, If you use the `--log-file=FILE` option, rclone will redirect `Error`,
`Info` and `Debug` messages along with standard error to FILE. `Info` and `Debug` messages along with standard error to FILE.
If you use the `--syslog` flag then rclone will log to syslog and the
`--syslog-facility` control which facility it uses.
Rclone prefixes all log messages with their level in capitals, eg INFO
which makes it easy to grep the log file for different kinds of
information.
Exit Code Exit Code
--------- ---------

View file

@ -26,6 +26,7 @@ import (
"github.com/Unknwon/goconfig" "github.com/Unknwon/goconfig"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/spf13/pflag"
"golang.org/x/crypto/nacl/secretbox" "golang.org/x/crypto/nacl/secretbox"
"golang.org/x/text/unicode/norm" "golang.org/x/text/unicode/norm"
) )
@ -56,8 +57,9 @@ var (
// Config is the global config // Config is the global config
Config = &ConfigInfo{} Config = &ConfigInfo{}
// Flags // Flags
verbose = BoolP("verbose", "v", false, "Print lots more stuff") verbose = CountP("verbose", "v", "Print lots more stuff (repeat for more)")
quiet = BoolP("quiet", "q", false, "Print as little stuff as possible") quiet = BoolP("quiet", "q", false, "Print as little stuff as possible")
logLevel = StringP("log-level", "", "INFO", "Log level DEBUG|INFO|NOTICE|ERROR")
modifyWindow = DurationP("modify-window", "", time.Nanosecond, "Max time diff to be considered the same") modifyWindow = DurationP("modify-window", "", time.Nanosecond, "Max time diff to be considered the same")
checkers = IntP("checkers", "", 8, "Number of checkers to run in parallel.") checkers = IntP("checkers", "", 8, "Number of checkers to run in parallel.")
transfers = IntP("transfers", "", 4, "Number of file transfers to run in parallel.") transfers = IntP("transfers", "", 4, "Number of file transfers to run in parallel.")
@ -182,8 +184,7 @@ func MustReveal(x string) string {
// ConfigInfo is filesystem config options // ConfigInfo is filesystem config options
type ConfigInfo struct { type ConfigInfo struct {
Verbose bool LogLevel LogLevel
Quiet bool
DryRun bool DryRun bool
CheckSum bool CheckSum bool
SizeOnly bool SizeOnly bool
@ -300,8 +301,39 @@ func LoadConfig() {
// Read some flags if set // Read some flags if set
// //
// FIXME read these from the config file too // FIXME read these from the config file too
Config.Verbose = *verbose Config.LogLevel = LogLevelNotice
Config.Quiet = *quiet if *verbose >= 2 {
Config.LogLevel = LogLevelDebug
} else if *verbose >= 1 {
Config.LogLevel = LogLevelInfo
}
if *quiet {
if *verbose > 0 {
log.Fatalf("Can't set -v and -q")
}
Config.LogLevel = LogLevelError
}
logLevelFlag := pflag.Lookup("log-level")
if logLevelFlag != nil && logLevelFlag.Changed {
if *verbose > 0 {
log.Fatalf("Can't set -v and --log-level")
}
if *quiet {
log.Fatalf("Can't set -q and --log-level")
}
switch strings.ToUpper(*logLevel) {
case "ERROR":
Config.LogLevel = LogLevelError
case "NOTICE":
Config.LogLevel = LogLevelNotice
case "INFO":
Config.LogLevel = LogLevelInfo
case "DEBUG":
Config.LogLevel = LogLevelDebug
default:
log.Fatalf("Unknown --log-level %q", *logLevel)
}
}
Config.ModifyWindow = *modifyWindow Config.ModifyWindow = *modifyWindow
Config.Checkers = *checkers Config.Checkers = *checkers
Config.Transfers = *transfers Config.Transfers = *transfers

View file

@ -313,3 +313,12 @@ func StringArrayP(name, shorthand string, value []string, usage string) (out *[]
setDefaultFromEnv(name) setDefaultFromEnv(name)
return out return out
} }
// CountP defines a flag which can be overridden by an environment variable
//
// It is a thin wrapper around pflag.CountP
func CountP(name, shorthand string, usage string) (out *int) {
out = pflag.CountP(name, shorthand, usage)
setDefaultFromEnv(name)
return out
}

View file

@ -11,9 +11,11 @@ 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 - a subset of the syslog logs // Log levels - a subset of the syslog logs
const ( const (
LogLevelEmergency = iota LogLevelEmergency LogLevel = iota
LogLevelAlert LogLevelAlert
LogLevelCritical LogLevelCritical
LogLevelError // Error - can't be suppressed LogLevelError // Error - can't be suppressed
@ -38,30 +40,37 @@ func makeLog(o interface{}, text string, args ...interface{}) string {
} }
// 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.Quiet or // unconditionally logs a message regardless of Config.LogLevel
// Config.Verbose.
func Errorf(o interface{}, text string, args ...interface{}) { func Errorf(o interface{}, text string, args ...interface{}) {
log.Print(makeLog(o, text, args...)) if Config.LogLevel >= LogLevelError {
}
// Logf writes log output for this Object or Fs. This should be
// considered to be Info level logging.
func Logf(o interface{}, text string, args ...interface{}) {
if !Config.Quiet {
log.Print(makeLog(o, text, args...)) log.Print(makeLog(o, text, args...))
} }
} }
// Infof writes info on transfers for this Object or Fs // Logf writes log output for this Object or Fs. This should be
// considered to be Info level logging. It is the default level. By
// default rclone should not log very much so only use this for
// important things the user should see. The user can filter these
// out with the -q flag.
func Logf(o interface{}, text string, args ...interface{}) {
if Config.LogLevel >= LogLevelNotice {
log.Print(makeLog(o, text, args...))
}
}
// Infof writes info on transfers for this Object or Fs. Use this
// level for logging transfers, deletions and things which should
// appear with the -v flag.
func Infof(o interface{}, text string, args ...interface{}) { func Infof(o interface{}, text string, args ...interface{}) {
if Config.Verbose { if Config.LogLevel >= LogLevelInfo {
DebugLogger.Print(makeLog(o, text, args...)) DebugLogger.Print(makeLog(o, text, args...))
} }
} }
// Debugf writes debugging output for this Object or Fs // Debugf writes debugging output for this Object or Fs. Use this for
// 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.Verbose { if Config.LogLevel >= LogLevelDebug {
DebugLogger.Print(makeLog(o, text, args...)) DebugLogger.Print(makeLog(o, text, args...))
} }
} }

16
fs/loglevel_string.go Normal file
View file

@ -0,0 +1,16 @@
// 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

@ -105,8 +105,9 @@ func newRun() *Run {
// "RCLONE_CONFIG_PASS=hunter2" (or your password) // "RCLONE_CONFIG_PASS=hunter2" (or your password)
*fs.AskPassword = false *fs.AskPassword = false
fs.LoadConfig() fs.LoadConfig()
fs.Config.Verbose = *Verbose if *Verbose {
fs.Config.Quiet = !*Verbose fs.Config.LogLevel = fs.LogLevelDebug
}
fs.Config.DumpHeaders = *DumpHeaders fs.Config.DumpHeaders = *DumpHeaders
fs.Config.DumpBodies = *DumpBodies fs.Config.DumpBodies = *DumpBodies
fs.Config.LowLevelRetries = *LowLevelRetries fs.Config.LowLevelRetries = *LowLevelRetries

View file

@ -73,8 +73,9 @@ func TestInit(t *testing.T) {
for _, item := range ExtraConfig { for _, item := range ExtraConfig {
fs.ConfigFileSet(item.Name, item.Key, item.Value) fs.ConfigFileSet(item.Name, item.Key, item.Value)
} }
fs.Config.Verbose = *verbose if *verbose {
fs.Config.Quiet = !*verbose fs.Config.LogLevel = fs.LogLevelDebug
}
fs.Config.DumpHeaders = *dumpHeaders fs.Config.DumpHeaders = *dumpHeaders
fs.Config.DumpBodies = *dumpBodies fs.Config.DumpBodies = *dumpBodies
t.Logf("Using remote %q", RemoteName) t.Logf("Using remote %q", RemoteName)