level.go raw

   1  // Copyright (c) 2016 Uber Technologies, Inc.
   2  //
   3  // Permission is hereby granted, free of charge, to any person obtaining a copy
   4  // of this software and associated documentation files (the "Software"), to deal
   5  // in the Software without restriction, including without limitation the rights
   6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   7  // copies of the Software, and to permit persons to whom the Software is
   8  // furnished to do so, subject to the following conditions:
   9  //
  10  // The above copyright notice and this permission notice shall be included in
  11  // all copies or substantial portions of the Software.
  12  //
  13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  19  // THE SOFTWARE.
  20  
  21  package zapcore
  22  
  23  import (
  24  	"bytes"
  25  	"errors"
  26  	"fmt"
  27  )
  28  
  29  var errUnmarshalNilLevel = errors.New("can't unmarshal a nil *Level")
  30  
  31  // A Level is a logging priority. Higher levels are more important.
  32  type Level int8
  33  
  34  const (
  35  	// DebugLevel logs are typically voluminous, and are usually disabled in
  36  	// production.
  37  	DebugLevel Level = iota - 1
  38  	// InfoLevel is the default logging priority.
  39  	InfoLevel
  40  	// WarnLevel logs are more important than Info, but don't need individual
  41  	// human review.
  42  	WarnLevel
  43  	// ErrorLevel logs are high-priority. If an application is running smoothly,
  44  	// it shouldn't generate any error-level logs.
  45  	ErrorLevel
  46  	// DPanicLevel logs are particularly important errors. In development the
  47  	// logger panics after writing the message.
  48  	DPanicLevel
  49  	// PanicLevel logs a message, then panics.
  50  	PanicLevel
  51  	// FatalLevel logs a message, then calls os.Exit(1).
  52  	FatalLevel
  53  
  54  	_minLevel = DebugLevel
  55  	_maxLevel = FatalLevel
  56  
  57  	// InvalidLevel is an invalid value for Level.
  58  	//
  59  	// Core implementations may panic if they see messages of this level.
  60  	InvalidLevel = _maxLevel + 1
  61  )
  62  
  63  // ParseLevel parses a level based on the lower-case or all-caps ASCII
  64  // representation of the log level. If the provided ASCII representation is
  65  // invalid an error is returned.
  66  //
  67  // This is particularly useful when dealing with text input to configure log
  68  // levels.
  69  func ParseLevel(text string) (Level, error) {
  70  	var level Level
  71  	err := level.UnmarshalText([]byte(text))
  72  	return level, err
  73  }
  74  
  75  type leveledEnabler interface {
  76  	LevelEnabler
  77  
  78  	Level() Level
  79  }
  80  
  81  // LevelOf reports the minimum enabled log level for the given LevelEnabler
  82  // from Zap's supported log levels, or [InvalidLevel] if none of them are
  83  // enabled.
  84  //
  85  // A LevelEnabler may implement a 'Level() Level' method to override the
  86  // behavior of this function.
  87  //
  88  //	func (c *core) Level() Level {
  89  //		return c.currentLevel
  90  //	}
  91  //
  92  // It is recommended that [Core] implementations that wrap other cores use
  93  // LevelOf to retrieve the level of the wrapped core. For example,
  94  //
  95  //	func (c *coreWrapper) Level() Level {
  96  //		return zapcore.LevelOf(c.wrappedCore)
  97  //	}
  98  func LevelOf(enab LevelEnabler) Level {
  99  	if lvler, ok := enab.(leveledEnabler); ok {
 100  		return lvler.Level()
 101  	}
 102  
 103  	for lvl := _minLevel; lvl <= _maxLevel; lvl++ {
 104  		if enab.Enabled(lvl) {
 105  			return lvl
 106  		}
 107  	}
 108  
 109  	return InvalidLevel
 110  }
 111  
 112  // String returns a lower-case ASCII representation of the log level.
 113  func (l Level) String() string {
 114  	switch l {
 115  	case DebugLevel:
 116  		return "debug"
 117  	case InfoLevel:
 118  		return "info"
 119  	case WarnLevel:
 120  		return "warn"
 121  	case ErrorLevel:
 122  		return "error"
 123  	case DPanicLevel:
 124  		return "dpanic"
 125  	case PanicLevel:
 126  		return "panic"
 127  	case FatalLevel:
 128  		return "fatal"
 129  	default:
 130  		return fmt.Sprintf("Level(%d)", l)
 131  	}
 132  }
 133  
 134  // CapitalString returns an all-caps ASCII representation of the log level.
 135  func (l Level) CapitalString() string {
 136  	// Printing levels in all-caps is common enough that we should export this
 137  	// functionality.
 138  	switch l {
 139  	case DebugLevel:
 140  		return "DEBUG"
 141  	case InfoLevel:
 142  		return "INFO"
 143  	case WarnLevel:
 144  		return "WARN"
 145  	case ErrorLevel:
 146  		return "ERROR"
 147  	case DPanicLevel:
 148  		return "DPANIC"
 149  	case PanicLevel:
 150  		return "PANIC"
 151  	case FatalLevel:
 152  		return "FATAL"
 153  	default:
 154  		return fmt.Sprintf("LEVEL(%d)", l)
 155  	}
 156  }
 157  
 158  // MarshalText marshals the Level to text. Note that the text representation
 159  // drops the -Level suffix (see example).
 160  func (l Level) MarshalText() ([]byte, error) {
 161  	return []byte(l.String()), nil
 162  }
 163  
 164  // UnmarshalText unmarshals text to a level. Like MarshalText, UnmarshalText
 165  // expects the text representation of a Level to drop the -Level suffix (see
 166  // example).
 167  //
 168  // In particular, this makes it easy to configure logging levels using YAML,
 169  // TOML, or JSON files.
 170  func (l *Level) UnmarshalText(text []byte) error {
 171  	if l == nil {
 172  		return errUnmarshalNilLevel
 173  	}
 174  	if !l.unmarshalText(text) && !l.unmarshalText(bytes.ToLower(text)) {
 175  		return fmt.Errorf("unrecognized level: %q", text)
 176  	}
 177  	return nil
 178  }
 179  
 180  func (l *Level) unmarshalText(text []byte) bool {
 181  	switch string(text) {
 182  	case "debug", "DEBUG":
 183  		*l = DebugLevel
 184  	case "info", "INFO", "": // make the zero value useful
 185  		*l = InfoLevel
 186  	case "warn", "WARN":
 187  		*l = WarnLevel
 188  	case "error", "ERROR":
 189  		*l = ErrorLevel
 190  	case "dpanic", "DPANIC":
 191  		*l = DPanicLevel
 192  	case "panic", "PANIC":
 193  		*l = PanicLevel
 194  	case "fatal", "FATAL":
 195  		*l = FatalLevel
 196  	default:
 197  		return false
 198  	}
 199  	return true
 200  }
 201  
 202  // Set sets the level for the flag.Value interface.
 203  func (l *Level) Set(s string) error {
 204  	return l.UnmarshalText([]byte(s))
 205  }
 206  
 207  // Get gets the level for the flag.Getter interface.
 208  func (l *Level) Get() interface{} {
 209  	return *l
 210  }
 211  
 212  // Enabled returns true if the given level is at or above this level.
 213  func (l Level) Enabled(lvl Level) bool {
 214  	return lvl >= l
 215  }
 216  
 217  // LevelEnabler decides whether a given logging level is enabled when logging a
 218  // message.
 219  //
 220  // Enablers are intended to be used to implement deterministic filters;
 221  // concerns like sampling are better implemented as a Core.
 222  //
 223  // Each concrete Level value implements a static LevelEnabler which returns
 224  // true for itself and all higher logging levels. For example WarnLevel.Enabled()
 225  // will return true for WarnLevel, ErrorLevel, DPanicLevel, PanicLevel, and
 226  // FatalLevel, but return false for InfoLevel and DebugLevel.
 227  type LevelEnabler interface {
 228  	Enabled(Level) bool
 229  }
 230