knative.dev/pkg@v0.0.0-20260602142205-ac97e43f6622/test/logging/logging.go (about)

     1  /*
     2  Copyright 2018 The Knative 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  // logging.go contains the logic to configure and interact with the
    18  // logging and metrics libraries.
    19  
    20  package logging
    21  
    22  import (
    23  	"context"
    24  	"flag"
    25  	"os"
    26  	"strconv"
    27  	"sync"
    28  
    29  	"go.opentelemetry.io/otel"
    30  	"go.opentelemetry.io/otel/trace"
    31  	"go.uber.org/zap"
    32  	"go.uber.org/zap/zapcore"
    33  	"k8s.io/klog/v2"
    34  )
    35  
    36  var tracer = otel.GetTracerProvider().Tracer("knative.dev/pkg/test/logging")
    37  
    38  // FormatLogger is a printf style function for logging in tests.
    39  type FormatLogger func(template string, args ...interface{})
    40  
    41  // GetEmitableSpan starts and returns a trace.Span with a name that
    42  // is used by the ExportSpan method to emit the span.
    43  //
    44  //nolint:spancheck
    45  func GetEmitableSpan(ctx context.Context, metricName string) trace.Span {
    46  	_, span := tracer.Start(ctx, metricName)
    47  	return span
    48  }
    49  
    50  const (
    51  	logrZapDebugLevel = 3
    52  )
    53  
    54  func zapLevelFromLogrLevel(logrLevel int) zapcore.Level {
    55  	// Zap levels are -1, 0, 1, 2,... corresponding to DebugLevel, InfoLevel, WarnLevel, ErrorLevel,...
    56  	// zapr library just does zapLevel := -1*logrLevel; which means:
    57  	//  1. Info level is only active at 0 (versus 2 in klog being generally equivalent to Info)
    58  	//  2. Only verbosity of 0 and 1 map to valid Zap levels
    59  	// According to https://github.com/uber-go/zap/issues/713 custom levels (i.e. < -1) aren't guaranteed to work, so not using them (for now).
    60  
    61  	l := zap.InfoLevel
    62  	if logrLevel >= logrZapDebugLevel {
    63  		l = zap.DebugLevel
    64  	}
    65  
    66  	return l
    67  }
    68  
    69  func printFlags() {
    70  	var flagList []interface{}
    71  	flag.CommandLine.VisitAll(func(f *flag.Flag) {
    72  		flagList = append(flagList, f.Name, f.Value.String())
    73  	})
    74  	logger.Sugar().Debugw("Test Flags", flagList...)
    75  }
    76  
    77  var (
    78  	zapCore              zapcore.Core
    79  	logger               *zap.Logger
    80  	verbosity            int // Amount of log verbosity
    81  	loggerInitializeOnce = &sync.Once{}
    82  )
    83  
    84  // InitializeLogger initializes logging for Knative tests.
    85  // It should be called prior to executing tests but after command-line flags have been processed.
    86  // It is recommended doing it in the TestMain() function.
    87  func InitializeLogger() {
    88  	loggerInitializeOnce.Do(func() {
    89  		humanEncoder := zapcore.NewConsoleEncoder(zap.NewDevelopmentEncoderConfig())
    90  
    91  		// Output streams
    92  		// TODO(coryrc): also open a log file if in Prow?
    93  		stdOut := zapcore.Lock(os.Stdout)
    94  
    95  		// Level function helper
    96  		zapLevel := zapLevelFromLogrLevel(verbosity)
    97  		isPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
    98  			return lvl >= zapLevel
    99  		})
   100  
   101  		// Assemble the output streams
   102  		zapCore = zapcore.NewTee(
   103  			// TODO(coryrc): log JSON output somewhere?
   104  			zapcore.NewCore(humanEncoder, stdOut, isPriority),
   105  		)
   106  
   107  		logger = zap.New(zapCore)
   108  		zap.ReplaceGlobals(logger) // Gets used by klog/glog proxy libraries
   109  
   110  		// Set klog/glog verbosities (works with and without proxy libraries)
   111  		klogLevel := klog.Level(0)
   112  		klogLevel.Set(strconv.Itoa(verbosity))
   113  
   114  		if verbosity > 2 {
   115  			printFlags()
   116  		}
   117  	})
   118  }
   119  
   120  func init() {
   121  	flag.IntVar(&verbosity, "verbosity", 2,
   122  		"Amount of verbosity, 0-10. See https://github.com/go-logr/logr#how-do-i-choose-my-v-levels and https://github.com/kubernetes/community/blob/master/contributors/devel/sig-instrumentation/logging.md")
   123  }