blob: ba3b405b4773e23d9602e5e594b8d89832c7ffa6 [file] [log] [blame]
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package elogr is a logr implementation that uses events.
package elogr
import (
"context"
"github.com/go-logr/logr"
"golang.org/x/exp/event"
"golang.org/x/exp/event/keys"
"golang.org/x/exp/event/logging/internal"
)
type logger struct {
exporter *event.Exporter
builder event.Builder // never delivered, only cloned
nameSep string
name string
verbosity int
}
var _ logr.Logger = (*logger)(nil)
func NewLogger(ctx context.Context, nameSep string) logr.Logger {
return &logger{
builder: event.To(ctx),
nameSep: nameSep,
}
}
// WithName adds a new element to the logger's name.
// Successive calls with WithName continue to append
// suffixes to the logger's name. It's strongly recommended
// that name segments contain only letters, digits, and hyphens
// (see the package documentation for more information).
func (l *logger) WithName(name string) logr.Logger {
l2 := *l
if l.name == "" {
l2.name = name
} else {
l2.name = l.name + l.nameSep + name
}
return &l2
}
// V returns an Logger value for a specific verbosity level, relative to
// this Logger. In other words, V values are additive. V higher verbosity
// level means a log message is less important. It's illegal to pass a log
// level less than zero.
func (l *logger) V(level int) logr.Logger {
l2 := *l
l2.verbosity += level
return &l2
}
// Enabled tests whether this Logger is enabled. For example, commandline
// flags might be used to set the logging verbosity and disable some info
// logs.
func (l *logger) Enabled() bool {
return true
}
// addLabels adds labels for keysAndValues to b.
func addLabels(b event.Builder, keysAndValues []interface{}) {
for i := 0; i < len(keysAndValues); i += 2 {
b.With(newLabel(keysAndValues[i], keysAndValues[i+1]))
}
}
// Info logs a non-error message with the given key/value pairs as context.
//
// The msg argument should be used to add some constant description to
// the log line. The key/value pairs can then be used to add additional
// variable information. The key/value pairs should alternate string
// keys and arbitrary values.
func (l *logger) Info(msg string, keysAndValues ...interface{}) {
l.log(msg, l.builder.Clone(), keysAndValues)
}
// Error logs an error, with the given message and key/value pairs as context.
// It functions similarly to calling Info with the "error" named value, but may
// have unique behavior, and should be preferred for logging errors (see the
// package documentations for more information).
//
// The msg field should be used to add context to any underlying error,
// while the err field should be used to attach the actual error that
// triggered this log line, if present.
func (l *logger) Error(err error, msg string, keysAndValues ...interface{}) {
l.log(msg, l.builder.Clone().With(internal.ErrorKey.Of(err)), keysAndValues)
}
func (l *logger) log(msg string, b event.Builder, keysAndValues []interface{}) {
b.With(internal.LevelKey.Of(l.verbosity)) // TODO: Convert verbosity to level.
b.With(internal.NameKey.Of(l.name))
addLabels(b, keysAndValues)
b.Log(msg)
}
// WithValues adds some key-value pairs of context to a logger.
// See Info for documentation on how key/value pairs work.
func (l *logger) WithValues(keysAndValues ...interface{}) logr.Logger {
l2 := *l
l2.builder = l2.builder.Clone()
addLabels(l2.builder, keysAndValues)
return &l2
}
func newLabel(key, value interface{}) event.Label {
return keys.Value(key.(string)).Of(value)
}