]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blobdiff - 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
index 9f90c2877aebbb5057a8b2ad504099ff91602959..2aaa1f895d57c18c752c86e644d57418710d16bb 100644 (file)
@@ -2,14 +2,19 @@ package hclog
 
 import (
        "bufio"
+       "bytes"
+       "encoding"
        "encoding/json"
        "fmt"
        "log"
        "os"
+       "reflect"
        "runtime"
+       "sort"
        "strconv"
        "strings"
        "sync"
+       "sync/atomic"
        "time"
 )
 
@@ -17,8 +22,8 @@ var (
        _levelToBracket = map[Level]string{
                Debug: "[DEBUG]",
                Trace: "[TRACE]",
-               Info:  "[INFO ]",
-               Warn:  "[WARN ]",
+               Info:  "[INFO",
+               Warn:  "[WARN",
                Error: "[ERROR]",
        }
 )
@@ -39,28 +44,40 @@ func New(opts *LoggerOptions) Logger {
                level = DefaultLevel
        }
 
-       return &intLogger{
-               m:      new(sync.Mutex),
-               json:   opts.JSONFormat,
-               caller: opts.IncludeLocation,
-               name:   opts.Name,
-               w:      bufio.NewWriter(output),
-               level:  level,
+       mtx := opts.Mutex
+       if mtx == nil {
+               mtx = new(sync.Mutex)
        }
+
+       ret := &intLogger{
+               m:          mtx,
+               json:       opts.JSONFormat,
+               caller:     opts.IncludeLocation,
+               name:       opts.Name,
+               timeFormat: TimeFormat,
+               w:          bufio.NewWriter(output),
+               level:      new(int32),
+       }
+       if opts.TimeFormat != "" {
+               ret.timeFormat = opts.TimeFormat
+       }
+       atomic.StoreInt32(ret.level, int32(level))
+       return ret
 }
 
 // The internal logger implementation. Internal in that it is defined entirely
 // by this package.
 type intLogger struct {
-       json   bool
-       caller bool
-       name   string
+       json       bool
+       caller     bool
+       name       string
+       timeFormat string
 
        // this is a pointer so that it's shared by any derived loggers, since
        // those derived loggers share the bufio.Writer as well.
        m     *sync.Mutex
        w     *bufio.Writer
-       level Level
+       level *int32
 
        implied []interface{}
 }
@@ -75,7 +92,7 @@ const TimeFormat = "2006-01-02T15:04:05.000Z0700"
 // Log a message and a set of key/value pairs if the given level is at
 // or more severe that the threshold configured in the Logger.
 func (z *intLogger) Log(level Level, msg string, args ...interface{}) {
-       if level < z.level {
+       if level < Level(atomic.LoadInt32(z.level)) {
                return
        }
 
@@ -126,14 +143,14 @@ func trimCallerPath(path string) string {
 
 // Non-JSON logging format function
 func (z *intLogger) log(t time.Time, level Level, msg string, args ...interface{}) {
-       z.w.WriteString(t.Format(TimeFormat))
+       z.w.WriteString(t.Format(z.timeFormat))
        z.w.WriteByte(' ')
 
        s, ok := _levelToBracket[level]
        if ok {
                z.w.WriteString(s)
        } else {
-               z.w.WriteString("[UNKN ]")
+               z.w.WriteString("[?????]")
        }
 
        if z.caller {
@@ -174,7 +191,10 @@ func (z *intLogger) log(t time.Time, level Level, msg string, args ...interface{
 
        FOR:
                for i := 0; i < len(args); i = i + 2 {
-                       var val string
+                       var (
+                               val string
+                               raw bool
+                       )
 
                        switch st := args[i+1].(type) {
                        case string:
@@ -202,15 +222,23 @@ func (z *intLogger) log(t time.Time, level Level, msg string, args ...interface{
                        case CapturedStacktrace:
                                stacktrace = st
                                continue FOR
+                       case Format:
+                               val = fmt.Sprintf(st[0].(string), st[1:]...)
                        default:
-                               val = fmt.Sprintf("%v", st)
+                               v := reflect.ValueOf(st)
+                               if v.Kind() == reflect.Slice {
+                                       val = z.renderSlice(v)
+                                       raw = true
+                               } else {
+                                       val = fmt.Sprintf("%v", st)
+                               }
                        }
 
                        z.w.WriteByte(' ')
                        z.w.WriteString(args[i].(string))
                        z.w.WriteByte('=')
 
-                       if strings.ContainsAny(val, " \t\n\r") {
+                       if !raw && strings.ContainsAny(val, " \t\n\r") {
                                z.w.WriteByte('"')
                                z.w.WriteString(val)
                                z.w.WriteByte('"')
@@ -227,6 +255,45 @@ func (z *intLogger) log(t time.Time, level Level, msg string, args ...interface{
        }
 }
 
+func (z *intLogger) renderSlice(v reflect.Value) string {
+       var buf bytes.Buffer
+
+       buf.WriteRune('[')
+
+       for i := 0; i < v.Len(); i++ {
+               if i > 0 {
+                       buf.WriteString(", ")
+               }
+
+               sv := v.Index(i)
+
+               var val string
+
+               switch sv.Kind() {
+               case reflect.String:
+                       val = sv.String()
+               case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64:
+                       val = strconv.FormatInt(sv.Int(), 10)
+               case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+                       val = strconv.FormatUint(sv.Uint(), 10)
+               default:
+                       val = fmt.Sprintf("%v", sv.Interface())
+               }
+
+               if strings.ContainsAny(val, " \t\n\r") {
+                       buf.WriteByte('"')
+                       buf.WriteString(val)
+                       buf.WriteByte('"')
+               } else {
+                       buf.WriteString(val)
+               }
+       }
+
+       buf.WriteRune(']')
+
+       return buf.String()
+}
+
 // JSON logging function
 func (z *intLogger) logJson(t time.Time, level Level, msg string, args ...interface{}) {
        vals := map[string]interface{}{
@@ -262,6 +329,8 @@ func (z *intLogger) logJson(t time.Time, level Level, msg string, args ...interf
                }
        }
 
+       args = append(z.implied, args...)
+
        if args != nil && len(args) > 0 {
                if len(args)%2 != 0 {
                        cs, ok := args[len(args)-1].(CapturedStacktrace)
@@ -279,7 +348,22 @@ func (z *intLogger) logJson(t time.Time, level Level, msg string, args ...interf
                                // without injecting into logs...
                                continue
                        }
-                       vals[args[i].(string)] = args[i+1]
+                       val := args[i+1]
+                       switch sv := val.(type) {
+                       case error:
+                               // Check if val is of type error. If error type doesn't
+                               // implement json.Marshaler or encoding.TextMarshaler
+                               // then set val to err.Error() so that it gets marshaled
+                               switch sv.(type) {
+                               case json.Marshaler, encoding.TextMarshaler:
+                               default:
+                                       val = sv.Error()
+                               }
+                       case Format:
+                               val = fmt.Sprintf(sv[0].(string), sv[1:]...)
+                       }
+
+                       vals[args[i].(string)] = val
                }
        }
 
@@ -316,36 +400,66 @@ func (z *intLogger) Error(msg string, args ...interface{}) {
 
 // Indicate that the logger would emit TRACE level logs
 func (z *intLogger) IsTrace() bool {
-       return z.level == Trace
+       return Level(atomic.LoadInt32(z.level)) == Trace
 }
 
 // Indicate that the logger would emit DEBUG level logs
 func (z *intLogger) IsDebug() bool {
-       return z.level <= Debug
+       return Level(atomic.LoadInt32(z.level)) <= Debug
 }
 
 // Indicate that the logger would emit INFO level logs
 func (z *intLogger) IsInfo() bool {
-       return z.level <= Info
+       return Level(atomic.LoadInt32(z.level)) <= Info
 }
 
 // Indicate that the logger would emit WARN level logs
 func (z *intLogger) IsWarn() bool {
-       return z.level <= Warn
+       return Level(atomic.LoadInt32(z.level)) <= Warn
 }
 
 // Indicate that the logger would emit ERROR level logs
 func (z *intLogger) IsError() bool {
-       return z.level <= Error
+       return Level(atomic.LoadInt32(z.level)) <= Error
 }
 
 // Return a sub-Logger for which every emitted log message will contain
 // the given key/value pairs. This is used to create a context specific
 // Logger.
 func (z *intLogger) With(args ...interface{}) Logger {
+       if len(args)%2 != 0 {
+               panic("With() call requires paired arguments")
+       }
+
        var nz intLogger = *z
 
-       nz.implied = append(nz.implied, args...)
+       result := make(map[string]interface{}, len(z.implied)+len(args))
+       keys := make([]string, 0, len(z.implied)+len(args))
+
+       // Read existing args, store map and key for consistent sorting
+       for i := 0; i < len(z.implied); i += 2 {
+               key := z.implied[i].(string)
+               keys = append(keys, key)
+               result[key] = z.implied[i+1]
+       }
+       // Read new args, store map and key for consistent sorting
+       for i := 0; i < len(args); i += 2 {
+               key := args[i].(string)
+               _, exists := result[key]
+               if !exists {
+                       keys = append(keys, key)
+               }
+               result[key] = args[i+1]
+       }
+
+       // Sort keys to be consistent
+       sort.Strings(keys)
+
+       nz.implied = make([]interface{}, 0, len(z.implied)+len(args))
+       for _, k := range keys {
+               nz.implied = append(nz.implied, k)
+               nz.implied = append(nz.implied, result[k])
+       }
 
        return &nz
 }
@@ -357,6 +471,8 @@ func (z *intLogger) Named(name string) Logger {
 
        if nz.name != "" {
                nz.name = nz.name + "." + name
+       } else {
+               nz.name = name
        }
 
        return &nz
@@ -373,6 +489,12 @@ func (z *intLogger) ResetNamed(name string) Logger {
        return &nz
 }
 
+// Update the logging level on-the-fly. This will affect all subloggers as
+// well.
+func (z *intLogger) SetLevel(level Level) {
+       atomic.StoreInt32(z.level, int32(level))
+}
+
 // Create a *log.Logger that will send it's data through this Logger. This
 // allows packages that expect to be using the standard library log to actually
 // use this logger.