mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-10-31 18:28:13 +00:00 
			
		
		
		
	| @@ -20,3 +20,14 @@ structured k8s.io/kubernetes/cmd/kubelet/.* | |||||||
| structured k8s.io/kubernetes/pkg/kubelet/.* | structured k8s.io/kubernetes/pkg/kubelet/.* | ||||||
| structured k8s.io/kubernetes/pkg/proxy/.* | structured k8s.io/kubernetes/pkg/proxy/.* | ||||||
| structured k8s.io/kubernetes/pkg/scheduler/.* | structured k8s.io/kubernetes/pkg/scheduler/.* | ||||||
|  |  | ||||||
|  | # The following packages have been migrated to contextual logging. | ||||||
|  | # Packages matched here do not have to be listed above because | ||||||
|  | # "contextual" implies "structured". | ||||||
|  | # TODO next: contextual k8s.io/kubernetes/pkg/scheduler/.* | ||||||
|  |  | ||||||
|  | # As long as contextual logging is alpha or beta, all WithName, WithValues, | ||||||
|  | # NewContext calls have to go through klog. Once it is GA, we can lift | ||||||
|  | # this restriction. Whether we then do a global search/replace remains | ||||||
|  | # to be decided. | ||||||
|  | with-helpers .* | ||||||
|   | |||||||
| @@ -58,7 +58,7 @@ func NewLoggerCommand() *cobra.Command { | |||||||
| 			} | 			} | ||||||
|  |  | ||||||
| 			// Initialize contextual logging. | 			// Initialize contextual logging. | ||||||
| 			logger := klog.Background().WithName("example").WithValues("foo", "bar") | 			logger := klog.LoggerWithValues(klog.LoggerWithName(klog.Background(), "example"), "foo", "bar") | ||||||
| 			ctx := klog.NewContext(context.Background(), logger) | 			ctx := klog.NewContext(context.Background(), logger) | ||||||
|  |  | ||||||
| 			runLogger(ctx) | 			runLogger(ctx) | ||||||
|   | |||||||
| @@ -78,6 +78,7 @@ func TestZapLoggerInfo(t *testing.T) { | |||||||
| 		writer := zapcore.AddSync(&buffer) | 		writer := zapcore.AddSync(&buffer) | ||||||
| 		sampleInfoLogger, _ := NewJSONLogger(0, writer, nil, nil) | 		sampleInfoLogger, _ := NewJSONLogger(0, writer, nil, nil) | ||||||
| 		for _, name := range data.names { | 		for _, name := range data.names { | ||||||
|  | 			// nolint:logcheck // This intentionally ignore the feature gate and always tests with a name. | ||||||
| 			sampleInfoLogger = sampleInfoLogger.WithName(name) | 			sampleInfoLogger = sampleInfoLogger.WithName(name) | ||||||
| 		} | 		} | ||||||
| 		// nolint:logcheck // The linter cannot and doesn't need to check the key/value pairs. | 		// nolint:logcheck // The linter cannot and doesn't need to check the key/value pairs. | ||||||
|   | |||||||
| @@ -28,6 +28,7 @@ import ( | |||||||
| 	"github.com/stretchr/testify/assert" | 	"github.com/stretchr/testify/assert" | ||||||
| 	"go.uber.org/zap/zapcore" | 	"go.uber.org/zap/zapcore" | ||||||
|  |  | ||||||
|  | 	"k8s.io/component-base/config" | ||||||
| 	"k8s.io/klog/v2" | 	"k8s.io/klog/v2" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -239,7 +240,9 @@ func TestKlogIntegration(t *testing.T) { | |||||||
| 		t.Run(tc.name, func(t *testing.T) { | 		t.Run(tc.name, func(t *testing.T) { | ||||||
| 			var buffer bytes.Buffer | 			var buffer bytes.Buffer | ||||||
| 			writer := zapcore.AddSync(&buffer) | 			writer := zapcore.AddSync(&buffer) | ||||||
| 			logger, _ := NewJSONLogger(100, writer, nil, nil) | 			// This level is high enough to enable all log messages from this test. | ||||||
|  | 			verbosityLevel := config.VerbosityLevel(100) | ||||||
|  | 			logger, _ := NewJSONLogger(verbosityLevel, writer, nil, nil) | ||||||
| 			klog.SetLogger(logger) | 			klog.SetLogger(logger) | ||||||
| 			defer klog.ClearLogger() | 			defer klog.ClearLogger() | ||||||
|  |  | ||||||
|   | |||||||
| @@ -128,7 +128,9 @@ func testContextualLogging(t *testing.T, enabled bool) { | |||||||
| 	defer klog.EnableContextualLogging(true) | 	defer klog.EnableContextualLogging(true) | ||||||
|  |  | ||||||
| 	ctx := context.Background() | 	ctx := context.Background() | ||||||
|  | 	// nolint:logcheck // This intentionally adds a name independently of the feature gate. | ||||||
| 	logger := klog.NewKlogr().WithName("contextual") | 	logger := klog.NewKlogr().WithName("contextual") | ||||||
|  | 	// nolint:logcheck // This intentionally creates a new context independently of the feature gate. | ||||||
| 	ctx = logr.NewContext(ctx, logger) | 	ctx = logr.NewContext(ctx, logger) | ||||||
| 	if enabled { | 	if enabled { | ||||||
| 		assert.Equal(t, logger, klog.FromContext(ctx), "FromContext") | 		assert.Equal(t, logger, klog.FromContext(ctx), "FromContext") | ||||||
|   | |||||||
| @@ -0,0 +1,245 @@ | |||||||
|  | /* | ||||||
|  | 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 contextuallogging | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"fmt" | ||||||
|  | 	"testing" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/go-logr/logr" | ||||||
|  |  | ||||||
|  | 	"k8s.io/klog/v2" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	klog.InitFlags(nil) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // BenchmarkRecursion measures the overhead of adding calling a function | ||||||
|  | // recursively with just the depth parameter. | ||||||
|  | func BenchmarkRecursion(b *testing.B) { | ||||||
|  | 	for depth := 10; depth <= 100000; depth *= 10 { | ||||||
|  | 		b.Run(fmt.Sprintf("%d", depth), func(b *testing.B) { | ||||||
|  | 			for i := 0; i < b.N; i++ { | ||||||
|  | 				recurse(depth) | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | //go:noinline | ||||||
|  | func recurse(depth int) { | ||||||
|  | 	if depth == 0 { | ||||||
|  | 		logr.Discard().Info("hello world") | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	recurse(depth - 1) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // BenchmarkRecursionWithLogger measures the overhead of adding a logr.Logger | ||||||
|  | // parameter. | ||||||
|  | func BenchmarkRecursionWithLogger(b *testing.B) { | ||||||
|  | 	logger := logr.Discard() | ||||||
|  |  | ||||||
|  | 	for depth := 10; depth <= 100000; depth *= 10 { | ||||||
|  | 		b.Run(fmt.Sprintf("%d", depth), func(b *testing.B) { | ||||||
|  | 			for i := 0; i < b.N; i++ { | ||||||
|  | 				recurseWithLogger(logger, depth) | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | //go:noinline | ||||||
|  | func recurseWithLogger(logger logr.Logger, depth int) { | ||||||
|  | 	if depth == 0 { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	recurseWithLogger(logger, depth-1) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // BenchmarkRecursionWithContext measures the overhead of adding a context | ||||||
|  | // parameter. | ||||||
|  | func BenchmarkRecursionWithContext(b *testing.B) { | ||||||
|  | 	logger := logr.Discard() | ||||||
|  | 	// nolint:logcheck // Intentionally using NewContext unconditionally here. | ||||||
|  | 	ctx := logr.NewContext(context.Background(), logger) | ||||||
|  |  | ||||||
|  | 	for depth := 10; depth <= 100000; depth *= 10 { | ||||||
|  | 		b.Run(fmt.Sprintf("%d", depth), func(b *testing.B) { | ||||||
|  | 			for i := 0; i < b.N; i++ { | ||||||
|  | 				recurseWithContext(ctx, depth) | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | //go:noinline | ||||||
|  | func recurseWithContext(ctx context.Context, depth int) { | ||||||
|  | 	if depth == 0 { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	recurseWithContext(ctx, depth-1) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // BenchmarkRecursionWithLogger measures the overhead of adding a logr.Logger | ||||||
|  | // parameter and using it once. | ||||||
|  | func BenchmarkRecursionWithLoggerAndLog(b *testing.B) { | ||||||
|  | 	logger := logr.Discard() | ||||||
|  |  | ||||||
|  | 	for depth := 10; depth <= 100000; depth *= 10 { | ||||||
|  | 		b.Run(fmt.Sprintf("%d", depth), func(b *testing.B) { | ||||||
|  | 			for i := 0; i < b.N; i++ { | ||||||
|  | 				recurseWithLoggerAndLog(logger, depth) | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | //go:noinline | ||||||
|  | func recurseWithLoggerAndLog(logger logr.Logger, depth int) { | ||||||
|  | 	if depth == 0 { | ||||||
|  | 		logger.Info("hello world") | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	recurseWithLoggerAndLog(logger, depth-1) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // BenchmarkRecursionWithContext measures the overhead of adding a context | ||||||
|  | // parameter and using it once to retrieve and call a logger. | ||||||
|  | func BenchmarkRecursionWithContextAndLog(b *testing.B) { | ||||||
|  | 	logger := logr.Discard() | ||||||
|  | 	// nolint:logcheck // Intentionally using NewContext unconditionally here. | ||||||
|  | 	ctx := logr.NewContext(context.Background(), logger) | ||||||
|  |  | ||||||
|  | 	for depth := 10; depth <= 100000; depth *= 10 { | ||||||
|  | 		b.Run(fmt.Sprintf("%d", depth), func(b *testing.B) { | ||||||
|  | 			for i := 0; i < b.N; i++ { | ||||||
|  | 				recurseWithContextAndLog(ctx, depth) | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | //go:noinline | ||||||
|  | func recurseWithContextAndLog(ctx context.Context, depth int) { | ||||||
|  | 	if depth == 0 { | ||||||
|  | 		logger := logr.FromContextOrDiscard(ctx) | ||||||
|  | 		logger.Info("hello world") | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	recurseWithContextAndLog(ctx, depth-1) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // BenchmarkNestedContextWithTimeouts benchmarks how quickly a function can be | ||||||
|  | // called that creates a new context at each call with context.WithTimeout. | ||||||
|  | func BenchmarkNestedContextWithTimeouts(b *testing.B) { | ||||||
|  | 	ctx := context.Background() | ||||||
|  |  | ||||||
|  | 	for depth := 1; depth <= 10000; depth *= 10 { | ||||||
|  | 		b.Run(fmt.Sprintf("%d", depth), func(b *testing.B) { | ||||||
|  | 			for i := 0; i < b.N; i++ { | ||||||
|  | 				nestedContextWithTimeout(ctx, depth) | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | //go:noinline | ||||||
|  | func nestedContextWithTimeout(ctx context.Context, depth int) { | ||||||
|  | 	if depth == 0 { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	ctx, cancel := context.WithTimeout(ctx, time.Hour) | ||||||
|  | 	defer cancel() | ||||||
|  | 	nestedContextWithTimeout(ctx, depth-1) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // BenchmarkNestedContextWithTimeouts benchmarks how quickly a function can be | ||||||
|  | // called that creates a new context at each call with context.WithTimeout | ||||||
|  | // and then looks up a logger. | ||||||
|  | func BenchmarkNestedContextWithTimeoutsAndLookup(b *testing.B) { | ||||||
|  | 	// nolint:logcheck // Intentionally using NewContext unconditionally here. | ||||||
|  | 	ctx := logr.NewContext(context.Background(), logr.Discard()) | ||||||
|  |  | ||||||
|  | 	for depth := 1; depth <= 10000; depth *= 10 { | ||||||
|  | 		b.Run(fmt.Sprintf("%d", depth), func(b *testing.B) { | ||||||
|  | 			for i := 0; i < b.N; i++ { | ||||||
|  | 				nestedContextWithTimeoutAndLookup(ctx, depth) | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | //go:noinline | ||||||
|  | func nestedContextWithTimeoutAndLookup(ctx context.Context, depth int) { | ||||||
|  | 	if depth == 0 { | ||||||
|  | 		logr.FromContextOrDiscard(ctx) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	ctx, cancel := context.WithTimeout(ctx, time.Hour) | ||||||
|  | 	defer cancel() | ||||||
|  | 	nestedContextWithTimeoutAndLookup(ctx, depth-1) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var logger logr.Logger | ||||||
|  |  | ||||||
|  | // BenchmarkNestedContextWithTimeouts benchmarks how quickly FromContextOrDiscard | ||||||
|  | // can look up a logger in nested contexts where WithTimeouts is used to | ||||||
|  | // created those nested contexts. | ||||||
|  | func BenchmarkLookupWithTimeouts(b *testing.B) { | ||||||
|  | 	for depth := 1; depth <= 10000; depth *= 10 { | ||||||
|  | 		b.Run(fmt.Sprintf("%d", depth), func(b *testing.B) { | ||||||
|  | 			// nolint:logcheck // Intentionally using NewContext unconditionally here. | ||||||
|  | 			ctx := logr.NewContext(context.Background(), logr.Discard()) | ||||||
|  | 			for i := 0; i < depth; i++ { | ||||||
|  | 				ctx2, cancel := context.WithTimeout(ctx, time.Hour) | ||||||
|  | 				defer cancel() | ||||||
|  | 				ctx = ctx2 | ||||||
|  | 			} | ||||||
|  | 			b.ResetTimer() | ||||||
|  | 			for i := 0; i < b.N; i++ { | ||||||
|  | 				logger = logr.FromContextOrDiscard(ctx) | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type keyT struct{} | ||||||
|  |  | ||||||
|  | var key keyT | ||||||
|  |  | ||||||
|  | // BenchmarkNestedContextWithTimeouts benchmarks how quickly FromContextOrDiscard | ||||||
|  | // can look up a logger in nested contexts where WithValue is used to | ||||||
|  | // created those nested contexts. | ||||||
|  | func BenchmarkLookupWithValues(b *testing.B) { | ||||||
|  | 	for depth := 1; depth <= 10000; depth *= 10 { | ||||||
|  | 		b.Run(fmt.Sprintf("%d", depth), func(b *testing.B) { | ||||||
|  | 			// nolint:logcheck // Intentionally using NewContext unconditionally here. | ||||||
|  | 			ctx := logr.NewContext(context.Background(), logr.Discard()) | ||||||
|  | 			for i := 0; i < depth; i++ { | ||||||
|  | 				ctx = context.WithValue(ctx, key, depth) | ||||||
|  | 			} | ||||||
|  | 			b.ResetTimer() | ||||||
|  | 			for i := 0; i < b.N; i++ { | ||||||
|  | 				logger = logr.FromContextOrDiscard(ctx) | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										78
									
								
								test/integration/logs/functional/json/output_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								test/integration/logs/functional/json/output_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,78 @@ | |||||||
|  | /* | ||||||
|  | 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 json | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"io" | ||||||
|  | 	"os" | ||||||
|  | 	"strings" | ||||||
|  | 	"testing" | ||||||
|  |  | ||||||
|  | 	"github.com/go-logr/logr" | ||||||
|  | 	"go.uber.org/zap/zapcore" | ||||||
|  |  | ||||||
|  | 	"k8s.io/component-base/config" | ||||||
|  | 	logsjson "k8s.io/component-base/logs/json" | ||||||
|  | 	"k8s.io/klog/v2/test" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	test.InitKlog() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // TestJsonOutput tests the JSON logger, directly and as backend for klog. | ||||||
|  | func TestJSONOutput(t *testing.T) { | ||||||
|  | 	newLogger := func(out io.Writer, v int, vmodule string) logr.Logger { | ||||||
|  | 		logger, _ := logsjson.NewJSONLogger(config.VerbosityLevel(v), logsjson.AddNopSync(out), nil, | ||||||
|  | 			&zapcore.EncoderConfig{ | ||||||
|  | 				MessageKey:     "msg", | ||||||
|  | 				CallerKey:      "caller", | ||||||
|  | 				NameKey:        "logger", | ||||||
|  | 				EncodeDuration: zapcore.StringDurationEncoder, | ||||||
|  | 				EncodeCaller:   zapcore.ShortCallerEncoder, | ||||||
|  | 			}) | ||||||
|  | 		return logger | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// If Go modules are turned off (for example, as in "make test-integration"), | ||||||
|  | 	// references to klog like k8s.io/klog/v2.ObjectRef.MarshalLog become | ||||||
|  | 	// k8s.io/kubernetes/vendor/k8s.io/klog/v2.ObjectRef.MarshalLog. | ||||||
|  | 	injectVendor := func(mapping map[string]string) map[string]string { | ||||||
|  | 		if os.Getenv("GO111MODULE") != "off" { | ||||||
|  | 			return mapping | ||||||
|  | 		} | ||||||
|  | 		for key, value := range mapping { | ||||||
|  | 			mapping[key] = strings.ReplaceAll(value, "k8s.io/klog/v2", "k8s.io/kubernetes/vendor/k8s.io/klog/v2") | ||||||
|  | 		} | ||||||
|  | 		return mapping | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	t.Run("direct", func(t *testing.T) { | ||||||
|  | 		test.Output(t, test.OutputConfig{ | ||||||
|  | 			NewLogger:             newLogger, | ||||||
|  | 			ExpectedOutputMapping: injectVendor(test.ZaprOutputMappingDirect()), | ||||||
|  | 		}) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	t.Run("klog-backend", func(t *testing.T) { | ||||||
|  | 		test.Output(t, test.OutputConfig{ | ||||||
|  | 			NewLogger:             newLogger, | ||||||
|  | 			AsBackend:             true, | ||||||
|  | 			ExpectedOutputMapping: injectVendor(test.ZaprOutputMappingIndirect()), | ||||||
|  | 		}) | ||||||
|  | 	}) | ||||||
|  | } | ||||||
							
								
								
									
										775
									
								
								vendor/k8s.io/klog/v2/test/output.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										775
									
								
								vendor/k8s.io/klog/v2/test/output.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,775 @@ | |||||||
|  | /* | ||||||
|  | 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 test contains a reusable unit test for logging output and behavior. | ||||||
|  | // | ||||||
|  | // Experimental | ||||||
|  | // | ||||||
|  | // Notice: This package is EXPERIMENTAL and may be changed or removed in a | ||||||
|  | // later release. | ||||||
|  | package test | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"bytes" | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"errors" | ||||||
|  | 	"flag" | ||||||
|  | 	"fmt" | ||||||
|  | 	"io" | ||||||
|  | 	"regexp" | ||||||
|  | 	"runtime" | ||||||
|  | 	"strconv" | ||||||
|  | 	"strings" | ||||||
|  | 	"testing" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/go-logr/logr" | ||||||
|  |  | ||||||
|  | 	"k8s.io/klog/v2" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // InitKlog must be called once in an init function of a test package to | ||||||
|  | // configure klog for testing with Output. | ||||||
|  | // | ||||||
|  | // Experimental | ||||||
|  | // | ||||||
|  | // Notice: This function is EXPERIMENTAL and may be changed or removed in a | ||||||
|  | // later release. | ||||||
|  | func InitKlog() { | ||||||
|  | 	// klog gets configured so that it writes to a single output file that | ||||||
|  | 	// will be set during tests with SetOutput. | ||||||
|  | 	klog.InitFlags(nil) | ||||||
|  | 	flag.Set("v", "10") | ||||||
|  | 	flag.Set("log_file", "/dev/null") | ||||||
|  | 	flag.Set("logtostderr", "false") | ||||||
|  | 	flag.Set("alsologtostderr", "false") | ||||||
|  | 	flag.Set("stderrthreshold", "10") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // OutputConfig contains optional settings for Output. | ||||||
|  | // | ||||||
|  | // Experimental | ||||||
|  | // | ||||||
|  | // Notice: This type is EXPERIMENTAL and may be changed or removed in a | ||||||
|  | // later release. | ||||||
|  | type OutputConfig struct { | ||||||
|  | 	// NewLogger is called to create a new logger. If nil, output via klog | ||||||
|  | 	// is tested. Support for -vmodule is optional.  ClearLogger is called | ||||||
|  | 	// after each test, therefore it is okay to user SetLogger without | ||||||
|  | 	// undoing that in the callback. | ||||||
|  | 	NewLogger func(out io.Writer, v int, vmodule string) logr.Logger | ||||||
|  |  | ||||||
|  | 	// AsBackend enables testing through klog and the logger set there with | ||||||
|  | 	// SetLogger. | ||||||
|  | 	AsBackend bool | ||||||
|  |  | ||||||
|  | 	// ExpectedOutputMapping replaces the builtin expected output for test | ||||||
|  | 	// cases with something else. If nil or a certain case is not present, | ||||||
|  | 	// the original text is used. | ||||||
|  | 	// | ||||||
|  | 	// The expected output uses <LINE> as a placeholder for the line of the | ||||||
|  | 	// log call. The source code is always the output.go file itself. When | ||||||
|  | 	// testing a logger directly, <WITH-VALUES-LINE> is used for the first | ||||||
|  | 	// WithValues call, <WITH-VALUES-LINE-2> for a second and | ||||||
|  | 	// <WITH-VALUES-LINE-3> for a third. | ||||||
|  | 	ExpectedOutputMapping map[string]string | ||||||
|  |  | ||||||
|  | 	// SupportsVModule indicates that the logger supports the vmodule | ||||||
|  | 	// parameter. Ignored when logging through klog. | ||||||
|  | 	SupportsVModule bool | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Output covers various special cases of emitting log output. | ||||||
|  | // It can be used for arbitrary logr.Logger implementations. | ||||||
|  | // | ||||||
|  | // The expected output is what klog would print. When testing loggers | ||||||
|  | // that emit different output, a mapping from klog output to the | ||||||
|  | // corresponding logger output must be provided, otherwise the | ||||||
|  | // test will compare against the expected klog output. | ||||||
|  | // | ||||||
|  | // Loggers will be tested with direct calls to Info or | ||||||
|  | // as backend for klog. | ||||||
|  | // | ||||||
|  | // Experimental | ||||||
|  | // | ||||||
|  | // Notice: This function is EXPERIMENTAL and may be changed or removed in a | ||||||
|  | // later release. The test cases and thus the expected output also may | ||||||
|  | // change. | ||||||
|  | func Output(t *testing.T, config OutputConfig) { | ||||||
|  | 	tests := map[string]struct { | ||||||
|  | 		withHelper bool // use wrappers that get skipped during stack unwinding | ||||||
|  | 		withNames  []string | ||||||
|  | 		// For a first WithValues call: logger1 := logger.WithValues() | ||||||
|  | 		withValues []interface{} | ||||||
|  | 		// For another WithValues call: logger2 := logger1.WithValues() | ||||||
|  | 		moreValues []interface{} | ||||||
|  | 		// For another WithValues call on the same logger as before: logger3 := logger1.WithValues() | ||||||
|  | 		evenMoreValues []interface{} | ||||||
|  | 		v              int | ||||||
|  | 		vmodule        string | ||||||
|  | 		text           string | ||||||
|  | 		values         []interface{} | ||||||
|  | 		err            error | ||||||
|  | 		expectedOutput string | ||||||
|  | 	}{ | ||||||
|  | 		"log with values": { | ||||||
|  | 			text:   "test", | ||||||
|  | 			values: []interface{}{"akey", "avalue"}, | ||||||
|  | 			expectedOutput: `I output.go:<LINE>] "test" akey="avalue" | ||||||
|  | `, | ||||||
|  | 		}, | ||||||
|  | 		"call depth": { | ||||||
|  | 			text:       "helper", | ||||||
|  | 			withHelper: true, | ||||||
|  | 			values:     []interface{}{"akey", "avalue"}, | ||||||
|  | 			expectedOutput: `I output.go:<LINE>] "helper" akey="avalue" | ||||||
|  | `, | ||||||
|  | 		}, | ||||||
|  | 		"verbosity enabled": { | ||||||
|  | 			text: "you see me", | ||||||
|  | 			v:    9, | ||||||
|  | 			expectedOutput: `I output.go:<LINE>] "you see me" | ||||||
|  | `, | ||||||
|  | 		}, | ||||||
|  | 		"verbosity disabled": { | ||||||
|  | 			text: "you don't see me", | ||||||
|  | 			v:    11, | ||||||
|  | 		}, | ||||||
|  | 		"vmodule": { | ||||||
|  | 			text:    "v=11: you see me because of -vmodule output=11", | ||||||
|  | 			v:       11, | ||||||
|  | 			vmodule: "output=11", | ||||||
|  | 		}, | ||||||
|  | 		"other vmodule": { | ||||||
|  | 			text:    "v=11: you still don't see me because of -vmodule output_helper=11", | ||||||
|  | 			v:       11, | ||||||
|  | 			vmodule: "output_helper=11", | ||||||
|  | 		}, | ||||||
|  | 		"log with name and values": { | ||||||
|  | 			withNames: []string{"me"}, | ||||||
|  | 			text:      "test", | ||||||
|  | 			values:    []interface{}{"akey", "avalue"}, | ||||||
|  | 			expectedOutput: `I output.go:<LINE>] "me: test" akey="avalue" | ||||||
|  | `, | ||||||
|  | 		}, | ||||||
|  | 		"log with multiple names and values": { | ||||||
|  | 			withNames: []string{"hello", "world"}, | ||||||
|  | 			text:      "test", | ||||||
|  | 			values:    []interface{}{"akey", "avalue"}, | ||||||
|  | 			expectedOutput: `I output.go:<LINE>] "hello/world: test" akey="avalue" | ||||||
|  | `, | ||||||
|  | 		}, | ||||||
|  | 		"override single value": { | ||||||
|  | 			withValues: []interface{}{"akey", "avalue"}, | ||||||
|  | 			text:       "test", | ||||||
|  | 			values:     []interface{}{"akey", "avalue2"}, | ||||||
|  | 			expectedOutput: `I output.go:<LINE>] "test" akey="avalue2" | ||||||
|  | `, | ||||||
|  | 		}, | ||||||
|  | 		"override WithValues": { | ||||||
|  | 			withValues: []interface{}{"duration", time.Hour, "X", "y"}, | ||||||
|  | 			text:       "test", | ||||||
|  | 			values:     []interface{}{"duration", time.Minute, "A", "b"}, | ||||||
|  | 			expectedOutput: `I output.go:<LINE>] "test" X="y" duration="1m0s" A="b" | ||||||
|  | `, | ||||||
|  | 		}, | ||||||
|  | 		"odd WithValues": { | ||||||
|  | 			withValues: []interface{}{"keyWithoutValue"}, | ||||||
|  | 			moreValues: []interface{}{"anotherKeyWithoutValue"}, | ||||||
|  | 			text:       "odd WithValues", | ||||||
|  | 			expectedOutput: `I output.go:<LINE>] "odd WithValues" keyWithoutValue="(MISSING)" | ||||||
|  | I output.go:<LINE>] "odd WithValues" keyWithoutValue="(MISSING)" anotherKeyWithoutValue="(MISSING)" | ||||||
|  | I output.go:<LINE>] "odd WithValues" keyWithoutValue="(MISSING)" | ||||||
|  | `, | ||||||
|  | 		}, | ||||||
|  | 		"multiple WithValues": { | ||||||
|  | 			withValues:     []interface{}{"firstKey", 1}, | ||||||
|  | 			moreValues:     []interface{}{"secondKey", 2}, | ||||||
|  | 			evenMoreValues: []interface{}{"secondKey", 3}, | ||||||
|  | 			text:           "test", | ||||||
|  | 			expectedOutput: `I output.go:<LINE>] "test" firstKey=1 | ||||||
|  | I output.go:<LINE>] "test" firstKey=1 secondKey=2 | ||||||
|  | I output.go:<LINE>] "test" firstKey=1 | ||||||
|  | I output.go:<LINE>] "test" firstKey=1 secondKey=3 | ||||||
|  | `, | ||||||
|  | 		}, | ||||||
|  | 		"empty WithValues": { | ||||||
|  | 			withValues: []interface{}{}, | ||||||
|  | 			text:       "test", | ||||||
|  | 			expectedOutput: `I output.go:<LINE>] "test" | ||||||
|  | `, | ||||||
|  | 		}, | ||||||
|  | 		// TODO: unify behavior of loggers. | ||||||
|  | 		// klog doesn't deduplicate, klogr and textlogger do. We can ensure via static code analysis | ||||||
|  | 		// that this doesn't occur, so we shouldn't pay the runtime overhead for deduplication here | ||||||
|  | 		// and remove that from klogr and textlogger (https://github.com/kubernetes/klog/issues/286). | ||||||
|  | 		// 		"print duplicate keys in arguments": { | ||||||
|  | 		// 			text:   "test", | ||||||
|  | 		// 			values: []interface{}{"akey", "avalue", "akey", "avalue2"}, | ||||||
|  | 		// 			expectedOutput: `I output.go:<LINE>] "test" akey="avalue" akey="avalue2" | ||||||
|  | 		// `, | ||||||
|  | 		// 		}, | ||||||
|  | 		"preserve order of key/value pairs": { | ||||||
|  | 			withValues: []interface{}{"akey9", "avalue9", "akey8", "avalue8", "akey1", "avalue1"}, | ||||||
|  | 			text:       "test", | ||||||
|  | 			values:     []interface{}{"akey5", "avalue5", "akey4", "avalue4"}, | ||||||
|  | 			expectedOutput: `I output.go:<LINE>] "test" akey9="avalue9" akey8="avalue8" akey1="avalue1" akey5="avalue5" akey4="avalue4" | ||||||
|  | `, | ||||||
|  | 		}, | ||||||
|  | 		"handle odd-numbers of KVs": { | ||||||
|  | 			text:   "odd arguments", | ||||||
|  | 			values: []interface{}{"akey", "avalue", "akey2"}, | ||||||
|  | 			expectedOutput: `I output.go:<LINE>] "odd arguments" akey="avalue" akey2="(MISSING)" | ||||||
|  | `, | ||||||
|  | 		}, | ||||||
|  | 		"html characters": { | ||||||
|  | 			text:   "test", | ||||||
|  | 			values: []interface{}{"akey", "<&>"}, | ||||||
|  | 			expectedOutput: `I output.go:<LINE>] "test" akey="<&>" | ||||||
|  | `, | ||||||
|  | 		}, | ||||||
|  | 		"quotation": { | ||||||
|  | 			text:   `"quoted"`, | ||||||
|  | 			values: []interface{}{"key", `"quoted value"`}, | ||||||
|  | 			expectedOutput: `I output.go:<LINE>] "\"quoted\"" key="\"quoted value\"" | ||||||
|  | `, | ||||||
|  | 		}, | ||||||
|  | 		"handle odd-numbers of KVs in both log values and Info args": { | ||||||
|  | 			withValues: []interface{}{"basekey1", "basevar1", "basekey2"}, | ||||||
|  | 			text:       "both odd", | ||||||
|  | 			values:     []interface{}{"akey", "avalue", "akey2"}, | ||||||
|  | 			expectedOutput: `I output.go:<LINE>] "both odd" basekey1="basevar1" basekey2="(MISSING)" akey="avalue" akey2="(MISSING)" | ||||||
|  | `, | ||||||
|  | 		}, | ||||||
|  | 		"KObj": { | ||||||
|  | 			text:   "test", | ||||||
|  | 			values: []interface{}{"pod", klog.KObj(&kmeta{Name: "pod-1", Namespace: "kube-system"})}, | ||||||
|  | 			expectedOutput: `I output.go:<LINE>] "test" pod="kube-system/pod-1" | ||||||
|  | `, | ||||||
|  | 		}, | ||||||
|  | 		"KObjs": { | ||||||
|  | 			text: "test", | ||||||
|  | 			values: []interface{}{"pods", | ||||||
|  | 				klog.KObjs([]interface{}{ | ||||||
|  | 					&kmeta{Name: "pod-1", Namespace: "kube-system"}, | ||||||
|  | 					&kmeta{Name: "pod-2", Namespace: "kube-system"}, | ||||||
|  | 				})}, | ||||||
|  | 			expectedOutput: `I output.go:<LINE>] "test" pods=[kube-system/pod-1 kube-system/pod-2] | ||||||
|  | `, | ||||||
|  | 		}, | ||||||
|  | 		"regular error types as value": { | ||||||
|  | 			text:   "test", | ||||||
|  | 			values: []interface{}{"err", errors.New("whoops")}, | ||||||
|  | 			expectedOutput: `I output.go:<LINE>] "test" err="whoops" | ||||||
|  | `, | ||||||
|  | 		}, | ||||||
|  | 		"ignore MarshalJSON": { | ||||||
|  | 			text:   "test", | ||||||
|  | 			values: []interface{}{"err", &customErrorJSON{"whoops"}}, | ||||||
|  | 			expectedOutput: `I output.go:<LINE>] "test" err="whoops" | ||||||
|  | `, | ||||||
|  | 		}, | ||||||
|  | 		"regular error types when using logr.Error": { | ||||||
|  | 			text: "test", | ||||||
|  | 			err:  errors.New("whoops"), | ||||||
|  | 			expectedOutput: `E output.go:<LINE>] "test" err="whoops" | ||||||
|  | `, | ||||||
|  | 		}, | ||||||
|  | 		"Error() for nil": { | ||||||
|  | 			text: "error nil", | ||||||
|  | 			err:  (*customErrorJSON)(nil), | ||||||
|  | 			expectedOutput: `E output.go:<LINE>] "error nil" err="<panic: runtime error: invalid memory address or nil pointer dereference>" | ||||||
|  | `, | ||||||
|  | 		}, | ||||||
|  | 		"String() for nil": { | ||||||
|  | 			text:   "stringer nil", | ||||||
|  | 			values: []interface{}{"stringer", (*stringer)(nil)}, | ||||||
|  | 			expectedOutput: `I output.go:<LINE>] "stringer nil" stringer="<panic: runtime error: invalid memory address or nil pointer dereference>" | ||||||
|  | `, | ||||||
|  | 		}, | ||||||
|  | 		"MarshalLog() for nil": { | ||||||
|  | 			text:   "marshaler nil", | ||||||
|  | 			values: []interface{}{"obj", (*klog.ObjectRef)(nil)}, | ||||||
|  | 			expectedOutput: `I output.go:<LINE>] "marshaler nil" obj="<panic: value method k8s.io/klog/v2.ObjectRef.String called using nil *ObjectRef pointer>" | ||||||
|  | `, | ||||||
|  | 		}, | ||||||
|  | 		"Error() that panics": { | ||||||
|  | 			text: "error panic", | ||||||
|  | 			err:  faultyError{}, | ||||||
|  | 			expectedOutput: `E output.go:<LINE>] "error panic" err="<panic: fake Error panic>" | ||||||
|  | `, | ||||||
|  | 		}, | ||||||
|  | 		"String() that panics": { | ||||||
|  | 			text:   "stringer panic", | ||||||
|  | 			values: []interface{}{"stringer", faultyStringer{}}, | ||||||
|  | 			expectedOutput: `I output.go:<LINE>] "stringer panic" stringer="<panic: fake String panic>" | ||||||
|  | `, | ||||||
|  | 		}, | ||||||
|  | 		"MarshalLog() that panics": { | ||||||
|  | 			text:   "marshaler panic", | ||||||
|  | 			values: []interface{}{"obj", faultyMarshaler{}}, | ||||||
|  | 			expectedOutput: `I output.go:<LINE>] "marshaler panic" obj={} | ||||||
|  | `, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | 	for n, test := range tests { | ||||||
|  | 		t.Run(n, func(t *testing.T) { | ||||||
|  | 			defer klog.ClearLogger() | ||||||
|  |  | ||||||
|  | 			printWithLogger := func(logger logr.Logger) { | ||||||
|  | 				for _, name := range test.withNames { | ||||||
|  | 					logger = logger.WithName(name) | ||||||
|  | 				} | ||||||
|  | 				// When we have multiple WithValues calls, we test | ||||||
|  | 				// first with the initial set of additional values, then | ||||||
|  | 				// the combination, then again the original logger. | ||||||
|  | 				// It must not have been modified. This produces | ||||||
|  | 				// three log entries. | ||||||
|  | 				logger = logger.WithValues(test.withValues...) // <WITH-VALUES> | ||||||
|  | 				loggers := []logr.Logger{logger} | ||||||
|  | 				if test.moreValues != nil { | ||||||
|  | 					loggers = append(loggers, logger.WithValues(test.moreValues...), logger) // <WITH-VALUES-2> | ||||||
|  | 				} | ||||||
|  | 				if test.evenMoreValues != nil { | ||||||
|  | 					loggers = append(loggers, logger.WithValues(test.evenMoreValues...)) // <WITH-VALUES-3> | ||||||
|  | 				} | ||||||
|  | 				for _, logger := range loggers { | ||||||
|  | 					if test.withHelper { | ||||||
|  | 						loggerHelper(logger, test.text, test.values) // <LINE> | ||||||
|  | 					} else if test.err != nil { | ||||||
|  | 						logger.Error(test.err, test.text, test.values...) // <LINE> | ||||||
|  | 					} else { | ||||||
|  | 						logger.V(test.v).Info(test.text, test.values...) // <LINE> | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			_, _, printWithLoggerLine, _ := runtime.Caller(0) | ||||||
|  |  | ||||||
|  | 			printWithKlog := func() { | ||||||
|  | 				kv := []interface{}{} | ||||||
|  | 				haveKeyInValues := func(key interface{}) bool { | ||||||
|  | 					for i := 0; i < len(test.values); i += 2 { | ||||||
|  | 						if key == test.values[i] { | ||||||
|  | 							return true | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  | 					return false | ||||||
|  | 				} | ||||||
|  | 				appendKV := func(withValues []interface{}) { | ||||||
|  | 					if len(withValues)%2 != 0 { | ||||||
|  | 						withValues = append(withValues, "(MISSING)") | ||||||
|  | 					} | ||||||
|  | 					for i := 0; i < len(withValues); i += 2 { | ||||||
|  | 						if !haveKeyInValues(withValues[i]) { | ||||||
|  | 							kv = append(kv, withValues[i], withValues[i+1]) | ||||||
|  | 						} | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 				// Here we need to emulate the handling of WithValues above. | ||||||
|  | 				appendKV(test.withValues) | ||||||
|  | 				kvs := [][]interface{}{copySlice(kv)} | ||||||
|  | 				if test.moreValues != nil { | ||||||
|  | 					appendKV(test.moreValues) | ||||||
|  | 					kvs = append(kvs, copySlice(kv), copySlice(kvs[0])) | ||||||
|  | 				} | ||||||
|  | 				if test.evenMoreValues != nil { | ||||||
|  | 					kv = copySlice(kvs[0]) | ||||||
|  | 					appendKV(test.evenMoreValues) | ||||||
|  | 					kvs = append(kvs, copySlice(kv)) | ||||||
|  | 				} | ||||||
|  | 				for _, kv := range kvs { | ||||||
|  | 					if len(test.values) > 0 { | ||||||
|  | 						kv = append(kv, test.values...) | ||||||
|  | 					} | ||||||
|  | 					text := test.text | ||||||
|  | 					if len(test.withNames) > 0 { | ||||||
|  | 						text = strings.Join(test.withNames, "/") + ": " + text | ||||||
|  | 					} | ||||||
|  | 					if test.withHelper { | ||||||
|  | 						klogHelper(text, kv) | ||||||
|  | 					} else if test.err != nil { | ||||||
|  | 						klog.ErrorS(test.err, text, kv...) | ||||||
|  | 					} else { | ||||||
|  | 						klog.V(klog.Level(test.v)).InfoS(text, kv...) | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			_, _, printWithKlogLine, _ := runtime.Caller(0) | ||||||
|  |  | ||||||
|  | 			testOutput := func(t *testing.T, expectedLine int, print func(buffer *bytes.Buffer)) { | ||||||
|  | 				var tmpWriteBuffer bytes.Buffer | ||||||
|  | 				klog.SetOutput(&tmpWriteBuffer) | ||||||
|  | 				print(&tmpWriteBuffer) | ||||||
|  | 				klog.Flush() | ||||||
|  |  | ||||||
|  | 				actual := tmpWriteBuffer.String() | ||||||
|  | 				// Strip varying header. | ||||||
|  | 				re := `(?m)^(.).... ..:..:......... ....... output.go` | ||||||
|  | 				actual = regexp.MustCompile(re).ReplaceAllString(actual, `${1} output.go`) | ||||||
|  |  | ||||||
|  | 				// Inject expected line. This matches the if checks above, which are | ||||||
|  | 				// the same for both printWithKlog and printWithLogger. | ||||||
|  | 				callLine := expectedLine | ||||||
|  | 				if test.withHelper { | ||||||
|  | 					callLine -= 8 | ||||||
|  | 				} else if test.err != nil { | ||||||
|  | 					callLine -= 6 | ||||||
|  | 				} else { | ||||||
|  | 					callLine -= 4 | ||||||
|  | 				} | ||||||
|  | 				expected := test.expectedOutput | ||||||
|  | 				if repl, ok := config.ExpectedOutputMapping[expected]; ok { | ||||||
|  | 					expected = repl | ||||||
|  | 				} | ||||||
|  | 				expectedWithPlaceholder := expected | ||||||
|  | 				expected = strings.ReplaceAll(expected, "<LINE>", fmt.Sprintf("%d", callLine)) | ||||||
|  | 				expected = strings.ReplaceAll(expected, "<WITH-VALUES>", fmt.Sprintf("%d", expectedLine-18)) | ||||||
|  | 				expected = strings.ReplaceAll(expected, "<WITH-VALUES-2>", fmt.Sprintf("%d", expectedLine-15)) | ||||||
|  | 				expected = strings.ReplaceAll(expected, "<WITH-VALUES-3>", fmt.Sprintf("%d", expectedLine-12)) | ||||||
|  | 				if actual != expected { | ||||||
|  | 					if expectedWithPlaceholder == test.expectedOutput { | ||||||
|  | 						t.Errorf("Output mismatch. Expected:\n%s\nActual:\n%s\n", expectedWithPlaceholder, actual) | ||||||
|  | 					} else { | ||||||
|  | 						t.Errorf("Output mismatch. klog:\n%s\nExpected:\n%s\nActual:\n%s\n", test.expectedOutput, expectedWithPlaceholder, actual) | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if config.NewLogger == nil { | ||||||
|  | 				// Test klog. | ||||||
|  | 				testOutput(t, printWithKlogLine, func(buffer *bytes.Buffer) { | ||||||
|  | 					printWithKlog() | ||||||
|  | 				}) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if config.AsBackend { | ||||||
|  | 				testOutput(t, printWithKlogLine, func(buffer *bytes.Buffer) { | ||||||
|  | 					klog.SetLogger(config.NewLogger(buffer, 10, "")) | ||||||
|  | 					printWithKlog() | ||||||
|  | 				}) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			if test.vmodule != "" && !config.SupportsVModule { | ||||||
|  | 				t.Skip("vmodule not supported") | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			testOutput(t, printWithLoggerLine, func(buffer *bytes.Buffer) { | ||||||
|  | 				printWithLogger(config.NewLogger(buffer, 10, test.vmodule)) | ||||||
|  | 			}) | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if config.NewLogger == nil || config.AsBackend { | ||||||
|  | 		// Test all klog output functions. | ||||||
|  | 		// | ||||||
|  | 		// Each test case must be defined with the same number of | ||||||
|  | 		// lines, then the source code location of the call itself | ||||||
|  | 		// can be computed below. | ||||||
|  | 		tests := []struct { | ||||||
|  | 			name    string | ||||||
|  | 			logFunc func() | ||||||
|  | 			output  string | ||||||
|  | 		}{ | ||||||
|  | 			{ | ||||||
|  | 				name:    "Info", | ||||||
|  | 				logFunc: func() { klog.Info("hello", "world") }, | ||||||
|  | 				output:  "I output.go:<LINE>] helloworld\n", // This looks odd, but simply is how klog works. | ||||||
|  | 			}, | ||||||
|  | 			{ | ||||||
|  | 				name:    "InfoDepth", | ||||||
|  | 				logFunc: func() { klog.InfoDepth(0, "hello", "world") }, | ||||||
|  | 				output:  "I output.go:<LINE>] helloworld\n", | ||||||
|  | 			}, | ||||||
|  | 			{ | ||||||
|  | 				name:    "Infoln", | ||||||
|  | 				logFunc: func() { klog.Infoln("hello", "world") }, | ||||||
|  | 				output:  "I output.go:<LINE>] hello world\n", | ||||||
|  | 			}, | ||||||
|  | 			{ | ||||||
|  | 				name:    "InfolnDepth", | ||||||
|  | 				logFunc: func() { klog.InfolnDepth(0, "hello", "world") }, | ||||||
|  | 				output:  "I output.go:<LINE>] hello world\n", | ||||||
|  | 			}, | ||||||
|  | 			{ | ||||||
|  | 				name:    "Infof", | ||||||
|  | 				logFunc: func() { klog.Infof("hello %s", "world") }, | ||||||
|  | 				output:  "I output.go:<LINE>] hello world\n", | ||||||
|  | 			}, | ||||||
|  | 			{ | ||||||
|  | 				name:    "InfofDepth", | ||||||
|  | 				logFunc: func() { klog.InfofDepth(0, "hello %s", "world") }, | ||||||
|  | 				output:  "I output.go:<LINE>] hello world\n", | ||||||
|  | 			}, | ||||||
|  | 			{ | ||||||
|  | 				name:    "InfoS", | ||||||
|  | 				logFunc: func() { klog.InfoS("hello", "what", "world") }, | ||||||
|  | 				output:  "I output.go:<LINE>] \"hello\" what=\"world\"\n", | ||||||
|  | 			}, | ||||||
|  | 			{ | ||||||
|  | 				name:    "InfoSDepth", | ||||||
|  | 				logFunc: func() { klog.InfoSDepth(0, "hello", "what", "world") }, | ||||||
|  | 				output:  "I output.go:<LINE>] \"hello\" what=\"world\"\n", | ||||||
|  | 			}, | ||||||
|  | 			{ | ||||||
|  | 				name:    "Warning", | ||||||
|  | 				logFunc: func() { klog.Warning("hello", "world") }, | ||||||
|  | 				output:  "W output.go:<LINE>] helloworld\n", | ||||||
|  | 			}, | ||||||
|  | 			{ | ||||||
|  | 				name:    "WarningDepth", | ||||||
|  | 				logFunc: func() { klog.WarningDepth(0, "hello", "world") }, | ||||||
|  | 				output:  "W output.go:<LINE>] helloworld\n", | ||||||
|  | 			}, | ||||||
|  | 			{ | ||||||
|  | 				name:    "Warningln", | ||||||
|  | 				logFunc: func() { klog.Warningln("hello", "world") }, | ||||||
|  | 				output:  "W output.go:<LINE>] hello world\n", | ||||||
|  | 			}, | ||||||
|  | 			{ | ||||||
|  | 				name:    "WarninglnDepth", | ||||||
|  | 				logFunc: func() { klog.WarninglnDepth(0, "hello", "world") }, | ||||||
|  | 				output:  "W output.go:<LINE>] hello world\n", | ||||||
|  | 			}, | ||||||
|  | 			{ | ||||||
|  | 				name:    "Warningf", | ||||||
|  | 				logFunc: func() { klog.Warningf("hello %s", "world") }, | ||||||
|  | 				output:  "W output.go:<LINE>] hello world\n", | ||||||
|  | 			}, | ||||||
|  | 			{ | ||||||
|  | 				name:    "WarningfDepth", | ||||||
|  | 				logFunc: func() { klog.WarningfDepth(0, "hello %s", "world") }, | ||||||
|  | 				output:  "W output.go:<LINE>] hello world\n", | ||||||
|  | 			}, | ||||||
|  | 			{ | ||||||
|  | 				name:    "Error", | ||||||
|  | 				logFunc: func() { klog.Error("hello", "world") }, | ||||||
|  | 				output:  "E output.go:<LINE>] helloworld\n", | ||||||
|  | 			}, | ||||||
|  | 			{ | ||||||
|  | 				name:    "ErrorDepth", | ||||||
|  | 				logFunc: func() { klog.ErrorDepth(0, "hello", "world") }, | ||||||
|  | 				output:  "E output.go:<LINE>] helloworld\n", | ||||||
|  | 			}, | ||||||
|  | 			{ | ||||||
|  | 				name:    "Errorln", | ||||||
|  | 				logFunc: func() { klog.Errorln("hello", "world") }, | ||||||
|  | 				output:  "E output.go:<LINE>] hello world\n", | ||||||
|  | 			}, | ||||||
|  | 			{ | ||||||
|  | 				name:    "ErrorlnDepth", | ||||||
|  | 				logFunc: func() { klog.ErrorlnDepth(0, "hello", "world") }, | ||||||
|  | 				output:  "E output.go:<LINE>] hello world\n", | ||||||
|  | 			}, | ||||||
|  | 			{ | ||||||
|  | 				name:    "Errorf", | ||||||
|  | 				logFunc: func() { klog.Errorf("hello %s", "world") }, | ||||||
|  | 				output:  "E output.go:<LINE>] hello world\n", | ||||||
|  | 			}, | ||||||
|  | 			{ | ||||||
|  | 				name:    "ErrorfDepth", | ||||||
|  | 				logFunc: func() { klog.ErrorfDepth(0, "hello %s", "world") }, | ||||||
|  | 				output:  "E output.go:<LINE>] hello world\n", | ||||||
|  | 			}, | ||||||
|  | 			{ | ||||||
|  | 				name:    "ErrorS", | ||||||
|  | 				logFunc: func() { klog.ErrorS(errors.New("hello"), "world") }, | ||||||
|  | 				output:  "E output.go:<LINE>] \"world\" err=\"hello\"\n", | ||||||
|  | 			}, | ||||||
|  | 			{ | ||||||
|  | 				name:    "ErrorSDepth", | ||||||
|  | 				logFunc: func() { klog.ErrorSDepth(0, errors.New("hello"), "world") }, | ||||||
|  | 				output:  "E output.go:<LINE>] \"world\" err=\"hello\"\n", | ||||||
|  | 			}, | ||||||
|  | 			{ | ||||||
|  | 				name:    "V().Info", | ||||||
|  | 				logFunc: func() { klog.V(1).Info("hello", "one", "world") }, | ||||||
|  | 				output:  "I output.go:<LINE>] hellooneworld\n", | ||||||
|  | 			}, | ||||||
|  | 			{ | ||||||
|  | 				name:    "V().InfoDepth", | ||||||
|  | 				logFunc: func() { klog.V(1).InfoDepth(0, "hello", "one", "world") }, | ||||||
|  | 				output:  "I output.go:<LINE>] hellooneworld\n", | ||||||
|  | 			}, | ||||||
|  | 			{ | ||||||
|  | 				name:    "V().Infoln", | ||||||
|  | 				logFunc: func() { klog.V(1).Infoln("hello", "one", "world") }, | ||||||
|  | 				output:  "I output.go:<LINE>] hello one world\n", | ||||||
|  | 			}, | ||||||
|  | 			{ | ||||||
|  | 				name:    "V().InfolnDepth", | ||||||
|  | 				logFunc: func() { klog.V(1).InfolnDepth(0, "hello", "one", "world") }, | ||||||
|  | 				output:  "I output.go:<LINE>] hello one world\n", | ||||||
|  | 			}, | ||||||
|  | 			{ | ||||||
|  | 				name:    "V().Infof", | ||||||
|  | 				logFunc: func() { klog.V(1).Infof("hello %s %s", "one", "world") }, | ||||||
|  | 				output:  "I output.go:<LINE>] hello one world\n", | ||||||
|  | 			}, | ||||||
|  | 			{ | ||||||
|  | 				name:    "V().InfofDepth", | ||||||
|  | 				logFunc: func() { klog.V(1).InfofDepth(0, "hello %s %s", "one", "world") }, | ||||||
|  | 				output:  "I output.go:<LINE>] hello one world\n", | ||||||
|  | 			}, | ||||||
|  | 			{ | ||||||
|  | 				name:    "V().InfoS", | ||||||
|  | 				logFunc: func() { klog.V(1).InfoS("hello", "what", "one world") }, | ||||||
|  | 				output:  "I output.go:<LINE>] \"hello\" what=\"one world\"\n", | ||||||
|  | 			}, | ||||||
|  | 			{ | ||||||
|  | 				name:    "V().InfoSDepth", | ||||||
|  | 				logFunc: func() { klog.V(1).InfoSDepth(0, "hello", "what", "one world") }, | ||||||
|  | 				output:  "I output.go:<LINE>] \"hello\" what=\"one world\"\n", | ||||||
|  | 			}, | ||||||
|  | 			{ | ||||||
|  | 				name:    "V().ErrorS", | ||||||
|  | 				logFunc: func() { klog.V(1).ErrorS(errors.New("hello"), "one world") }, | ||||||
|  | 				output:  "E output.go:<LINE>] \"one world\" err=\"hello\"\n", | ||||||
|  | 			}, | ||||||
|  | 		} | ||||||
|  | 		_, _, line, _ := runtime.Caller(0) | ||||||
|  |  | ||||||
|  | 		for i, test := range tests { | ||||||
|  | 			t.Run(test.name, func(t *testing.T) { | ||||||
|  | 				var buffer bytes.Buffer | ||||||
|  | 				if config.NewLogger == nil { | ||||||
|  | 					klog.SetOutput(&buffer) | ||||||
|  | 				} else { | ||||||
|  | 					klog.SetLogger(config.NewLogger(&buffer, 10, "")) | ||||||
|  | 					defer klog.ClearLogger() | ||||||
|  | 				} | ||||||
|  | 				test.logFunc() | ||||||
|  | 				klog.Flush() | ||||||
|  |  | ||||||
|  | 				actual := buffer.String() | ||||||
|  | 				// Strip varying header. | ||||||
|  | 				re := `(?m)^(.).... ..:..:......... ....... output.go` | ||||||
|  | 				actual = regexp.MustCompile(re).ReplaceAllString(actual, `${1} output.go`) | ||||||
|  |  | ||||||
|  | 				// Inject expected line. This matches the if checks above, which are | ||||||
|  | 				// the same for both printWithKlog and printWithLogger. | ||||||
|  | 				callLine := line + 1 - (len(tests)-i)*5 | ||||||
|  | 				expected := test.output | ||||||
|  |  | ||||||
|  | 				// When klog does string formating for | ||||||
|  | 				// non-structured calls, it passes the entire | ||||||
|  | 				// result, including a trailing newline, to | ||||||
|  | 				// Logger.Info. | ||||||
|  | 				if config.NewLogger != nil && | ||||||
|  | 					!strings.HasSuffix(test.name, "S") && | ||||||
|  | 					!strings.HasSuffix(test.name, "SDepth") { | ||||||
|  | 					// klog: I output.go:<LINE>] hello world | ||||||
|  | 					// with logger: I output.go:<LINE>] "hello world\n" | ||||||
|  | 					index := strings.Index(expected, "] ") | ||||||
|  | 					if index == -1 { | ||||||
|  | 						t.Fatalf("did not find ] separator: %s", expected) | ||||||
|  | 					} | ||||||
|  | 					expected = expected[0:index+2] + strconv.Quote(expected[index+2:]) + "\n" | ||||||
|  |  | ||||||
|  | 					// Warnings become info messages. | ||||||
|  | 					if strings.HasPrefix(expected, "W") { | ||||||
|  | 						expected = "I" + expected[1:] | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  |  | ||||||
|  | 				if repl, ok := config.ExpectedOutputMapping[expected]; ok { | ||||||
|  | 					expected = repl | ||||||
|  | 				} | ||||||
|  | 				expectedWithPlaceholder := expected | ||||||
|  | 				expected = strings.ReplaceAll(expected, "<LINE>", fmt.Sprintf("%d", callLine)) | ||||||
|  | 				if actual != expected { | ||||||
|  | 					if expectedWithPlaceholder == test.output { | ||||||
|  | 						t.Errorf("Output mismatch. Expected:\n%s\nActual:\n%s\n", expectedWithPlaceholder, actual) | ||||||
|  | 					} else { | ||||||
|  | 						t.Errorf("Output mismatch. klog:\n%s\nExpected:\n%s\nActual:\n%s\n", test.output, expectedWithPlaceholder, actual) | ||||||
|  | 					} | ||||||
|  | 				} | ||||||
|  | 			}) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func copySlice(in []interface{}) []interface{} { | ||||||
|  | 	return append([]interface{}{}, in...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type kmeta struct { | ||||||
|  | 	Name, Namespace string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (k kmeta) GetName() string { | ||||||
|  | 	return k.Name | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (k kmeta) GetNamespace() string { | ||||||
|  | 	return k.Namespace | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var _ klog.KMetadata = kmeta{} | ||||||
|  |  | ||||||
|  | type customErrorJSON struct { | ||||||
|  | 	s string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var _ error = &customErrorJSON{} | ||||||
|  | var _ json.Marshaler = &customErrorJSON{} | ||||||
|  |  | ||||||
|  | func (e *customErrorJSON) Error() string { | ||||||
|  | 	return e.s | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (e *customErrorJSON) MarshalJSON() ([]byte, error) { | ||||||
|  | 	return json.Marshal(strings.ToUpper(e.s)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type stringer struct { | ||||||
|  | 	s string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // String crashes when called for nil. | ||||||
|  | func (s *stringer) String() string { | ||||||
|  | 	return s.s | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var _ fmt.Stringer = &stringer{} | ||||||
|  |  | ||||||
|  | type faultyStringer struct{} | ||||||
|  |  | ||||||
|  | // String always panics. | ||||||
|  | func (f faultyStringer) String() string { | ||||||
|  | 	panic("fake String panic") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var _ fmt.Stringer = faultyStringer{} | ||||||
|  |  | ||||||
|  | type faultyMarshaler struct{} | ||||||
|  |  | ||||||
|  | // MarshalLog always panics. | ||||||
|  | func (f faultyMarshaler) MarshalLog() interface{} { | ||||||
|  | 	panic("fake MarshalLog panic") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var _ logr.Marshaler = faultyMarshaler{} | ||||||
|  |  | ||||||
|  | type faultyError struct{} | ||||||
|  |  | ||||||
|  | // Error always panics. | ||||||
|  | func (f faultyError) Error() string { | ||||||
|  | 	panic("fake Error panic") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | var _ error = faultyError{} | ||||||
							
								
								
									
										32
									
								
								vendor/k8s.io/klog/v2/test/output_helper.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								vendor/k8s.io/klog/v2/test/output_helper.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | |||||||
|  | /* | ||||||
|  | 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 test | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"github.com/go-logr/logr" | ||||||
|  |  | ||||||
|  | 	"k8s.io/klog/v2" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func loggerHelper(logger logr.Logger, msg string, kv []interface{}) { | ||||||
|  | 	logger = logger.WithCallDepth(1) | ||||||
|  | 	logger.Info(msg, kv...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func klogHelper(msg string, kv []interface{}) { | ||||||
|  | 	klog.InfoSDepth(1, msg, kv...) | ||||||
|  | } | ||||||
							
								
								
									
										271
									
								
								vendor/k8s.io/klog/v2/test/zapr.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										271
									
								
								vendor/k8s.io/klog/v2/test/zapr.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,271 @@ | |||||||
|  | /* | ||||||
|  | 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 test | ||||||
|  |  | ||||||
|  | // ZaprOutputMappingDirect provides a mapping from klog output to the | ||||||
|  | // corresponding zapr output when zapr is called directly. | ||||||
|  | // | ||||||
|  | // Experimental | ||||||
|  | // | ||||||
|  | // Notice: This package is EXPERIMENTAL and may be changed or removed in a | ||||||
|  | // later release. | ||||||
|  | func ZaprOutputMappingDirect() map[string]string { | ||||||
|  | 	return map[string]string{ | ||||||
|  | 		`I output.go:<LINE>] "test" akey="<&>" | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"test","v":0,"akey":"<&>"} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		`E output.go:<LINE>] "test" err="whoops" | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"test","err":"whoops"} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		`I output.go:<LINE>] "helper" akey="avalue" | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"helper","v":0,"akey":"avalue"} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		`I output.go:<LINE>] "hello/world: test" akey="avalue" | ||||||
|  | `: `{"logger":"hello.world","caller":"test/output.go:<LINE>","msg":"test","v":0,"akey":"avalue"} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		`I output.go:<LINE>] "test" X="y" duration="1m0s" A="b" | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"test","duration":"1h0m0s","X":"y","v":0,"duration":"1m0s","A":"b"} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		`I output.go:<LINE>] "test" akey9="avalue9" akey8="avalue8" akey1="avalue1" akey5="avalue5" akey4="avalue4" | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"test","akey9":"avalue9","akey8":"avalue8","akey1":"avalue1","v":0,"akey5":"avalue5","akey4":"avalue4"} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		`I output.go:<LINE>] "test" | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"test","v":0} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		`I output.go:<LINE>] "\"quoted\"" key="\"quoted value\"" | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"\"quoted\"","v":0,"key":"\"quoted value\""} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		`I output.go:<LINE>] "test" err="whoops" | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"test","v":0,"err":"whoops"} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		`I output.go:<LINE>] "test" pod="kube-system/pod-1" | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"test","v":0,"pod":{"name":"pod-1","namespace":"kube-system"}} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		`I output.go:<LINE>] "test" pods=[kube-system/pod-1 kube-system/pod-2] | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"test","v":0,"pods":[{"name":"pod-1","namespace":"kube-system"},{"name":"pod-2","namespace":"kube-system"}]} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		`I output.go:<LINE>] "test" akey="avalue" | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"test","v":0,"akey":"avalue"} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		`I output.go:<LINE>] "me: test" akey="avalue" | ||||||
|  | `: `{"logger":"me","caller":"test/output.go:<LINE>","msg":"test","v":0,"akey":"avalue"} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		`I output.go:<LINE>] "test" akey="avalue2" | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"test","akey":"avalue","v":0,"akey":"avalue2"} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		`I output.go:<LINE>] "you see me" | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"you see me","v":9} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		`I output.go:<LINE>] "test" firstKey=1 | ||||||
|  | I output.go:<LINE>] "test" firstKey=1 secondKey=2 | ||||||
|  | I output.go:<LINE>] "test" firstKey=1 | ||||||
|  | I output.go:<LINE>] "test" firstKey=1 secondKey=3 | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"test","firstKey":1,"v":0} | ||||||
|  | {"caller":"test/output.go:<LINE>","msg":"test","firstKey":1,"secondKey":2,"v":0} | ||||||
|  | {"caller":"test/output.go:<LINE>","msg":"test","firstKey":1,"v":0} | ||||||
|  | {"caller":"test/output.go:<LINE>","msg":"test","firstKey":1,"secondKey":3,"v":0} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		`I output.go:<LINE>] "odd WithValues" keyWithoutValue="(MISSING)" | ||||||
|  | I output.go:<LINE>] "odd WithValues" keyWithoutValue="(MISSING)" anotherKeyWithoutValue="(MISSING)" | ||||||
|  | I output.go:<LINE>] "odd WithValues" keyWithoutValue="(MISSING)" | ||||||
|  | `: `{"caller":"test/output.go:<WITH-VALUES>","msg":"odd number of arguments passed as key-value pairs for logging","ignored key":"keyWithoutValue"} | ||||||
|  | {"caller":"test/output.go:<WITH-VALUES-2>","msg":"odd number of arguments passed as key-value pairs for logging","ignored key":"anotherKeyWithoutValue"} | ||||||
|  | {"caller":"test/output.go:<LINE>","msg":"odd WithValues","v":0} | ||||||
|  | {"caller":"test/output.go:<LINE>","msg":"odd WithValues","v":0} | ||||||
|  | {"caller":"test/output.go:<LINE>","msg":"odd WithValues","v":0} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		`I output.go:<LINE>] "odd arguments" akey="avalue" akey2="(MISSING)" | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"odd number of arguments passed as key-value pairs for logging","ignored key":"akey2"} | ||||||
|  | {"caller":"test/output.go:<LINE>","msg":"odd arguments","v":0,"akey":"avalue"} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		`I output.go:<LINE>] "both odd" basekey1="basevar1" basekey2="(MISSING)" akey="avalue" akey2="(MISSING)" | ||||||
|  | `: `{"caller":"test/output.go:<WITH-VALUES>","msg":"odd number of arguments passed as key-value pairs for logging","ignored key":"basekey2"} | ||||||
|  | {"caller":"test/output.go:<LINE>","msg":"odd number of arguments passed as key-value pairs for logging","basekey1":"basevar1","ignored key":"akey2"} | ||||||
|  | {"caller":"test/output.go:<LINE>","msg":"both odd","basekey1":"basevar1","v":0,"akey":"avalue"} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		`I output.go:<LINE>] "marshaler nil" obj="<panic: value method k8s.io/klog/v2.ObjectRef.String called using nil *ObjectRef pointer>" | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"marshaler nil","v":0,"objError":"PANIC=value method k8s.io/klog/v2.ObjectRef.MarshalLog called using nil *ObjectRef pointer"} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		// zap replaces a panic for a nil object with <nil>. | ||||||
|  | 		`E output.go:<LINE>] "error nil" err="<panic: runtime error: invalid memory address or nil pointer dereference>" | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"error nil","err":"<nil>"} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		`I output.go:<LINE>] "stringer nil" stringer="<panic: runtime error: invalid memory address or nil pointer dereference>" | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"stringer nil","v":0,"stringer":"<nil>"} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		`I output.go:<LINE>] "stringer panic" stringer="<panic: fake String panic>" | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"stringer panic","v":0,"stringerError":"PANIC=fake String panic"} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		`E output.go:<LINE>] "error panic" err="<panic: fake Error panic>" | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"error panic","errError":"PANIC=fake Error panic"} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		`I output.go:<LINE>] "marshaler panic" obj={} | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"marshaler panic","v":0,"objError":"PANIC=fake MarshalLog panic"} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		// klog.Info | ||||||
|  | 		`I output.go:<LINE>] "helloworld\n" | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"helloworld\n","v":0} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		// klog.Infoln | ||||||
|  | 		`I output.go:<LINE>] "hello world\n" | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"hello world\n","v":0} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		// klog.Error | ||||||
|  | 		`E output.go:<LINE>] "helloworld\n" | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"helloworld\n"} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		// klog.Errorln | ||||||
|  | 		`E output.go:<LINE>] "hello world\n" | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"hello world\n"} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		// klog.ErrorS | ||||||
|  | 		`E output.go:<LINE>] "world" err="hello" | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"world","err":"hello"} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		// klog.InfoS | ||||||
|  | 		`I output.go:<LINE>] "hello" what="world" | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"hello","v":0,"what":"world"} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		// klog.V(1).Info | ||||||
|  | 		`I output.go:<LINE>] "hellooneworld\n" | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"hellooneworld\n","v":1} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		// klog.V(1).Infoln | ||||||
|  | 		`I output.go:<LINE>] "hello one world\n" | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"hello one world\n","v":1} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		// klog.V(1).ErrorS | ||||||
|  | 		`E output.go:<LINE>] "one world" err="hello" | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"one world","err":"hello"} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		// klog.V(1).InfoS | ||||||
|  | 		`I output.go:<LINE>] "hello" what="one world" | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"hello","v":1,"what":"one world"} | ||||||
|  | `, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ZaprOutputMappingIndirect provides a mapping from klog output to the | ||||||
|  | // corresponding zapr output when zapr is called indirectly through | ||||||
|  | // klog. | ||||||
|  | // | ||||||
|  | // This is different from ZaprOutputMappingDirect because: | ||||||
|  | // - WithName gets added to the message by Output. | ||||||
|  | // - zap uses . as separator instead of / between WithName values, | ||||||
|  | //   here we get slashes because Output concatenates these values. | ||||||
|  | // - WithValues are added to the normal key/value parameters by | ||||||
|  | //   Output, which puts them after "v". | ||||||
|  | // - Output does that without emitting the warning that we get | ||||||
|  | //   from zapr. | ||||||
|  | // - zap drops keys with missing values, here we get "(MISSING)". | ||||||
|  | // - zap does not de-duplicate key/value pairs, here klog does that | ||||||
|  | //   for it. | ||||||
|  | // | ||||||
|  | // Experimental | ||||||
|  | // | ||||||
|  | // Notice: This package is EXPERIMENTAL and may be changed or removed in a | ||||||
|  | // later release. | ||||||
|  | func ZaprOutputMappingIndirect() map[string]string { | ||||||
|  | 	mapping := ZaprOutputMappingDirect() | ||||||
|  |  | ||||||
|  | 	for key, value := range map[string]string{ | ||||||
|  | 		`I output.go:<LINE>] "hello/world: test" akey="avalue" | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"hello/world: test","v":0,"akey":"avalue"} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		`I output.go:<LINE>] "me: test" akey="avalue" | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"me: test","v":0,"akey":"avalue"} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		`I output.go:<LINE>] "odd parameters" basekey1="basevar1" basekey2="(MISSING)" akey="avalue" akey2="(MISSING)" | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"odd number of arguments passed as key-value pairs for logging","ignored key":"akey2"} | ||||||
|  | {"caller":"test/output.go:<LINE>","msg":"odd parameters","v":0,"basekey1":"basevar1","basekey2":"(MISSING)","akey":"avalue"} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		`I output.go:<LINE>] "odd WithValues" keyWithoutValue="(MISSING)" | ||||||
|  | I output.go:<LINE>] "odd WithValues" keyWithoutValue="(MISSING)" anotherKeyWithoutValue="(MISSING)" | ||||||
|  | I output.go:<LINE>] "odd WithValues" keyWithoutValue="(MISSING)" | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"odd WithValues","v":0,"keyWithoutValue":"(MISSING)"} | ||||||
|  | {"caller":"test/output.go:<LINE>","msg":"odd WithValues","v":0,"keyWithoutValue":"(MISSING)","anotherKeyWithoutValue":"(MISSING)"} | ||||||
|  | {"caller":"test/output.go:<LINE>","msg":"odd WithValues","v":0,"keyWithoutValue":"(MISSING)"} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		`I output.go:<LINE>] "both odd" basekey1="basevar1" basekey2="(MISSING)" akey="avalue" akey2="(MISSING)" | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"odd number of arguments passed as key-value pairs for logging","ignored key":"akey2"} | ||||||
|  | {"caller":"test/output.go:<LINE>","msg":"both odd","v":0,"basekey1":"basevar1","basekey2":"(MISSING)","akey":"avalue"} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		`I output.go:<LINE>] "test" akey9="avalue9" akey8="avalue8" akey1="avalue1" akey5="avalue5" akey4="avalue4" | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"test","v":0,"akey9":"avalue9","akey8":"avalue8","akey1":"avalue1","akey5":"avalue5","akey4":"avalue4"} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		`I output.go:<LINE>] "test" akey="avalue2" | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"test","v":0,"akey":"avalue2"} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		`I output.go:<LINE>] "test" X="y" duration="1m0s" A="b" | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"test","v":0,"X":"y","duration":"1m0s","A":"b"} | ||||||
|  | `, | ||||||
|  |  | ||||||
|  | 		`I output.go:<LINE>] "test" firstKey=1 | ||||||
|  | I output.go:<LINE>] "test" firstKey=1 secondKey=2 | ||||||
|  | I output.go:<LINE>] "test" firstKey=1 | ||||||
|  | I output.go:<LINE>] "test" firstKey=1 secondKey=3 | ||||||
|  | `: `{"caller":"test/output.go:<LINE>","msg":"test","v":0,"firstKey":1} | ||||||
|  | {"caller":"test/output.go:<LINE>","msg":"test","v":0,"firstKey":1,"secondKey":2} | ||||||
|  | {"caller":"test/output.go:<LINE>","msg":"test","v":0,"firstKey":1} | ||||||
|  | {"caller":"test/output.go:<LINE>","msg":"test","v":0,"firstKey":1,"secondKey":3} | ||||||
|  | `, | ||||||
|  | 	} { | ||||||
|  | 		mapping[key] = value | ||||||
|  | 	} | ||||||
|  | 	return mapping | ||||||
|  | } | ||||||
							
								
								
									
										1
									
								
								vendor/modules.txt
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								vendor/modules.txt
									
									
									
									
										vendored
									
									
								
							| @@ -2137,6 +2137,7 @@ k8s.io/klog/v2/internal/buffer | |||||||
| k8s.io/klog/v2/internal/clock | k8s.io/klog/v2/internal/clock | ||||||
| k8s.io/klog/v2/internal/serialize | k8s.io/klog/v2/internal/serialize | ||||||
| k8s.io/klog/v2/internal/severity | k8s.io/klog/v2/internal/severity | ||||||
|  | k8s.io/klog/v2/test | ||||||
| # k8s.io/kube-aggregator v0.0.0 => ./staging/src/k8s.io/kube-aggregator | # k8s.io/kube-aggregator v0.0.0 => ./staging/src/k8s.io/kube-aggregator | ||||||
| ## explicit; go 1.18 | ## explicit; go 1.18 | ||||||
| k8s.io/kube-aggregator/pkg/apis/apiregistration | k8s.io/kube-aggregator/pkg/apis/apiregistration | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Kubernetes Prow Robot
					Kubernetes Prow Robot