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  }