...

Source file src/k8s.io/klog/v2/internal/verbosity/verbosity.go

Documentation: k8s.io/klog/v2/internal/verbosity

     1  /*
     2  Copyright 2013 Google Inc. All Rights Reserved.
     3  Copyright 2022 The Kubernetes Authors.
     4  
     5  Licensed under the Apache License, Version 2.0 (the "License");
     6  you may not use this file except in compliance with the License.
     7  You may obtain a copy of the License at
     8  
     9      http://www.apache.org/licenses/LICENSE-2.0
    10  
    11  Unless required by applicable law or agreed to in writing, software
    12  distributed under the License is distributed on an "AS IS" BASIS,
    13  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14  See the License for the specific language governing permissions and
    15  limitations under the License.
    16  */
    17  
    18  package verbosity
    19  
    20  import (
    21  	"bytes"
    22  	"errors"
    23  	"flag"
    24  	"fmt"
    25  	"path/filepath"
    26  	"runtime"
    27  	"strconv"
    28  	"strings"
    29  	"sync"
    30  	"sync/atomic"
    31  )
    32  
    33  // New returns a struct that implements -v and -vmodule support. Changing and
    34  // checking these settings is thread-safe, with all concurrency issues handled
    35  // internally.
    36  func New() *VState {
    37  	vs := new(VState)
    38  
    39  	// The two fields must have a pointer to the overal struct for their
    40  	// implementation of Set.
    41  	vs.vmodule.vs = vs
    42  	vs.verbosity.vs = vs
    43  
    44  	return vs
    45  }
    46  
    47  // Value is an extension that makes it possible to use the values in pflag.
    48  type Value interface {
    49  	flag.Value
    50  	Type() string
    51  }
    52  
    53  func (vs *VState) V() Value {
    54  	return &vs.verbosity
    55  }
    56  
    57  func (vs *VState) VModule() Value {
    58  	return &vs.vmodule
    59  }
    60  
    61  // VState contains settings and state. Some of its fields can be accessed
    62  // through atomic read/writes, in other cases a mutex must be held.
    63  type VState struct {
    64  	mu sync.Mutex
    65  
    66  	// These flags are modified only under lock, although verbosity may be fetched
    67  	// safely using atomic.LoadInt32.
    68  	vmodule   moduleSpec // The state of the -vmodule flag.
    69  	verbosity levelSpec  // V logging level, the value of the -v flag/
    70  
    71  	// pcs is used in V to avoid an allocation when computing the caller's PC.
    72  	pcs [1]uintptr
    73  	// vmap is a cache of the V Level for each V() call site, identified by PC.
    74  	// It is wiped whenever the vmodule flag changes state.
    75  	vmap map[uintptr]Level
    76  	// filterLength stores the length of the vmodule filter chain. If greater
    77  	// than zero, it means vmodule is enabled. It may be read safely
    78  	// using sync.LoadInt32, but is only modified under mu.
    79  	filterLength int32
    80  }
    81  
    82  // Level must be an int32 to support atomic read/writes.
    83  type Level int32
    84  
    85  type levelSpec struct {
    86  	vs *VState
    87  	l  Level
    88  }
    89  
    90  // get returns the value of the level.
    91  func (l *levelSpec) get() Level {
    92  	return Level(atomic.LoadInt32((*int32)(&l.l)))
    93  }
    94  
    95  // set sets the value of the level.
    96  func (l *levelSpec) set(val Level) {
    97  	atomic.StoreInt32((*int32)(&l.l), int32(val))
    98  }
    99  
   100  // String is part of the flag.Value interface.
   101  func (l *levelSpec) String() string {
   102  	return strconv.FormatInt(int64(l.l), 10)
   103  }
   104  
   105  // Get is part of the flag.Getter interface. It returns the
   106  // verbosity level as int32.
   107  func (l *levelSpec) Get() interface{} {
   108  	return int32(l.l)
   109  }
   110  
   111  // Type is part of pflag.Value.
   112  func (l *levelSpec) Type() string {
   113  	return "Level"
   114  }
   115  
   116  // Set is part of the flag.Value interface.
   117  func (l *levelSpec) Set(value string) error {
   118  	v, err := strconv.ParseInt(value, 10, 32)
   119  	if err != nil {
   120  		return err
   121  	}
   122  	l.vs.mu.Lock()
   123  	defer l.vs.mu.Unlock()
   124  	l.vs.set(Level(v), l.vs.vmodule.filter, false)
   125  	return nil
   126  }
   127  
   128  // moduleSpec represents the setting of the -vmodule flag.
   129  type moduleSpec struct {
   130  	vs     *VState
   131  	filter []modulePat
   132  }
   133  
   134  // modulePat contains a filter for the -vmodule flag.
   135  // It holds a verbosity level and a file pattern to match.
   136  type modulePat struct {
   137  	pattern string
   138  	literal bool // The pattern is a literal string
   139  	level   Level
   140  }
   141  
   142  // match reports whether the file matches the pattern. It uses a string
   143  // comparison if the pattern contains no metacharacters.
   144  func (m *modulePat) match(file string) bool {
   145  	if m.literal {
   146  		return file == m.pattern
   147  	}
   148  	match, _ := filepath.Match(m.pattern, file)
   149  	return match
   150  }
   151  
   152  func (m *moduleSpec) String() string {
   153  	// Lock because the type is not atomic. TODO: clean this up.
   154  	// Empty instances don't have and don't need a lock (can
   155  	// happen when flag uses introspection).
   156  	if m.vs != nil {
   157  		m.vs.mu.Lock()
   158  		defer m.vs.mu.Unlock()
   159  	}
   160  	var b bytes.Buffer
   161  	for i, f := range m.filter {
   162  		if i > 0 {
   163  			b.WriteRune(',')
   164  		}
   165  		fmt.Fprintf(&b, "%s=%d", f.pattern, f.level)
   166  	}
   167  	return b.String()
   168  }
   169  
   170  // Get is part of the (Go 1.2)  flag.Getter interface. It always returns nil for this flag type since the
   171  // struct is not exported.
   172  func (m *moduleSpec) Get() interface{} {
   173  	return nil
   174  }
   175  
   176  // Type is part of pflag.Value
   177  func (m *moduleSpec) Type() string {
   178  	return "pattern=N,..."
   179  }
   180  
   181  var errVmoduleSyntax = errors.New("syntax error: expect comma-separated list of filename=N")
   182  
   183  // Set will sets module value
   184  // Syntax: -vmodule=recordio=2,file=1,gfs*=3
   185  func (m *moduleSpec) Set(value string) error {
   186  	var filter []modulePat
   187  	for _, pat := range strings.Split(value, ",") {
   188  		if len(pat) == 0 {
   189  			// Empty strings such as from a trailing comma can be ignored.
   190  			continue
   191  		}
   192  		patLev := strings.Split(pat, "=")
   193  		if len(patLev) != 2 || len(patLev[0]) == 0 || len(patLev[1]) == 0 {
   194  			return errVmoduleSyntax
   195  		}
   196  		pattern := patLev[0]
   197  		v, err := strconv.ParseInt(patLev[1], 10, 32)
   198  		if err != nil {
   199  			return errors.New("syntax error: expect comma-separated list of filename=N")
   200  		}
   201  		if v < 0 {
   202  			return errors.New("negative value for vmodule level")
   203  		}
   204  		if v == 0 {
   205  			continue // Ignore. It's harmless but no point in paying the overhead.
   206  		}
   207  		// TODO: check syntax of filter?
   208  		filter = append(filter, modulePat{pattern, isLiteral(pattern), Level(v)})
   209  	}
   210  	m.vs.mu.Lock()
   211  	defer m.vs.mu.Unlock()
   212  	m.vs.set(m.vs.verbosity.l, filter, true)
   213  	return nil
   214  }
   215  
   216  // isLiteral reports whether the pattern is a literal string, that is, has no metacharacters
   217  // that require filepath.Match to be called to match the pattern.
   218  func isLiteral(pattern string) bool {
   219  	return !strings.ContainsAny(pattern, `\*?[]`)
   220  }
   221  
   222  // set sets a consistent state for V logging.
   223  // The mutex must be held.
   224  func (vs *VState) set(l Level, filter []modulePat, setFilter bool) {
   225  	// Turn verbosity off so V will not fire while we are in transition.
   226  	vs.verbosity.set(0)
   227  	// Ditto for filter length.
   228  	atomic.StoreInt32(&vs.filterLength, 0)
   229  
   230  	// Set the new filters and wipe the pc->Level map if the filter has changed.
   231  	if setFilter {
   232  		vs.vmodule.filter = filter
   233  		vs.vmap = make(map[uintptr]Level)
   234  	}
   235  
   236  	// Things are consistent now, so enable filtering and verbosity.
   237  	// They are enabled in order opposite to that in V.
   238  	atomic.StoreInt32(&vs.filterLength, int32(len(filter)))
   239  	vs.verbosity.set(l)
   240  }
   241  
   242  // Enabled checks whether logging is enabled at the given level. This must be
   243  // called with depth=0 when the caller of enabled will do the logging and
   244  // higher values when more stack levels need to be skipped.
   245  //
   246  // The mutex will be locked only if needed.
   247  func (vs *VState) Enabled(level Level, depth int) bool {
   248  	// This function tries hard to be cheap unless there's work to do.
   249  	// The fast path is two atomic loads and compares.
   250  
   251  	// Here is a cheap but safe test to see if V logging is enabled globally.
   252  	if vs.verbosity.get() >= level {
   253  		return true
   254  	}
   255  
   256  	// It's off globally but vmodule may still be set.
   257  	// Here is another cheap but safe test to see if vmodule is enabled.
   258  	if atomic.LoadInt32(&vs.filterLength) > 0 {
   259  		// Now we need a proper lock to use the logging structure. The pcs field
   260  		// is shared so we must lock before accessing it. This is fairly expensive,
   261  		// but if V logging is enabled we're slow anyway.
   262  		vs.mu.Lock()
   263  		defer vs.mu.Unlock()
   264  		if runtime.Callers(depth+2, vs.pcs[:]) == 0 {
   265  			return false
   266  		}
   267  		// runtime.Callers returns "return PCs", but we want
   268  		// to look up the symbolic information for the call,
   269  		// so subtract 1 from the PC. runtime.CallersFrames
   270  		// would be cleaner, but allocates.
   271  		pc := vs.pcs[0] - 1
   272  		v, ok := vs.vmap[pc]
   273  		if !ok {
   274  			v = vs.setV(pc)
   275  		}
   276  		return v >= level
   277  	}
   278  	return false
   279  }
   280  
   281  // setV computes and remembers the V level for a given PC
   282  // when vmodule is enabled.
   283  // File pattern matching takes the basename of the file, stripped
   284  // of its .go suffix, and uses filepath.Match, which is a little more
   285  // general than the *? matching used in C++.
   286  // Mutex is held.
   287  func (vs *VState) setV(pc uintptr) Level {
   288  	fn := runtime.FuncForPC(pc)
   289  	file, _ := fn.FileLine(pc)
   290  	// The file is something like /a/b/c/d.go. We want just the d.
   291  	file = strings.TrimSuffix(file, ".go")
   292  	if slash := strings.LastIndex(file, "/"); slash >= 0 {
   293  		file = file[slash+1:]
   294  	}
   295  	for _, filter := range vs.vmodule.filter {
   296  		if filter.match(file) {
   297  			vs.vmap[pc] = filter.level
   298  			return filter.level
   299  		}
   300  	}
   301  	vs.vmap[pc] = 0
   302  	return 0
   303  }
   304  

View as plain text