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  }