ContainerImage.Pinniped/internal/plog/global.go

78 lines
2.5 KiB
Go

// Copyright 2020-2022 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package plog
import (
"context"
"fmt"
"net/url"
"sync"
"github.com/go-logr/logr"
"go.uber.org/zap"
"k8s.io/component-base/logs"
"k8s.io/klog/v2"
)
//nolint:gochecknoglobals
var (
// note that these globals have no locks on purpose - they are expected to be set at init and then again after config parsing.
globalLevel zap.AtomicLevel
globalLogger logr.Logger
globalFlush func()
// used as a temporary storage for a buffer per call of newLogr. see the init function below for more details.
sinkMap sync.Map
)
//nolint:gochecknoinits
func init() {
// make sure we always have a functional global logger
globalLevel = zap.NewAtomicLevelAt(0) // log at the 0 verbosity level to start with, i.e. the "always" logs
// use json encoding to start with
// the context here is just used for test injection and thus can be ignored
log, flush, err := newLogr(context.Background(), "json", 0)
if err != nil {
panic(err) // default logging config must always work
}
setGlobalLoggers(log, flush)
// this is a little crazy but zap's builder code does not allow us to directly specify what
// writer we want to use as our log sink. to get around this limitation in tests, we use a
// global map to temporarily hold the writer (the key is a random string that is generated
// per invocation of newLogr). we register a fake "pinniped" scheme so that we can lookup
// the writer via pinniped:///<per newLogr invocation random string>.
if err := zap.RegisterSink("pinniped", func(u *url.URL) (zap.Sink, error) {
value, ok := sinkMap.Load(u.Path)
if !ok {
return nil, fmt.Errorf("key %q not in global sink", u.Path)
}
return value.(zap.Sink), nil
}); err != nil {
panic(err) // custom sink must always work
}
}
// Deprecated: Use New instead. This is meant for old code only.
// New provides a more ergonomic API and correctly responds to global log config change.
func Logr() logr.Logger {
return globalLogger
}
func Setup() func() {
logs.InitLogs()
return func() {
logs.FlushLogs()
globalFlush()
}
}
// setGlobalLoggers sets the plog and klog global loggers. it is *not* go routine safe.
func setGlobalLoggers(log logr.Logger, flush func()) {
// a contextual logger does its own level based enablement checks, which is true for all of our loggers
klog.SetLoggerWithOptions(log, klog.ContextualLogger(true), klog.FlushLogger(flush))
globalLogger = log
globalFlush = flush
}