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