Make tracing interface to coalesce logging/tracing (#519)
* Define and use an interface for logging. This allows alternative implementations to use whatever logging package they want. Currently the interface just mimicks what logrus already implements, with minor modifications to not rely on logrus itself. I think the interface is pretty solid in terms of logging implementations being able to do what they need to. * Make tracing interface to coalesce logging/tracing Allows us to share data between the tracer and the logger so we can simplify log/trace handling wher we generally want data to go both places.
This commit is contained in:
76
log/log.go
76
log/log.go
@@ -14,77 +14,71 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package log defines the interfaces used for logging in virtual-kubelet.
|
||||
// It uses a context.Context to store logger details. Additionally you can set
|
||||
// the default logger to use by setting log.L. This is used when no logger is
|
||||
// stored in the passed in context.
|
||||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
// G is an alias for GetLogger.
|
||||
//
|
||||
// We may want to define this locally to a package to get package tagged log
|
||||
// messages.
|
||||
G = GetLogger
|
||||
|
||||
// L is an alias for the the standard logger.
|
||||
L = logrus.NewEntry(logrus.StandardLogger())
|
||||
// L is the default logger. It should be initialized before using `G` or `GetLogger`
|
||||
// If L is unitialized and no logger is available in a provided context, a
|
||||
// panic will occur.
|
||||
L Logger = nopLogger{}
|
||||
)
|
||||
|
||||
type (
|
||||
loggerKey struct{}
|
||||
)
|
||||
|
||||
// TraceLevel is the log level for tracing. Trace level is lower than debug level,
|
||||
// and is usually used to trace detailed behavior of the program.
|
||||
const TraceLevel = logrus.Level(uint32(logrus.DebugLevel + 1))
|
||||
// Logger is the interface used for logging in virtual-kubelet
|
||||
//
|
||||
// virtual-kubelet will access the logger via context using `GetLogger` (or its alias, `G`)
|
||||
// You can set the default logger to use by setting the `L` variable.
|
||||
type Logger interface {
|
||||
Debug(...interface{})
|
||||
Debugf(string, ...interface{})
|
||||
Info(...interface{})
|
||||
Infof(string, ...interface{})
|
||||
Warn(...interface{})
|
||||
Warnf(string, ...interface{})
|
||||
Error(...interface{})
|
||||
Errorf(string, ...interface{})
|
||||
Fatal(...interface{})
|
||||
Fatalf(string, ...interface{})
|
||||
|
||||
// RFC3339NanoFixed is time.RFC3339Nano with nanoseconds padded using zeros to
|
||||
// ensure the formatted time is always the same number of characters.
|
||||
const RFC3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00"
|
||||
|
||||
// ParseLevel takes a string level and returns the Logrus log level constant.
|
||||
// It supports trace level.
|
||||
func ParseLevel(lvl string) (logrus.Level, error) {
|
||||
if lvl == "trace" {
|
||||
return TraceLevel, nil
|
||||
}
|
||||
return logrus.ParseLevel(lvl)
|
||||
WithField(string, interface{}) Logger
|
||||
WithFields(Fields) Logger
|
||||
WithError(error) Logger
|
||||
}
|
||||
|
||||
// Fields allows setting multiple fields on a logger at one time.
|
||||
type Fields map[string]interface{}
|
||||
|
||||
// WithLogger returns a new context with the provided logger. Use in
|
||||
// combination with logger.WithField(s) for great effect.
|
||||
func WithLogger(ctx context.Context, logger *logrus.Entry) context.Context {
|
||||
func WithLogger(ctx context.Context, logger Logger) context.Context {
|
||||
return context.WithValue(ctx, loggerKey{}, logger)
|
||||
}
|
||||
|
||||
// GetLogger retrieves the current logger from the context. If no logger is
|
||||
// available, the default logger is returned.
|
||||
func GetLogger(ctx context.Context) *logrus.Entry {
|
||||
func GetLogger(ctx context.Context) Logger {
|
||||
logger := ctx.Value(loggerKey{})
|
||||
|
||||
if logger == nil {
|
||||
if L == nil {
|
||||
panic("default logger not initialized")
|
||||
}
|
||||
return L
|
||||
}
|
||||
|
||||
return logger.(*logrus.Entry)
|
||||
}
|
||||
|
||||
// Trace logs a message at level Trace with the log entry passed-in.
|
||||
func Trace(e *logrus.Entry, args ...interface{}) {
|
||||
level := logrus.Level(atomic.LoadUint32((*uint32)(&e.Logger.Level)))
|
||||
if level >= TraceLevel {
|
||||
e.Debug(args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Tracef logs a message at level Trace with the log entry passed-in.
|
||||
func Tracef(e *logrus.Entry, format string, args ...interface{}) {
|
||||
level := logrus.Level(atomic.LoadUint32((*uint32)(&e.Logger.Level)))
|
||||
if level >= TraceLevel {
|
||||
e.Debugf(format, args...)
|
||||
}
|
||||
return logger.(Logger)
|
||||
}
|
||||
|
||||
34
log/logrus/logrus.go
Normal file
34
log/logrus/logrus.go
Normal file
@@ -0,0 +1,34 @@
|
||||
// Package logrus implements a github.com/virtual-kubelet/virtual-kubelet/log.Logger using Logrus as a backend
|
||||
// You can use this by creating a logrus logger and calling `FromLogrus(entry)`.
|
||||
// If you want this to be the default logger for virtual-kubelet, set `log.L` to the value returned by `FromLogrus`
|
||||
package logrus
|
||||
|
||||
import (
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/virtual-kubelet/virtual-kubelet/log"
|
||||
)
|
||||
|
||||
// Adapter implements the `log.Logger` interface for logrus
|
||||
type Adapter struct {
|
||||
*logrus.Entry
|
||||
}
|
||||
|
||||
// FromLogrus creates a new `log.Logger` from the provided entry
|
||||
func FromLogrus(entry *logrus.Entry) log.Logger {
|
||||
return &Adapter{entry}
|
||||
}
|
||||
|
||||
// WithField adds a field to the log entry.
|
||||
func (l *Adapter) WithField(key string, val interface{}) log.Logger {
|
||||
return FromLogrus(l.Entry.WithField(key, val))
|
||||
}
|
||||
|
||||
// WithFields adds multiple fields to a log entry.
|
||||
func (l *Adapter) WithFields(f log.Fields) log.Logger {
|
||||
return FromLogrus(l.Entry.WithFields(logrus.Fields(f)))
|
||||
}
|
||||
|
||||
// WithError adds an error to the log entry
|
||||
func (l *Adapter) WithError(err error) log.Logger {
|
||||
return FromLogrus(l.Entry.WithError(err))
|
||||
}
|
||||
16
log/logrus/logrus_test.go
Normal file
16
log/logrus/logrus_test.go
Normal file
@@ -0,0 +1,16 @@
|
||||
package logrus
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/virtual-kubelet/virtual-kubelet/log"
|
||||
)
|
||||
|
||||
func TestImplementsLoggerInterface(t *testing.T) {
|
||||
l := FromLogrus(&logrus.Entry{})
|
||||
|
||||
if _, ok := l.(log.Logger); !ok {
|
||||
t.Fatal("does not implement log.Logger interface")
|
||||
}
|
||||
}
|
||||
18
log/nop.go
Normal file
18
log/nop.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package log
|
||||
|
||||
type nopLogger struct{}
|
||||
|
||||
func (nopLogger) Debug(...interface{}) {}
|
||||
func (nopLogger) Debugf(string, ...interface{}) {}
|
||||
func (nopLogger) Info(...interface{}) {}
|
||||
func (nopLogger) Infof(string, ...interface{}) {}
|
||||
func (nopLogger) Warn(...interface{}) {}
|
||||
func (nopLogger) Warnf(string, ...interface{}) {}
|
||||
func (nopLogger) Error(...interface{}) {}
|
||||
func (nopLogger) Errorf(string, ...interface{}) {}
|
||||
func (nopLogger) Fatal(...interface{}) {}
|
||||
func (nopLogger) Fatalf(string, ...interface{}) {}
|
||||
|
||||
func (l nopLogger) WithField(string, interface{}) Logger { return l }
|
||||
func (l nopLogger) WithFields(Fields) Logger { return l }
|
||||
func (l nopLogger) WithError(error) Logger { return l }
|
||||
Reference in New Issue
Block a user