-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlevel.go
More file actions
202 lines (187 loc) · 4.99 KB
/
Copy pathlevel.go
File metadata and controls
202 lines (187 loc) · 4.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
package loglayer
import "sync/atomic"
// LogLevel represents the severity of a log entry.
// Higher numeric values indicate higher severity.
//
// Values are non-uniformly spaced (Trace=5, Debug=10, Info=20, ...) so a
// future intermediate level (e.g. Notice between Info and Warn) can land
// without colliding. Panic sits above Fatal because panic is the most
// severe class of emission this library supports.
//
// The set is fixed: [levelIndex], [LogLevel.String], and [ParseLogLevel]
// are each switches over the seven built-ins. Replacing them with a
// registry lookup would unlock user-registered custom levels without
// changing the public API; the design is deliberately deferred until
// there's a concrete need (collision policy, ordering rules, mutability
// semantics) worth resolving.
type LogLevel int
const (
LogLevelTrace LogLevel = 5
LogLevelDebug LogLevel = 10
LogLevelInfo LogLevel = 20
LogLevelWarn LogLevel = 30
LogLevelError LogLevel = 40
LogLevelFatal LogLevel = 50
LogLevelPanic LogLevel = 60
)
const (
// numLevels is the count of distinct levels (Trace, Debug, Info, Warn,
// Error, Fatal, Panic).
numLevels = 7
// allLevelsBits is bits 0..numLevels-1 set: every level enabled.
allLevelsBits uint32 = 1<<numLevels - 1
// masterEnabledBit lives just above the per-level bits and represents the
// global on/off toggle.
masterEnabledBit uint32 = 1 << numLevels
// initialState is the default: all levels enabled, master on.
initialState = allLevelsBits | masterEnabledBit
)
// levelIndex maps a LogLevel to its slot in the levelState bitmap.
// Returns -1 for unknown levels. Switch instead of arithmetic so future
// non-uniform values stay supported without surprise.
func levelIndex(l LogLevel) int {
switch l {
case LogLevelTrace:
return 0
case LogLevelDebug:
return 1
case LogLevelInfo:
return 2
case LogLevelWarn:
return 3
case LogLevelError:
return 4
case LogLevelFatal:
return 5
case LogLevelPanic:
return 6
default:
return -1
}
}
// String returns the lowercase string name of a log level.
func (l LogLevel) String() string {
switch l {
case LogLevelTrace:
return "trace"
case LogLevelDebug:
return "debug"
case LogLevelInfo:
return "info"
case LogLevelWarn:
return "warn"
case LogLevelError:
return "error"
case LogLevelFatal:
return "fatal"
case LogLevelPanic:
return "panic"
default:
return "unknown"
}
}
// ParseLogLevel converts a string level name to a LogLevel.
// Returns LogLevelInfo and false if the name is not recognized.
func ParseLogLevel(s string) (LogLevel, bool) {
switch s {
case "trace":
return LogLevelTrace, true
case "debug":
return LogLevelDebug, true
case "info":
return LogLevelInfo, true
case "warn":
return LogLevelWarn, true
case "error":
return LogLevelError, true
case "fatal":
return LogLevelFatal, true
case "panic":
return LogLevelPanic, true
default:
return LogLevelInfo, false
}
}
// levelState tracks which levels are enabled plus the master logging switch.
//
// Stored as a single atomic.Uint32 bitmap (bits 0..4 = per-level enabled, bit
// 5 = master) so emission and runtime reconfiguration (e.g. SIGUSR1-driven
// level toggles, admin endpoints flipping debug logging) compose without
// locks. Mirrors zap.AtomicLevel.
type levelState struct {
bits atomic.Uint32
}
func newLevelState() *levelState {
s := &levelState{}
s.bits.Store(initialState)
return s
}
// clone returns an independent copy of s holding a snapshot of the current bits.
func (s *levelState) clone() *levelState {
c := &levelState{}
c.bits.Store(s.bits.Load())
return c
}
func (s *levelState) isEnabled(l LogLevel) bool {
cur := s.bits.Load()
if cur&masterEnabledBit == 0 {
return false
}
idx := levelIndex(l)
if idx < 0 {
return false
}
return cur&(1<<idx) != 0
}
// setLevel enables all levels >= l and disables all levels below l.
// No-op for unknown levels. Preserves the master enabled bit.
func (s *levelState) setLevel(l LogLevel) {
target := levelIndex(l)
if target < 0 {
return
}
var levelBits uint32
for i := 0; i < numLevels; i++ {
if i >= target {
levelBits |= 1 << i
}
}
s.update(func(old uint32) uint32 {
return (old & masterEnabledBit) | levelBits
})
}
// setEnabled toggles a single level. No-op for unknown levels.
func (s *levelState) setEnabled(l LogLevel, on bool) {
idx := levelIndex(l)
if idx < 0 {
return
}
bit := uint32(1 << idx)
s.update(func(old uint32) uint32 {
if on {
return old | bit
}
return old &^ bit
})
}
// setMaster toggles the master logging switch.
func (s *levelState) setMaster(on bool) {
s.update(func(old uint32) uint32 {
if on {
return old | masterEnabledBit
}
return old &^ masterEnabledBit
})
}
// update applies fn to the current bits and CAS-stores the result, retrying
// on contention. Lock-free; safe to call concurrently with anything that
// reads s.bits.
func (s *levelState) update(fn func(uint32) uint32) {
for {
old := s.bits.Load()
next := fn(old)
if next == old || s.bits.CompareAndSwap(old, next) {
return
}
}
}