pkg/xcontext/logger/logadapter/logrus/wrapper.go (72 lines of code) (raw):
// Copyright (c) Facebook, Inc. and its affiliates.
//
// This source code is licensed under the MIT license found in the
// LICENSE file in the root directory of this source tree.
package logrus
import (
"context"
"github.com/facebookincubator/contest/pkg/xcontext/logger"
"github.com/facebookincubator/contest/pkg/xcontext/logger/internal"
"github.com/sirupsen/logrus"
)
var (
_ internal.LoggerCompact = Wrapper{}
)
type Fields = internal.Fields
func wrap(backend *logrus.Entry) logger.Logger {
// Each structured logger has own types and we do intermediate methods
// to convert one types to another. And to do not duplicate
// Debugf/Infof/... in each such adapter we define only Logf
// and unroll it using internal.UncompactLoggerCompact.
// Since we do all this wrapping the caller-detection in LogRus works
// wrong. To fix it we use a special hook `fixCallerHook`. But to do not
// define it multiple times on each wrapping of the logger, we recheck
// if it is already set.
foundTheHook := false
for _, hook := range backend.Logger.Hooks[logrus.ErrorLevel] {
_, foundTheHook = hook.(fixCallerHook)
if foundTheHook {
break
}
}
if !foundTheHook {
backend.Logger.AddHook(fixCallerHook{})
}
// And here we do the main idea of this function (`wrap`):
return internal.UncompactLoggerCompact{
LoggerCompact: Wrapper{
Backend: backend,
},
}
}
// Wrapper converts *logrus.Entry to internal.LoggerCompact (which could be
// easily converted to logger.Logger).
type Wrapper struct {
Backend *logrus.Entry
}
type contextKey string
// ContextKeyFormat is the key to extract log format from a log entry context.
const ContextKeyFormat = contextKey("format")
// Logf implements internal.MinimalLoggerCompact.
func (l Wrapper) Logf(level logger.Level, format string, args ...interface{}) {
// Passing through "format" for better automatic error categorization
// by Sentry-like services. This way we can detect which exactly
// Errorf line was used even when lines being shifted up and down.
ctx := l.Backend.Context
if ctx == nil {
ctx = context.Background()
}
ctx = context.WithValue(ctx, ContextKeyFormat, format)
internal.MinimalLoggerLogf(l.Backend.WithContext(ctx), level, format, args...)
}
// OriginalLogger implements internal.LoggerExtensions.
func (l Wrapper) OriginalLogger() interface{} {
return l.Backend
}
// Level implements internal.WithLevels.
func (l Wrapper) Level() logger.Level {
return Adapter.ConvertLevel(l.Backend.Logger.Level)
}
// WithLevel implements internal.WithLevels.
func (l Wrapper) WithLevel(logLevel logger.Level) logger.Logger {
// I haven't found an easier way to change loglevel for a child logger,
// but not to affect the loglevel of the parent.
newLogger := &logrus.Logger{
Out: l.Backend.Logger.Out,
Hooks: l.Backend.Logger.Hooks,
Formatter: l.Backend.Logger.Formatter,
ReportCaller: l.Backend.Logger.ReportCaller,
Level: l.Backend.Logger.Level,
ExitFunc: l.Backend.Logger.ExitFunc,
}
newLogger.SetLevel(Adapter.Level(logLevel))
return internal.UncompactLoggerCompact{LoggerCompact: Wrapper{
Backend: &logrus.Entry{
Logger: newLogger,
Data: l.Backend.Data,
Time: l.Backend.Time,
Context: l.Backend.Context,
},
}}
}
// WithField implements internal.WithFields.
func (l Wrapper) WithField(field string, value interface{}) logger.Logger {
return wrap(l.Backend.WithField(field, value))
}
// WithFields implements internal.WithFields.
func (l Wrapper) WithFields(fields Fields) logger.Logger {
return wrap(l.Backend.WithFields(map[string]interface{}(fields)))
}