github.com/crowdsecurity/crowdsec@v1.6.1/pkg/csplugin/hclog_adapter.go (about) 1 // Copyright 2021 Workrise Technologies Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package csplugin 16 17 import ( 18 "fmt" 19 "io" 20 "log" 21 "os" 22 "reflect" 23 24 "github.com/hashicorp/go-hclog" 25 "github.com/sirupsen/logrus" 26 ) 27 28 // NewHCLogAdapter takes an instance of a Logrus logger and returns an hclog 29 // logger in the form of an HCLogAdapter. 30 func NewHCLogAdapter(l *logrus.Logger, name string) hclog.Logger { 31 return &HCLogAdapter{l, name, nil} 32 } 33 34 // HCLogAdapter implements the hclog interface. Plugins use hclog to send 35 // log entries back to ephemeral-iam and this adapter allows for those logs 36 // to be handled by ephemeral-iam's Logrus logger. 37 type HCLogAdapter struct { 38 log *logrus.Logger 39 name string 40 41 impliedArgs []interface{} 42 } 43 44 func (h HCLogAdapter) Log(level hclog.Level, msg string, args ...interface{}) { 45 switch level { 46 case hclog.NoLevel: 47 return 48 case hclog.Trace: 49 h.Trace(msg, args...) 50 case hclog.Debug: 51 h.Debug(msg, args...) 52 case hclog.Info: 53 h.Info(msg, args...) 54 case hclog.Warn: 55 h.Warn(msg, args...) 56 case hclog.Error: 57 h.Error(msg, args...) 58 } 59 } 60 61 func (h HCLogAdapter) Trace(msg string, args ...interface{}) { 62 h.log.WithFields(toLogrusFields(args)).Trace(msg) 63 } 64 65 func (h HCLogAdapter) Debug(msg string, args ...interface{}) { 66 h.log.WithFields(toLogrusFields(args)).Debug(msg) 67 } 68 69 func (h HCLogAdapter) Info(msg string, args ...interface{}) { 70 h.log.WithFields(toLogrusFields(args)).Info(msg) 71 } 72 73 func (h HCLogAdapter) Warn(msg string, args ...interface{}) { 74 h.log.WithFields(toLogrusFields(args)).Warn(msg) 75 } 76 77 func (h HCLogAdapter) Error(msg string, args ...interface{}) { 78 h.log.WithFields(toLogrusFields(args)).Error(msg) 79 } 80 81 func (h HCLogAdapter) IsTrace() bool { 82 return h.log.GetLevel() >= logrus.TraceLevel 83 } 84 85 func (h HCLogAdapter) IsDebug() bool { 86 return h.log.GetLevel() >= logrus.DebugLevel 87 } 88 89 func (h HCLogAdapter) IsInfo() bool { 90 return h.log.GetLevel() >= logrus.InfoLevel 91 } 92 93 func (h HCLogAdapter) IsWarn() bool { 94 return h.log.GetLevel() >= logrus.WarnLevel 95 } 96 97 func (h HCLogAdapter) IsError() bool { 98 return h.log.GetLevel() >= logrus.ErrorLevel 99 } 100 101 func (h HCLogAdapter) ImpliedArgs() []interface{} { 102 // Not supported. 103 return nil 104 } 105 106 func (h HCLogAdapter) With(args ...interface{}) hclog.Logger { 107 return &h 108 } 109 110 func (h HCLogAdapter) Name() string { 111 return h.name 112 } 113 114 func (h HCLogAdapter) Named(name string) hclog.Logger { 115 return NewHCLogAdapter(h.log, name) 116 } 117 118 func (h HCLogAdapter) ResetNamed(name string) hclog.Logger { 119 return &h 120 } 121 122 func (h *HCLogAdapter) SetLevel(level hclog.Level) { 123 h.log.SetLevel(level2logrus(level)) 124 } 125 126 func (h HCLogAdapter) GetLevel() hclog.Level { 127 return level2hclog(h.log.GetLevel()) 128 } 129 130 func (h HCLogAdapter) StandardLogger(opts *hclog.StandardLoggerOptions) *log.Logger { 131 if opts == nil { 132 opts = &hclog.StandardLoggerOptions{} 133 } 134 return log.New(h.StandardWriter(opts), "", 0) 135 } 136 137 func (h HCLogAdapter) StandardWriter(opts *hclog.StandardLoggerOptions) io.Writer { 138 return os.Stderr 139 } 140 141 // level2logrus maps hclog levels to Logrus levels. 142 func level2logrus(level hclog.Level) logrus.Level { 143 switch level { 144 case hclog.NoLevel: 145 // Logrus does not have NoLevel, so use Info instead. 146 return logrus.InfoLevel 147 case hclog.Trace: 148 return logrus.TraceLevel 149 case hclog.Debug: 150 return logrus.DebugLevel 151 case hclog.Info: 152 return logrus.InfoLevel 153 case hclog.Warn: 154 return logrus.WarnLevel 155 case hclog.Error: 156 return logrus.ErrorLevel 157 default: 158 return logrus.InfoLevel 159 } 160 } 161 162 func level2hclog(level logrus.Level) hclog.Level { 163 switch level { 164 case logrus.TraceLevel: 165 return hclog.Trace 166 case logrus.DebugLevel: 167 return hclog.Debug 168 case logrus.InfoLevel: 169 return hclog.Info 170 case logrus.WarnLevel: 171 return hclog.Warn 172 case logrus.ErrorLevel: 173 return hclog.Error 174 default: 175 return hclog.Info 176 } 177 } 178 179 // toLogrusFields takes a list of key/value pairs passed to the hclog logger 180 // and converts them to a map to be used as Logrus fields. 181 func toLogrusFields(kvPairs []interface{}) map[string]interface{} { 182 m := map[string]interface{}{} 183 if len(kvPairs) == 0 { 184 return m 185 } 186 187 if len(kvPairs)%2 == 1 { 188 // There are an odd number of key/value pairs so append nil as the final value. 189 kvPairs = append(kvPairs, nil) 190 } 191 192 for i := 0; i < len(kvPairs); i += 2 { 193 // hclog automatically adds the timestamp field, ignore it. 194 if kvPairs[i] != "timestamp" { 195 merge(m, kvPairs[i], kvPairs[i+1]) 196 } 197 } 198 return m 199 } 200 201 // merge takes a key/value pair and converts them to strings then adds them to 202 // the dst map. 203 func merge(dst map[string]interface{}, k, v interface{}) { 204 var key string 205 206 switch x := k.(type) { 207 case string: 208 key = x 209 case fmt.Stringer: 210 key = safeString(x) 211 default: 212 key = fmt.Sprint(x) 213 } 214 215 dst[key] = v 216 } 217 218 // safeString takes an interface that implements the String() function and calls it 219 // to attempt to convert it to a string. If a panic occurs, and it's caused by a 220 // nil pointer, the value will be set to "NULL". 221 func safeString(str fmt.Stringer) (s string) { 222 defer func() { 223 if panicVal := recover(); panicVal != nil { 224 if v := reflect.ValueOf(str); v.Kind() == reflect.Ptr && v.IsNil() { 225 s = "NULL" 226 } else { 227 panic(panicVal) 228 } 229 } 230 }() 231 232 s = str.String() 233 return 234 }