[67] | 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 | }
|
---|