golang.org/x/exp@v0.0.0-20240506185415-9bf2ced13842/slog/level.go (about) 1 // Copyright 2022 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package slog 6 7 import ( 8 "errors" 9 "fmt" 10 "strconv" 11 "strings" 12 "sync/atomic" 13 ) 14 15 // A Level is the importance or severity of a log event. 16 // The higher the level, the more important or severe the event. 17 type Level int 18 19 // Level numbers are inherently arbitrary, 20 // but we picked them to satisfy three constraints. 21 // Any system can map them to another numbering scheme if it wishes. 22 // 23 // First, we wanted the default level to be Info, Since Levels are ints, Info is 24 // the default value for int, zero. 25 // 26 27 // Second, we wanted to make it easy to use levels to specify logger verbosity. 28 // Since a larger level means a more severe event, a logger that accepts events 29 // with smaller (or more negative) level means a more verbose logger. Logger 30 // verbosity is thus the negation of event severity, and the default verbosity 31 // of 0 accepts all events at least as severe as INFO. 32 // 33 // Third, we wanted some room between levels to accommodate schemes with named 34 // levels between ours. For example, Google Cloud Logging defines a Notice level 35 // between Info and Warn. Since there are only a few of these intermediate 36 // levels, the gap between the numbers need not be large. Our gap of 4 matches 37 // OpenTelemetry's mapping. Subtracting 9 from an OpenTelemetry level in the 38 // DEBUG, INFO, WARN and ERROR ranges converts it to the corresponding slog 39 // Level range. OpenTelemetry also has the names TRACE and FATAL, which slog 40 // does not. But those OpenTelemetry levels can still be represented as slog 41 // Levels by using the appropriate integers. 42 // 43 // Names for common levels. 44 const ( 45 LevelDebug Level = -4 46 LevelInfo Level = 0 47 LevelWarn Level = 4 48 LevelError Level = 8 49 ) 50 51 // String returns a name for the level. 52 // If the level has a name, then that name 53 // in uppercase is returned. 54 // If the level is between named values, then 55 // an integer is appended to the uppercased name. 56 // Examples: 57 // 58 // LevelWarn.String() => "WARN" 59 // (LevelInfo+2).String() => "INFO+2" 60 func (l Level) String() string { 61 str := func(base string, val Level) string { 62 if val == 0 { 63 return base 64 } 65 return fmt.Sprintf("%s%+d", base, val) 66 } 67 68 switch { 69 case l < LevelInfo: 70 return str("DEBUG", l-LevelDebug) 71 case l < LevelWarn: 72 return str("INFO", l-LevelInfo) 73 case l < LevelError: 74 return str("WARN", l-LevelWarn) 75 default: 76 return str("ERROR", l-LevelError) 77 } 78 } 79 80 // MarshalJSON implements [encoding/json.Marshaler] 81 // by quoting the output of [Level.String]. 82 func (l Level) MarshalJSON() ([]byte, error) { 83 // AppendQuote is sufficient for JSON-encoding all Level strings. 84 // They don't contain any runes that would produce invalid JSON 85 // when escaped. 86 return strconv.AppendQuote(nil, l.String()), nil 87 } 88 89 // UnmarshalJSON implements [encoding/json.Unmarshaler] 90 // It accepts any string produced by [Level.MarshalJSON], 91 // ignoring case. 92 // It also accepts numeric offsets that would result in a different string on 93 // output. For example, "Error-8" would marshal as "INFO". 94 func (l *Level) UnmarshalJSON(data []byte) error { 95 s, err := strconv.Unquote(string(data)) 96 if err != nil { 97 return err 98 } 99 return l.parse(s) 100 } 101 102 // MarshalText implements [encoding.TextMarshaler] 103 // by calling [Level.String]. 104 func (l Level) MarshalText() ([]byte, error) { 105 return []byte(l.String()), nil 106 } 107 108 // UnmarshalText implements [encoding.TextUnmarshaler]. 109 // It accepts any string produced by [Level.MarshalText], 110 // ignoring case. 111 // It also accepts numeric offsets that would result in a different string on 112 // output. For example, "Error-8" would marshal as "INFO". 113 func (l *Level) UnmarshalText(data []byte) error { 114 return l.parse(string(data)) 115 } 116 117 func (l *Level) parse(s string) (err error) { 118 defer func() { 119 if err != nil { 120 err = fmt.Errorf("slog: level string %q: %w", s, err) 121 } 122 }() 123 124 name := s 125 offset := 0 126 if i := strings.IndexAny(s, "+-"); i >= 0 { 127 name = s[:i] 128 offset, err = strconv.Atoi(s[i:]) 129 if err != nil { 130 return err 131 } 132 } 133 switch strings.ToUpper(name) { 134 case "DEBUG": 135 *l = LevelDebug 136 case "INFO": 137 *l = LevelInfo 138 case "WARN": 139 *l = LevelWarn 140 case "ERROR": 141 *l = LevelError 142 default: 143 return errors.New("unknown name") 144 } 145 *l += Level(offset) 146 return nil 147 } 148 149 // Level returns the receiver. 150 // It implements Leveler. 151 func (l Level) Level() Level { return l } 152 153 // A LevelVar is a Level variable, to allow a Handler level to change 154 // dynamically. 155 // It implements Leveler as well as a Set method, 156 // and it is safe for use by multiple goroutines. 157 // The zero LevelVar corresponds to LevelInfo. 158 type LevelVar struct { 159 val atomic.Int64 160 } 161 162 // Level returns v's level. 163 func (v *LevelVar) Level() Level { 164 return Level(int(v.val.Load())) 165 } 166 167 // Set sets v's level to l. 168 func (v *LevelVar) Set(l Level) { 169 v.val.Store(int64(l)) 170 } 171 172 func (v *LevelVar) String() string { 173 return fmt.Sprintf("LevelVar(%s)", v.Level()) 174 } 175 176 // MarshalText implements [encoding.TextMarshaler] 177 // by calling [Level.MarshalText]. 178 func (v *LevelVar) MarshalText() ([]byte, error) { 179 return v.Level().MarshalText() 180 } 181 182 // UnmarshalText implements [encoding.TextUnmarshaler] 183 // by calling [Level.UnmarshalText]. 184 func (v *LevelVar) UnmarshalText(data []byte) error { 185 var l Level 186 if err := l.UnmarshalText(data); err != nil { 187 return err 188 } 189 v.Set(l) 190 return nil 191 } 192 193 // A Leveler provides a Level value. 194 // 195 // As Level itself implements Leveler, clients typically supply 196 // a Level value wherever a Leveler is needed, such as in HandlerOptions. 197 // Clients who need to vary the level dynamically can provide a more complex 198 // Leveler implementation such as *LevelVar. 199 type Leveler interface { 200 Level() Level 201 }