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