mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-19 01:40:13 +00:00
kubelet: convert node shutdown manager to contextual logging
This will make output checking easier (done in a separate commit). kubelet itself still uses the global logger.
This commit is contained in:
parent
0669ba386b
commit
65385fec20
@ -334,6 +334,8 @@ func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration,
|
||||
nodeStatusMaxImages int32,
|
||||
seccompDefault bool,
|
||||
) (*Kubelet, error) {
|
||||
logger := klog.TODO()
|
||||
|
||||
if rootDirectory == "" {
|
||||
return nil, fmt.Errorf("invalid root directory %q", rootDirectory)
|
||||
}
|
||||
@ -819,6 +821,7 @@ func NewMainKubelet(kubeCfg *kubeletconfiginternal.KubeletConfiguration,
|
||||
|
||||
// setup node shutdown manager
|
||||
shutdownManager, shutdownAdmitHandler := nodeshutdown.NewManager(&nodeshutdown.Config{
|
||||
Logger: logger,
|
||||
ProbeManager: klet.probeManager,
|
||||
Recorder: kubeDeps.Recorder,
|
||||
NodeRef: nodeRef,
|
||||
|
@ -44,6 +44,7 @@ import (
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
"k8s.io/client-go/tools/record"
|
||||
"k8s.io/client-go/util/flowcontrol"
|
||||
"k8s.io/klog/v2/ktesting"
|
||||
cadvisortest "k8s.io/kubernetes/pkg/kubelet/cadvisor/testing"
|
||||
"k8s.io/kubernetes/pkg/kubelet/cm"
|
||||
"k8s.io/kubernetes/pkg/kubelet/config"
|
||||
@ -149,6 +150,8 @@ func newTestKubeletWithImageList(
|
||||
imageList []kubecontainer.Image,
|
||||
controllerAttachDetachEnabled bool,
|
||||
initFakeVolumePlugin bool) *TestKubelet {
|
||||
logger, _ := ktesting.NewTestContext(t)
|
||||
|
||||
fakeRuntime := &containertest.FakeRuntime{
|
||||
ImageList: imageList,
|
||||
// Set ready conditions by default.
|
||||
@ -321,6 +324,7 @@ func newTestKubeletWithImageList(
|
||||
|
||||
// setup shutdown manager
|
||||
shutdownManager, shutdownAdmitHandler := nodeshutdown.NewManager(&nodeshutdown.Config{
|
||||
Logger: logger,
|
||||
ProbeManager: kubelet.probeManager,
|
||||
Recorder: fakeRecorder,
|
||||
NodeRef: nodeRef,
|
||||
|
@ -21,6 +21,7 @@ import (
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/client-go/tools/record"
|
||||
"k8s.io/klog/v2"
|
||||
kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config"
|
||||
"k8s.io/kubernetes/pkg/kubelet/eviction"
|
||||
"k8s.io/kubernetes/pkg/kubelet/lifecycle"
|
||||
@ -37,6 +38,7 @@ type Manager interface {
|
||||
|
||||
// Config represents Manager configuration
|
||||
type Config struct {
|
||||
Logger klog.Logger
|
||||
ProbeManager prober.Manager
|
||||
Recorder record.EventRecorder
|
||||
NodeRef *v1.ObjectReference
|
||||
|
@ -67,6 +67,7 @@ type dbusInhibiter interface {
|
||||
|
||||
// managerImpl has functions that can be used to interact with the Node Shutdown Manager.
|
||||
type managerImpl struct {
|
||||
logger klog.Logger
|
||||
recorder record.EventRecorder
|
||||
nodeRef *v1.ObjectReference
|
||||
probeManager prober.Manager
|
||||
@ -118,6 +119,7 @@ func NewManager(conf *Config) (Manager, lifecycle.PodAdmitHandler) {
|
||||
conf.Clock = clock.RealClock{}
|
||||
}
|
||||
manager := &managerImpl{
|
||||
logger: conf.Logger,
|
||||
probeManager: conf.ProbeManager,
|
||||
recorder: conf.Recorder,
|
||||
nodeRef: conf.NodeRef,
|
||||
@ -131,7 +133,7 @@ func NewManager(conf *Config) (Manager, lifecycle.PodAdmitHandler) {
|
||||
Path: filepath.Join(conf.StateDirectory, localStorageStateFile),
|
||||
},
|
||||
}
|
||||
klog.InfoS("Creating node shutdown manager",
|
||||
manager.logger.Info("Creating node shutdown manager",
|
||||
"shutdownGracePeriodRequested", conf.ShutdownGracePeriodRequested,
|
||||
"shutdownGracePeriodCriticalPods", conf.ShutdownGracePeriodCriticalPods,
|
||||
"shutdownGracePeriodByPodPriority", shutdownGracePeriodByPodPriority,
|
||||
@ -159,7 +161,7 @@ func (m *managerImpl) setMetrics() {
|
||||
sta := state{}
|
||||
err := m.storage.Load(&sta)
|
||||
if err != nil {
|
||||
klog.ErrorS(err, "Failed to load graceful shutdown state")
|
||||
m.logger.Error(err, "Failed to load graceful shutdown state")
|
||||
} else {
|
||||
if !sta.StartTime.IsZero() {
|
||||
metrics.GracefulShutdownStartTime.Set(timestamp(sta.StartTime))
|
||||
@ -184,10 +186,10 @@ func (m *managerImpl) Start() error {
|
||||
}
|
||||
|
||||
time.Sleep(dbusReconnectPeriod)
|
||||
klog.V(1).InfoS("Restarting watch for node shutdown events")
|
||||
m.logger.V(1).Info("Restarting watch for node shutdown events")
|
||||
stop, err = m.start()
|
||||
if err != nil {
|
||||
klog.ErrorS(err, "Unable to watch the node for shutdown events")
|
||||
m.logger.Error(err, "Unable to watch the node for shutdown events")
|
||||
}
|
||||
}
|
||||
}()
|
||||
@ -255,11 +257,11 @@ func (m *managerImpl) start() (chan struct{}, error) {
|
||||
select {
|
||||
case isShuttingDown, ok := <-events:
|
||||
if !ok {
|
||||
klog.ErrorS(err, "Ended to watching the node for shutdown events")
|
||||
m.logger.Error(err, "Ended to watching the node for shutdown events")
|
||||
close(stop)
|
||||
return
|
||||
}
|
||||
klog.V(1).InfoS("Shutdown manager detected new shutdown event, isNodeShuttingDownNow", "event", isShuttingDown)
|
||||
m.logger.V(1).Info("Shutdown manager detected new shutdown event, isNodeShuttingDownNow", "event", isShuttingDown)
|
||||
|
||||
var shutdownType string
|
||||
if isShuttingDown {
|
||||
@ -267,7 +269,7 @@ func (m *managerImpl) start() (chan struct{}, error) {
|
||||
} else {
|
||||
shutdownType = "cancelled"
|
||||
}
|
||||
klog.V(1).InfoS("Shutdown manager detected new shutdown event", "event", shutdownType)
|
||||
m.logger.V(1).Info("Shutdown manager detected new shutdown event", "event", shutdownType)
|
||||
if isShuttingDown {
|
||||
m.recorder.Event(m.nodeRef, v1.EventTypeNormal, kubeletevents.NodeShutdown, "Shutdown manager detected shutdown event")
|
||||
} else {
|
||||
@ -316,12 +318,12 @@ func (m *managerImpl) ShutdownStatus() error {
|
||||
}
|
||||
|
||||
func (m *managerImpl) processShutdownEvent() error {
|
||||
klog.V(1).InfoS("Shutdown manager processing shutdown event")
|
||||
m.logger.V(1).Info("Shutdown manager processing shutdown event")
|
||||
activePods := m.getPods()
|
||||
|
||||
defer func() {
|
||||
m.dbusCon.ReleaseInhibitLock(m.inhibitLock)
|
||||
klog.V(1).InfoS("Shutdown manager completed processing shutdown event, node will shutdown shortly")
|
||||
m.logger.V(1).Info("Shutdown manager completed processing shutdown event, node will shutdown shortly")
|
||||
}()
|
||||
|
||||
if m.enableMetrics && m.storage != nil {
|
||||
@ -330,7 +332,7 @@ func (m *managerImpl) processShutdownEvent() error {
|
||||
StartTime: startTime,
|
||||
})
|
||||
if err != nil {
|
||||
klog.ErrorS(err, "Failed to store graceful shutdown state")
|
||||
m.logger.Error(err, "Failed to store graceful shutdown state")
|
||||
}
|
||||
metrics.GracefulShutdownStartTime.Set(timestamp(startTime))
|
||||
metrics.GracefulShutdownEndTime.Set(0)
|
||||
@ -342,7 +344,7 @@ func (m *managerImpl) processShutdownEvent() error {
|
||||
EndTime: endTime,
|
||||
})
|
||||
if err != nil {
|
||||
klog.ErrorS(err, "Failed to store graceful shutdown state")
|
||||
m.logger.Error(err, "Failed to store graceful shutdown state")
|
||||
}
|
||||
metrics.GracefulShutdownStartTime.Set(timestamp(endTime))
|
||||
}()
|
||||
@ -369,7 +371,7 @@ func (m *managerImpl) processShutdownEvent() error {
|
||||
gracePeriodOverride = *pod.Spec.TerminationGracePeriodSeconds
|
||||
}
|
||||
|
||||
klog.V(1).InfoS("Shutdown manager killing pod with gracePeriod", "pod", klog.KObj(pod), "gracePeriod", gracePeriodOverride)
|
||||
m.logger.V(1).Info("Shutdown manager killing pod with gracePeriod", "pod", klog.KObj(pod), "gracePeriod", gracePeriodOverride)
|
||||
|
||||
if err := m.killPodFunc(pod, false, &gracePeriodOverride, func(status *v1.PodStatus) {
|
||||
// set the pod status to failed (unless it was already in a successful terminal phase)
|
||||
@ -379,9 +381,9 @@ func (m *managerImpl) processShutdownEvent() error {
|
||||
status.Message = nodeShutdownMessage
|
||||
status.Reason = nodeShutdownReason
|
||||
}); err != nil {
|
||||
klog.V(1).InfoS("Shutdown manager failed killing pod", "pod", klog.KObj(pod), "err", err)
|
||||
m.logger.V(1).Info("Shutdown manager failed killing pod", "pod", klog.KObj(pod), "err", err)
|
||||
} else {
|
||||
klog.V(1).InfoS("Shutdown manager finished killing pod", "pod", klog.KObj(pod))
|
||||
m.logger.V(1).Info("Shutdown manager finished killing pod", "pod", klog.KObj(pod))
|
||||
}
|
||||
}(pod, group)
|
||||
}
|
||||
@ -399,7 +401,7 @@ func (m *managerImpl) processShutdownEvent() error {
|
||||
case <-doneCh:
|
||||
timer.Stop()
|
||||
case <-timer.C():
|
||||
klog.V(1).InfoS("Shutdown manager pod killing time out", "gracePeriod", group.ShutdownGracePeriodSeconds, "priority", group.Priority)
|
||||
m.logger.V(1).Info("Shutdown manager pod killing time out", "gracePeriod", group.ShutdownGracePeriodSeconds, "priority", group.Priority)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,8 @@ import (
|
||||
"k8s.io/client-go/tools/record"
|
||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
||||
"k8s.io/klog/v2"
|
||||
"k8s.io/klog/v2/ktesting"
|
||||
_ "k8s.io/klog/v2/ktesting/init" // activate ktesting command line flags
|
||||
"k8s.io/kubernetes/pkg/apis/scheduling"
|
||||
pkgfeatures "k8s.io/kubernetes/pkg/features"
|
||||
kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config"
|
||||
@ -211,6 +213,8 @@ func TestManager(t *testing.T) {
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
logger, _ := ktesting.NewTestContext(t)
|
||||
|
||||
activePodsFunc := func() []*v1.Pod {
|
||||
return tc.activePods
|
||||
}
|
||||
@ -243,6 +247,7 @@ func TestManager(t *testing.T) {
|
||||
fakeRecorder := &record.FakeRecorder{}
|
||||
nodeRef := &v1.ObjectReference{Kind: "Node", Name: "test", UID: types.UID("test"), Namespace: ""}
|
||||
manager, _ := NewManager(&Config{
|
||||
Logger: logger,
|
||||
ProbeManager: proberManager,
|
||||
Recorder: fakeRecorder,
|
||||
NodeRef: nodeRef,
|
||||
@ -326,6 +331,7 @@ func TestFeatureEnabled(t *testing.T) {
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
logger, _ := ktesting.NewTestContext(t)
|
||||
activePodsFunc := func() []*v1.Pod {
|
||||
return nil
|
||||
}
|
||||
@ -339,6 +345,7 @@ func TestFeatureEnabled(t *testing.T) {
|
||||
nodeRef := &v1.ObjectReference{Kind: "Node", Name: "test", UID: types.UID("test"), Namespace: ""}
|
||||
|
||||
manager, _ := NewManager(&Config{
|
||||
Logger: logger,
|
||||
ProbeManager: proberManager,
|
||||
Recorder: fakeRecorder,
|
||||
NodeRef: nodeRef,
|
||||
@ -355,6 +362,7 @@ func TestFeatureEnabled(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestRestart(t *testing.T) {
|
||||
logger, _ := ktesting.NewTestContext(t)
|
||||
systemDbusTmp := systemDbus
|
||||
defer func() {
|
||||
systemDbus = systemDbusTmp
|
||||
@ -393,6 +401,7 @@ func TestRestart(t *testing.T) {
|
||||
fakeRecorder := &record.FakeRecorder{}
|
||||
nodeRef := &v1.ObjectReference{Kind: "Node", Name: "test", UID: types.UID("test"), Namespace: ""}
|
||||
manager, _ := NewManager(&Config{
|
||||
Logger: logger,
|
||||
ProbeManager: proberManager,
|
||||
Recorder: fakeRecorder,
|
||||
NodeRef: nodeRef,
|
||||
@ -706,6 +715,7 @@ func Test_managerImpl_processShutdownEvent(t *testing.T) {
|
||||
klog.LogToStderr(false)
|
||||
|
||||
m := &managerImpl{
|
||||
logger: klog.TODO(), // This test will be updated in a separate commit.
|
||||
recorder: tt.fields.recorder,
|
||||
nodeRef: tt.fields.nodeRef,
|
||||
probeManager: tt.fields.probeManager,
|
||||
|
305
vendor/k8s.io/klog/v2/internal/verbosity/verbosity.go
generated
vendored
Normal file
305
vendor/k8s.io/klog/v2/internal/verbosity/verbosity.go
generated
vendored
Normal file
@ -0,0 +1,305 @@
|
||||
/*
|
||||
Copyright 2013 Google Inc. All Rights Reserved.
|
||||
Copyright 2022 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 verbosity
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// New returns a struct that implements -v and -vmodule support. Changing and
|
||||
// checking these settings is thread-safe, with all concurrency issues handled
|
||||
// internally.
|
||||
func New() *VState {
|
||||
vs := new(VState)
|
||||
|
||||
// The two fields must have a pointer to the overal struct for their
|
||||
// implementation of Set.
|
||||
vs.vmodule.vs = vs
|
||||
vs.verbosity.vs = vs
|
||||
|
||||
return vs
|
||||
}
|
||||
|
||||
// Value is an extension that makes it possible to use the values in pflag.
|
||||
type Value interface {
|
||||
flag.Value
|
||||
Type() string
|
||||
}
|
||||
|
||||
func (vs *VState) V() Value {
|
||||
return &vs.verbosity
|
||||
}
|
||||
|
||||
func (vs *VState) VModule() Value {
|
||||
return &vs.vmodule
|
||||
}
|
||||
|
||||
// VState contains settings and state. Some of its fields can be accessed
|
||||
// through atomic read/writes, in other cases a mutex must be held.
|
||||
type VState struct {
|
||||
mu sync.Mutex
|
||||
|
||||
// These flags are modified only under lock, although verbosity may be fetched
|
||||
// safely using atomic.LoadInt32.
|
||||
vmodule moduleSpec // The state of the -vmodule flag.
|
||||
verbosity levelSpec // V logging level, the value of the -v flag/
|
||||
|
||||
// pcs is used in V to avoid an allocation when computing the caller's PC.
|
||||
pcs [1]uintptr
|
||||
// vmap is a cache of the V Level for each V() call site, identified by PC.
|
||||
// It is wiped whenever the vmodule flag changes state.
|
||||
vmap map[uintptr]Level
|
||||
// filterLength stores the length of the vmodule filter chain. If greater
|
||||
// than zero, it means vmodule is enabled. It may be read safely
|
||||
// using sync.LoadInt32, but is only modified under mu.
|
||||
filterLength int32
|
||||
}
|
||||
|
||||
// Level must be an int32 to support atomic read/writes.
|
||||
type Level int32
|
||||
|
||||
type levelSpec struct {
|
||||
vs *VState
|
||||
l Level
|
||||
}
|
||||
|
||||
// get returns the value of the level.
|
||||
func (l *levelSpec) get() Level {
|
||||
return Level(atomic.LoadInt32((*int32)(&l.l)))
|
||||
}
|
||||
|
||||
// set sets the value of the level.
|
||||
func (l *levelSpec) set(val Level) {
|
||||
atomic.StoreInt32((*int32)(&l.l), int32(val))
|
||||
}
|
||||
|
||||
// String is part of the flag.Value interface.
|
||||
func (l *levelSpec) String() string {
|
||||
return strconv.FormatInt(int64(l.l), 10)
|
||||
}
|
||||
|
||||
// Get is part of the flag.Getter interface. It returns the
|
||||
// verbosity level as int32.
|
||||
func (l *levelSpec) Get() interface{} {
|
||||
return int32(l.l)
|
||||
}
|
||||
|
||||
// Type is part of pflag.Value.
|
||||
func (l *levelSpec) Type() string {
|
||||
return "Level"
|
||||
}
|
||||
|
||||
// Set is part of the flag.Value interface.
|
||||
func (l *levelSpec) Set(value string) error {
|
||||
v, err := strconv.ParseInt(value, 10, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
l.vs.mu.Lock()
|
||||
defer l.vs.mu.Unlock()
|
||||
l.vs.set(Level(v), l.vs.vmodule.filter, false)
|
||||
return nil
|
||||
}
|
||||
|
||||
// moduleSpec represents the setting of the -vmodule flag.
|
||||
type moduleSpec struct {
|
||||
vs *VState
|
||||
filter []modulePat
|
||||
}
|
||||
|
||||
// modulePat contains a filter for the -vmodule flag.
|
||||
// It holds a verbosity level and a file pattern to match.
|
||||
type modulePat struct {
|
||||
pattern string
|
||||
literal bool // The pattern is a literal string
|
||||
level Level
|
||||
}
|
||||
|
||||
// match reports whether the file matches the pattern. It uses a string
|
||||
// comparison if the pattern contains no metacharacters.
|
||||
func (m *modulePat) match(file string) bool {
|
||||
if m.literal {
|
||||
return file == m.pattern
|
||||
}
|
||||
match, _ := filepath.Match(m.pattern, file)
|
||||
return match
|
||||
}
|
||||
|
||||
func (m *moduleSpec) String() string {
|
||||
// Lock because the type is not atomic. TODO: clean this up.
|
||||
// Empty instances don't have and don't need a lock (can
|
||||
// happen when flag uses introspection).
|
||||
if m.vs != nil {
|
||||
m.vs.mu.Lock()
|
||||
defer m.vs.mu.Unlock()
|
||||
}
|
||||
var b bytes.Buffer
|
||||
for i, f := range m.filter {
|
||||
if i > 0 {
|
||||
b.WriteRune(',')
|
||||
}
|
||||
fmt.Fprintf(&b, "%s=%d", f.pattern, f.level)
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// Get is part of the (Go 1.2) flag.Getter interface. It always returns nil for this flag type since the
|
||||
// struct is not exported.
|
||||
func (m *moduleSpec) Get() interface{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Type is part of pflag.Value
|
||||
func (m *moduleSpec) Type() string {
|
||||
return "pattern=N,..."
|
||||
}
|
||||
|
||||
var errVmoduleSyntax = errors.New("syntax error: expect comma-separated list of filename=N")
|
||||
|
||||
// Set will sets module value
|
||||
// Syntax: -vmodule=recordio=2,file=1,gfs*=3
|
||||
func (m *moduleSpec) Set(value string) error {
|
||||
var filter []modulePat
|
||||
for _, pat := range strings.Split(value, ",") {
|
||||
if len(pat) == 0 {
|
||||
// Empty strings such as from a trailing comma can be ignored.
|
||||
continue
|
||||
}
|
||||
patLev := strings.Split(pat, "=")
|
||||
if len(patLev) != 2 || len(patLev[0]) == 0 || len(patLev[1]) == 0 {
|
||||
return errVmoduleSyntax
|
||||
}
|
||||
pattern := patLev[0]
|
||||
v, err := strconv.ParseInt(patLev[1], 10, 32)
|
||||
if err != nil {
|
||||
return errors.New("syntax error: expect comma-separated list of filename=N")
|
||||
}
|
||||
if v < 0 {
|
||||
return errors.New("negative value for vmodule level")
|
||||
}
|
||||
if v == 0 {
|
||||
continue // Ignore. It's harmless but no point in paying the overhead.
|
||||
}
|
||||
// TODO: check syntax of filter?
|
||||
filter = append(filter, modulePat{pattern, isLiteral(pattern), Level(v)})
|
||||
}
|
||||
m.vs.mu.Lock()
|
||||
defer m.vs.mu.Unlock()
|
||||
m.vs.set(m.vs.verbosity.l, filter, true)
|
||||
return nil
|
||||
}
|
||||
|
||||
// isLiteral reports whether the pattern is a literal string, that is, has no metacharacters
|
||||
// that require filepath.Match to be called to match the pattern.
|
||||
func isLiteral(pattern string) bool {
|
||||
return !strings.ContainsAny(pattern, `\*?[]`)
|
||||
}
|
||||
|
||||
// set sets a consistent state for V logging.
|
||||
// The mutex must be held.
|
||||
func (vs *VState) set(l Level, filter []modulePat, setFilter bool) {
|
||||
// Turn verbosity off so V will not fire while we are in transition.
|
||||
vs.verbosity.set(0)
|
||||
// Ditto for filter length.
|
||||
atomic.StoreInt32(&vs.filterLength, 0)
|
||||
|
||||
// Set the new filters and wipe the pc->Level map if the filter has changed.
|
||||
if setFilter {
|
||||
vs.vmodule.filter = filter
|
||||
vs.vmap = make(map[uintptr]Level)
|
||||
}
|
||||
|
||||
// Things are consistent now, so enable filtering and verbosity.
|
||||
// They are enabled in order opposite to that in V.
|
||||
atomic.StoreInt32(&vs.filterLength, int32(len(filter)))
|
||||
vs.verbosity.set(l)
|
||||
}
|
||||
|
||||
// Enabled checks whether logging is enabled at the given level. This must be
|
||||
// called with depth=0 when the caller of enabled will do the logging and
|
||||
// higher values when more stack levels need to be skipped.
|
||||
//
|
||||
// The mutex will be locked only if needed.
|
||||
func (vs *VState) Enabled(level Level, depth int) bool {
|
||||
// This function tries hard to be cheap unless there's work to do.
|
||||
// The fast path is two atomic loads and compares.
|
||||
|
||||
// Here is a cheap but safe test to see if V logging is enabled globally.
|
||||
if vs.verbosity.get() >= level {
|
||||
return true
|
||||
}
|
||||
|
||||
// It's off globally but vmodule may still be set.
|
||||
// Here is another cheap but safe test to see if vmodule is enabled.
|
||||
if atomic.LoadInt32(&vs.filterLength) > 0 {
|
||||
// Now we need a proper lock to use the logging structure. The pcs field
|
||||
// is shared so we must lock before accessing it. This is fairly expensive,
|
||||
// but if V logging is enabled we're slow anyway.
|
||||
vs.mu.Lock()
|
||||
defer vs.mu.Unlock()
|
||||
if runtime.Callers(depth+2, vs.pcs[:]) == 0 {
|
||||
return false
|
||||
}
|
||||
// runtime.Callers returns "return PCs", but we want
|
||||
// to look up the symbolic information for the call,
|
||||
// so subtract 1 from the PC. runtime.CallersFrames
|
||||
// would be cleaner, but allocates.
|
||||
pc := vs.pcs[0] - 1
|
||||
v, ok := vs.vmap[pc]
|
||||
if !ok {
|
||||
v = vs.setV(pc)
|
||||
}
|
||||
return v >= level
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// setV computes and remembers the V level for a given PC
|
||||
// when vmodule is enabled.
|
||||
// File pattern matching takes the basename of the file, stripped
|
||||
// of its .go suffix, and uses filepath.Match, which is a little more
|
||||
// general than the *? matching used in C++.
|
||||
// Mutex is held.
|
||||
func (vs *VState) setV(pc uintptr) Level {
|
||||
fn := runtime.FuncForPC(pc)
|
||||
file, _ := fn.FileLine(pc)
|
||||
// The file is something like /a/b/c/d.go. We want just the d.
|
||||
if strings.HasSuffix(file, ".go") {
|
||||
file = file[:len(file)-3]
|
||||
}
|
||||
if slash := strings.LastIndex(file, "/"); slash >= 0 {
|
||||
file = file[slash+1:]
|
||||
}
|
||||
for _, filter := range vs.vmodule.filter {
|
||||
if filter.match(file) {
|
||||
vs.vmap[pc] = filter.level
|
||||
return filter.level
|
||||
}
|
||||
}
|
||||
vs.vmap[pc] = 0
|
||||
return 0
|
||||
}
|
35
vendor/k8s.io/klog/v2/ktesting/init/init.go
generated
vendored
Normal file
35
vendor/k8s.io/klog/v2/ktesting/init/init.go
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
Copyright 2021 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 init registers the command line flags for k8s.io/klogr/testing in
|
||||
// the flag.CommandLine. This is done during initialization, so merely
|
||||
// importing it is enough.
|
||||
//
|
||||
// Experimental
|
||||
//
|
||||
// Notice: This package is EXPERIMENTAL and may be changed or removed in a
|
||||
// later release.
|
||||
package init
|
||||
|
||||
import (
|
||||
"flag"
|
||||
|
||||
"k8s.io/klog/v2/ktesting"
|
||||
)
|
||||
|
||||
func init() {
|
||||
ktesting.DefaultConfig.AddFlags(flag.CommandLine)
|
||||
}
|
130
vendor/k8s.io/klog/v2/ktesting/options.go
generated
vendored
Normal file
130
vendor/k8s.io/klog/v2/ktesting/options.go
generated
vendored
Normal file
@ -0,0 +1,130 @@
|
||||
/*
|
||||
Copyright 2021 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 ktesting
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"strconv"
|
||||
|
||||
"k8s.io/klog/v2/internal/verbosity"
|
||||
)
|
||||
|
||||
// Config influences logging in a test logger. To make this configurable via
|
||||
// command line flags, instantiate this once per program and use AddFlags to
|
||||
// bind command line flags to the instance before passing it to NewTestContext.
|
||||
//
|
||||
// Must be constructed with NewConfig.
|
||||
//
|
||||
// Experimental
|
||||
//
|
||||
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
||||
// later release.
|
||||
type Config struct {
|
||||
vstate *verbosity.VState
|
||||
co configOptions
|
||||
}
|
||||
|
||||
// ConfigOption implements functional parameters for NewConfig.
|
||||
//
|
||||
// Experimental
|
||||
//
|
||||
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
||||
// later release.
|
||||
type ConfigOption func(co *configOptions)
|
||||
|
||||
type configOptions struct {
|
||||
verbosityFlagName string
|
||||
vmoduleFlagName string
|
||||
verbosityDefault int
|
||||
}
|
||||
|
||||
// VerbosityFlagName overrides the default -testing.v for the verbosity level.
|
||||
//
|
||||
// Experimental
|
||||
//
|
||||
// Notice: This function is EXPERIMENTAL and may be changed or removed in a
|
||||
// later release.
|
||||
func VerbosityFlagName(name string) ConfigOption {
|
||||
return func(co *configOptions) {
|
||||
co.verbosityFlagName = name
|
||||
}
|
||||
}
|
||||
|
||||
// VModulFlagName overrides the default -testing.vmodule for the per-module
|
||||
// verbosity levels.
|
||||
//
|
||||
// Experimental
|
||||
//
|
||||
// Notice: This function is EXPERIMENTAL and may be changed or removed in a
|
||||
// later release.
|
||||
func VModuleFlagName(name string) ConfigOption {
|
||||
return func(co *configOptions) {
|
||||
co.vmoduleFlagName = name
|
||||
}
|
||||
}
|
||||
|
||||
// Verbosity overrides the default verbosity level of 5. That default is higher
|
||||
// than in klog itself because it enables logging entries for "the steps
|
||||
// leading up to errors and warnings" and "troubleshooting" (see
|
||||
// https://github.com/kubernetes/community/blob/9406b4352fe2d5810cb21cc3cb059ce5886de157/contributors/devel/sig-instrumentation/logging.md#logging-conventions),
|
||||
// which is useful when debugging a failed test. `go test` only shows the log
|
||||
// output for failed tests. To see all output, use `go test -v`.
|
||||
//
|
||||
// Experimental
|
||||
//
|
||||
// Notice: This function is EXPERIMENTAL and may be changed or removed in a
|
||||
// later release.
|
||||
func Verbosity(level int) ConfigOption {
|
||||
return func(co *configOptions) {
|
||||
co.verbosityDefault = level
|
||||
}
|
||||
}
|
||||
|
||||
// NewConfig returns a configuration with recommended defaults and optional
|
||||
// modifications. Command line flags are not bound to any FlagSet yet.
|
||||
//
|
||||
// Experimental
|
||||
//
|
||||
// Notice: This function is EXPERIMENTAL and may be changed or removed in a
|
||||
// later release.
|
||||
func NewConfig(opts ...ConfigOption) *Config {
|
||||
c := &Config{
|
||||
co: configOptions{
|
||||
verbosityFlagName: "testing.v",
|
||||
vmoduleFlagName: "testing.vmodule",
|
||||
verbosityDefault: 5,
|
||||
},
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(&c.co)
|
||||
}
|
||||
|
||||
c.vstate = verbosity.New()
|
||||
c.vstate.V().Set(strconv.FormatInt(int64(c.co.verbosityDefault), 10))
|
||||
return c
|
||||
}
|
||||
|
||||
// AddFlags registers the command line flags that control the configuration.
|
||||
//
|
||||
// Experimental
|
||||
//
|
||||
// Notice: This function is EXPERIMENTAL and may be changed or removed in a
|
||||
// later release.
|
||||
func (c *Config) AddFlags(fs *flag.FlagSet) {
|
||||
fs.Var(c.vstate.V(), c.co.verbosityFlagName, "number for the log level verbosity of the testing logger")
|
||||
fs.Var(c.vstate.VModule(), c.co.vmoduleFlagName, "comma-separated list of pattern=N log level settings for files matching the patterns")
|
||||
}
|
48
vendor/k8s.io/klog/v2/ktesting/setup.go
generated
vendored
Normal file
48
vendor/k8s.io/klog/v2/ktesting/setup.go
generated
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
Copyright 2021 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 ktesting
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
)
|
||||
|
||||
// DefaultConfig is the global default logging configuration for a unit
|
||||
// test. It is used by NewTestContext and k8s.io/klogr/testing/init.
|
||||
//
|
||||
// Experimental
|
||||
//
|
||||
// Notice: This variable is EXPERIMENTAL and may be changed or removed in a
|
||||
// later release.
|
||||
var DefaultConfig = NewConfig()
|
||||
|
||||
// NewTestContext returns a logger and context for use in a unit test case or
|
||||
// benchmark. The tl parameter can be a testing.T or testing.B pointer that
|
||||
// will receive all log output. Importing k8s.io/klogr/testing/init will add
|
||||
// command line flags that modify the configuration of that log output.
|
||||
//
|
||||
// Experimental
|
||||
//
|
||||
// Notice: This function is EXPERIMENTAL and may be changed or removed in a
|
||||
// later release.
|
||||
func NewTestContext(tl TL) (logr.Logger, context.Context) {
|
||||
logger := NewLogger(tl, DefaultConfig)
|
||||
ctx := logr.NewContext(context.Background(), logger)
|
||||
return logger, ctx
|
||||
|
||||
}
|
338
vendor/k8s.io/klog/v2/ktesting/testinglogger.go
generated
vendored
Normal file
338
vendor/k8s.io/klog/v2/ktesting/testinglogger.go
generated
vendored
Normal file
@ -0,0 +1,338 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
Copyright 2020 Intel Coporation.
|
||||
|
||||
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 testinglogger contains an implementation of the logr interface
|
||||
// which is logging through a function like testing.TB.Log function.
|
||||
// Therefore it can be used in standard Go tests and Gingko test suites
|
||||
// to ensure that output is associated with the currently running test.
|
||||
//
|
||||
// In addition, the log data is captured in a buffer and can be used by the
|
||||
// test to verify that the code under test is logging as expected. To get
|
||||
// access to that data, cast the LogSink into the Underlier type and retrieve
|
||||
// it:
|
||||
//
|
||||
// logger := ktesting.NewLogger(...)
|
||||
// if testingLogger, ok := logger.GetSink().(ktesting.Underlier); ok {
|
||||
// t := testingLogger.GetUnderlying()
|
||||
// buffer := testingLogger.GetBuffer()
|
||||
// text := buffer.String()
|
||||
// log := buffer.Data()
|
||||
//
|
||||
// Serialization of the structured log parameters is done in the same way
|
||||
// as for klog.InfoS.
|
||||
//
|
||||
// Experimental
|
||||
//
|
||||
// Notice: This package is EXPERIMENTAL and may be changed or removed in a
|
||||
// later release.
|
||||
package ktesting
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
|
||||
"k8s.io/klog/v2/internal/serialize"
|
||||
"k8s.io/klog/v2/internal/verbosity"
|
||||
)
|
||||
|
||||
// TL is the relevant subset of testing.TB.
|
||||
//
|
||||
// Experimental
|
||||
//
|
||||
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
||||
// later release.
|
||||
type TL interface {
|
||||
Helper()
|
||||
Log(args ...interface{})
|
||||
}
|
||||
|
||||
// NopTL implements TL with empty stubs. It can be used when only capturing
|
||||
// output in memory is relevant.
|
||||
//
|
||||
// Experimental
|
||||
//
|
||||
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
||||
// later release.
|
||||
type NopTL struct{}
|
||||
|
||||
func (n NopTL) Helper() {}
|
||||
func (n NopTL) Log(args ...interface{}) {}
|
||||
|
||||
var _TL = NopTL{}
|
||||
|
||||
// NewLogger constructs a new logger for the given test interface.
|
||||
//
|
||||
// Experimental
|
||||
//
|
||||
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
||||
// later release.
|
||||
func NewLogger(t TL, c *Config) logr.Logger {
|
||||
return logr.New(&tlogger{
|
||||
t: t,
|
||||
prefix: "",
|
||||
values: nil,
|
||||
config: c,
|
||||
buffer: new(buffer),
|
||||
})
|
||||
}
|
||||
|
||||
// Buffer stores log entries as formatted text and structured data.
|
||||
// It is safe to use this concurrently.
|
||||
//
|
||||
// Experimental
|
||||
//
|
||||
// Notice: This interface is EXPERIMENTAL and may be changed or removed in a
|
||||
// later release.
|
||||
type Buffer interface {
|
||||
// String returns the log entries in a format that is similar to the
|
||||
// klog text output.
|
||||
String() string
|
||||
|
||||
// Data returns the log entries as structs.
|
||||
Data() Log
|
||||
}
|
||||
|
||||
// Log contains log entries in the order in which they were generated.
|
||||
//
|
||||
// Experimental
|
||||
//
|
||||
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
||||
// later release.
|
||||
type Log []LogEntry
|
||||
|
||||
// DeepCopy returns a copy of the log. The error instance and key/value
|
||||
// pairs remain shared.
|
||||
//
|
||||
// Experimental
|
||||
//
|
||||
// Notice: This function is EXPERIMENTAL and may be changed or removed in a
|
||||
// later release.
|
||||
func (l Log) DeepCopy() Log {
|
||||
log := make(Log, 0, len(l))
|
||||
log = append(log, l...)
|
||||
return log
|
||||
}
|
||||
|
||||
// LogEntry represents all information captured for a log entry.
|
||||
//
|
||||
// Experimental
|
||||
//
|
||||
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
||||
// later release.
|
||||
type LogEntry struct {
|
||||
// Timestamp stores the time when the log entry was created.
|
||||
Timestamp time.Time
|
||||
|
||||
// Type is either LogInfo or LogError.
|
||||
Type LogType
|
||||
|
||||
// Prefix contains the WithName strings concatenated with a slash.
|
||||
Prefix string
|
||||
|
||||
// Message is the fixed log message string.
|
||||
Message string
|
||||
|
||||
// Verbosity is always 0 for LogError.
|
||||
Verbosity int
|
||||
|
||||
// Err is always nil for LogInfo. It may or may not be
|
||||
// nil for LogError.
|
||||
Err error
|
||||
|
||||
// WithKVList are the concatenated key/value pairs from WithValues
|
||||
// calls. It's guaranteed to have an even number of entries because
|
||||
// the logger ensures that when WithValues is called.
|
||||
WithKVList []interface{}
|
||||
|
||||
// ParameterKVList are the key/value pairs passed into the call,
|
||||
// without any validation.
|
||||
ParameterKVList []interface{}
|
||||
}
|
||||
|
||||
// LogType determines whether a log entry was created with an Error or Info
|
||||
// call.
|
||||
//
|
||||
// Experimental
|
||||
//
|
||||
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
||||
// later release.
|
||||
type LogType string
|
||||
|
||||
const (
|
||||
// LogError is the special value used for Error log entries.
|
||||
//
|
||||
// Experimental
|
||||
//
|
||||
// Notice: This value is EXPERIMENTAL and may be changed or removed in
|
||||
// a later release.
|
||||
LogError = LogType("ERROR")
|
||||
|
||||
// LogInfo is the special value used for Info log entries.
|
||||
//
|
||||
// Experimental
|
||||
//
|
||||
// Notice: This value is EXPERIMENTAL and may be changed or removed in
|
||||
// a later release.
|
||||
LogInfo = LogType("INFO")
|
||||
)
|
||||
|
||||
// Underlier is implemented by the LogSink of this logger. It provides access
|
||||
// to additional APIs that are normally hidden behind the Logger API.
|
||||
//
|
||||
// Experimental
|
||||
//
|
||||
// Notice: This type is EXPERIMENTAL and may be changed or removed in a
|
||||
// later release.
|
||||
type Underlier interface {
|
||||
// GetUnderlying returns the testing instance that logging goes to.
|
||||
GetUnderlying() TL
|
||||
|
||||
// GetBuffer grants access to the in-memory copy of the log entries.
|
||||
GetBuffer() Buffer
|
||||
}
|
||||
|
||||
type buffer struct {
|
||||
mutex sync.Mutex
|
||||
text strings.Builder
|
||||
log Log
|
||||
}
|
||||
|
||||
func (b *buffer) String() string {
|
||||
b.mutex.Lock()
|
||||
defer b.mutex.Unlock()
|
||||
return b.text.String()
|
||||
}
|
||||
|
||||
func (b *buffer) Data() Log {
|
||||
b.mutex.Lock()
|
||||
defer b.mutex.Unlock()
|
||||
return b.log.DeepCopy()
|
||||
}
|
||||
|
||||
type tlogger struct {
|
||||
t TL
|
||||
prefix string
|
||||
values []interface{}
|
||||
config *Config
|
||||
buffer *buffer
|
||||
}
|
||||
|
||||
func (l *tlogger) Init(info logr.RuntimeInfo) {
|
||||
}
|
||||
|
||||
func (l *tlogger) GetCallStackHelper() func() {
|
||||
return l.t.Helper
|
||||
}
|
||||
|
||||
func (l *tlogger) Info(level int, msg string, kvList ...interface{}) {
|
||||
l.t.Helper()
|
||||
buffer := &bytes.Buffer{}
|
||||
merged := serialize.MergeKVs(l.values, kvList)
|
||||
serialize.KVListFormat(buffer, merged...)
|
||||
l.log(LogInfo, msg, level, buffer, nil, kvList)
|
||||
}
|
||||
|
||||
func (l *tlogger) Enabled(level int) bool {
|
||||
return l.config.vstate.Enabled(verbosity.Level(level), 1)
|
||||
}
|
||||
|
||||
func (l *tlogger) Error(err error, msg string, kvList ...interface{}) {
|
||||
l.t.Helper()
|
||||
buffer := &bytes.Buffer{}
|
||||
if err != nil {
|
||||
serialize.KVListFormat(buffer, "err", err)
|
||||
}
|
||||
merged := serialize.MergeKVs(l.values, kvList)
|
||||
serialize.KVListFormat(buffer, merged...)
|
||||
l.log(LogError, msg, 0, buffer, err, kvList)
|
||||
}
|
||||
|
||||
func (l *tlogger) log(what LogType, msg string, level int, buffer *bytes.Buffer, err error, kvList []interface{}) {
|
||||
l.t.Helper()
|
||||
args := []interface{}{what}
|
||||
if l.prefix != "" {
|
||||
args = append(args, l.prefix+":")
|
||||
}
|
||||
args = append(args, msg)
|
||||
if buffer.Len() > 0 {
|
||||
// Skip leading space inserted by serialize.KVListFormat.
|
||||
args = append(args, string(buffer.Bytes()[1:]))
|
||||
}
|
||||
l.t.Log(args...)
|
||||
|
||||
l.buffer.mutex.Lock()
|
||||
defer l.buffer.mutex.Unlock()
|
||||
|
||||
// Store as text.
|
||||
l.buffer.text.WriteString(string(what))
|
||||
for i := 1; i < len(args); i++ {
|
||||
l.buffer.text.WriteByte(' ')
|
||||
l.buffer.text.WriteString(args[i].(string))
|
||||
}
|
||||
lastArg := args[len(args)-1].(string)
|
||||
if lastArg[len(lastArg)-1] != '\n' {
|
||||
l.buffer.text.WriteByte('\n')
|
||||
}
|
||||
|
||||
// Store as raw data.
|
||||
l.buffer.log = append(l.buffer.log,
|
||||
LogEntry{
|
||||
Timestamp: time.Now(),
|
||||
Type: what,
|
||||
Prefix: l.prefix,
|
||||
Message: msg,
|
||||
Verbosity: level,
|
||||
Err: err,
|
||||
WithKVList: l.values,
|
||||
ParameterKVList: kvList,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// WithName returns a new logr.Logger with the specified name appended. klogr
|
||||
// uses '/' characters to separate name elements. Callers should not pass '/'
|
||||
// in the provided name string, but this library does not actually enforce that.
|
||||
func (l *tlogger) WithName(name string) logr.LogSink {
|
||||
new := *l
|
||||
if len(l.prefix) > 0 {
|
||||
new.prefix = l.prefix + "/"
|
||||
}
|
||||
new.prefix += name
|
||||
return &new
|
||||
}
|
||||
|
||||
func (l *tlogger) WithValues(kvList ...interface{}) logr.LogSink {
|
||||
new := *l
|
||||
new.values = serialize.WithValues(l.values, kvList)
|
||||
return &new
|
||||
}
|
||||
|
||||
func (l *tlogger) GetUnderlying() TL {
|
||||
return l.t
|
||||
}
|
||||
|
||||
func (l *tlogger) GetBuffer() Buffer {
|
||||
return l.buffer
|
||||
}
|
||||
|
||||
var _ logr.LogSink = &tlogger{}
|
||||
var _ logr.CallStackHelperLogSink = &tlogger{}
|
||||
var _ Underlier = &tlogger{}
|
3
vendor/modules.txt
vendored
3
vendor/modules.txt
vendored
@ -2143,6 +2143,9 @@ k8s.io/klog/v2/internal/buffer
|
||||
k8s.io/klog/v2/internal/clock
|
||||
k8s.io/klog/v2/internal/serialize
|
||||
k8s.io/klog/v2/internal/severity
|
||||
k8s.io/klog/v2/internal/verbosity
|
||||
k8s.io/klog/v2/ktesting
|
||||
k8s.io/klog/v2/ktesting/init
|
||||
k8s.io/klog/v2/test
|
||||
# k8s.io/kube-aggregator v0.0.0 => ./staging/src/k8s.io/kube-aggregator
|
||||
## explicit; go 1.18
|
||||
|
Loading…
Reference in New Issue
Block a user