基于 `zap` 包封装。除了实现 `Go` 日志包的基本功能外,还实现了很多高级功能 本包基于`github.com/tkestack/tke/pkg/util/log`裁剪
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

553 lines
16 KiB

package log
import (
"context"
"fmt"
"log"
"sync"
"cynking.com/pkg/log/klog"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
// InfoLogger represents the ability to log non-error messages, at a particular verbosity.
type InfoLogger interface {
// Info logs a non-error message with the given key/value pairs as context.
//
// The msg argument should be used to add some constant description to
// the log line. The key/value pairs can then be used to add additional
// variable information. The key/value pairs should alternate string
// keys and arbitrary values.
Info(msg string, fields ...Field)
Infof(format string, v ...interface{})
Infow(msg string, keysAndValues ...interface{})
// Enabled tests whether this InfoLogger is enabled. For example,
// commandline flags might be used to set the logging verbosity and disable
// some info logs.
Enabled() bool
}
// Logger represents the ability to log messages, both errors and not.
type Logger interface {
// All Loggers implement InfoLogger. Calling InfoLogger methods directly on
// a Logger value is equivalent to calling them on a V(0) InfoLogger. For
// example, logger.Info() produces the same result as logger.V(0).Info.
InfoLogger
Debug(msg string, fields ...Field)
Debugf(format string, v ...interface{})
Debugw(msg string, keysAndValues ...interface{})
Warn(msg string, fields ...Field)
Warnf(format string, v ...interface{})
Warnw(msg string, keysAndValues ...interface{})
Error(msg string, fields ...Field)
Errorf(format string, v ...interface{})
Errorw(msg string, keysAndValues ...interface{})
Panic(msg string, fields ...Field)
Panicf(format string, v ...interface{})
Panicw(msg string, keysAndValues ...interface{})
Fatal(msg string, fields ...Field)
Fatalf(format string, v ...interface{})
Fatalw(msg string, keysAndValues ...interface{})
// V returns an InfoLogger value for a specific verbosity level. A higher
// verbosity level means a log message is less important. It's illegal to
// pass a log level less than zero.
V(level Level) InfoLogger
Write(p []byte) (n int, err error)
// WithValues adds some key-value pairs of context to a logger.
// See Info for documentation on how key/value pairs work.
WithValues(keysAndValues ...interface{}) Logger
// WithName adds a new element to the logger's name.
// Successive calls with WithName continue to append
// suffixes to the logger's name. It's strongly recommended
// that name segments contain only letters, digits, and hyphens
// (see the package documentation for more information).
WithName(name string) Logger
// WithContext returns a copy of context in which the log value is set.
WithContext(ctx context.Context) context.Context
// Flush calls the underlying Core's Sync method, flushing any buffered
// log entries. Applications should take care to call Sync before exiting.
Flush()
}
var _ Logger = &zapLogger{}
// noopInfoLogger is a logr.InfoLogger that's always disabled, and does nothing.
type noopInfoLogger struct{}
func (l *noopInfoLogger) Enabled() bool { return false }
func (l *noopInfoLogger) Info(_ string, _ ...Field) {}
func (l *noopInfoLogger) Infof(_ string, _ ...interface{}) {}
func (l *noopInfoLogger) Infow(_ string, _ ...interface{}) {}
var disabledInfoLogger = &noopInfoLogger{}
// NB: right now, we always use the equivalent of sugared logging.
// This is necessary, since logr doesn't define non-suggared types,
// and using zap-specific non-suggared types would make uses tied
// directly to Zap.
// infoLogger is a logr.InfoLogger that uses Zap to log at a particular
// level. The level has already been converted to a Zap level, which
// is to say that `logrLevel = -1*zapLevel`.
type infoLogger struct {
level zapcore.Level
log *zap.Logger
}
func (l *infoLogger) Enabled() bool { return true }
func (l *infoLogger) Info(msg string, fields ...Field) {
if checkedEntry := l.log.Check(l.level, msg); checkedEntry != nil {
checkedEntry.Write(fields...)
}
}
func (l *infoLogger) Infof(format string, args ...interface{}) {
if checkedEntry := l.log.Check(l.level, fmt.Sprintf(format, args...)); checkedEntry != nil {
checkedEntry.Write()
}
}
func (l *infoLogger) Infow(msg string, keysAndValues ...interface{}) {
if checkedEntry := l.log.Check(l.level, msg); checkedEntry != nil {
checkedEntry.Write(handleFields(l.log, keysAndValues)...)
}
}
// zapLogger is a logr.Logger that uses Zap to log.
type zapLogger struct {
// NB: this looks very similar to zap.SugaredLogger, but
// deals with our desire to have multiple verbosity levels.
zapLogger *zap.Logger
infoLogger
}
// 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 handleFields(l *zap.Logger, 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 additional
}
// 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))
for i := 0; i < len(args); {
// 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", zap.Any("zap field", args[i]))
break
}
// make sure this isn't a mismatched key
if i == len(args)-1 {
l.DPanic("odd number of arguments passed as key-value pairs for logging", zap.Any("ignored key", args[i]))
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, DPanic and stop logging
l.DPanic(
"non-string key argument passed to logging, ignoring all later arguments",
zap.Any("invalid key", key),
)
break
}
fields = append(fields, zap.Any(keyStr, val))
i += 2
}
return append(fields, additional...)
}
var (
std = New(NewOptions())
mu sync.Mutex
)
// Init initializes logger with specified options.
func Init(opts *Options) {
mu.Lock()
defer mu.Unlock()
std = New(opts)
}
// New create logger by opts which can custmoized by command arguments.
func New(opts *Options) *zapLogger {
if opts == nil {
opts = NewOptions()
}
var zapLevel zapcore.Level
if err := zapLevel.UnmarshalText([]byte(opts.Level)); err != nil {
zapLevel = zapcore.InfoLevel
}
encodeLevel := zapcore.CapitalLevelEncoder
// when output to local path, with color is forbidden
if opts.Format == ConsoleFormat && opts.EnableColor {
encodeLevel = zapcore.CapitalColorLevelEncoder
}
encoderConfig := zapcore.EncoderConfig{
MessageKey: "message",
LevelKey: "level",
TimeKey: "timestamp",
NameKey: "logger",
CallerKey: "caller",
StacktraceKey: "stacktrace",
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: encodeLevel,
EncodeTime: timeEncoder,
EncodeDuration: autoDurationEncoder,
EncodeCaller: zapcore.ShortCallerEncoder,
}
loggerConfig := &zap.Config{
Level: zap.NewAtomicLevelAt(zapLevel),
Development: opts.Development,
DisableCaller: opts.DisableCaller,
DisableStacktrace: opts.DisableStacktrace,
Sampling: &zap.SamplingConfig{
Initial: 100,
Thereafter: 100,
},
Encoding: opts.Format,
EncoderConfig: encoderConfig,
OutputPaths: opts.OutputPaths,
ErrorOutputPaths: opts.ErrorOutputPaths,
}
var err error
l, err := loggerConfig.Build(zap.AddStacktrace(zapcore.PanicLevel), zap.AddCallerSkip(1))
if err != nil {
panic(err)
}
logger := &zapLogger{
zapLogger: l.Named(opts.Name),
infoLogger: infoLogger{
log: l,
level: zap.InfoLevel,
},
}
klog.InitLogger(l)
zap.RedirectStdLog(l)
return logger
}
// SugaredLogger returns global sugared logger.
func SugaredLogger() *zap.SugaredLogger {
return std.zapLogger.Sugar()
}
// StdErrLogger returns logger of standard library which writes to supplied zap
// logger at error level.
func StdErrLogger() *log.Logger {
if std == nil {
return nil
}
if l, err := zap.NewStdLogAt(std.zapLogger, zapcore.ErrorLevel); err == nil {
return l
}
return nil
}
// StdInfoLogger returns logger of standard library which writes to supplied zap
// logger at info level.
func StdInfoLogger() *log.Logger {
if std == nil {
return nil
}
if l, err := zap.NewStdLogAt(std.zapLogger, zapcore.InfoLevel); err == nil {
return l
}
return nil
}
// V return a leveled InfoLogger.
func V(level Level) InfoLogger { return std.V(level) }
func (l *zapLogger) V(level Level) InfoLogger {
if l.zapLogger.Core().Enabled(level) {
return &infoLogger{
level: level,
log: l.zapLogger,
}
}
return disabledInfoLogger
}
func (l *zapLogger) Write(p []byte) (n int, err error) {
l.zapLogger.Info(string(p))
return len(p), nil
}
// WithValues creates a child logger and adds adds Zap fields to it.
func WithValues(keysAndValues ...interface{}) Logger { return std.WithValues(keysAndValues...) }
func (l *zapLogger) WithValues(keysAndValues ...interface{}) Logger {
newLogger := l.zapLogger.With(handleFields(l.zapLogger, keysAndValues)...)
return NewLogger(newLogger)
}
// WithName adds a new path segment to the logger's name. Segments are joined by
// periods. By default, Loggers are unnamed.
func WithName(s string) Logger { return std.WithName(s) }
func (l *zapLogger) WithName(name string) Logger {
newLogger := l.zapLogger.Named(name)
return NewLogger(newLogger)
}
// Flush calls the underlying Core's Sync method, flushing any buffered
// log entries. Applications should take care to call Sync before exiting.
func Flush() { std.Flush() }
func (l *zapLogger) Flush() {
_ = l.zapLogger.Sync()
}
// NewLogger creates a new logr.Logger using the given Zap Logger to log.
func NewLogger(l *zap.Logger) Logger {
return &zapLogger{
zapLogger: l,
infoLogger: infoLogger{
log: l,
level: zap.InfoLevel,
},
}
}
// ZapLogger used for other log wrapper such as klog.
func ZapLogger() *zap.Logger {
return std.zapLogger
}
// CheckIntLevel used for other log wrapper such as klog which return if logging a
// message at the specified level is enabled.
func CheckIntLevel(level int32) bool {
var lvl zapcore.Level
if level < 5 {
lvl = zapcore.InfoLevel
} else {
lvl = zapcore.DebugLevel
}
checkEntry := std.zapLogger.Check(lvl, "")
return checkEntry != nil
}
// Debug method output debug level log.
func Debug(msg string, fields ...Field) {
std.zapLogger.Debug(msg, fields...)
}
func (l *zapLogger) Debug(msg string, fields ...Field) {
l.zapLogger.Debug(msg, fields...)
}
// Debugf method output debug level log.
func Debugf(format string, v ...interface{}) {
std.zapLogger.Sugar().Debugf(format, v...)
}
func (l *zapLogger) Debugf(format string, v ...interface{}) {
l.zapLogger.Sugar().Debugf(format, v...)
}
// Debugw method output debug level log.
func Debugw(msg string, keysAndValues ...interface{}) {
std.zapLogger.Sugar().Debugw(msg, keysAndValues...)
}
func (l *zapLogger) Debugw(msg string, keysAndValues ...interface{}) {
l.zapLogger.Sugar().Debugw(msg, keysAndValues...)
}
// Info method output info level log.
func Info(msg string, fields ...Field) {
std.zapLogger.Info(msg, fields...)
}
func (l *zapLogger) Info(msg string, fields ...Field) {
l.zapLogger.Info(msg, fields...)
}
// Infof method output info level log.
func Infof(format string, v ...interface{}) {
std.zapLogger.Sugar().Infof(format, v...)
}
func (l *zapLogger) Infof(format string, v ...interface{}) {
l.zapLogger.Sugar().Infof(format, v...)
}
// Infow method output info level log.
func Infow(msg string, keysAndValues ...interface{}) {
std.zapLogger.Sugar().Infow(msg, keysAndValues...)
}
func (l *zapLogger) Infow(msg string, keysAndValues ...interface{}) {
l.zapLogger.Sugar().Infow(msg, keysAndValues...)
}
// Warn method output warning level log.
func Warn(msg string, fields ...Field) {
std.zapLogger.Warn(msg, fields...)
}
func (l *zapLogger) Warn(msg string, fields ...Field) {
l.zapLogger.Warn(msg, fields...)
}
// Warnf method output warning level log.
func Warnf(format string, v ...interface{}) {
std.zapLogger.Sugar().Warnf(format, v...)
}
func (l *zapLogger) Warnf(format string, v ...interface{}) {
l.zapLogger.Sugar().Warnf(format, v...)
}
// Warnw method output warning level log.
func Warnw(msg string, keysAndValues ...interface{}) {
std.zapLogger.Sugar().Warnw(msg, keysAndValues...)
}
func (l *zapLogger) Warnw(msg string, keysAndValues ...interface{}) {
l.zapLogger.Sugar().Warnw(msg, keysAndValues...)
}
// Error method output error level log.
func Error(msg string, fields ...Field) {
std.zapLogger.Error(msg, fields...)
}
func (l *zapLogger) Error(msg string, fields ...Field) {
l.zapLogger.Error(msg, fields...)
}
// Errorf method output error level log.
func Errorf(format string, v ...interface{}) {
std.zapLogger.Sugar().Errorf(format, v...)
}
func (l *zapLogger) Errorf(format string, v ...interface{}) {
l.zapLogger.Sugar().Errorf(format, v...)
}
// Errorw method output error level log.
func Errorw(msg string, keysAndValues ...interface{}) {
std.zapLogger.Sugar().Errorw(msg, keysAndValues...)
}
func (l *zapLogger) Errorw(msg string, keysAndValues ...interface{}) {
l.zapLogger.Sugar().Errorw(msg, keysAndValues...)
}
// Panic method output panic level log and shutdown application.
func Panic(msg string, fields ...Field) {
std.zapLogger.Panic(msg, fields...)
}
func (l *zapLogger) Panic(msg string, fields ...Field) {
l.zapLogger.Panic(msg, fields...)
}
// Panicf method output panic level log and shutdown application.
func Panicf(format string, v ...interface{}) {
std.zapLogger.Sugar().Panicf(format, v...)
}
func (l *zapLogger) Panicf(format string, v ...interface{}) {
l.zapLogger.Sugar().Panicf(format, v...)
}
// Panicw method output panic level log.
func Panicw(msg string, keysAndValues ...interface{}) {
std.zapLogger.Sugar().Panicw(msg, keysAndValues...)
}
func (l *zapLogger) Panicw(msg string, keysAndValues ...interface{}) {
l.zapLogger.Sugar().Panicw(msg, keysAndValues...)
}
// Fatal method output fatal level log.
func Fatal(msg string, fields ...Field) {
std.zapLogger.Fatal(msg, fields...)
}
func (l *zapLogger) Fatal(msg string, fields ...Field) {
l.zapLogger.Fatal(msg, fields...)
}
// Fatalf method output fatal level log.
func Fatalf(format string, v ...interface{}) {
std.zapLogger.Sugar().Fatalf(format, v...)
}
func (l *zapLogger) Fatalf(format string, v ...interface{}) {
l.zapLogger.Sugar().Fatalf(format, v...)
}
// Fatalw method output Fatalw level log.
func Fatalw(msg string, keysAndValues ...interface{}) {
std.zapLogger.Sugar().Fatalw(msg, keysAndValues...)
}
func (l *zapLogger) Fatalw(msg string, keysAndValues ...interface{}) {
l.zapLogger.Sugar().Fatalw(msg, keysAndValues...)
}
// L method output with specified context value.
func L(ctx context.Context) *zapLogger {
return std.L(ctx)
}
func (l *zapLogger) L(ctx context.Context) *zapLogger {
lg := l.clone()
if requestID := ctx.Value(KeyRequestID); requestID != nil {
lg.zapLogger = lg.zapLogger.With(zap.Any(KeyRequestID, requestID))
}
if username := ctx.Value(KeyUsername); username != nil {
lg.zapLogger = lg.zapLogger.With(zap.Any(KeyUsername, username))
}
if watcherName := ctx.Value(KeyWatcherName); watcherName != nil {
lg.zapLogger = lg.zapLogger.With(zap.Any(KeyWatcherName, watcherName))
}
return lg
}
//nolint:predeclared
func (l *zapLogger) clone() *zapLogger {
clone := *l
return &clone
}