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 | }
|
---|