mirror of https://github.com/k3s-io/k3s
179 lines
4.9 KiB
Go
179 lines
4.9 KiB
Go
/*
|
|
Copyright 2020 The Kubernetes 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 logs
|
|
|
|
import (
|
|
"os"
|
|
"time"
|
|
|
|
"github.com/go-logr/logr"
|
|
"go.uber.org/zap"
|
|
"go.uber.org/zap/zapcore"
|
|
)
|
|
|
|
// Inspired from https://github.com/go-logr/zapr, some functions is copy from the repo.
|
|
|
|
var (
|
|
// JSONLogger is global json log format logr
|
|
JSONLogger logr.Logger
|
|
|
|
// timeNow stubbed out for testing
|
|
timeNow = time.Now
|
|
)
|
|
|
|
// zapLogger is a logr.Logger that uses Zap to record log.
|
|
type zapLogger struct {
|
|
// NB: this looks very similar to zap.SugaredLogger, but
|
|
// deals with our desire to have multiple verbosity levels.
|
|
l *zap.Logger
|
|
lvl int
|
|
}
|
|
|
|
// implement logr.Logger
|
|
var _ logr.Logger = &zapLogger{}
|
|
|
|
// Enabled should always return true
|
|
func (l *zapLogger) Enabled() bool {
|
|
return true
|
|
}
|
|
|
|
// Info write message to error level log
|
|
func (l *zapLogger) Info(msg string, keysAndVals ...interface{}) {
|
|
entry := zapcore.Entry{
|
|
Time: timeNow(),
|
|
Message: msg,
|
|
}
|
|
checkedEntry := l.l.Core().Check(entry, nil)
|
|
checkedEntry.Write(l.handleFields(keysAndVals)...)
|
|
}
|
|
|
|
// dPanic write message to DPanicLevel level log
|
|
// we need implement this because unit test case need stub time.Now
|
|
// otherwise the ts field always changed
|
|
func (l *zapLogger) dPanic(msg string) {
|
|
entry := zapcore.Entry{
|
|
Level: zapcore.DPanicLevel,
|
|
Time: timeNow(),
|
|
Message: msg,
|
|
}
|
|
checkedEntry := l.l.Core().Check(entry, nil)
|
|
checkedEntry.Write(zap.Int("v", l.lvl))
|
|
}
|
|
|
|
// handleFields converts a bunch of arbitrary key-value pairs into Zap fields. It takes
|
|
// additional pre-converted Zap fields, for use with automatically attached fields, like
|
|
// `error`.
|
|
func (l *zapLogger) handleFields(args []interface{}, additional ...zap.Field) []zap.Field {
|
|
// a slightly modified version of zap.SugaredLogger.sweetenFields
|
|
if len(args) == 0 {
|
|
// fast-return if we have no suggared fields.
|
|
return append(additional, zap.Int("v", l.lvl))
|
|
}
|
|
|
|
// unlike Zap, we can be pretty sure users aren't passing structured
|
|
// fields (since logr has no concept of that), so guess that we need a
|
|
// little less space.
|
|
fields := make([]zap.Field, 0, len(args)/2+len(additional)+1)
|
|
fields = append(fields, zap.Int("v", l.lvl))
|
|
for i := 0; i < len(args)-1; i += 2 {
|
|
// check just in case for strongly-typed Zap fields, which is illegal (since
|
|
// it breaks implementation agnosticism), so we can give a better error message.
|
|
if _, ok := args[i].(zap.Field); ok {
|
|
l.dPanic("strongly-typed Zap Field passed to logr")
|
|
break
|
|
}
|
|
|
|
// process a key-value pair,
|
|
// ensuring that the key is a string
|
|
key, val := args[i], args[i+1]
|
|
keyStr, isString := key.(string)
|
|
if !isString {
|
|
// if the key isn't a string, stop logging
|
|
l.dPanic("non-string key argument passed to logging, ignoring all later arguments")
|
|
break
|
|
}
|
|
|
|
fields = append(fields, zap.Any(keyStr, val))
|
|
}
|
|
|
|
return append(fields, additional...)
|
|
}
|
|
|
|
// Error write log message to error level
|
|
func (l *zapLogger) Error(err error, msg string, keysAndVals ...interface{}) {
|
|
entry := zapcore.Entry{
|
|
Level: zapcore.ErrorLevel,
|
|
Time: timeNow(),
|
|
Message: msg,
|
|
}
|
|
checkedEntry := l.l.Core().Check(entry, nil)
|
|
checkedEntry.Write(l.handleFields(keysAndVals, handleError(err))...)
|
|
}
|
|
|
|
// V return info logr.Logger with specified level
|
|
func (l *zapLogger) V(level int) logr.Logger {
|
|
return &zapLogger{
|
|
lvl: l.lvl + level,
|
|
l: l.l,
|
|
}
|
|
}
|
|
|
|
// WithValues return logr.Logger with some keys And Values
|
|
func (l *zapLogger) WithValues(keysAndValues ...interface{}) logr.Logger {
|
|
l.l = l.l.With(l.handleFields(keysAndValues)...)
|
|
return l
|
|
}
|
|
|
|
// WithName return logger Named with specified name
|
|
func (l *zapLogger) WithName(name string) logr.Logger {
|
|
l.l = l.l.Named(name)
|
|
return l
|
|
}
|
|
|
|
// encoderConfig config zap encodetime format
|
|
var encoderConfig = zapcore.EncoderConfig{
|
|
MessageKey: "msg",
|
|
|
|
TimeKey: "ts",
|
|
EncodeTime: zapcore.EpochMillisTimeEncoder,
|
|
EncodeDuration: zapcore.StringDurationEncoder,
|
|
}
|
|
|
|
// NewJSONLogger creates a new json logr.Logger using the given Zap Logger to log.
|
|
func NewJSONLogger(w zapcore.WriteSyncer) logr.Logger {
|
|
l, _ := zap.NewProduction()
|
|
if w == nil {
|
|
w = os.Stdout
|
|
}
|
|
log := l.WithOptions(zap.AddCallerSkip(1),
|
|
zap.WrapCore(
|
|
func(zapcore.Core) zapcore.Core {
|
|
return zapcore.NewCore(zapcore.NewJSONEncoder(encoderConfig), zapcore.AddSync(w), zapcore.DebugLevel)
|
|
}))
|
|
return &zapLogger{
|
|
l: log,
|
|
}
|
|
}
|
|
|
|
func handleError(err error) zap.Field {
|
|
return zap.NamedError("err", err)
|
|
}
|
|
|
|
func init() {
|
|
JSONLogger = NewJSONLogger(nil)
|
|
}
|