mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-01-07 13:55:20 +00:00
Moving kopia logging to remove kopia from indirect dependency in velero plugins
when running `go mod why -m github.com/kopia/kopia` in velero-plugins prior to this change you will see following ``` ❯ go mod why -m github.com/kopia/kopia github.com/konveyor/openshift-velero-plugin/velero-plugins github.com/vmware-tanzu/velero/pkg/plugin/framework github.com/vmware-tanzu/velero/pkg/util/logging github.com/kopia/kopia/repo/logging ``` after ``` ❯ go mod why -m github.com/kopia/kopia (main module does not need module github.com/kopia/kopia) ``` Signed-off-by: Tiger Kaovilai <tkaovila@redhat.com>
This commit is contained in:
162
pkg/kopia/kopia_log.go
Normal file
162
pkg/kopia/kopia_log.go
Normal file
@@ -0,0 +1,162 @@
|
||||
package kopia
|
||||
|
||||
/*
|
||||
Copyright the Velero contributors.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/kopia/kopia/repo/logging"
|
||||
"github.com/sirupsen/logrus"
|
||||
"go.uber.org/zap"
|
||||
"go.uber.org/zap/zapcore"
|
||||
)
|
||||
|
||||
type kopiaLog struct {
|
||||
module string
|
||||
logger logrus.FieldLogger
|
||||
}
|
||||
|
||||
// SetupKopiaLog sets the Kopia log handler to the specific context, Kopia modules
|
||||
// call the logger in the context to write logs
|
||||
func SetupKopiaLog(ctx context.Context, logger logrus.FieldLogger) context.Context {
|
||||
return logging.WithLogger(ctx, func(module string) logging.Logger {
|
||||
kpLog := &kopiaLog{module, logger}
|
||||
return zap.New(kpLog).Sugar()
|
||||
})
|
||||
}
|
||||
|
||||
// Enabled decides whether a given logging level is enabled when logging a message
|
||||
func (kl *kopiaLog) Enabled(level zapcore.Level) bool {
|
||||
entry := kl.logger.WithField("null", "null")
|
||||
switch level {
|
||||
case zapcore.DebugLevel:
|
||||
return (entry.Logger.GetLevel() >= logrus.DebugLevel)
|
||||
case zapcore.InfoLevel:
|
||||
return (entry.Logger.GetLevel() >= logrus.InfoLevel)
|
||||
case zapcore.WarnLevel:
|
||||
return (entry.Logger.GetLevel() >= logrus.WarnLevel)
|
||||
case zapcore.ErrorLevel:
|
||||
return (entry.Logger.GetLevel() >= logrus.ErrorLevel)
|
||||
case zapcore.DPanicLevel:
|
||||
return (entry.Logger.GetLevel() >= logrus.PanicLevel)
|
||||
case zapcore.PanicLevel:
|
||||
return (entry.Logger.GetLevel() >= logrus.PanicLevel)
|
||||
case zapcore.FatalLevel:
|
||||
return (entry.Logger.GetLevel() >= logrus.FatalLevel)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// With adds structured context to the Core.
|
||||
func (kl *kopiaLog) With(fields []zapcore.Field) zapcore.Core {
|
||||
copied := kl.logrusFields(fields)
|
||||
|
||||
return &kopiaLog{
|
||||
module: kl.module,
|
||||
logger: kl.logger.WithFields(copied),
|
||||
}
|
||||
}
|
||||
|
||||
// Check determines whether the supplied Entry should be logged. If the entry
|
||||
// should be logged, the Core adds itself to the CheckedEntry and returns the result.
|
||||
func (kl *kopiaLog) Check(ent zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry {
|
||||
if kl.Enabled(ent.Level) {
|
||||
return ce.AddCore(ent, kl)
|
||||
}
|
||||
|
||||
return ce
|
||||
}
|
||||
|
||||
// Write serializes the Entry and any Fields supplied at the log site and writes them to their destination.
|
||||
func (kl *kopiaLog) Write(ent zapcore.Entry, fields []zapcore.Field) error {
|
||||
copied := kl.logrusFieldsForWrite(ent, fields)
|
||||
logger := kl.logger.WithFields(copied)
|
||||
|
||||
switch ent.Level {
|
||||
case zapcore.DebugLevel:
|
||||
logger.Debug(ent.Message)
|
||||
case zapcore.InfoLevel:
|
||||
logger.Info(ent.Message)
|
||||
case zapcore.WarnLevel:
|
||||
logger.Warn(ent.Message)
|
||||
case zapcore.ErrorLevel:
|
||||
// We see Kopia generates error logs for some normal cases or non-critical
|
||||
// cases. So Kopia's error logs are regarded as warning logs so that they don't
|
||||
// affect Velero's workflow.
|
||||
logger.Warn(ent.Message)
|
||||
case zapcore.DPanicLevel:
|
||||
logger.Panic(ent.Message)
|
||||
case zapcore.PanicLevel:
|
||||
logger.Panic(ent.Message)
|
||||
case zapcore.FatalLevel:
|
||||
logger.Fatal(ent.Message)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Sync flushes buffered logs (if any).
|
||||
func (kl *kopiaLog) Sync() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (kl *kopiaLog) logrusFields(fields []zapcore.Field) logrus.Fields {
|
||||
if fields == nil {
|
||||
return logrus.Fields{}
|
||||
}
|
||||
|
||||
m := zapcore.NewMapObjectEncoder()
|
||||
for _, field := range fields {
|
||||
field.AddTo(m)
|
||||
}
|
||||
|
||||
return m.Fields
|
||||
}
|
||||
|
||||
func (kl *kopiaLog) getLogModule() string {
|
||||
return "kopia/" + kl.module
|
||||
}
|
||||
|
||||
func (kl *kopiaLog) logrusFieldsForWrite(ent zapcore.Entry, fields []zapcore.Field) logrus.Fields {
|
||||
copied := kl.logrusFields(fields)
|
||||
|
||||
copied["logModule"] = kl.getLogModule()
|
||||
|
||||
if ent.Caller.Function != "" {
|
||||
copied["function"] = ent.Caller.Function
|
||||
}
|
||||
|
||||
path := ent.Caller.FullPath()
|
||||
if path != "undefined" {
|
||||
copied["path"] = path
|
||||
}
|
||||
|
||||
if ent.LoggerName != "" {
|
||||
copied["logger name"] = ent.LoggerName
|
||||
}
|
||||
|
||||
if ent.Stack != "" {
|
||||
copied["stack"] = ent.Stack
|
||||
}
|
||||
|
||||
if ent.Level == zap.ErrorLevel {
|
||||
copied["sublevel"] = "error"
|
||||
}
|
||||
|
||||
return copied
|
||||
}
|
||||
349
pkg/kopia/kopia_log_test.go
Normal file
349
pkg/kopia/kopia_log_test.go
Normal file
@@ -0,0 +1,349 @@
|
||||
/*
|
||||
Copyright the Velero contributors.
|
||||
|
||||
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 kopia
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/zap/zapcore"
|
||||
|
||||
"github.com/vmware-tanzu/velero/pkg/test"
|
||||
)
|
||||
|
||||
func TestEnabled(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
level logrus.Level
|
||||
zapLevel zapcore.Level
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
name: "check debug again debug",
|
||||
level: logrus.DebugLevel,
|
||||
zapLevel: zapcore.DebugLevel,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "check debug again info",
|
||||
level: logrus.InfoLevel,
|
||||
zapLevel: zapcore.DebugLevel,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "check info again debug",
|
||||
level: logrus.DebugLevel,
|
||||
zapLevel: zapcore.InfoLevel,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "check info again info",
|
||||
level: logrus.InfoLevel,
|
||||
zapLevel: zapcore.InfoLevel,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "check warn again warn",
|
||||
level: logrus.WarnLevel,
|
||||
zapLevel: zapcore.WarnLevel,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "check info again error",
|
||||
level: logrus.ErrorLevel,
|
||||
zapLevel: zapcore.InfoLevel,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
name: "check error again error",
|
||||
level: logrus.ErrorLevel,
|
||||
zapLevel: zapcore.ErrorLevel,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "check dppanic again panic",
|
||||
level: logrus.PanicLevel,
|
||||
zapLevel: zapcore.DPanicLevel,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "check panic again error",
|
||||
level: logrus.ErrorLevel,
|
||||
zapLevel: zapcore.PanicLevel,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
name: "check fatal again fatal",
|
||||
level: logrus.FatalLevel,
|
||||
zapLevel: zapcore.FatalLevel,
|
||||
expected: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
log := kopiaLog{
|
||||
logger: test.NewLoggerWithLevel(tc.level),
|
||||
}
|
||||
m := log.Enabled(tc.zapLevel)
|
||||
|
||||
require.Equal(t, tc.expected, m)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogrusFieldsForWrite(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
module string
|
||||
zapEntry zapcore.Entry
|
||||
zapFields []zapcore.Field
|
||||
expected logrus.Fields
|
||||
}{
|
||||
{
|
||||
name: "debug with nil fields",
|
||||
module: "module-01",
|
||||
zapEntry: zapcore.Entry{
|
||||
Level: zapcore.DebugLevel,
|
||||
},
|
||||
zapFields: nil,
|
||||
expected: logrus.Fields{
|
||||
"logModule": "kopia/module-01",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "error with nil fields",
|
||||
module: "module-02",
|
||||
zapEntry: zapcore.Entry{
|
||||
Level: zapcore.ErrorLevel,
|
||||
},
|
||||
zapFields: nil,
|
||||
expected: logrus.Fields{
|
||||
"logModule": "kopia/module-02",
|
||||
"sublevel": "error",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "info with nil string filed",
|
||||
module: "module-03",
|
||||
zapEntry: zapcore.Entry{
|
||||
Level: zapcore.InfoLevel,
|
||||
},
|
||||
zapFields: []zapcore.Field{
|
||||
{
|
||||
Key: "key-01",
|
||||
Type: zapcore.StringType,
|
||||
String: "value-01",
|
||||
},
|
||||
},
|
||||
expected: logrus.Fields{
|
||||
"logModule": "kopia/module-03",
|
||||
"key-01": "value-01",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "info with logger name",
|
||||
module: "module-04",
|
||||
zapEntry: zapcore.Entry{
|
||||
Level: zapcore.InfoLevel,
|
||||
LoggerName: "logger-name-01",
|
||||
},
|
||||
zapFields: nil,
|
||||
expected: logrus.Fields{
|
||||
"logModule": "kopia/module-04",
|
||||
"logger name": "logger-name-01",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "info with function name",
|
||||
module: "module-05",
|
||||
zapEntry: zapcore.Entry{
|
||||
Level: zapcore.InfoLevel,
|
||||
Caller: zapcore.EntryCaller{
|
||||
Function: "function-name-01",
|
||||
},
|
||||
},
|
||||
zapFields: nil,
|
||||
expected: logrus.Fields{
|
||||
"logModule": "kopia/module-05",
|
||||
"function": "function-name-01",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "info with undefined path",
|
||||
module: "module-06",
|
||||
zapEntry: zapcore.Entry{
|
||||
Level: zapcore.InfoLevel,
|
||||
Caller: zapcore.EntryCaller{
|
||||
Defined: false,
|
||||
},
|
||||
},
|
||||
zapFields: nil,
|
||||
expected: logrus.Fields{
|
||||
"logModule": "kopia/module-06",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "info with defined path",
|
||||
module: "module-06",
|
||||
zapEntry: zapcore.Entry{
|
||||
Level: zapcore.InfoLevel,
|
||||
Caller: zapcore.EntryCaller{
|
||||
Defined: true,
|
||||
File: "file-name-01",
|
||||
Line: 100,
|
||||
},
|
||||
},
|
||||
zapFields: nil,
|
||||
expected: logrus.Fields{
|
||||
"logModule": "kopia/module-06",
|
||||
"path": "file-name-01:100",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "info with stack",
|
||||
module: "module-07",
|
||||
zapEntry: zapcore.Entry{
|
||||
Level: zapcore.InfoLevel,
|
||||
Stack: "fake-stack",
|
||||
},
|
||||
zapFields: nil,
|
||||
expected: logrus.Fields{
|
||||
"logModule": "kopia/module-07",
|
||||
"stack": "fake-stack",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
log := kopiaLog{
|
||||
module: tc.module,
|
||||
logger: test.NewLogger(),
|
||||
}
|
||||
m := log.logrusFieldsForWrite(tc.zapEntry, tc.zapFields)
|
||||
|
||||
require.Equal(t, tc.expected, m)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWrite(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
ent zapcore.Entry
|
||||
logMessage string
|
||||
logLevel string
|
||||
shouldPanic bool
|
||||
}{
|
||||
{
|
||||
name: "write debug",
|
||||
ent: zapcore.Entry{
|
||||
Level: zapcore.DebugLevel,
|
||||
Message: "fake-message",
|
||||
},
|
||||
logMessage: "fake-message",
|
||||
logLevel: "level=debug",
|
||||
},
|
||||
{
|
||||
name: "write info",
|
||||
ent: zapcore.Entry{
|
||||
Level: zapcore.InfoLevel,
|
||||
Message: "fake-message",
|
||||
},
|
||||
logMessage: "fake-message",
|
||||
logLevel: "level=info",
|
||||
},
|
||||
{
|
||||
name: "write warn",
|
||||
ent: zapcore.Entry{
|
||||
Level: zapcore.WarnLevel,
|
||||
Message: "fake-message",
|
||||
},
|
||||
logMessage: "fake-message",
|
||||
logLevel: "level=warn",
|
||||
},
|
||||
{
|
||||
name: "write error",
|
||||
ent: zapcore.Entry{
|
||||
Level: zapcore.ErrorLevel,
|
||||
Message: "fake-message",
|
||||
},
|
||||
logMessage: "fake-message",
|
||||
logLevel: "level=warn",
|
||||
},
|
||||
{
|
||||
name: "write DPanic",
|
||||
ent: zapcore.Entry{
|
||||
Level: zapcore.DPanicLevel,
|
||||
Message: "fake-message",
|
||||
},
|
||||
logMessage: "fake-message",
|
||||
logLevel: "level=panic",
|
||||
shouldPanic: true,
|
||||
},
|
||||
{
|
||||
name: "write panic",
|
||||
ent: zapcore.Entry{
|
||||
Level: zapcore.PanicLevel,
|
||||
Message: "fake-message",
|
||||
},
|
||||
logMessage: "fake-message",
|
||||
logLevel: "level=panic",
|
||||
shouldPanic: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
logMessage := ""
|
||||
|
||||
log := kopiaLog{
|
||||
logger: test.NewSingleLogger(&logMessage),
|
||||
}
|
||||
|
||||
if tc.shouldPanic {
|
||||
defer func() {
|
||||
r := recover()
|
||||
assert.NotNil(t, r)
|
||||
|
||||
if len(tc.logMessage) > 0 {
|
||||
assert.Contains(t, logMessage, tc.logMessage)
|
||||
}
|
||||
|
||||
if len(tc.logLevel) > 0 {
|
||||
assert.Contains(t, logMessage, tc.logLevel)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
err := log.Write(tc.ent, nil)
|
||||
|
||||
assert.NoError(t, err)
|
||||
|
||||
if len(tc.logMessage) > 0 {
|
||||
assert.Contains(t, logMessage, tc.logMessage)
|
||||
}
|
||||
|
||||
if len(tc.logLevel) > 0 {
|
||||
assert.Contains(t, logMessage, tc.logLevel)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user