github.com/ethersphere/bee/v2@v2.2.0/pkg/log/logger.go (about) 1 // Copyright 2022 The Swarm Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package log 6 7 import ( 8 "bytes" 9 "fmt" 10 "io" 11 "os" 12 "reflect" 13 "strings" 14 "time" 15 16 m "github.com/ethersphere/bee/v2/pkg/metrics" 17 "github.com/hashicorp/go-multierror" 18 "github.com/prometheus/client_golang/prometheus" 19 ) 20 21 var _ Logger = (*logger)(nil) 22 23 // levelHooks is a helper type for storing and 24 // help triggering the hooks on a logger instance. 25 type levelHooks map[Level][]Hook 26 27 // fire triggers all the hooks for the given level. 28 // If level V is enabled in debug verbosity, then 29 // the VerbosityAll hooks are triggered. 30 func (lh levelHooks) fire(level Level) error { 31 if level > VerbosityDebug { 32 level = VerbosityAll 33 } 34 for _, hook := range lh[level] { 35 if err := hook.Fire(level); err != nil { 36 return err 37 } 38 } 39 return nil 40 } 41 42 type builder struct { 43 l *logger 44 45 // clone indicates whether this builder was cloned. 46 cloned bool 47 48 // v level represents the granularity of debug calls. 49 v uint 50 51 // names represents a path in the tree, 52 // element 0 is the root of the tree. 53 names []string 54 55 // namesStr is a cache of render names slice, so 56 // we don't have to render them on each Build call. 57 namesStr string 58 59 // values holds additional key/value pairs 60 // that are included on every log call. 61 values []interface{} 62 63 // valuesStr is a cache of render values slice, so 64 // we don't have to render them on each Build call. 65 valuesStr string 66 } 67 68 // V implements the Builder interface V method. 69 func (b *builder) V(level uint) Builder { 70 if level > 0 { 71 c := b.clone() 72 c.v += level 73 return c 74 } 75 return b 76 } 77 78 // WithName implements the Builder interface WithName method. 79 func (b *builder) WithName(name string) Builder { 80 c := b.clone() 81 c.names = append(c.names, name) 82 return c 83 } 84 85 // WithValues implements the Builder interface WithValues method. 86 func (b *builder) WithValues(keysAndValues ...interface{}) Builder { 87 c := b.clone() 88 c.values = append(c.values, keysAndValues...) 89 return c 90 } 91 92 // Build implements the Builder interface Build method. 93 func (b *builder) Build() Logger { 94 if !b.cloned && b.l.id != "" { 95 return b.l 96 } 97 98 b.namesStr = strings.Join(b.names, "/") 99 // ~5 is the average length of an English word; 4 is the rune size. 100 bufCap := nextPowOf2(uint64(5 * 4 * len(b.values))) 101 buf := bytes.NewBuffer(make([]byte, 0, bufCap)) 102 b.l.formatter.flatten(buf, b.values, false, false) 103 b.valuesStr = buf.String() 104 105 key := hash(b.namesStr, b.v, b.valuesStr, b.l.sink) 106 if i, ok := loggers.Load(key); ok { 107 // Nothing to build, the instance exists. 108 return i.(*logger) 109 } 110 // A new child instance. 111 c := *b.l 112 b.l = &c 113 c.builder = b 114 c.cloned = false 115 c.id = key 116 117 return &c 118 } 119 120 // Register implements the Builder interface Register method. 121 func (b *builder) Register() Logger { 122 val := b.Build() 123 key := hash(b.namesStr, b.v, b.valuesStr, b.l.sink) 124 res, _ := loggers.LoadOrStore(key, val) 125 return res.(*logger) 126 } 127 128 func (b *builder) clone() *builder { 129 if b.cloned { 130 return b 131 } 132 133 c := *b 134 c.cloned = true 135 c.names = append(make([]string, 0, len(c.names)), c.names...) 136 c.values = append(make([]interface{}, 0, len(c.values)), c.values...) 137 return &c 138 } 139 140 // logger implements the Logger interface. 141 type logger struct { 142 *builder 143 144 // id is the unique identifier of a logger. 145 // It identifies the instance of a logger in the logger registry. 146 id string 147 148 // formatter formats log messages before they are written to the sink. 149 formatter *formatter 150 151 // verbosity represents the current verbosity level. 152 // This variable is used to modify the verbosity of the logger instance. 153 // Higher values enable more logs. Logs at or below this level 154 // will be written, while logs above this level will be discarded. 155 verbosity Level 156 157 // sink represents the stream where the logs are written. 158 sink io.Writer 159 160 // levelHooks allow triggering of registered hooks 161 // on their associated severity log levels. 162 levelHooks levelHooks 163 164 // metrics collects basic statistics about logged messages. 165 metrics *metrics 166 } 167 168 // Metrics implements metrics.Collector interface. 169 func (l *logger) Metrics() []prometheus.Collector { 170 return m.PrometheusCollectorsFromFields(l.metrics) 171 } 172 173 // Verbosity implements the Logger interface Verbosity method. 174 func (l *logger) Verbosity() Level { 175 return l.verbosity.get() 176 } 177 178 // Debug implements the Logger interface Debug method. 179 func (l *logger) Debug(msg string, keysAndValues ...interface{}) { 180 if int(l.verbosity.get()) >= int(l.v) { 181 if err := l.log(VerbosityDebug, CategoryDebug, nil, msg, keysAndValues...); err != nil { 182 fmt.Fprintln(os.Stderr, err) 183 } 184 } 185 } 186 187 // Info implements the Logger interface Info method. 188 func (l *logger) Info(msg string, keysAndValues ...interface{}) { 189 if l.verbosity.get() >= VerbosityInfo { 190 if err := l.log(VerbosityInfo, CategoryInfo, nil, msg, keysAndValues...); err != nil { 191 fmt.Fprintln(os.Stderr, err) 192 } 193 } 194 } 195 196 // Warning implements the Logger interface Warning method. 197 func (l *logger) Warning(msg string, keysAndValues ...interface{}) { 198 if l.verbosity.get() >= VerbosityWarning { 199 if err := l.log(VerbosityWarning, CategoryWarning, nil, msg, keysAndValues...); err != nil { 200 fmt.Fprintln(os.Stderr, err) 201 } 202 } 203 } 204 205 // Error implements the Logger interface Error method. 206 func (l *logger) Error(err error, msg string, keysAndValues ...interface{}) { 207 if l.verbosity.get() >= VerbosityError { 208 if err := l.log(VerbosityError, CategoryError, err, msg, keysAndValues...); err != nil { 209 fmt.Fprintln(os.Stderr, err) 210 } 211 } 212 } 213 214 // setVerbosity changes the verbosity level or the logger. 215 func (l *logger) setVerbosity(v Level) { 216 l.verbosity.set(v) 217 } 218 219 // log logs the given msg and key-value pairs with the given level 220 // and the given message category caller (if enabled) to the sink. 221 func (l *logger) log(vl Level, mc MessageCategory, err error, msg string, keysAndValues ...interface{}) error { 222 base := make([]interface{}, 0, 14+len(keysAndValues)) 223 if l.formatter.opts.logTimestamp { 224 base = append(base, "time", time.Now().Format(l.formatter.opts.timestampLayout)) 225 } 226 base = append(base, "level", vl.String(), "logger", l.namesStr) 227 if vl == VerbosityDebug && l.v > 0 { 228 base = append(base, "v", l.v) 229 } 230 if policy := l.formatter.opts.caller; policy == CategoryAll || policy == mc { 231 base = append(base, "caller", l.formatter.caller()) 232 } 233 base = append(base, "msg", msg) 234 if vl == VerbosityError { 235 if err != nil { 236 base = append(base, "error", err.Error()) 237 } 238 } 239 if len(l.values) > 0 { 240 base = append(base, l.values...) 241 } 242 buf := l.formatter.render(base, keysAndValues) 243 244 var merr *multierror.Error 245 if _, err = l.sink.Write(buf); err != nil { 246 merr = multierror.Append( 247 merr, 248 fmt.Errorf("log %s: failed to write message: %w", vl, err), 249 ) 250 } 251 if err := l.levelHooks.fire(vl + Level(l.v)); err != nil { 252 merr = multierror.Append( 253 merr, 254 fmt.Errorf("log %s: failed to fire hooks: %w", vl, err), 255 ) 256 } 257 return merr.ErrorOrNil() 258 } 259 260 // hash is a hashing function for creating unique identifiers. 261 func hash(prefix string, v uint, values string, w io.Writer) string { 262 var sink uintptr 263 if reflect.ValueOf(w).Kind() == reflect.Ptr { 264 sink = reflect.ValueOf(w).Pointer() 265 } else { 266 sink = reflect.ValueOf(&w).Pointer() 267 } 268 return fmt.Sprintf("%s[%d][%s]>>%d", prefix, v, values, sink) 269 } 270 271 // nextPowOf2 rounds up n to the next highest power of 2. 272 // See: https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 273 func nextPowOf2(n uint64) uint64 { 274 n-- 275 n |= n >> 1 276 n |= n >> 2 277 n |= n >> 4 278 n |= n >> 8 279 n |= n >> 16 280 n |= n >> 32 281 n++ 282 return n 283 } 284 285 // TODO: 286 // - Implement the HTTP log middleware 287 // - Write benchmarks and do optimizations; consider `func (l *VLogger) getBuffer() *buffer` from glog