1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package logger
16
17 import (
18 "bytes"
19 "fmt"
20 "os"
21 "path/filepath"
22 "reflect"
23 "strconv"
24 "strings"
25 "sync"
26 "text/template"
27 "time"
28 )
29
30
31 const (
32 DefaultDateFormat = "{year}-{month}-{day} {hour}:{minute}:{second},{millisecond}"
33 DefaultFormat = "{date} - {Level | printf \"%-8s\"} - {file}:{line}:{function}(): {message}"
34 DefaultPlaceholder = "p"
35
36 kilo = 1e3
37 mega = 1e6
38 percentage = 100
39 )
40
41
42 type FormatterFuncs map[string]interface{}
43
44
45
46 type Formatter struct {
47 format string
48 dateFormat string
49 template *template.Template
50 placeholder string
51 timeBuffer *bytes.Buffer
52 formatBuffer *bytes.Buffer
53 messageBuffer *bytes.Buffer
54 mutex sync.RWMutex
55 usedArguments map[int]bool
56 }
57
58
59 func NewFormatter() *Formatter {
60 f := &Formatter{
61 format: DefaultFormat,
62 dateFormat: DefaultDateFormat,
63 template: template.New("").Delims("{", "}"),
64 placeholder: DefaultPlaceholder,
65 timeBuffer: new(bytes.Buffer),
66 formatBuffer: new(bytes.Buffer),
67 messageBuffer: new(bytes.Buffer),
68 }
69
70 return f
71 }
72
73
74 func (f *Formatter) Reset() *Formatter {
75 f.mutex.Lock()
76 defer f.mutex.Unlock()
77
78 f.format = DefaultFormat
79 f.dateFormat = DefaultDateFormat
80 f.placeholder = DefaultPlaceholder
81
82 return f
83 }
84
85
86
87 func (f *Formatter) SetPlaceholder(placeholder string) *Formatter {
88 f.mutex.Lock()
89 defer f.mutex.Unlock()
90
91 f.placeholder = placeholder
92
93 return f
94 }
95
96
97
98 func (f *Formatter) GetPlaceholder() string {
99 f.mutex.RLock()
100 defer f.mutex.RUnlock()
101
102 return f.placeholder
103 }
104
105
106 func (f *Formatter) AddFuncs(funcs FormatterFuncs) *Formatter {
107 f.mutex.Lock()
108 defer f.mutex.Unlock()
109
110 f.template.Funcs(template.FuncMap(funcs))
111
112 return f
113 }
114
115
116 func (f *Formatter) SetFormat(format string) *Formatter {
117 f.mutex.Lock()
118 defer f.mutex.Unlock()
119
120 f.format = format
121
122 return f
123 }
124
125
126 func (f *Formatter) GetFormat() string {
127 f.mutex.RLock()
128 defer f.mutex.RUnlock()
129
130 return f.format
131 }
132
133
134 func (f *Formatter) SetDateFormat(dateFormat string) *Formatter {
135 f.mutex.Lock()
136 defer f.mutex.Unlock()
137
138 f.dateFormat = dateFormat
139
140 return f
141 }
142
143
144 func (f *Formatter) GetDateFormat() string {
145 f.mutex.RLock()
146 defer f.mutex.RUnlock()
147
148 return f.format
149 }
150
151
152
153 func (f *Formatter) Format(record *Record) (string, error) {
154 f.mutex.Lock()
155 defer f.mutex.Unlock()
156
157 f.template.Funcs(f.getRecordFuncs(record))
158
159 message, err := f.formatString(f.template, f.formatBuffer, f.format, nil)
160
161 if err != nil {
162 return "", NewRuntimeError("cannot format record", err)
163 }
164
165 return message, nil
166 }
167
168
169 func (f *Formatter) FormatTime(record *Record) (string, error) {
170 f.mutex.Lock()
171 defer f.mutex.Unlock()
172
173 f.template.Funcs(f.getRecordFuncs(record))
174
175 message, err := f.formatString(f.template, f.timeBuffer, f.dateFormat, nil)
176
177 if err != nil {
178 return "", NewRuntimeError("cannot format time", err)
179 }
180
181 return message, nil
182 }
183
184
185
186 func (f *Formatter) FormatMessage(record *Record) (string, error) {
187 f.mutex.Lock()
188 defer f.mutex.Unlock()
189
190 message, err := f.formatMessageRecord(record)
191
192 if err != nil {
193 return "", NewRuntimeError("cannot format message", err)
194 }
195
196 return message, nil
197 }
198
199
200
201 func (f *Formatter) formatMessageRecord(record *Record) (string, error) {
202 if len(record.Arguments) == 0 {
203 return record.Message, nil
204 }
205
206 var err error
207
208 var object interface{}
209
210 message := record.Message
211
212 f.usedArguments = make(map[int]bool)
213
214 funcMap := make(template.FuncMap)
215
216 funcMap[f.placeholder] = f.argumentAutomatic(record)
217
218 for position, argument := range record.Arguments {
219 placeholder := f.placeholder + strconv.Itoa(position)
220
221 funcMap[placeholder] = f.argumentValue(position, argument)
222
223 valueOf := reflect.ValueOf(argument)
224
225 switch valueOf.Kind() {
226 case reflect.Map:
227 if reflect.TypeOf(argument).Key().Kind() == reflect.String {
228 for _, key := range valueOf.MapKeys() {
229 funcMap[key.String()] = f.argumentValue(position, valueOf.MapIndex(key).Interface())
230 }
231 }
232 case reflect.Struct:
233 object = argument
234 }
235 }
236
237 if message, err = f.formatString(
238 template.New("").Delims("{", "}").Funcs(f.getRecordFuncs(record)).Funcs(funcMap),
239 f.messageBuffer,
240 message,
241 object,
242 ); err != nil {
243 return "", err
244 }
245
246 if len(f.usedArguments) >= len(record.Arguments) {
247 return message, nil
248 }
249
250 for position, argument := range record.Arguments {
251 if !f.isArgumentUsed(position, argument) {
252 if message != "" {
253 message += " "
254 }
255
256 message += fmt.Sprint(argument)
257 }
258 }
259
260 return message, nil
261 }
262
263 func (f *Formatter) isArgumentUsed(position int, argument interface{}) bool {
264 valueOf := reflect.ValueOf(argument)
265
266 switch valueOf.Kind() {
267 case reflect.Map:
268 if reflect.TypeOf(argument).Key().Kind() == reflect.String {
269 return true
270 }
271 case reflect.Struct:
272 return true
273 }
274
275 return f.usedArguments[position]
276 }
277
278
279 func (f *Formatter) argumentValue(position int, argument interface{}) func() interface{} {
280 return func() interface{} {
281 f.usedArguments[position] = true
282 return argument
283 }
284 }
285
286
287
288 func (f *Formatter) argumentAutomatic(record *Record) func() interface{} {
289 position := 0
290 arguments := len(record.Arguments)
291
292 return func() interface{} {
293 var argument interface{}
294
295 if position < arguments {
296 f.usedArguments[position] = true
297 argument = record.Arguments[position]
298 position++
299 }
300
301 return argument
302 }
303 }
304
305
306 func (*Formatter) formatString(templ *template.Template, buffer *bytes.Buffer, format string, object interface{}) (string, error) {
307 var message string
308
309 if format != "" {
310 var err error
311
312 templ, err = templ.Parse(format)
313
314 if err != nil {
315 return "", NewRuntimeError("cannot parse text template", err)
316 }
317
318 buffer.Reset()
319
320 err = templ.Execute(buffer, object)
321
322 if err != nil {
323 return "", NewRuntimeError("cannot execute text template", err)
324 }
325
326 message = buffer.String()
327 }
328
329 return message, nil
330 }
331
332
333 func (f *Formatter) getRecordFuncs(record *Record) template.FuncMap {
334 return template.FuncMap{
335 "uid": os.Getuid,
336 "gid": os.Getgid,
337 "pid": os.Getpid,
338 "egid": os.Getegid,
339 "euid": os.Geteuid,
340 "ppid": os.Getppid,
341 "getEnv": os.Getenv,
342 "expandEnv": os.ExpandEnv,
343 "executable": func() string {
344 return filepath.Base(os.Args[0])
345 },
346 "date": func() string {
347 date, err := f.formatString(f.template, f.timeBuffer, f.dateFormat, nil)
348
349 if err != nil {
350 printError(NewRuntimeError("cannot format date", err))
351 }
352
353 return date
354 },
355 "message": func() string {
356 message, err := f.formatMessageRecord(record)
357
358 if err != nil {
359 printError(NewRuntimeError("cannot format message", err))
360 }
361
362 return message
363 },
364 "levelValue": func() int {
365 return record.Level.Value
366 },
367 "level": func() string {
368 return strings.ToLower(record.Level.Name)
369 },
370 "Level": func() string {
371 return strings.Title(strings.ToLower(record.Level.Name))
372 },
373 "LEVEL": func() string {
374 return strings.ToUpper(record.Level.Name)
375 },
376 "iso8601": func() string {
377 return record.Time.Format(time.RFC3339)
378 },
379 "id": func() interface{} {
380 return record.ID
381 },
382 "name": func() string {
383 return record.Name
384 },
385 "host": func() string {
386 return record.Address
387 },
388 "hostname": func() string {
389 return record.Hostname
390 },
391 "address": func() string {
392 return record.Address
393 },
394 "nanosecond": func() string {
395 return fmt.Sprintf("%09d", record.Time.Nanosecond())
396 },
397 "microsecond": func() string {
398 return fmt.Sprintf("%06d", record.Time.Nanosecond()/kilo)
399 },
400 "millisecond": func() string {
401 return fmt.Sprintf("%03d", record.Time.Nanosecond()/mega)
402 },
403 "second": func() string {
404 return fmt.Sprintf("%02d", record.Time.Second())
405 },
406 "minute": func() string {
407 return fmt.Sprintf("%02d", record.Time.Minute())
408 },
409 "hour": func() string {
410 return fmt.Sprintf("%02d", record.Time.Hour())
411 },
412 "day": func() string {
413 return fmt.Sprintf("%02d", record.Time.Day())
414 },
415 "month": func() string {
416 return fmt.Sprintf("%02d", record.Time.Month())
417 },
418 "YEAR": func() string {
419 return fmt.Sprintf("%02d", record.Time.Year()%percentage)
420 },
421 "year": func() int {
422 return record.Time.Year()
423 },
424 "file": func() string {
425 return record.File.Name
426 },
427 "line": func() int {
428 return record.File.Line
429 },
430 "function": func() string {
431 return record.File.Function
432 },
433 }
434 }
435
View as plain text