blob: 320e5d5b8be3cd62bafefe9dc24dc869a79fa9e7 [file] [log] [blame]
David K. Bainbridge215e0242017-09-05 23:18:24 -07001package logrus
2
3import (
4 "bytes"
5 "fmt"
6 "os"
7 "sync"
8 "time"
9)
10
11var bufferPool *sync.Pool
12
13func init() {
14 bufferPool = &sync.Pool{
15 New: func() interface{} {
16 return new(bytes.Buffer)
17 },
18 }
19}
20
21// Defines the key when adding errors using WithError.
22var ErrorKey = "error"
23
24// An entry is the final or intermediate Logrus logging entry. It contains all
25// the fields passed with WithField{,s}. It's finally logged when Debug, Info,
26// Warn, Error, Fatal or Panic is called on it. These objects can be reused and
27// passed around as much as you wish to avoid field duplication.
28type Entry struct {
29 Logger *Logger
30
31 // Contains all the fields set by the user.
32 Data Fields
33
34 // Time at which the log entry was created
35 Time time.Time
36
37 // Level the log entry was logged at: Debug, Info, Warn, Error, Fatal or Panic
38 Level Level
39
40 // Message passed to Debug, Info, Warn, Error, Fatal or Panic
41 Message string
42
43 // When formatter is called in entry.log(), an Buffer may be set to entry
44 Buffer *bytes.Buffer
45}
46
47func NewEntry(logger *Logger) *Entry {
48 return &Entry{
49 Logger: logger,
50 // Default is three fields, give a little extra room
51 Data: make(Fields, 5),
52 }
53}
54
55// Returns the string representation from the reader and ultimately the
56// formatter.
57func (entry *Entry) String() (string, error) {
58 serialized, err := entry.Logger.Formatter.Format(entry)
59 if err != nil {
60 return "", err
61 }
62 str := string(serialized)
63 return str, nil
64}
65
66// Add an error as single field (using the key defined in ErrorKey) to the Entry.
67func (entry *Entry) WithError(err error) *Entry {
68 return entry.WithField(ErrorKey, err)
69}
70
71// Add a single field to the Entry.
72func (entry *Entry) WithField(key string, value interface{}) *Entry {
73 return entry.WithFields(Fields{key: value})
74}
75
76// Add a map of fields to the Entry.
77func (entry *Entry) WithFields(fields Fields) *Entry {
78 data := make(Fields, len(entry.Data)+len(fields))
79 for k, v := range entry.Data {
80 data[k] = v
81 }
82 for k, v := range fields {
83 data[k] = v
84 }
85 return &Entry{Logger: entry.Logger, Data: data}
86}
87
88// This function is not declared with a pointer value because otherwise
89// race conditions will occur when using multiple goroutines
90func (entry Entry) log(level Level, msg string) {
91 var buffer *bytes.Buffer
92 entry.Time = time.Now()
93 entry.Level = level
94 entry.Message = msg
95
96 if err := entry.Logger.Hooks.Fire(level, &entry); err != nil {
97 entry.Logger.mu.Lock()
98 fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
99 entry.Logger.mu.Unlock()
100 }
101 buffer = bufferPool.Get().(*bytes.Buffer)
102 buffer.Reset()
103 defer bufferPool.Put(buffer)
104 entry.Buffer = buffer
105 serialized, err := entry.Logger.Formatter.Format(&entry)
106 entry.Buffer = nil
107 if err != nil {
108 entry.Logger.mu.Lock()
109 fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
110 entry.Logger.mu.Unlock()
111 } else {
112 entry.Logger.mu.Lock()
113 _, err = entry.Logger.Out.Write(serialized)
114 if err != nil {
115 fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
116 }
117 entry.Logger.mu.Unlock()
118 }
119
120 // To avoid Entry#log() returning a value that only would make sense for
121 // panic() to use in Entry#Panic(), we avoid the allocation by checking
122 // directly here.
123 if level <= PanicLevel {
124 panic(&entry)
125 }
126}
127
128func (entry *Entry) Debug(args ...interface{}) {
129 if entry.Logger.level() >= DebugLevel {
130 entry.log(DebugLevel, fmt.Sprint(args...))
131 }
132}
133
134func (entry *Entry) Print(args ...interface{}) {
135 entry.Info(args...)
136}
137
138func (entry *Entry) Info(args ...interface{}) {
139 if entry.Logger.level() >= InfoLevel {
140 entry.log(InfoLevel, fmt.Sprint(args...))
141 }
142}
143
144func (entry *Entry) Warn(args ...interface{}) {
145 if entry.Logger.level() >= WarnLevel {
146 entry.log(WarnLevel, fmt.Sprint(args...))
147 }
148}
149
150func (entry *Entry) Warning(args ...interface{}) {
151 entry.Warn(args...)
152}
153
154func (entry *Entry) Error(args ...interface{}) {
155 if entry.Logger.level() >= ErrorLevel {
156 entry.log(ErrorLevel, fmt.Sprint(args...))
157 }
158}
159
160func (entry *Entry) Fatal(args ...interface{}) {
161 if entry.Logger.level() >= FatalLevel {
162 entry.log(FatalLevel, fmt.Sprint(args...))
163 }
164 Exit(1)
165}
166
167func (entry *Entry) Panic(args ...interface{}) {
168 if entry.Logger.level() >= PanicLevel {
169 entry.log(PanicLevel, fmt.Sprint(args...))
170 }
171 panic(fmt.Sprint(args...))
172}
173
174// Entry Printf family functions
175
176func (entry *Entry) Debugf(format string, args ...interface{}) {
177 if entry.Logger.level() >= DebugLevel {
178 entry.Debug(fmt.Sprintf(format, args...))
179 }
180}
181
182func (entry *Entry) Infof(format string, args ...interface{}) {
183 if entry.Logger.level() >= InfoLevel {
184 entry.Info(fmt.Sprintf(format, args...))
185 }
186}
187
188func (entry *Entry) Printf(format string, args ...interface{}) {
189 entry.Infof(format, args...)
190}
191
192func (entry *Entry) Warnf(format string, args ...interface{}) {
193 if entry.Logger.level() >= WarnLevel {
194 entry.Warn(fmt.Sprintf(format, args...))
195 }
196}
197
198func (entry *Entry) Warningf(format string, args ...interface{}) {
199 entry.Warnf(format, args...)
200}
201
202func (entry *Entry) Errorf(format string, args ...interface{}) {
203 if entry.Logger.level() >= ErrorLevel {
204 entry.Error(fmt.Sprintf(format, args...))
205 }
206}
207
208func (entry *Entry) Fatalf(format string, args ...interface{}) {
209 if entry.Logger.level() >= FatalLevel {
210 entry.Fatal(fmt.Sprintf(format, args...))
211 }
212 Exit(1)
213}
214
215func (entry *Entry) Panicf(format string, args ...interface{}) {
216 if entry.Logger.level() >= PanicLevel {
217 entry.Panic(fmt.Sprintf(format, args...))
218 }
219}
220
221// Entry Println family functions
222
223func (entry *Entry) Debugln(args ...interface{}) {
224 if entry.Logger.level() >= DebugLevel {
225 entry.Debug(entry.sprintlnn(args...))
226 }
227}
228
229func (entry *Entry) Infoln(args ...interface{}) {
230 if entry.Logger.level() >= InfoLevel {
231 entry.Info(entry.sprintlnn(args...))
232 }
233}
234
235func (entry *Entry) Println(args ...interface{}) {
236 entry.Infoln(args...)
237}
238
239func (entry *Entry) Warnln(args ...interface{}) {
240 if entry.Logger.level() >= WarnLevel {
241 entry.Warn(entry.sprintlnn(args...))
242 }
243}
244
245func (entry *Entry) Warningln(args ...interface{}) {
246 entry.Warnln(args...)
247}
248
249func (entry *Entry) Errorln(args ...interface{}) {
250 if entry.Logger.level() >= ErrorLevel {
251 entry.Error(entry.sprintlnn(args...))
252 }
253}
254
255func (entry *Entry) Fatalln(args ...interface{}) {
256 if entry.Logger.level() >= FatalLevel {
257 entry.Fatal(entry.sprintlnn(args...))
258 }
259 Exit(1)
260}
261
262func (entry *Entry) Panicln(args ...interface{}) {
263 if entry.Logger.level() >= PanicLevel {
264 entry.Panic(entry.sprintlnn(args...))
265 }
266}
267
268// Sprintlnn => Sprint no newline. This is to get the behavior of how
269// fmt.Sprintln where spaces are always added between operands, regardless of
270// their type. Instead of vendoring the Sprintln implementation to spare a
271// string allocation, we do the simplest thing.
272func (entry *Entry) sprintlnn(args ...interface{}) string {
273 msg := fmt.Sprintln(args...)
274 return msg[:len(msg)-1]
275}