k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/test/e2e/framework/ginkgologger.go (about) 1 /* 2 Copyright 2015 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // Package framework contains provider-independent helper code for 18 // building and running E2E tests with Ginkgo. The actual Ginkgo test 19 // suites gets assembled by combining this framework, the optional 20 // provider support code and specific tests via a separate .go file 21 // like Kubernetes' test/e2e.go. 22 package framework 23 24 import ( 25 "flag" 26 "fmt" 27 "os" 28 "strings" 29 "time" 30 31 "github.com/onsi/ginkgo/v2" 32 ginkgotypes "github.com/onsi/ginkgo/v2/types" 33 34 "k8s.io/klog/v2" 35 "k8s.io/klog/v2/textlogger" 36 37 _ "k8s.io/component-base/logs/testinit" // Ensure command line flags are registered. 38 ) 39 40 var ( 41 logConfig = textlogger.NewConfig( 42 textlogger.Output(ginkgo.GinkgoWriter), 43 textlogger.Backtrace(unwind), 44 ) 45 ginkgoLogger = textlogger.NewLogger(logConfig) 46 TimeNow = time.Now // Can be stubbed out for testing. 47 Pid = os.Getpid() // Can be stubbed out for testing. 48 ) 49 50 func init() { 51 // ktesting and testinit already registered the -v and -vmodule 52 // command line flags. To configure the textlogger and klog 53 // consistently, we need to intercept the Set call. This 54 // can be done by swapping out the flag.Value for the -v and 55 // -vmodule flags with a wrapper which calls both. 56 var fs flag.FlagSet 57 logConfig.AddFlags(&fs) 58 fs.VisitAll(func(loggerFlag *flag.Flag) { 59 klogFlag := flag.CommandLine.Lookup(loggerFlag.Name) 60 if klogFlag != nil { 61 klogFlag.Value = &valueChain{Value: loggerFlag.Value, parentValue: klogFlag.Value} 62 } 63 }) 64 65 // Now install the textlogger as the klog default logger. 66 // Calls like klog.Info then will write to ginkgo.GingoWriter 67 // through the textlogger. 68 // 69 // However, stack unwinding is then still being done by klog and thus 70 // ignores ginkgo.GingkoHelper. Tests should use framework.Logf or 71 // structured, contextual logging. 72 writer, _ := ginkgoLogger.GetSink().(textlogger.KlogBufferWriter) 73 opts := []klog.LoggerOption{ 74 klog.ContextualLogger(true), 75 klog.WriteKlogBuffer(writer.WriteKlogBuffer), 76 } 77 klog.SetLoggerWithOptions(ginkgoLogger, opts...) 78 } 79 80 type valueChain struct { 81 flag.Value 82 parentValue flag.Value 83 } 84 85 func (v *valueChain) Set(value string) error { 86 if err := v.Value.Set(value); err != nil { 87 return err 88 } 89 if err := v.parentValue.Set(value); err != nil { 90 return err 91 } 92 return nil 93 } 94 95 func unwind(skip int) (string, int) { 96 location := ginkgotypes.NewCodeLocation(skip + 1) 97 return location.FileName, location.LineNumber 98 } 99 100 // log re-implements klog.Info: same header, but stack unwinding 101 // with support for ginkgo.GinkgoWriter and skipping stack levels. 102 func log(offset int, msg string) { 103 now := TimeNow() 104 file, line := unwind(offset + 1) 105 if file == "" { 106 file = "???" 107 line = 1 108 } else if slash := strings.LastIndex(file, "/"); slash >= 0 { 109 file = file[slash+1:] 110 } 111 _, month, day := now.Date() 112 hour, minute, second := now.Clock() 113 header := fmt.Sprintf("I%02d%02d %02d:%02d:%02d.%06d %d %s:%d]", 114 month, day, hour, minute, second, now.Nanosecond()/1000, Pid, file, line) 115 116 fmt.Fprintln(ginkgo.GinkgoWriter, header, msg) 117 }