github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/fs/log.go (about) 1 package fs 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "log" 8 "os" 9 10 "github.com/sirupsen/logrus" 11 ) 12 13 // LogLevel describes rclone's logs. These are a subset of the syslog log levels. 14 type LogLevel = Enum[logLevelChoices] 15 16 // Log levels. These are the syslog levels of which we only use a 17 // subset. 18 // 19 // LOG_EMERG system is unusable 20 // LOG_ALERT action must be taken immediately 21 // LOG_CRIT critical conditions 22 // LOG_ERR error conditions 23 // LOG_WARNING warning conditions 24 // LOG_NOTICE normal, but significant, condition 25 // LOG_INFO informational message 26 // LOG_DEBUG debug-level message 27 const ( 28 LogLevelEmergency LogLevel = iota 29 LogLevelAlert 30 LogLevelCritical 31 LogLevelError // Error - can't be suppressed 32 LogLevelWarning 33 LogLevelNotice // Normal logging, -q suppresses 34 LogLevelInfo // Transfers, needs -v 35 LogLevelDebug // Debug level, needs -vv 36 ) 37 38 type logLevelChoices struct{} 39 40 func (logLevelChoices) Choices() []string { 41 return []string{ 42 LogLevelEmergency: "EMERGENCY", 43 LogLevelAlert: "ALERT", 44 LogLevelCritical: "CRITICAL", 45 LogLevelError: "ERROR", 46 LogLevelWarning: "WARNING", 47 LogLevelNotice: "NOTICE", 48 LogLevelInfo: "INFO", 49 LogLevelDebug: "DEBUG", 50 } 51 } 52 53 func (logLevelChoices) Type() string { 54 return "LogLevel" 55 } 56 57 // LogPrintPid enables process pid in log 58 var LogPrintPid = false 59 60 // LogPrint sends the text to the logger of level 61 var LogPrint = func(level LogLevel, text string) { 62 text = fmt.Sprintf("%-6s: %s", level, text) 63 if LogPrintPid { 64 text = fmt.Sprintf("[%d] %s", os.Getpid(), text) 65 } 66 _ = log.Output(4, text) 67 } 68 69 // LogValueItem describes keyed item for a JSON log entry 70 type LogValueItem struct { 71 key string 72 value interface{} 73 render bool 74 } 75 76 // LogValue should be used as an argument to any logging calls to 77 // augment the JSON output with more structured information. 78 // 79 // key is the dictionary parameter used to store value. 80 func LogValue(key string, value interface{}) LogValueItem { 81 return LogValueItem{key: key, value: value, render: true} 82 } 83 84 // LogValueHide should be used as an argument to any logging calls to 85 // augment the JSON output with more structured information. 86 // 87 // key is the dictionary parameter used to store value. 88 // 89 // String() will return a blank string - this is useful to put items 90 // in which don't print into the log. 91 func LogValueHide(key string, value interface{}) LogValueItem { 92 return LogValueItem{key: key, value: value, render: false} 93 } 94 95 // String returns the representation of value. If render is fals this 96 // is an empty string so LogValueItem entries won't show in the 97 // textual representation of logs. 98 func (j LogValueItem) String() string { 99 if !j.render { 100 return "" 101 } 102 if do, ok := j.value.(fmt.Stringer); ok { 103 return do.String() 104 } 105 return fmt.Sprint(j.value) 106 } 107 108 // LogPrintf produces a log string from the arguments passed in 109 func LogPrintf(level LogLevel, o interface{}, text string, args ...interface{}) { 110 out := fmt.Sprintf(text, args...) 111 112 if GetConfig(context.TODO()).UseJSONLog { 113 fields := logrus.Fields{} 114 if o != nil { 115 fields = logrus.Fields{ 116 "object": fmt.Sprintf("%+v", o), 117 "objectType": fmt.Sprintf("%T", o), 118 } 119 } 120 for _, arg := range args { 121 if item, ok := arg.(LogValueItem); ok { 122 fields[item.key] = item.value 123 } 124 } 125 switch level { 126 case LogLevelDebug: 127 logrus.WithFields(fields).Debug(out) 128 case LogLevelInfo: 129 logrus.WithFields(fields).Info(out) 130 case LogLevelNotice, LogLevelWarning: 131 logrus.WithFields(fields).Warn(out) 132 case LogLevelError: 133 logrus.WithFields(fields).Error(out) 134 case LogLevelCritical: 135 logrus.WithFields(fields).Fatal(out) 136 case LogLevelEmergency, LogLevelAlert: 137 logrus.WithFields(fields).Panic(out) 138 } 139 } else { 140 if o != nil { 141 out = fmt.Sprintf("%v: %s", o, out) 142 } 143 LogPrint(level, out) 144 } 145 } 146 147 // LogLevelPrintf writes logs at the given level 148 func LogLevelPrintf(level LogLevel, o interface{}, text string, args ...interface{}) { 149 if GetConfig(context.TODO()).LogLevel >= level { 150 LogPrintf(level, o, text, args...) 151 } 152 } 153 154 // Errorf writes error log output for this Object or Fs. It 155 // should always be seen by the user. 156 func Errorf(o interface{}, text string, args ...interface{}) { 157 if GetConfig(context.TODO()).LogLevel >= LogLevelError { 158 LogPrintf(LogLevelError, o, text, args...) 159 } 160 } 161 162 // Logf writes log output for this Object or Fs. This should be 163 // considered to be Notice level logging. It is the default level. 164 // By default rclone should not log very much so only use this for 165 // important things the user should see. The user can filter these 166 // out with the -q flag. 167 func Logf(o interface{}, text string, args ...interface{}) { 168 if GetConfig(context.TODO()).LogLevel >= LogLevelNotice { 169 LogPrintf(LogLevelNotice, o, text, args...) 170 } 171 } 172 173 // Infof writes info on transfers for this Object or Fs. Use this 174 // level for logging transfers, deletions and things which should 175 // appear with the -v flag. 176 func Infof(o interface{}, text string, args ...interface{}) { 177 if GetConfig(context.TODO()).LogLevel >= LogLevelInfo { 178 LogPrintf(LogLevelInfo, o, text, args...) 179 } 180 } 181 182 // Debugf writes debugging output for this Object or Fs. Use this for 183 // debug only. The user must have to specify -vv to see this. 184 func Debugf(o interface{}, text string, args ...interface{}) { 185 if GetConfig(context.TODO()).LogLevel >= LogLevelDebug { 186 LogPrintf(LogLevelDebug, o, text, args...) 187 } 188 } 189 190 // LogDirName returns an object for the logger, logging a root 191 // directory which would normally be "" as the Fs 192 func LogDirName(f Fs, dir string) interface{} { 193 if dir != "" { 194 return dir 195 } 196 return f 197 } 198 199 // PrettyPrint formats JSON for improved readability in debug logs. 200 // If it can't Marshal JSON, it falls back to fmt. 201 func PrettyPrint(in any, label string, level LogLevel) { 202 if GetConfig(context.TODO()).LogLevel < level { 203 return 204 } 205 inBytes, err := json.MarshalIndent(in, "", "\t") 206 if err != nil || string(inBytes) == "{}" || string(inBytes) == "[]" { 207 LogPrintf(level, label, "\n%+v\n", in) 208 return 209 } 210 LogPrintf(level, label, "\n%s\n", string(inBytes)) 211 }