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