alias log package to github.com/containerd/log v0.1.0

This "soft" deprecates the package, but keeps the local uses of the package,
which can make backporting this to release-branches easier (we can
still move all uses in those branches as well though).

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn
2023-09-11 21:28:02 -07:00
parent 00666764b8
commit d69ae811d6
14 changed files with 387 additions and 64 deletions

View File

@@ -1,182 +0,0 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package log provides types and functions related to logging, passing
// loggers through a context, and attaching context to the logger.
//
// # Transitional types
//
// This package contains various types that are aliases for types in [logrus].
// These aliases are intended for transitioning away from hard-coding logrus
// as logging implementation. Consumers of this package are encouraged to use
// the type-aliases from this package instead of directly using their logrus
// equivalent.
//
// The intent is to replace these aliases with locally defined types and
// interfaces once all consumers are no longer directly importing logrus
// types.
//
// IMPORTANT: due to the transitional purpose of this package, it is not
// guaranteed for the full logrus API to be provided in the future. As
// outlined, these aliases are provided as a step to transition away from
// a specific implementation which, as a result, exposes the full logrus API.
// While no decisions have been made on the ultimate design and interface
// provided by this package, we do not expect carrying "less common" features.
package log
import (
"context"
"fmt"
"github.com/sirupsen/logrus"
)
// G is a shorthand for [GetLogger].
//
// We may want to define this locally to a package to get package tagged log
// messages.
var G = GetLogger
// L is an alias for the standard logger.
var L = &Entry{
Logger: logrus.StandardLogger(),
// Default is three fields plus a little extra room.
Data: make(Fields, 6),
}
type loggerKey struct{}
// Fields type to pass to "WithFields".
type Fields = map[string]any
// Entry is a logging entry. It contains all the fields passed with
// [Entry.WithFields]. It's finally logged when Trace, Debug, Info, Warn,
// Error, Fatal or Panic is called on it. These objects can be reused and
// passed around as much as you wish to avoid field duplication.
//
// Entry is a transitional type, and currently an alias for [logrus.Entry].
type Entry = logrus.Entry
// 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"
// Level is a logging level.
type Level = logrus.Level
// Supported log levels.
const (
// TraceLevel level. Designates finer-grained informational events
// than [DebugLevel].
TraceLevel Level = logrus.TraceLevel
// DebugLevel level. Usually only enabled when debugging. Very verbose
// logging.
DebugLevel Level = logrus.DebugLevel
// InfoLevel level. General operational entries about what's going on
// inside the application.
InfoLevel Level = logrus.InfoLevel
// WarnLevel level. Non-critical entries that deserve eyes.
WarnLevel Level = logrus.WarnLevel
// ErrorLevel level. Logs errors that should definitely be noted.
// Commonly used for hooks to send errors to an error tracking service.
ErrorLevel Level = logrus.ErrorLevel
// FatalLevel level. Logs and then calls "logger.Exit(1)". It exits
// even if the logging level is set to Panic.
FatalLevel Level = logrus.FatalLevel
// PanicLevel level. This is the highest level of severity. Logs and
// then calls panic with the message passed to Debug, Info, ...
PanicLevel Level = logrus.PanicLevel
)
// SetLevel sets log level globally. It returns an error if the given
// level is not supported.
//
// level can be one of:
//
// - "trace" ([TraceLevel])
// - "debug" ([DebugLevel])
// - "info" ([InfoLevel])
// - "warn" ([WarnLevel])
// - "error" ([ErrorLevel])
// - "fatal" ([FatalLevel])
// - "panic" ([PanicLevel])
func SetLevel(level string) error {
lvl, err := logrus.ParseLevel(level)
if err != nil {
return err
}
L.Logger.SetLevel(lvl)
return nil
}
// GetLevel returns the current log level.
func GetLevel() Level {
return L.Logger.GetLevel()
}
// OutputFormat specifies a log output format.
type OutputFormat string
// Supported log output formats.
const (
// TextFormat represents the text logging format.
TextFormat OutputFormat = "text"
// JSONFormat represents the JSON logging format.
JSONFormat OutputFormat = "json"
)
// SetFormat sets the log output format ([TextFormat] or [JSONFormat]).
func SetFormat(format OutputFormat) error {
switch format {
case TextFormat:
L.Logger.SetFormatter(&logrus.TextFormatter{
TimestampFormat: RFC3339NanoFixed,
FullTimestamp: true,
})
return nil
case JSONFormat:
L.Logger.SetFormatter(&logrus.JSONFormatter{
TimestampFormat: RFC3339NanoFixed,
})
return nil
default:
return fmt.Errorf("unknown log format: %s", format)
}
}
// 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 *Entry) context.Context {
return context.WithValue(ctx, loggerKey{}, logger.WithContext(ctx))
}
// GetLogger retrieves the current logger from the context. If no logger is
// available, the default logger is returned.
func GetLogger(ctx context.Context) *Entry {
if logger := ctx.Value(loggerKey{}); logger != nil {
return logger.(*Entry)
}
return L.WithContext(ctx)
}

107
log/context_deprecated.go Normal file
View File

@@ -0,0 +1,107 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package log
import (
"context"
"github.com/containerd/log"
)
// G is a shorthand for [GetLogger].
var G = log.G
// L is an alias for the standard logger.
var L = log.L
// Fields type to pass to "WithFields".
type Fields = log.Fields
// Entry is a logging entry.
type Entry = log.Entry
// RFC3339NanoFixed is [time.RFC3339Nano] with nanoseconds padded using
// zeros to ensure the formatted time is always the same number of
// characters.
const RFC3339NanoFixed = log.RFC3339NanoFixed
// Level is a logging level.
type Level = log.Level
// Supported log levels.
const (
// TraceLevel level.
TraceLevel Level = log.TraceLevel
// DebugLevel level.
DebugLevel Level = log.DebugLevel
// InfoLevel level.
InfoLevel Level = log.InfoLevel
// WarnLevel level.
WarnLevel Level = log.WarnLevel
// ErrorLevel level
ErrorLevel Level = log.ErrorLevel
// FatalLevel level.
FatalLevel Level = log.FatalLevel
// PanicLevel level.
PanicLevel Level = log.PanicLevel
)
// SetLevel sets log level globally. It returns an error if the given
// level is not supported.
func SetLevel(level string) error {
return log.SetLevel(level)
}
// GetLevel returns the current log level.
func GetLevel() log.Level {
return log.GetLevel()
}
// OutputFormat specifies a log output format.
type OutputFormat = log.OutputFormat
// Supported log output formats.
const (
// TextFormat represents the text logging format.
TextFormat log.OutputFormat = "text"
// JSONFormat represents the JSON logging format.
JSONFormat log.OutputFormat = "json"
)
// SetFormat sets the log output format.
func SetFormat(format OutputFormat) error {
return log.SetFormat(format)
}
// 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 *log.Entry) context.Context {
return log.WithLogger(ctx, logger)
}
// GetLogger retrieves the current logger from the context. If no logger is
// available, the default logger is returned.
func GetLogger(ctx context.Context) *log.Entry {
return log.GetLogger(ctx)
}

View File

@@ -1,63 +0,0 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package log
import (
"context"
"reflect"
"testing"
"github.com/sirupsen/logrus"
)
func TestLoggerContext(t *testing.T) {
const expected = "one"
ctx := context.Background()
ctx = WithLogger(ctx, G(ctx).WithField("test", expected))
if actual := GetLogger(ctx).Data["test"]; actual != expected {
t.Errorf("expected: %v, got: %v", expected, actual)
}
a := G(ctx)
b := GetLogger(ctx)
if !reflect.DeepEqual(a, b) || a != b {
t.Errorf("should be the same: %+v, %+v", a, b)
}
}
func TestCompat(t *testing.T) {
expected := Fields{
"hello1": "world1",
"hello2": "world2",
"hello3": "world3",
}
l := G(context.TODO())
l = l.WithFields(logrus.Fields{"hello1": "world1"})
l = l.WithFields(Fields{"hello2": "world2"})
l = l.WithFields(map[string]any{"hello3": "world3"})
if !reflect.DeepEqual(Fields(l.Data), expected) {
t.Errorf("expected: (%[1]T) %+[1]v, got: (%[2]T) %+[2]v", expected, l.Data)
}
l2 := L
l2 = l2.WithFields(logrus.Fields{"hello1": "world1"})
l2 = l2.WithFields(Fields{"hello2": "world2"})
l2 = l2.WithFields(map[string]any{"hello3": "world3"})
if !reflect.DeepEqual(Fields(l2.Data), expected) {
t.Errorf("expected: (%[1]T) %+[1]v, got: (%[2]T) %+[2]v", expected, l2.Data)
}
}

View File

@@ -1,56 +0,0 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package logtest
import (
"context"
"fmt"
"io"
"path/filepath"
"runtime"
"testing"
"github.com/containerd/containerd/log"
"github.com/sirupsen/logrus"
)
// WithT adds a logging hook for the given test
// Changes debug level to debug, clears output, and
// outputs all log messages as test logs.
func WithT(ctx context.Context, t testing.TB) context.Context {
// Create a new logger to avoid adding hooks from multiple tests
l := logrus.New()
// Increase debug level for tests
l.SetLevel(logrus.DebugLevel)
l.SetOutput(io.Discard)
l.SetReportCaller(true)
// Add testing hook
l.AddHook(&testHook{
t: t,
fmt: &logrus.TextFormatter{
DisableColors: true,
TimestampFormat: log.RFC3339NanoFixed,
CallerPrettyfier: func(frame *runtime.Frame) (string, string) {
return filepath.Base(frame.Function), fmt.Sprintf("%s:%d", frame.File, frame.Line)
},
},
})
return log.WithLogger(ctx, logrus.NewEntry(l).WithField("testcase", t.Name()))
}

View File

@@ -17,34 +17,15 @@
package logtest
import (
"bytes"
"sync"
"context"
"testing"
"github.com/sirupsen/logrus"
"github.com/containerd/log/logtest"
)
type testHook struct {
t testing.TB
fmt logrus.Formatter
mu sync.Mutex
}
func (*testHook) Levels() []logrus.Level {
return logrus.AllLevels
}
func (h *testHook) Fire(e *logrus.Entry) error {
s, err := h.fmt.Format(e)
if err != nil {
return err
}
// Because the logger could be called from multiple goroutines,
// but t.Log() is not designed for.
h.mu.Lock()
defer h.mu.Unlock()
h.t.Log(string(bytes.TrimRight(s, "\n")))
return nil
// WithT adds a logging hook for the given test
// Changes debug level to debug, clears output, and
// outputs all log messages as test logs.
func WithT(ctx context.Context, t testing.TB) context.Context {
return logtest.WithT(ctx, t)
}