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