mirror of
https://git.mirrors.martin98.com/https://github.com/ceph/ceph-csi.git
synced 2025-08-04 14:40:37 +08:00
267 lines
7.1 KiB
Go
267 lines
7.1 KiB
Go
package flume
|
|
|
|
import (
|
|
"fmt"
|
|
"go.uber.org/multierr"
|
|
"go.uber.org/zap"
|
|
"go.uber.org/zap/zapcore"
|
|
"runtime"
|
|
"sync/atomic"
|
|
"time"
|
|
)
|
|
|
|
var _ Logger = (*Core)(nil)
|
|
|
|
type atomicInnerCore struct {
|
|
innerLoggerPtr atomic.Value
|
|
}
|
|
|
|
func (af *atomicInnerCore) get() *innerCore {
|
|
return af.innerLoggerPtr.Load().(*innerCore)
|
|
}
|
|
|
|
func (af *atomicInnerCore) set(ic *innerCore) {
|
|
af.innerLoggerPtr.Store(ic)
|
|
}
|
|
|
|
// innerCore holds state which can be reconfigured at the factory level.
|
|
// if these settings are changed in the factory, the factory builds new
|
|
// innerCore instances, and atomically injects them into all existing loggers.
|
|
type innerCore struct {
|
|
name string
|
|
zapcore.Core
|
|
addCaller bool
|
|
errorOutput zapcore.WriteSyncer
|
|
hooks []HookFunc
|
|
}
|
|
|
|
// Core is the concrete implementation of Logger. It has some additional
|
|
// lower-level methods which can be used by other logging packages which wrap
|
|
// flume, to build alternate logging interfaces.
|
|
type Core struct {
|
|
*atomicInnerCore
|
|
context []zap.Field
|
|
callerSkip int
|
|
// these are logger-scoped hooks, which only hook into this particular logger
|
|
hooks []HookFunc
|
|
}
|
|
|
|
// Log is the core logging method, used by the convenience methods Debug(), Info(), and Error().
|
|
//
|
|
// Returns true if the log was actually logged.
|
|
//
|
|
// AddCaller option will report the caller of this method. If wrapping this, be sure to
|
|
// use the AddCallerSkip option.
|
|
func (l *Core) Log(lvl Level, template string, fmtArgs, context []interface{}) bool {
|
|
// call another method, just to add a caller to the call stack, so the
|
|
// add caller option resolves the right caller in the stack
|
|
return l.log(lvl, template, fmtArgs, context)
|
|
}
|
|
|
|
// log must be called directly from one of the public methods to make the addcaller
|
|
// resolution resolve the caller of the public method.
|
|
func (l *Core) log(lvl Level, template string, fmtArgs, context []interface{}) bool {
|
|
c := l.get()
|
|
|
|
if !c.Enabled(zapcore.Level(lvl)) {
|
|
return false
|
|
}
|
|
|
|
msg := template
|
|
if msg == "" && len(fmtArgs) > 0 {
|
|
msg = fmt.Sprint(fmtArgs...)
|
|
} else if msg != "" && len(fmtArgs) > 0 {
|
|
msg = fmt.Sprintf(template, fmtArgs...)
|
|
}
|
|
|
|
// check must always be called directly by a method in the Logger interface
|
|
// (e.g., Log, Info, Debug).
|
|
const callerSkipOffset = 2
|
|
|
|
// Create basic checked entry thru the core; this will be non-nil if the
|
|
// log message will actually be written somewhere.
|
|
ent := zapcore.Entry{
|
|
LoggerName: c.name,
|
|
Time: time.Now(),
|
|
Level: zapcore.Level(lvl),
|
|
Message: msg,
|
|
}
|
|
ce := c.Check(ent, nil)
|
|
if ce == nil {
|
|
return false
|
|
}
|
|
|
|
// Thread the error output through to the CheckedEntry.
|
|
ce.ErrorOutput = c.errorOutput
|
|
if c.addCaller {
|
|
ce.Entry.Caller = zapcore.NewEntryCaller(runtime.Caller(l.callerSkip + callerSkipOffset))
|
|
if !ce.Entry.Caller.Defined {
|
|
_, _ = fmt.Fprintf(c.errorOutput, "%v Logger.check error: failed to get caller\n", time.Now().UTC())
|
|
_ = ce.ErrorOutput.Sync()
|
|
}
|
|
}
|
|
|
|
fields := append(l.context, l.sweetenFields(context)...) //nolint:gocritic
|
|
|
|
// execute global hooks, which might modify the fields
|
|
for i := range c.hooks {
|
|
if f := c.hooks[i](ce, fields); f != nil {
|
|
fields = f
|
|
}
|
|
}
|
|
|
|
// execute logger hooks
|
|
for i := range l.hooks {
|
|
if f := l.hooks[i](ce, fields); f != nil {
|
|
fields = f
|
|
}
|
|
}
|
|
|
|
ce.Write(fields...)
|
|
return true
|
|
}
|
|
|
|
// IsEnabled returns true if the specified level is enabled.
|
|
func (l *Core) IsEnabled(lvl Level) bool {
|
|
return l.get().Enabled(zapcore.Level(lvl))
|
|
}
|
|
|
|
const (
|
|
_oddNumberErrMsg = "Ignored key without a value."
|
|
_nonStringKeyErrMsg = "Ignored key-value pairs with non-string keys."
|
|
)
|
|
|
|
func (l *Core) sweetenFields(args []interface{}) []zap.Field {
|
|
if len(args) == 0 {
|
|
return nil
|
|
}
|
|
|
|
// Allocate enough space for the worst case; if users pass only structured
|
|
// fields, we shouldn't penalize them with extra allocations.
|
|
fields := make([]zap.Field, 0, len(args))
|
|
var invalid invalidPairs
|
|
|
|
for i := 0; i < len(args); {
|
|
// This is a strongly-typed field. Consume it and move on.
|
|
if f, ok := args[i].(zap.Field); ok {
|
|
fields = append(fields, f)
|
|
i++
|
|
continue
|
|
}
|
|
|
|
if len(args) == 1 {
|
|
// passed a bare arg with no key. We'll handle this
|
|
// as a special case
|
|
if err, ok := args[0].(error); ok {
|
|
return append(fields, zap.Error(err))
|
|
}
|
|
return append(fields, zap.Any("", args[0]))
|
|
}
|
|
|
|
// Make sure this element isn't a dangling key.
|
|
if i == len(args)-1 {
|
|
l.Error(_oddNumberErrMsg, zap.Any("ignored", args[i]))
|
|
break
|
|
}
|
|
|
|
// Consume this value and the next, treating them as a key-value pair. If the
|
|
// key isn't a string, add this pair to the slice of invalid pairs.
|
|
key, val := args[i], args[i+1]
|
|
if keyStr, ok := key.(string); !ok {
|
|
// Subsequent errors are likely, so allocate once up front.
|
|
if cap(invalid) == 0 {
|
|
invalid = make(invalidPairs, 0, len(args)/2)
|
|
}
|
|
invalid = append(invalid, invalidPair{i, key, val})
|
|
} else {
|
|
fields = append(fields, zap.Any(keyStr, val))
|
|
}
|
|
i += 2
|
|
}
|
|
|
|
// If we encountered any invalid key-value pairs, log an error.
|
|
if len(invalid) > 0 {
|
|
l.Error(_nonStringKeyErrMsg, zap.Array("invalid", invalid))
|
|
}
|
|
return fields
|
|
}
|
|
|
|
type invalidPair struct {
|
|
position int
|
|
key, value interface{}
|
|
}
|
|
|
|
func (p invalidPair) MarshalLogObject(enc zapcore.ObjectEncoder) error {
|
|
enc.AddInt64("position", int64(p.position))
|
|
zap.Any("key", p.key).AddTo(enc)
|
|
zap.Any("value", p.value).AddTo(enc)
|
|
return nil
|
|
}
|
|
|
|
type invalidPairs []invalidPair
|
|
|
|
func (ps invalidPairs) MarshalLogArray(enc zapcore.ArrayEncoder) error {
|
|
var err error
|
|
for i := range ps {
|
|
err = multierr.Append(err, enc.AppendObject(ps[i]))
|
|
}
|
|
return err
|
|
}
|
|
|
|
// Debug logs at DBG level. args should be alternative keys and values. keys should be strings.
|
|
func (l *Core) Debug(msg string, args ...interface{}) {
|
|
l.log(DebugLevel, msg, nil, args)
|
|
}
|
|
|
|
// Info logs at INF level. args should be alternative keys and values. keys should be strings.
|
|
func (l *Core) Info(msg string, args ...interface{}) {
|
|
l.log(InfoLevel, msg, nil, args)
|
|
}
|
|
|
|
// Error logs at ERR level. args should be alternative keys and values. keys should be strings.
|
|
func (l *Core) Error(msg string, args ...interface{}) {
|
|
l.log(ErrorLevel, msg, nil, args)
|
|
}
|
|
|
|
// IsDebug returns true if DBG level is enabled.
|
|
func (l *Core) IsDebug() bool {
|
|
return l.IsEnabled(DebugLevel)
|
|
}
|
|
|
|
// IsDebug returns true if INF level is enabled
|
|
func (l *Core) IsInfo() bool {
|
|
return l.IsEnabled(InfoLevel)
|
|
}
|
|
|
|
// With returns a new Logger with some context baked in. All entries
|
|
// logged with the new logger will include this context.
|
|
//
|
|
// args should be alternative keys and values. keys should be strings.
|
|
//
|
|
// reqLogger := l.With("requestID", reqID)
|
|
//
|
|
func (l *Core) With(args ...interface{}) Logger {
|
|
return l.WithArgs(args...)
|
|
}
|
|
|
|
// WithArgs is the same as With() but returns the concrete type. Useful
|
|
// for other logging packages which wrap this one.
|
|
func (l *Core) WithArgs(args ...interface{}) *Core {
|
|
l2 := l.clone()
|
|
switch len(args) {
|
|
case 0:
|
|
default:
|
|
l2.context = append(l2.context, l.sweetenFields(args)...)
|
|
}
|
|
return l2
|
|
}
|
|
|
|
func (l *Core) clone() *Core {
|
|
l2 := *l
|
|
l2.context = nil
|
|
if len(l.context) > 0 {
|
|
l2.context = append(l2.context, l.context...)
|
|
}
|
|
return &l2
|
|
}
|