github.com/mailgun/holster/v4@v4.20.0/consul/log.go (about) 1 package consul 2 3 import ( 4 "bytes" 5 "io" 6 "log" 7 "os" 8 9 "github.com/hashicorp/go-hclog" 10 "github.com/sirupsen/logrus" 11 ) 12 13 // HCLogAdapter implements the hclog interface, and wraps it 14 // around a Logrus entry 15 type HCLogAdapter struct { 16 log logrus.FieldLogger 17 name string 18 args []interface{} // key/value pairs if this logger was created via With() 19 } 20 21 func NewHCLogAdapter(logger logrus.FieldLogger, name string) *HCLogAdapter { 22 return &HCLogAdapter{ 23 log: logger, 24 name: name, 25 } 26 } 27 28 // HCLog has one more level than we do. As such, we will never 29 // set trace level. 30 func (*HCLogAdapter) Trace(_ string, _ ...interface{}) { 31 } 32 33 func (a *HCLogAdapter) Debug(msg string, args ...interface{}) { 34 a.CreateEntry(args).Debug(msg) 35 } 36 37 func (a *HCLogAdapter) Info(msg string, args ...interface{}) { 38 a.CreateEntry(args).Info(msg) 39 } 40 41 func (a *HCLogAdapter) Warn(msg string, args ...interface{}) { 42 a.CreateEntry(args).Warn(msg) 43 } 44 45 func (a *HCLogAdapter) Error(msg string, args ...interface{}) { 46 a.CreateEntry(args).Error(msg) 47 } 48 49 func (a *HCLogAdapter) Log(level hclog.Level, msg string, args ...interface{}) { 50 switch level { 51 case hclog.Trace: 52 a.Trace(msg, args...) 53 case hclog.Debug: 54 a.Debug(msg, args...) 55 case hclog.Info: 56 a.Info(msg, args...) 57 case hclog.Warn: 58 a.Warn(msg, args...) 59 case hclog.Error: 60 a.Error(msg, args...) 61 } 62 } 63 64 func (a *HCLogAdapter) IsTrace() bool { 65 return false 66 } 67 68 func (a *HCLogAdapter) IsDebug() bool { 69 return a.shouldEmit(logrus.DebugLevel) 70 } 71 72 func (a *HCLogAdapter) IsInfo() bool { 73 return a.shouldEmit(logrus.InfoLevel) 74 } 75 76 func (a *HCLogAdapter) IsWarn() bool { 77 return a.shouldEmit(logrus.WarnLevel) 78 } 79 80 func (a *HCLogAdapter) IsError() bool { 81 return a.shouldEmit(logrus.ErrorLevel) 82 } 83 84 func (a *HCLogAdapter) SetLevel(hclog.Level) { 85 // interface definition says it is ok for this to be a noop if 86 // implementations don't need/want to support dynamic level changing, which 87 // we don't currently. 88 } 89 90 func (a *HCLogAdapter) GetLevel() hclog.Level { 91 return hclog.Level(a.log.WithFields(logrus.Fields{}).Level) 92 } 93 94 func (a *HCLogAdapter) With(args ...interface{}) hclog.Logger { 95 e := a.CreateEntry(args) 96 return &HCLogAdapter{ 97 log: e, 98 args: concatFields(a.args, args), 99 } 100 } 101 102 // concatFields combines two sets of key/value pairs. 103 // It allocates a new slice to avoid using append() and 104 // accidentally overriding the original slice a, e.g. 105 // when logger.With() is called multiple times to create 106 // sub-scoped loggers. 107 func concatFields(a, b []interface{}) []interface{} { 108 c := make([]interface{}, len(a)+len(b)) 109 copy(c, a) 110 copy(c[len(a):], b) 111 return c 112 } 113 114 // ImpliedArgs returns With key/value pairs 115 func (a *HCLogAdapter) ImpliedArgs() []interface{} { 116 return a.args 117 } 118 119 func (a *HCLogAdapter) Name() string { 120 return a.name 121 } 122 123 func (a *HCLogAdapter) Named(name string) hclog.Logger { 124 var newName bytes.Buffer 125 if a.name != "" { 126 newName.WriteString(a.name) 127 newName.WriteString(".") 128 } 129 newName.WriteString(name) 130 131 return a.ResetNamed(newName.String()) 132 } 133 134 func (a *HCLogAdapter) ResetNamed(name string) hclog.Logger { 135 fields := []interface{}{"subsystem_name", name} 136 e := a.CreateEntry(fields) 137 return &HCLogAdapter{log: e, name: name} 138 } 139 140 // StandardLogger is meant to return a stdlib Logger type which wraps around 141 // hclog. It does this by providing an io.Writer and instantiating a new 142 // Logger. It then tries to interpret the log level by parsing the message. 143 // 144 // Since we are not using `hclog` in a generic way, and I cannot find any 145 // calls to this method from go-plugin, we will poorly support this method. 146 // Rather than pull in all of hclog writer parsing logic, pass it a Logrus 147 // writer, and hardcode the level to INFO. 148 // 149 // Apologies to those who find themselves here. 150 func (a *HCLogAdapter) StandardLogger(opts *hclog.StandardLoggerOptions) *log.Logger { 151 entry := a.log.WithFields(logrus.Fields{}) 152 return log.New(entry.WriterLevel(logrus.InfoLevel), "", 0) 153 } 154 155 func (a *HCLogAdapter) StandardWriter(opts *hclog.StandardLoggerOptions) io.Writer { 156 var w io.Writer 157 logger, ok := a.log.(*logrus.Logger) 158 if ok { 159 w = logger.Out 160 } 161 if w == nil { 162 w = os.Stderr 163 } 164 return w 165 } 166 167 func (a *HCLogAdapter) shouldEmit(level logrus.Level) bool { 168 return a.log.WithFields(logrus.Fields{}).Level >= level 169 } 170 171 func (a *HCLogAdapter) CreateEntry(args []interface{}) *logrus.Entry { 172 if len(args)%2 != 0 { 173 args = append(args, "<unknown>") 174 } 175 176 fields := make(logrus.Fields) 177 for i := 0; i < len(args); i += 2 { 178 k, ok := args[i].(string) 179 if !ok { 180 continue 181 } 182 v := args[i+1] 183 fields[k] = v 184 } 185 186 return a.log.WithFields(fields) 187 }