github.com/sagernet/gvisor@v0.0.0-20240428053021-e691de28565f/pkg/log/log.go (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package log implements a library for logging. 16 // 17 // This is separate from the standard logging package because logging may be a 18 // high-impact activity, and therefore we wanted to provide as much flexibility 19 // as possible in the underlying implementation. 20 // 21 // Note that logging should still be considered high-impact, and should not be 22 // done in the hot path. If necessary, logging statements should be protected 23 // with guards regarding the logging level. For example, 24 // 25 // if log.IsLogging(log.Debug) { 26 // log.Debugf(...) 27 // } 28 // 29 // This is because the log.Debugf(...) statement alone will generate a 30 // significant amount of garbage and churn in many cases, even if no log 31 // message is ultimately emitted. 32 // 33 // +checkalignedignore 34 package log 35 36 import ( 37 "fmt" 38 singAtomic "github.com/sagernet/sing/common/atomic" 39 "io" 40 stdlog "log" 41 "os" 42 "regexp" 43 "runtime" 44 "sync/atomic" 45 "time" 46 47 "github.com/sagernet/gvisor/pkg/linewriter" 48 "github.com/sagernet/gvisor/pkg/sync" 49 ) 50 51 // Level is the log level. 52 type Level uint32 53 54 // The following levels are fixed, and can never be changed. Since some control 55 // RPCs allow for changing the level as an integer, it is only possible to add 56 // additional levels, and the existing one cannot be removed. 57 const ( 58 // Warning indicates that output should always be emitted. 59 Warning Level = iota 60 61 // Info indicates that output should normally be emitted. 62 Info 63 64 // Debug indicates that output should not normally be emitted. 65 Debug 66 ) 67 68 func (l Level) String() string { 69 switch l { 70 case Warning: 71 return "Warning" 72 case Info: 73 return "Info" 74 case Debug: 75 return "Debug" 76 default: 77 return fmt.Sprintf("Invalid level: %d", l) 78 } 79 } 80 81 // Emitter is the final destination for logs. 82 type Emitter interface { 83 // Emit emits the given log statement. This allows for control over the 84 // timestamp used for logging. 85 Emit(depth int, level Level, timestamp time.Time, format string, v ...any) 86 } 87 88 // Writer writes the output to the given writer. 89 type Writer struct { 90 // Next is where output is written. 91 Next io.Writer 92 93 // mu protects fields below. 94 mu sync.Mutex 95 96 // errors counts failures to write log messages so it can be reported 97 // when writer start to work again. Needs to be accessed using atomics 98 // to make race detector happy because it's read outside the mutex. 99 // +checklocks 100 atomicErrors int32 101 } 102 103 // Write writes out the given bytes, handling non-blocking sockets. 104 func (l *Writer) Write(data []byte) (int, error) { 105 n := 0 106 107 for n < len(data) { 108 w, err := l.Next.Write(data[n:]) 109 n += w 110 111 // Is it a non-blocking socket? 112 if pathErr, ok := err.(*os.PathError); ok && pathErr.Timeout() { 113 runtime.Gosched() 114 continue 115 } 116 117 // Some other error? 118 if err != nil { 119 l.mu.Lock() 120 atomic.AddInt32(&l.atomicErrors, 1) 121 l.mu.Unlock() 122 return n, err 123 } 124 } 125 126 // Do we need to end with a '\n'? 127 if len(data) == 0 || data[len(data)-1] != '\n' { 128 l.Write([]byte{'\n'}) 129 } 130 131 // Dirty read in case there were errors (rare). 132 if atomic.LoadInt32(&l.atomicErrors) > 0 { 133 l.mu.Lock() 134 defer l.mu.Unlock() 135 136 // Recheck condition under lock. 137 if e := atomic.LoadInt32(&l.atomicErrors); e > 0 { 138 msg := fmt.Sprintf("\n*** Dropped %d log messages ***\n", e) 139 if _, err := l.Next.Write([]byte(msg)); err == nil { 140 atomic.StoreInt32(&l.atomicErrors, 0) 141 } 142 } 143 } 144 145 return n, nil 146 } 147 148 // Emit emits the message. 149 func (l *Writer) Emit(_ int, _ Level, _ time.Time, format string, args ...any) { 150 fmt.Fprintf(l, format, args...) 151 } 152 153 // MultiEmitter is an emitter that emits to multiple Emitters. 154 type MultiEmitter []Emitter 155 156 // Emit emits to all emitters. 157 func (m *MultiEmitter) Emit(depth int, level Level, timestamp time.Time, format string, v ...any) { 158 for _, e := range *m { 159 e.Emit(1+depth, level, timestamp, format, v...) 160 } 161 } 162 163 // TestLogger is implemented by testing.T and testing.B. 164 type TestLogger interface { 165 Logf(format string, v ...any) 166 } 167 168 // TestEmitter may be used for wrapping tests. 169 type TestEmitter struct { 170 TestLogger 171 } 172 173 // Emit emits to the TestLogger. 174 func (t *TestEmitter) Emit(_ int, level Level, timestamp time.Time, format string, v ...any) { 175 t.Logf(format, v...) 176 } 177 178 // Logger is a high-level logging interface. It is in fact, not used within the 179 // log package. Rather it is provided for others to provide contextual loggers 180 // that may append some addition information to log statement. BasicLogger 181 // satisfies this interface, and may be passed around as a Logger. 182 type Logger interface { 183 // Debugf logs a debug statement. 184 Debugf(format string, v ...any) 185 186 // Infof logs at an info level. 187 Infof(format string, v ...any) 188 189 // Warningf logs at a warning level. 190 Warningf(format string, v ...any) 191 192 // IsLogging returns true iff this level is being logged. This may be 193 // used to short-circuit expensive operations for debugging calls. 194 IsLogging(level Level) bool 195 } 196 197 // BasicLogger is the default implementation of Logger. 198 type BasicLogger struct { 199 Level 200 Emitter 201 } 202 203 // Debugf implements logger.Debugf. 204 func (l *BasicLogger) Debugf(format string, v ...any) { 205 l.DebugfAtDepth(1, format, v...) 206 } 207 208 // Infof implements logger.Infof. 209 func (l *BasicLogger) Infof(format string, v ...any) { 210 l.InfofAtDepth(1, format, v...) 211 } 212 213 // Warningf implements logger.Warningf. 214 func (l *BasicLogger) Warningf(format string, v ...any) { 215 l.WarningfAtDepth(1, format, v...) 216 } 217 218 // DebugfAtDepth logs at a specific depth. 219 func (l *BasicLogger) DebugfAtDepth(depth int, format string, v ...any) { 220 if l.IsLogging(Debug) { 221 l.Emit(1+depth, Debug, time.Now(), format, v...) 222 } 223 } 224 225 // InfofAtDepth logs at a specific depth. 226 func (l *BasicLogger) InfofAtDepth(depth int, format string, v ...any) { 227 if l.IsLogging(Info) { 228 l.Emit(1+depth, Info, time.Now(), format, v...) 229 } 230 } 231 232 // WarningfAtDepth logs at a specific depth. 233 func (l *BasicLogger) WarningfAtDepth(depth int, format string, v ...any) { 234 if l.IsLogging(Warning) { 235 l.Emit(1+depth, Warning, time.Now(), format, v...) 236 } 237 } 238 239 // IsLogging implements logger.IsLogging. 240 func (l *BasicLogger) IsLogging(level Level) bool { 241 return atomic.LoadUint32((*uint32)(&l.Level)) >= uint32(level) 242 } 243 244 // SetLevel sets the logging level. 245 func (l *BasicLogger) SetLevel(level Level) { 246 atomic.StoreUint32((*uint32)(&l.Level), uint32(level)) 247 } 248 249 // logMu protects Log below. We use atomic operations to read the value, but 250 // updates require logMu to ensure consistency. 251 var logMu sync.Mutex 252 253 // log is the default logger. 254 var log singAtomic.Pointer[BasicLogger] 255 256 // Log retrieves the global logger. 257 func Log() *BasicLogger { 258 return log.Load() 259 } 260 261 // SetTarget sets the log target. 262 // 263 // This is not thread safe and shouldn't be called concurrently with any 264 // logging calls. 265 // 266 // SetTarget should be called before any instances of log.Log() to avoid race conditions 267 func SetTarget(target Emitter) { 268 logMu.Lock() 269 defer logMu.Unlock() 270 oldLog := Log() 271 log.Store(&BasicLogger{Level: oldLog.Level, Emitter: target}) 272 } 273 274 // SetLevel sets the log level. 275 func SetLevel(newLevel Level) { 276 Log().SetLevel(newLevel) 277 } 278 279 // Debugf logs to the global logger. 280 func Debugf(format string, v ...any) { 281 Log().DebugfAtDepth(1, format, v...) 282 } 283 284 // Infof logs to the global logger. 285 func Infof(format string, v ...any) { 286 Log().InfofAtDepth(1, format, v...) 287 } 288 289 // Warningf logs to the global logger. 290 func Warningf(format string, v ...any) { 291 Log().WarningfAtDepth(1, format, v...) 292 } 293 294 // DebugfAtDepth logs to the global logger. 295 func DebugfAtDepth(depth int, format string, v ...any) { 296 Log().DebugfAtDepth(1+depth, format, v...) 297 } 298 299 // InfofAtDepth logs to the global logger. 300 func InfofAtDepth(depth int, format string, v ...any) { 301 Log().InfofAtDepth(1+depth, format, v...) 302 } 303 304 // WarningfAtDepth logs to the global logger. 305 func WarningfAtDepth(depth int, format string, v ...any) { 306 Log().WarningfAtDepth(1+depth, format, v...) 307 } 308 309 // defaultStackSize is the default buffer size to allocate for stack traces. 310 const defaultStackSize = 1 << 16 // 64KB 311 312 // maxStackSize is the maximum buffer size to allocate for stack traces. 313 const maxStackSize = 1 << 26 // 64MB 314 315 // Stacks returns goroutine stacks, like panic. 316 func Stacks(all bool) []byte { 317 var trace []byte 318 for s := defaultStackSize; s <= maxStackSize; s *= 4 { 319 trace = make([]byte, s) 320 nbytes := runtime.Stack(trace, all) 321 if nbytes == s { 322 continue 323 } 324 return trace[:nbytes] 325 } 326 trace = append(trace, []byte("\n\n...<too large, truncated>")...) 327 return trace 328 } 329 330 // stackRegexp matches one level within a stack trace. 331 var stackRegexp = regexp.MustCompile("(?m)^\\S+\\(.*\\)$\\r?\\n^\\t\\S+:\\d+.*$\\r?\\n") 332 333 // LocalStack returns the local goroutine stack, excluding the top N entries. 334 // LocalStack's own entry is excluded by default and does not need to be counted in excludeTopN. 335 func LocalStack(excludeTopN int) []byte { 336 replaceNext := excludeTopN + 1 337 return stackRegexp.ReplaceAllFunc(Stacks(false), func(s []byte) []byte { 338 if replaceNext > 0 { 339 replaceNext-- 340 return nil 341 } 342 return s 343 }) 344 } 345 346 // Traceback logs the given message and dumps a stacktrace of the current 347 // goroutine. 348 // 349 // This will be print a traceback, tb, as Warningf(format+":\n%s", v..., tb). 350 func Traceback(format string, v ...any) { 351 v = append(v, Stacks(false)) 352 Warningf(format+":\n%s", v...) 353 } 354 355 // TracebackAll logs the given message and dumps a stacktrace of all goroutines. 356 // 357 // This will be print a traceback, tb, as Warningf(format+":\n%s", v..., tb). 358 func TracebackAll(format string, v ...any) { 359 v = append(v, Stacks(true)) 360 Warningf(format+":\n%s", v...) 361 } 362 363 // IsLogging returns whether the global logger is logging. 364 func IsLogging(level Level) bool { 365 return Log().IsLogging(level) 366 } 367 368 // CopyStandardLogTo redirects the stdlib log package global output to the global 369 // logger for the specified level. 370 func CopyStandardLogTo(l Level) error { 371 var f func(string, ...any) 372 373 switch l { 374 case Debug: 375 f = Debugf 376 case Info: 377 f = Infof 378 case Warning: 379 f = Warningf 380 default: 381 return fmt.Errorf("unknown log level %v", l) 382 } 383 384 stdlog.SetOutput(linewriter.NewWriter(func(p []byte) { 385 // We must not retain p, but log formatting is not required to 386 // be synchronous (though the in-package implementations are), 387 // so we must make a copy. 388 b := make([]byte, len(p)) 389 copy(b, p) 390 391 f("%s", b) 392 })) 393 394 return nil 395 } 396 397 func init() { 398 // Store the initial value for the log. 399 log.Store(&BasicLogger{Level: Info, Emitter: GoogleEmitter{&Writer{Next: os.Stderr}}}) 400 }