1package logrus 2 3import ( 4 "bytes" 5 "fmt" 6 "os" 7 "reflect" 8 "runtime" 9 "strings" 10 "sync" 11 "time" 12) 13 14var ( 15 bufferPool *sync.Pool 16 17 // qualified package name, cached at first use 18 logrusPackage string 19 20 // Positions in the call stack when tracing to report the calling method 21 minimumCallerDepth int 22 23 // Used for caller information initialisation 24 callerInitOnce sync.Once 25) 26 27const ( 28 maximumCallerDepth int = 25 29 knownLogrusFrames int = 4 30) 31 32func init() { 33 bufferPool = &sync.Pool{ 34 New: func() interface{} { 35 return new(bytes.Buffer) 36 }, 37 } 38 39 // start at the bottom of the stack before the package-name cache is primed 40 minimumCallerDepth = 1 41} 42 43// Defines the key when adding errors using WithError. 44var ErrorKey = "error" 45 46// An entry is the final or intermediate Logrus logging entry. It contains all 47// the fields passed with WithField{,s}. It's finally logged when Trace, Debug, 48// Info, Warn, Error, Fatal or Panic is called on it. These objects can be 49// reused and passed around as much as you wish to avoid field duplication. 50type Entry struct { 51 Logger *Logger 52 53 // Contains all the fields set by the user. 54 Data Fields 55 56 // Time at which the log entry was created 57 Time time.Time 58 59 // Level the log entry was logged at: Trace, Debug, Info, Warn, Error, Fatal or Panic 60 // This field will be set on entry firing and the value will be equal to the one in Logger struct field. 61 Level Level 62 63 // Calling method, with package name 64 Caller *runtime.Frame 65 66 // Message passed to Trace, Debug, Info, Warn, Error, Fatal or Panic 67 Message string 68 69 // When formatter is called in entry.log(), a Buffer may be set to entry 70 Buffer *bytes.Buffer 71 72 // err may contain a field formatting error 73 err string 74} 75 76func NewEntry(logger *Logger) *Entry { 77 return &Entry{ 78 Logger: logger, 79 // Default is three fields, plus one optional. Give a little extra room. 80 Data: make(Fields, 6), 81 } 82} 83 84// Returns the string representation from the reader and ultimately the 85// formatter. 86func (entry *Entry) String() (string, error) { 87 serialized, err := entry.Logger.Formatter.Format(entry) 88 if err != nil { 89 return "", err 90 } 91 str := string(serialized) 92 return str, nil 93} 94 95// Add an error as single field (using the key defined in ErrorKey) to the Entry. 96func (entry *Entry) WithError(err error) *Entry { 97 return entry.WithField(ErrorKey, err) 98} 99 100// Add a single field to the Entry. 101func (entry *Entry) WithField(key string, value interface{}) *Entry { 102 return entry.WithFields(Fields{key: value}) 103} 104 105// Add a map of fields to the Entry. 106func (entry *Entry) WithFields(fields Fields) *Entry { 107 data := make(Fields, len(entry.Data)+len(fields)) 108 for k, v := range entry.Data { 109 data[k] = v 110 } 111 var field_err string 112 for k, v := range fields { 113 if t := reflect.TypeOf(v); t != nil && t.Kind() == reflect.Func { 114 field_err = fmt.Sprintf("can not add field %q", k) 115 if entry.err != "" { 116 field_err = entry.err + ", " + field_err 117 } 118 } else { 119 data[k] = v 120 } 121 } 122 return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: field_err} 123} 124 125// Overrides the time of the Entry. 126func (entry *Entry) WithTime(t time.Time) *Entry { 127 return &Entry{Logger: entry.Logger, Data: entry.Data, Time: t} 128} 129 130// getPackageName reduces a fully qualified function name to the package name 131// There really ought to be to be a better way... 132func getPackageName(f string) string { 133 for { 134 lastPeriod := strings.LastIndex(f, ".") 135 lastSlash := strings.LastIndex(f, "/") 136 if lastPeriod > lastSlash { 137 f = f[:lastPeriod] 138 } else { 139 break 140 } 141 } 142 143 return f 144} 145 146// getCaller retrieves the name of the first non-logrus calling function 147func getCaller() *runtime.Frame { 148 // Restrict the lookback frames to avoid runaway lookups 149 pcs := make([]uintptr, maximumCallerDepth) 150 depth := runtime.Callers(minimumCallerDepth, pcs) 151 frames := runtime.CallersFrames(pcs[:depth]) 152 153 // cache this package's fully-qualified name 154 callerInitOnce.Do(func() { 155 logrusPackage = getPackageName(runtime.FuncForPC(pcs[0]).Name()) 156 157 // now that we have the cache, we can skip a minimum count of known-logrus functions 158 // XXX this is dubious, the number of frames may vary store an entry in a logger interface 159 minimumCallerDepth = knownLogrusFrames 160 }) 161 162 for f, again := frames.Next(); again; f, again = frames.Next() { 163 pkg := getPackageName(f.Function) 164 165 // If the caller isn't part of this package, we're done 166 if pkg != logrusPackage { 167 return &f 168 } 169 } 170 171 // if we got here, we failed to find the caller's context 172 return nil 173} 174 175func (entry Entry) HasCaller() (has bool) { 176 return entry.Logger != nil && 177 entry.Logger.ReportCaller && 178 entry.Caller != nil 179} 180 181// This function is not declared with a pointer value because otherwise 182// race conditions will occur when using multiple goroutines 183func (entry Entry) log(level Level, msg string) { 184 var buffer *bytes.Buffer 185 186 // Default to now, but allow users to override if they want. 187 // 188 // We don't have to worry about polluting future calls to Entry#log() 189 // with this assignment because this function is declared with a 190 // non-pointer receiver. 191 if entry.Time.IsZero() { 192 entry.Time = time.Now() 193 } 194 195 entry.Level = level 196 entry.Message = msg 197 if entry.Logger.ReportCaller { 198 entry.Caller = getCaller() 199 } 200 201 entry.fireHooks() 202 203 buffer = bufferPool.Get().(*bytes.Buffer) 204 buffer.Reset() 205 defer bufferPool.Put(buffer) 206 entry.Buffer = buffer 207 208 entry.write() 209 210 entry.Buffer = nil 211 212 // To avoid Entry#log() returning a value that only would make sense for 213 // panic() to use in Entry#Panic(), we avoid the allocation by checking 214 // directly here. 215 if level <= PanicLevel { 216 panic(&entry) 217 } 218} 219 220func (entry *Entry) fireHooks() { 221 entry.Logger.mu.Lock() 222 defer entry.Logger.mu.Unlock() 223 err := entry.Logger.Hooks.Fire(entry.Level, entry) 224 if err != nil { 225 fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err) 226 } 227} 228 229func (entry *Entry) write() { 230 entry.Logger.mu.Lock() 231 defer entry.Logger.mu.Unlock() 232 serialized, err := entry.Logger.Formatter.Format(entry) 233 if err != nil { 234 fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err) 235 } else { 236 _, err = entry.Logger.Out.Write(serialized) 237 if err != nil { 238 fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err) 239 } 240 } 241} 242 243func (entry *Entry) Trace(args ...interface{}) { 244 if entry.Logger.IsLevelEnabled(TraceLevel) { 245 entry.log(TraceLevel, fmt.Sprint(args...)) 246 } 247} 248 249func (entry *Entry) Debug(args ...interface{}) { 250 if entry.Logger.IsLevelEnabled(DebugLevel) { 251 entry.log(DebugLevel, fmt.Sprint(args...)) 252 } 253} 254 255func (entry *Entry) Print(args ...interface{}) { 256 entry.Info(args...) 257} 258 259func (entry *Entry) Info(args ...interface{}) { 260 if entry.Logger.IsLevelEnabled(InfoLevel) { 261 entry.log(InfoLevel, fmt.Sprint(args...)) 262 } 263} 264 265func (entry *Entry) Warn(args ...interface{}) { 266 if entry.Logger.IsLevelEnabled(WarnLevel) { 267 entry.log(WarnLevel, fmt.Sprint(args...)) 268 } 269} 270 271func (entry *Entry) Warning(args ...interface{}) { 272 entry.Warn(args...) 273} 274 275func (entry *Entry) Error(args ...interface{}) { 276 if entry.Logger.IsLevelEnabled(ErrorLevel) { 277 entry.log(ErrorLevel, fmt.Sprint(args...)) 278 } 279} 280 281func (entry *Entry) Fatal(args ...interface{}) { 282 if entry.Logger.IsLevelEnabled(FatalLevel) { 283 entry.log(FatalLevel, fmt.Sprint(args...)) 284 } 285 entry.Logger.Exit(1) 286} 287 288func (entry *Entry) Panic(args ...interface{}) { 289 if entry.Logger.IsLevelEnabled(PanicLevel) { 290 entry.log(PanicLevel, fmt.Sprint(args...)) 291 } 292 panic(fmt.Sprint(args...)) 293} 294 295// Entry Printf family functions 296 297func (entry *Entry) Tracef(format string, args ...interface{}) { 298 if entry.Logger.IsLevelEnabled(TraceLevel) { 299 entry.Trace(fmt.Sprintf(format, args...)) 300 } 301} 302 303func (entry *Entry) Debugf(format string, args ...interface{}) { 304 if entry.Logger.IsLevelEnabled(DebugLevel) { 305 entry.Debug(fmt.Sprintf(format, args...)) 306 } 307} 308 309func (entry *Entry) Infof(format string, args ...interface{}) { 310 if entry.Logger.IsLevelEnabled(InfoLevel) { 311 entry.Info(fmt.Sprintf(format, args...)) 312 } 313} 314 315func (entry *Entry) Printf(format string, args ...interface{}) { 316 entry.Infof(format, args...) 317} 318 319func (entry *Entry) Warnf(format string, args ...interface{}) { 320 if entry.Logger.IsLevelEnabled(WarnLevel) { 321 entry.Warn(fmt.Sprintf(format, args...)) 322 } 323} 324 325func (entry *Entry) Warningf(format string, args ...interface{}) { 326 entry.Warnf(format, args...) 327} 328 329func (entry *Entry) Errorf(format string, args ...interface{}) { 330 if entry.Logger.IsLevelEnabled(ErrorLevel) { 331 entry.Error(fmt.Sprintf(format, args...)) 332 } 333} 334 335func (entry *Entry) Fatalf(format string, args ...interface{}) { 336 if entry.Logger.IsLevelEnabled(FatalLevel) { 337 entry.Fatal(fmt.Sprintf(format, args...)) 338 } 339 entry.Logger.Exit(1) 340} 341 342func (entry *Entry) Panicf(format string, args ...interface{}) { 343 if entry.Logger.IsLevelEnabled(PanicLevel) { 344 entry.Panic(fmt.Sprintf(format, args...)) 345 } 346} 347 348// Entry Println family functions 349 350func (entry *Entry) Traceln(args ...interface{}) { 351 if entry.Logger.IsLevelEnabled(TraceLevel) { 352 entry.Trace(entry.sprintlnn(args...)) 353 } 354} 355 356func (entry *Entry) Debugln(args ...interface{}) { 357 if entry.Logger.IsLevelEnabled(DebugLevel) { 358 entry.Debug(entry.sprintlnn(args...)) 359 } 360} 361 362func (entry *Entry) Infoln(args ...interface{}) { 363 if entry.Logger.IsLevelEnabled(InfoLevel) { 364 entry.Info(entry.sprintlnn(args...)) 365 } 366} 367 368func (entry *Entry) Println(args ...interface{}) { 369 entry.Infoln(args...) 370} 371 372func (entry *Entry) Warnln(args ...interface{}) { 373 if entry.Logger.IsLevelEnabled(WarnLevel) { 374 entry.Warn(entry.sprintlnn(args...)) 375 } 376} 377 378func (entry *Entry) Warningln(args ...interface{}) { 379 entry.Warnln(args...) 380} 381 382func (entry *Entry) Errorln(args ...interface{}) { 383 if entry.Logger.IsLevelEnabled(ErrorLevel) { 384 entry.Error(entry.sprintlnn(args...)) 385 } 386} 387 388func (entry *Entry) Fatalln(args ...interface{}) { 389 if entry.Logger.IsLevelEnabled(FatalLevel) { 390 entry.Fatal(entry.sprintlnn(args...)) 391 } 392 entry.Logger.Exit(1) 393} 394 395func (entry *Entry) Panicln(args ...interface{}) { 396 if entry.Logger.IsLevelEnabled(PanicLevel) { 397 entry.Panic(entry.sprintlnn(args...)) 398 } 399} 400 401// Sprintlnn => Sprint no newline. This is to get the behavior of how 402// fmt.Sprintln where spaces are always added between operands, regardless of 403// their type. Instead of vendoring the Sprintln implementation to spare a 404// string allocation, we do the simplest thing. 405func (entry *Entry) sprintlnn(args ...interface{}) string { 406 msg := fmt.Sprintln(args...) 407 return msg[:len(msg)-1] 408} 409