2018-01-12 17:30:54 +01:00
|
|
|
// Package log provides logging for rclone
|
|
|
|
package log
|
|
|
|
|
|
|
|
import (
|
2018-04-06 20:53:06 +02:00
|
|
|
"io"
|
2018-01-12 17:30:54 +01:00
|
|
|
"log"
|
|
|
|
"os"
|
|
|
|
"reflect"
|
|
|
|
"runtime"
|
|
|
|
"strings"
|
|
|
|
|
2019-07-28 19:47:38 +02:00
|
|
|
"github.com/rclone/rclone/fs"
|
2020-06-26 14:05:56 +02:00
|
|
|
"github.com/sirupsen/logrus"
|
2018-01-12 17:30:54 +01:00
|
|
|
)
|
|
|
|
|
2019-12-10 14:54:00 +01:00
|
|
|
// Options contains options for the remote control server
|
|
|
|
type Options struct {
|
|
|
|
File string // Log everything to this file
|
|
|
|
Format string // Comma separated list of log format options
|
|
|
|
UseSyslog bool // Use Syslog for logging
|
2020-10-13 23:49:58 +02:00
|
|
|
SyslogFacility string // Facility for syslog, e.g. KERN,USER,...
|
2019-12-10 14:54:00 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// DefaultOpt is the default values used for Opt
|
|
|
|
var DefaultOpt = Options{
|
|
|
|
Format: "date,time",
|
|
|
|
SyslogFacility: "DAEMON",
|
|
|
|
}
|
|
|
|
|
|
|
|
// Opt is the options for the logger
|
|
|
|
var Opt = DefaultOpt
|
2018-01-12 17:30:54 +01:00
|
|
|
|
|
|
|
// fnName returns the name of the calling +2 function
|
|
|
|
func fnName() string {
|
|
|
|
pc, _, _, ok := runtime.Caller(2)
|
|
|
|
name := "*Unknown*"
|
|
|
|
if ok {
|
|
|
|
name = runtime.FuncForPC(pc).Name()
|
|
|
|
dot := strings.LastIndex(name, ".")
|
|
|
|
if dot >= 0 {
|
|
|
|
name = name[dot+1:]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return name
|
|
|
|
}
|
|
|
|
|
|
|
|
// Trace debugs the entry and exit of the calling function
|
|
|
|
//
|
|
|
|
// It is designed to be used in a defer statement so it returns a
|
|
|
|
// function that logs the exit parameters.
|
|
|
|
//
|
|
|
|
// Any pointers in the exit function will be dereferenced
|
|
|
|
func Trace(o interface{}, format string, a ...interface{}) func(string, ...interface{}) {
|
|
|
|
if fs.Config.LogLevel < fs.LogLevelDebug {
|
|
|
|
return func(format string, a ...interface{}) {}
|
|
|
|
}
|
|
|
|
name := fnName()
|
|
|
|
fs.LogPrintf(fs.LogLevelDebug, o, name+": "+format, a...)
|
|
|
|
return func(format string, a ...interface{}) {
|
|
|
|
for i := range a {
|
|
|
|
// read the values of the pointed to items
|
|
|
|
typ := reflect.TypeOf(a[i])
|
|
|
|
if typ.Kind() == reflect.Ptr {
|
|
|
|
value := reflect.ValueOf(a[i])
|
|
|
|
if value.IsNil() {
|
|
|
|
a[i] = nil
|
|
|
|
} else {
|
|
|
|
pointedToValue := reflect.Indirect(value)
|
|
|
|
a[i] = pointedToValue.Interface()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fs.LogPrintf(fs.LogLevelDebug, o, ">"+name+": "+format, a...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-09-20 12:51:04 +02:00
|
|
|
// Stack logs a stack trace of callers with the o and info passed in
|
|
|
|
func Stack(o interface{}, info string) {
|
|
|
|
if fs.Config.LogLevel < fs.LogLevelDebug {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
arr := [16 * 1024]byte{}
|
|
|
|
buf := arr[:]
|
|
|
|
n := runtime.Stack(buf, false)
|
|
|
|
buf = buf[:n]
|
|
|
|
fs.LogPrintf(fs.LogLevelDebug, o, "%s\nStack trace:\n%s", info, buf)
|
|
|
|
}
|
|
|
|
|
2018-01-12 17:30:54 +01:00
|
|
|
// InitLogging start the logging as per the command line flags
|
|
|
|
func InitLogging() {
|
2019-12-10 14:54:00 +01:00
|
|
|
flagsStr := "," + Opt.Format + ","
|
2018-09-02 19:11:09 +02:00
|
|
|
var flags int
|
2020-09-18 17:37:54 +02:00
|
|
|
if !fs.Config.LogSystemdSupport {
|
|
|
|
if strings.Contains(flagsStr, ",date,") {
|
|
|
|
flags |= log.Ldate
|
|
|
|
}
|
|
|
|
if strings.Contains(flagsStr, ",time,") {
|
|
|
|
flags |= log.Ltime
|
|
|
|
}
|
|
|
|
if strings.Contains(flagsStr, ",microseconds,") {
|
|
|
|
flags |= log.Lmicroseconds
|
|
|
|
}
|
|
|
|
if strings.Contains(flagsStr, ",UTC,") {
|
|
|
|
flags |= log.LUTC
|
|
|
|
}
|
2018-09-02 19:11:09 +02:00
|
|
|
}
|
|
|
|
if strings.Contains(flagsStr, ",longfile,") {
|
|
|
|
flags |= log.Llongfile
|
|
|
|
}
|
|
|
|
if strings.Contains(flagsStr, ",shortfile,") {
|
|
|
|
flags |= log.Lshortfile
|
|
|
|
}
|
|
|
|
log.SetFlags(flags)
|
|
|
|
|
2018-01-12 17:30:54 +01:00
|
|
|
// Log file output
|
2019-12-10 14:54:00 +01:00
|
|
|
if Opt.File != "" {
|
|
|
|
f, err := os.OpenFile(Opt.File, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0640)
|
2018-01-12 17:30:54 +01:00
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("Failed to open log file: %v", err)
|
|
|
|
}
|
2018-04-06 20:53:06 +02:00
|
|
|
_, err = f.Seek(0, io.SeekEnd)
|
2018-01-12 17:30:54 +01:00
|
|
|
if err != nil {
|
|
|
|
fs.Errorf(nil, "Failed to seek log file to end: %v", err)
|
|
|
|
}
|
|
|
|
log.SetOutput(f)
|
2020-06-26 14:05:56 +02:00
|
|
|
logrus.SetOutput(f)
|
2018-01-12 17:30:54 +01:00
|
|
|
redirectStderr(f)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Syslog output
|
2019-12-10 14:54:00 +01:00
|
|
|
if Opt.UseSyslog {
|
|
|
|
if Opt.File != "" {
|
2018-01-12 17:30:54 +01:00
|
|
|
log.Fatalf("Can't use --syslog and --log-file together")
|
|
|
|
}
|
|
|
|
startSysLog()
|
|
|
|
}
|
|
|
|
}
|
2018-06-30 17:05:31 +02:00
|
|
|
|
|
|
|
// Redirected returns true if the log has been redirected from stdout
|
|
|
|
func Redirected() bool {
|
2019-12-10 14:54:00 +01:00
|
|
|
return Opt.UseSyslog || Opt.File != ""
|
2018-06-30 17:05:31 +02:00
|
|
|
}
|