OSDN Git Service

add package
[bytom/vapor.git] / vendor / github.com / hashicorp / go-hclog / int.go
1 package hclog
2
3 import (
4         "bufio"
5         "encoding"
6         "encoding/json"
7         "fmt"
8         "log"
9         "os"
10         "runtime"
11         "sort"
12         "strconv"
13         "strings"
14         "sync"
15         "sync/atomic"
16         "time"
17 )
18
19 var (
20         _levelToBracket = map[Level]string{
21                 Debug: "[DEBUG]",
22                 Trace: "[TRACE]",
23                 Info:  "[INFO ]",
24                 Warn:  "[WARN ]",
25                 Error: "[ERROR]",
26         }
27 )
28
29 // Given the options (nil for defaults), create a new Logger
30 func New(opts *LoggerOptions) Logger {
31         if opts == nil {
32                 opts = &LoggerOptions{}
33         }
34
35         output := opts.Output
36         if output == nil {
37                 output = os.Stderr
38         }
39
40         level := opts.Level
41         if level == NoLevel {
42                 level = DefaultLevel
43         }
44
45         mtx := opts.Mutex
46         if mtx == nil {
47                 mtx = new(sync.Mutex)
48         }
49
50         ret := &intLogger{
51                 m:          mtx,
52                 json:       opts.JSONFormat,
53                 caller:     opts.IncludeLocation,
54                 name:       opts.Name,
55                 timeFormat: TimeFormat,
56                 w:          bufio.NewWriter(output),
57                 level:      new(int32),
58         }
59         if opts.TimeFormat != "" {
60                 ret.timeFormat = opts.TimeFormat
61         }
62         atomic.StoreInt32(ret.level, int32(level))
63         return ret
64 }
65
66 // The internal logger implementation. Internal in that it is defined entirely
67 // by this package.
68 type intLogger struct {
69         json       bool
70         caller     bool
71         name       string
72         timeFormat string
73
74         // this is a pointer so that it's shared by any derived loggers, since
75         // those derived loggers share the bufio.Writer as well.
76         m     *sync.Mutex
77         w     *bufio.Writer
78         level *int32
79
80         implied []interface{}
81 }
82
83 // Make sure that intLogger is a Logger
84 var _ Logger = &intLogger{}
85
86 // The time format to use for logging. This is a version of RFC3339 that
87 // contains millisecond precision
88 const TimeFormat = "2006-01-02T15:04:05.000Z0700"
89
90 // Log a message and a set of key/value pairs if the given level is at
91 // or more severe that the threshold configured in the Logger.
92 func (z *intLogger) Log(level Level, msg string, args ...interface{}) {
93         if level < Level(atomic.LoadInt32(z.level)) {
94                 return
95         }
96
97         t := time.Now()
98
99         z.m.Lock()
100         defer z.m.Unlock()
101
102         if z.json {
103                 z.logJson(t, level, msg, args...)
104         } else {
105                 z.log(t, level, msg, args...)
106         }
107
108         z.w.Flush()
109 }
110
111 // Cleanup a path by returning the last 2 segments of the path only.
112 func trimCallerPath(path string) string {
113         // lovely borrowed from zap
114         // nb. To make sure we trim the path correctly on Windows too, we
115         // counter-intuitively need to use '/' and *not* os.PathSeparator here,
116         // because the path given originates from Go stdlib, specifically
117         // runtime.Caller() which (as of Mar/17) returns forward slashes even on
118         // Windows.
119         //
120         // See https://github.com/golang/go/issues/3335
121         // and https://github.com/golang/go/issues/18151
122         //
123         // for discussion on the issue on Go side.
124         //
125
126         // Find the last separator.
127         //
128         idx := strings.LastIndexByte(path, '/')
129         if idx == -1 {
130                 return path
131         }
132
133         // Find the penultimate separator.
134         idx = strings.LastIndexByte(path[:idx], '/')
135         if idx == -1 {
136                 return path
137         }
138
139         return path[idx+1:]
140 }
141
142 // Non-JSON logging format function
143 func (z *intLogger) log(t time.Time, level Level, msg string, args ...interface{}) {
144         z.w.WriteString(t.Format(z.timeFormat))
145         z.w.WriteByte(' ')
146
147         s, ok := _levelToBracket[level]
148         if ok {
149                 z.w.WriteString(s)
150         } else {
151                 z.w.WriteString("[UNKN ]")
152         }
153
154         if z.caller {
155                 if _, file, line, ok := runtime.Caller(3); ok {
156                         z.w.WriteByte(' ')
157                         z.w.WriteString(trimCallerPath(file))
158                         z.w.WriteByte(':')
159                         z.w.WriteString(strconv.Itoa(line))
160                         z.w.WriteByte(':')
161                 }
162         }
163
164         z.w.WriteByte(' ')
165
166         if z.name != "" {
167                 z.w.WriteString(z.name)
168                 z.w.WriteString(": ")
169         }
170
171         z.w.WriteString(msg)
172
173         args = append(z.implied, args...)
174
175         var stacktrace CapturedStacktrace
176
177         if args != nil && len(args) > 0 {
178                 if len(args)%2 != 0 {
179                         cs, ok := args[len(args)-1].(CapturedStacktrace)
180                         if ok {
181                                 args = args[:len(args)-1]
182                                 stacktrace = cs
183                         } else {
184                                 args = append(args, "<unknown>")
185                         }
186                 }
187
188                 z.w.WriteByte(':')
189
190         FOR:
191                 for i := 0; i < len(args); i = i + 2 {
192                         var val string
193
194                         switch st := args[i+1].(type) {
195                         case string:
196                                 val = st
197                         case int:
198                                 val = strconv.FormatInt(int64(st), 10)
199                         case int64:
200                                 val = strconv.FormatInt(int64(st), 10)
201                         case int32:
202                                 val = strconv.FormatInt(int64(st), 10)
203                         case int16:
204                                 val = strconv.FormatInt(int64(st), 10)
205                         case int8:
206                                 val = strconv.FormatInt(int64(st), 10)
207                         case uint:
208                                 val = strconv.FormatUint(uint64(st), 10)
209                         case uint64:
210                                 val = strconv.FormatUint(uint64(st), 10)
211                         case uint32:
212                                 val = strconv.FormatUint(uint64(st), 10)
213                         case uint16:
214                                 val = strconv.FormatUint(uint64(st), 10)
215                         case uint8:
216                                 val = strconv.FormatUint(uint64(st), 10)
217                         case CapturedStacktrace:
218                                 stacktrace = st
219                                 continue FOR
220                         case Format:
221                                 val = fmt.Sprintf(st[0].(string), st[1:]...)
222                         default:
223                                 val = fmt.Sprintf("%v", st)
224                         }
225
226                         z.w.WriteByte(' ')
227                         z.w.WriteString(args[i].(string))
228                         z.w.WriteByte('=')
229
230                         if strings.ContainsAny(val, " \t\n\r") {
231                                 z.w.WriteByte('"')
232                                 z.w.WriteString(val)
233                                 z.w.WriteByte('"')
234                         } else {
235                                 z.w.WriteString(val)
236                         }
237                 }
238         }
239
240         z.w.WriteString("\n")
241
242         if stacktrace != "" {
243                 z.w.WriteString(string(stacktrace))
244         }
245 }
246
247 // JSON logging function
248 func (z *intLogger) logJson(t time.Time, level Level, msg string, args ...interface{}) {
249         vals := map[string]interface{}{
250                 "@message":   msg,
251                 "@timestamp": t.Format("2006-01-02T15:04:05.000000Z07:00"),
252         }
253
254         var levelStr string
255         switch level {
256         case Error:
257                 levelStr = "error"
258         case Warn:
259                 levelStr = "warn"
260         case Info:
261                 levelStr = "info"
262         case Debug:
263                 levelStr = "debug"
264         case Trace:
265                 levelStr = "trace"
266         default:
267                 levelStr = "all"
268         }
269
270         vals["@level"] = levelStr
271
272         if z.name != "" {
273                 vals["@module"] = z.name
274         }
275
276         if z.caller {
277                 if _, file, line, ok := runtime.Caller(3); ok {
278                         vals["@caller"] = fmt.Sprintf("%s:%d", file, line)
279                 }
280         }
281
282         args = append(z.implied, args...)
283
284         if args != nil && len(args) > 0 {
285                 if len(args)%2 != 0 {
286                         cs, ok := args[len(args)-1].(CapturedStacktrace)
287                         if ok {
288                                 args = args[:len(args)-1]
289                                 vals["stacktrace"] = cs
290                         } else {
291                                 args = append(args, "<unknown>")
292                         }
293                 }
294
295                 for i := 0; i < len(args); i = i + 2 {
296                         if _, ok := args[i].(string); !ok {
297                                 // As this is the logging function not much we can do here
298                                 // without injecting into logs...
299                                 continue
300                         }
301                         val := args[i+1]
302                         switch sv := val.(type) {
303                         case error:
304                                 // Check if val is of type error. If error type doesn't
305                                 // implement json.Marshaler or encoding.TextMarshaler
306                                 // then set val to err.Error() so that it gets marshaled
307                                 switch sv.(type) {
308                                 case json.Marshaler, encoding.TextMarshaler:
309                                 default:
310                                         val = sv.Error()
311                                 }
312                         case Format:
313                                 val = fmt.Sprintf(sv[0].(string), sv[1:]...)
314                         }
315
316                         vals[args[i].(string)] = val
317                 }
318         }
319
320         err := json.NewEncoder(z.w).Encode(vals)
321         if err != nil {
322                 panic(err)
323         }
324 }
325
326 // Emit the message and args at DEBUG level
327 func (z *intLogger) Debug(msg string, args ...interface{}) {
328         z.Log(Debug, msg, args...)
329 }
330
331 // Emit the message and args at TRACE level
332 func (z *intLogger) Trace(msg string, args ...interface{}) {
333         z.Log(Trace, msg, args...)
334 }
335
336 // Emit the message and args at INFO level
337 func (z *intLogger) Info(msg string, args ...interface{}) {
338         z.Log(Info, msg, args...)
339 }
340
341 // Emit the message and args at WARN level
342 func (z *intLogger) Warn(msg string, args ...interface{}) {
343         z.Log(Warn, msg, args...)
344 }
345
346 // Emit the message and args at ERROR level
347 func (z *intLogger) Error(msg string, args ...interface{}) {
348         z.Log(Error, msg, args...)
349 }
350
351 // Indicate that the logger would emit TRACE level logs
352 func (z *intLogger) IsTrace() bool {
353         return Level(atomic.LoadInt32(z.level)) == Trace
354 }
355
356 // Indicate that the logger would emit DEBUG level logs
357 func (z *intLogger) IsDebug() bool {
358         return Level(atomic.LoadInt32(z.level)) <= Debug
359 }
360
361 // Indicate that the logger would emit INFO level logs
362 func (z *intLogger) IsInfo() bool {
363         return Level(atomic.LoadInt32(z.level)) <= Info
364 }
365
366 // Indicate that the logger would emit WARN level logs
367 func (z *intLogger) IsWarn() bool {
368         return Level(atomic.LoadInt32(z.level)) <= Warn
369 }
370
371 // Indicate that the logger would emit ERROR level logs
372 func (z *intLogger) IsError() bool {
373         return Level(atomic.LoadInt32(z.level)) <= Error
374 }
375
376 // Return a sub-Logger for which every emitted log message will contain
377 // the given key/value pairs. This is used to create a context specific
378 // Logger.
379 func (z *intLogger) With(args ...interface{}) Logger {
380         if len(args)%2 != 0 {
381                 panic("With() call requires paired arguments")
382         }
383
384         var nz intLogger = *z
385
386         result := make(map[string]interface{}, len(z.implied)+len(args))
387         keys := make([]string, 0, len(z.implied)+len(args))
388
389         // Read existing args, store map and key for consistent sorting
390         for i := 0; i < len(z.implied); i += 2 {
391                 key := z.implied[i].(string)
392                 keys = append(keys, key)
393                 result[key] = z.implied[i+1]
394         }
395         // Read new args, store map and key for consistent sorting
396         for i := 0; i < len(args); i += 2 {
397                 key := args[i].(string)
398                 _, exists := result[key]
399                 if !exists {
400                         keys = append(keys, key)
401                 }
402                 result[key] = args[i+1]
403         }
404
405         // Sort keys to be consistent
406         sort.Strings(keys)
407
408         nz.implied = make([]interface{}, 0, len(z.implied)+len(args))
409         for _, k := range keys {
410                 nz.implied = append(nz.implied, k)
411                 nz.implied = append(nz.implied, result[k])
412         }
413
414         return &nz
415 }
416
417 // Create a new sub-Logger that a name decending from the current name.
418 // This is used to create a subsystem specific Logger.
419 func (z *intLogger) Named(name string) Logger {
420         var nz intLogger = *z
421
422         if nz.name != "" {
423                 nz.name = nz.name + "." + name
424         } else {
425                 nz.name = name
426         }
427
428         return &nz
429 }
430
431 // Create a new sub-Logger with an explicit name. This ignores the current
432 // name. This is used to create a standalone logger that doesn't fall
433 // within the normal hierarchy.
434 func (z *intLogger) ResetNamed(name string) Logger {
435         var nz intLogger = *z
436
437         nz.name = name
438
439         return &nz
440 }
441
442 // Update the logging level on-the-fly. This will affect all subloggers as
443 // well.
444 func (z *intLogger) SetLevel(level Level) {
445         atomic.StoreInt32(z.level, int32(level))
446 }
447
448 // Create a *log.Logger that will send it's data through this Logger. This
449 // allows packages that expect to be using the standard library log to actually
450 // use this logger.
451 func (z *intLogger) StandardLogger(opts *StandardLoggerOptions) *log.Logger {
452         if opts == nil {
453                 opts = &StandardLoggerOptions{}
454         }
455
456         return log.New(&stdlogAdapter{z, opts.InferLevels}, "", 0)
457 }