github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/fs/log/log.go (about) 1 // Package log provides logging for rclone 2 package log 3 4 import ( 5 "context" 6 "io" 7 "log" 8 "os" 9 "reflect" 10 "runtime" 11 "strings" 12 13 "github.com/rclone/rclone/fs" 14 "github.com/sirupsen/logrus" 15 ) 16 17 // Options contains options for controlling the logging 18 type Options struct { 19 File string // Log everything to this file 20 Format string // Comma separated list of log format options 21 UseSyslog bool // Use Syslog for logging 22 SyslogFacility string // Facility for syslog, e.g. KERN,USER,... 23 LogSystemdSupport bool // set if using systemd logging 24 } 25 26 // DefaultOpt is the default values used for Opt 27 var DefaultOpt = Options{ 28 Format: "date,time", 29 SyslogFacility: "DAEMON", 30 } 31 32 // Opt is the options for the logger 33 var Opt = DefaultOpt 34 35 // fnName returns the name of the calling +2 function 36 func fnName() string { 37 pc, _, _, ok := runtime.Caller(2) 38 name := "*Unknown*" 39 if ok { 40 name = runtime.FuncForPC(pc).Name() 41 dot := strings.LastIndex(name, ".") 42 if dot >= 0 { 43 name = name[dot+1:] 44 } 45 } 46 return name 47 } 48 49 // Trace debugs the entry and exit of the calling function 50 // 51 // It is designed to be used in a defer statement, so it returns a 52 // function that logs the exit parameters. 53 // 54 // Any pointers in the exit function will be dereferenced 55 func Trace(o interface{}, format string, a ...interface{}) func(string, ...interface{}) { 56 if fs.GetConfig(context.Background()).LogLevel < fs.LogLevelDebug { 57 return func(format string, a ...interface{}) {} 58 } 59 name := fnName() 60 fs.LogPrintf(fs.LogLevelDebug, o, name+": "+format, a...) 61 return func(format string, a ...interface{}) { 62 for i := range a { 63 // read the values of the pointed to items 64 typ := reflect.TypeOf(a[i]) 65 if typ.Kind() == reflect.Ptr { 66 value := reflect.ValueOf(a[i]) 67 if value.IsNil() { 68 a[i] = nil 69 } else { 70 pointedToValue := reflect.Indirect(value) 71 a[i] = pointedToValue.Interface() 72 } 73 } 74 } 75 fs.LogPrintf(fs.LogLevelDebug, o, ">"+name+": "+format, a...) 76 } 77 } 78 79 // Stack logs a stack trace of callers with the o and info passed in 80 func Stack(o interface{}, info string) { 81 if fs.GetConfig(context.Background()).LogLevel < fs.LogLevelDebug { 82 return 83 } 84 arr := [16 * 1024]byte{} 85 buf := arr[:] 86 n := runtime.Stack(buf, false) 87 buf = buf[:n] 88 fs.LogPrintf(fs.LogLevelDebug, o, "%s\nStack trace:\n%s", info, buf) 89 } 90 91 // InitLogging start the logging as per the command line flags 92 func InitLogging() { 93 flagsStr := "," + Opt.Format + "," 94 var flags int 95 if strings.Contains(flagsStr, ",date,") { 96 flags |= log.Ldate 97 } 98 if strings.Contains(flagsStr, ",time,") { 99 flags |= log.Ltime 100 } 101 if strings.Contains(flagsStr, ",microseconds,") { 102 flags |= log.Lmicroseconds 103 } 104 if strings.Contains(flagsStr, ",UTC,") { 105 flags |= log.LUTC 106 } 107 if strings.Contains(flagsStr, ",longfile,") { 108 flags |= log.Llongfile 109 } 110 if strings.Contains(flagsStr, ",shortfile,") { 111 flags |= log.Lshortfile 112 } 113 log.SetFlags(flags) 114 115 fs.LogPrintPid = strings.Contains(flagsStr, ",pid,") 116 117 // Log file output 118 if Opt.File != "" { 119 f, err := os.OpenFile(Opt.File, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0640) 120 if err != nil { 121 log.Fatalf("Failed to open log file: %v", err) 122 } 123 _, err = f.Seek(0, io.SeekEnd) 124 if err != nil { 125 fs.Errorf(nil, "Failed to seek log file to end: %v", err) 126 } 127 log.SetOutput(f) 128 logrus.SetOutput(f) 129 redirectStderr(f) 130 } 131 132 // Syslog output 133 if Opt.UseSyslog { 134 if Opt.File != "" { 135 log.Fatalf("Can't use --syslog and --log-file together") 136 } 137 startSysLog() 138 } 139 140 // Activate systemd logger support if systemd invocation ID is 141 // detected and output is going to stderr (not logging to a file or syslog) 142 if !Redirected() { 143 if isJournalStream() { 144 Opt.LogSystemdSupport = true 145 } 146 } 147 148 // Systemd logging output 149 if Opt.LogSystemdSupport { 150 startSystemdLog() 151 } 152 } 153 154 // Redirected returns true if the log has been redirected from stdout 155 func Redirected() bool { 156 return Opt.UseSyslog || Opt.File != "" 157 }