2019-04-29 21:25:05 +00:00
|
|
|
// Copyright 2018 The gVisor Authors.
|
2018-04-27 17:37:02 +00:00
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
//
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
//
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
// limitations under the License.
|
|
|
|
|
|
|
|
// Package log implements a library for logging.
|
|
|
|
//
|
|
|
|
// This is separate from the standard logging package because logging may be a
|
|
|
|
// high-impact activity, and therefore we wanted to provide as much flexibility
|
|
|
|
// as possible in the underlying implementation.
|
|
|
|
package log
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
stdlog "log"
|
|
|
|
"os"
|
|
|
|
"runtime"
|
|
|
|
"sync"
|
|
|
|
"sync/atomic"
|
|
|
|
"syscall"
|
|
|
|
"time"
|
|
|
|
|
2019-06-13 23:49:09 +00:00
|
|
|
"gvisor.dev/gvisor/pkg/linewriter"
|
2018-04-27 17:37:02 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Level is the log level.
|
|
|
|
type Level uint32
|
|
|
|
|
|
|
|
// The following levels are fixed, and can never be changed. Since some control
|
|
|
|
// RPCs allow for changing the level as an integer, it is only possible to add
|
|
|
|
// additional levels, and the existing one cannot be removed.
|
|
|
|
const (
|
|
|
|
// Warning indicates that output should always be emitted.
|
|
|
|
Warning Level = iota
|
|
|
|
|
|
|
|
// Info indicates that output should normally be emitted.
|
|
|
|
Info
|
|
|
|
|
|
|
|
// Debug indicates that output should not normally be emitted.
|
|
|
|
Debug
|
|
|
|
)
|
|
|
|
|
2019-06-24 22:01:58 +00:00
|
|
|
func (l Level) String() string {
|
|
|
|
switch l {
|
|
|
|
case Warning:
|
|
|
|
return "Warning"
|
|
|
|
case Info:
|
|
|
|
return "Info"
|
|
|
|
case Debug:
|
|
|
|
return "Debug"
|
|
|
|
default:
|
|
|
|
return fmt.Sprintf("Invalid level: %d", l)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-27 17:37:02 +00:00
|
|
|
// Emitter is the final destination for logs.
|
|
|
|
type Emitter interface {
|
|
|
|
// Emit emits the given log statement. This allows for control over the
|
|
|
|
// timestamp used for logging.
|
|
|
|
Emit(level Level, timestamp time.Time, format string, v ...interface{})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Writer writes the output to the given writer.
|
|
|
|
type Writer struct {
|
|
|
|
// Next is where output is written.
|
|
|
|
Next io.Writer
|
|
|
|
|
|
|
|
// mu protects fields below.
|
|
|
|
mu sync.Mutex
|
|
|
|
|
|
|
|
// errors counts failures to write log messages so it can be reported
|
|
|
|
// when writer start to work again. Needs to be accessed using atomics
|
|
|
|
// to make race detector happy because it's read outside the mutex.
|
|
|
|
errors int32
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write writes out the given bytes, handling non-blocking sockets.
|
|
|
|
func (l *Writer) Write(data []byte) (int, error) {
|
|
|
|
n := 0
|
|
|
|
|
|
|
|
for n < len(data) {
|
|
|
|
w, err := l.Next.Write(data[n:])
|
|
|
|
n += w
|
|
|
|
|
|
|
|
// Is it a non-blocking socket?
|
|
|
|
if pathErr, ok := err.(*os.PathError); ok && pathErr.Err == syscall.EAGAIN {
|
|
|
|
runtime.Gosched()
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// Some other error?
|
|
|
|
if err != nil {
|
|
|
|
l.mu.Lock()
|
|
|
|
atomic.AddInt32(&l.errors, 1)
|
|
|
|
l.mu.Unlock()
|
|
|
|
return n, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Do we need to end with a '\n'?
|
|
|
|
if len(data) == 0 || data[len(data)-1] != '\n' {
|
|
|
|
l.Write([]byte{'\n'})
|
|
|
|
}
|
|
|
|
|
|
|
|
// Dirty read in case there were errors (rare).
|
|
|
|
if atomic.LoadInt32(&l.errors) > 0 {
|
|
|
|
l.mu.Lock()
|
|
|
|
defer l.mu.Unlock()
|
|
|
|
|
|
|
|
// Recheck condition under lock.
|
|
|
|
if e := atomic.LoadInt32(&l.errors); e > 0 {
|
|
|
|
msg := fmt.Sprintf("\n*** Dropped %d log messages ***\n", e)
|
|
|
|
if _, err := l.Next.Write([]byte(msg)); err == nil {
|
|
|
|
atomic.StoreInt32(&l.errors, 0)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return n, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Emit emits the message.
|
|
|
|
func (l *Writer) Emit(level Level, timestamp time.Time, format string, args ...interface{}) {
|
|
|
|
fmt.Fprintf(l, format, args...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// MultiEmitter is an emitter that emits to multiple Emitters.
|
|
|
|
type MultiEmitter []Emitter
|
|
|
|
|
|
|
|
// Emit emits to all emitters.
|
|
|
|
func (m MultiEmitter) Emit(level Level, timestamp time.Time, format string, v ...interface{}) {
|
|
|
|
for _, e := range m {
|
|
|
|
e.Emit(level, timestamp, format, v...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestLogger is implemented by testing.T and testing.B.
|
|
|
|
type TestLogger interface {
|
|
|
|
Logf(format string, v ...interface{})
|
|
|
|
}
|
|
|
|
|
|
|
|
// TestEmitter may be used for wrapping tests.
|
|
|
|
type TestEmitter struct {
|
|
|
|
TestLogger
|
|
|
|
}
|
|
|
|
|
|
|
|
// Emit emits to the TestLogger.
|
|
|
|
func (t TestEmitter) Emit(level Level, timestamp time.Time, format string, v ...interface{}) {
|
|
|
|
t.Logf(format, v...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Logger is a high-level logging interface. It is in fact, not used within the
|
|
|
|
// log package. Rather it is provided for others to provide contextual loggers
|
|
|
|
// that may append some addition information to log statement. BasicLogger
|
2018-05-03 21:05:25 +00:00
|
|
|
// satisfies this interface, and may be passed around as a Logger.
|
2018-04-27 17:37:02 +00:00
|
|
|
type Logger interface {
|
|
|
|
// Debugf logs a debug statement.
|
|
|
|
Debugf(format string, v ...interface{})
|
|
|
|
|
|
|
|
// Infof logs at an info level.
|
|
|
|
Infof(format string, v ...interface{})
|
|
|
|
|
|
|
|
// Warningf logs at a warning level.
|
|
|
|
Warningf(format string, v ...interface{})
|
|
|
|
|
|
|
|
// IsLogging returns true iff this level is being logged. This may be
|
|
|
|
// used to short-circuit expensive operations for debugging calls.
|
|
|
|
IsLogging(level Level) bool
|
|
|
|
}
|
|
|
|
|
|
|
|
// BasicLogger is the default implementation of Logger.
|
|
|
|
type BasicLogger struct {
|
|
|
|
Level
|
|
|
|
Emitter
|
|
|
|
}
|
|
|
|
|
|
|
|
// Debugf implements logger.Debugf.
|
|
|
|
func (l *BasicLogger) Debugf(format string, v ...interface{}) {
|
|
|
|
if l.IsLogging(Debug) {
|
|
|
|
l.Emit(Debug, time.Now(), format, v...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Infof implements logger.Infof.
|
|
|
|
func (l *BasicLogger) Infof(format string, v ...interface{}) {
|
|
|
|
if l.IsLogging(Info) {
|
|
|
|
l.Emit(Info, time.Now(), format, v...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Warningf implements logger.Warningf.
|
|
|
|
func (l *BasicLogger) Warningf(format string, v ...interface{}) {
|
|
|
|
if l.IsLogging(Warning) {
|
|
|
|
l.Emit(Warning, time.Now(), format, v...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsLogging implements logger.IsLogging.
|
|
|
|
func (l *BasicLogger) IsLogging(level Level) bool {
|
|
|
|
return atomic.LoadUint32((*uint32)(&l.Level)) >= uint32(level)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetLevel sets the logging level.
|
|
|
|
func (l *BasicLogger) SetLevel(level Level) {
|
|
|
|
atomic.StoreUint32((*uint32)(&l.Level), uint32(level))
|
|
|
|
}
|
|
|
|
|
|
|
|
// logMu protects Log below. We use atomic operations to read the value, but
|
|
|
|
// updates require logMu to ensure consistency.
|
|
|
|
var logMu sync.Mutex
|
|
|
|
|
|
|
|
// log is the default logger.
|
|
|
|
var log atomic.Value
|
|
|
|
|
2019-06-27 21:23:29 +00:00
|
|
|
// Log retrieves the global logger.
|
2018-04-27 17:37:02 +00:00
|
|
|
func Log() *BasicLogger {
|
|
|
|
return log.Load().(*BasicLogger)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetTarget sets the log target.
|
|
|
|
//
|
|
|
|
// This is not thread safe and shouldn't be called concurrently with any
|
|
|
|
// logging calls.
|
|
|
|
func SetTarget(target Emitter) {
|
|
|
|
logMu.Lock()
|
|
|
|
defer logMu.Unlock()
|
|
|
|
oldLog := Log()
|
|
|
|
log.Store(&BasicLogger{Level: oldLog.Level, Emitter: target})
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetLevel sets the log level.
|
|
|
|
func SetLevel(newLevel Level) {
|
|
|
|
Log().SetLevel(newLevel)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Debugf logs to the global logger.
|
|
|
|
func Debugf(format string, v ...interface{}) {
|
|
|
|
Log().Debugf(format, v...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Infof logs to the global logger.
|
|
|
|
func Infof(format string, v ...interface{}) {
|
|
|
|
Log().Infof(format, v...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Warningf logs to the global logger.
|
|
|
|
func Warningf(format string, v ...interface{}) {
|
|
|
|
Log().Warningf(format, v...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// defaultStackSize is the default buffer size to allocate for stack traces.
|
|
|
|
const defaultStackSize = 1 << 16 // 64KB
|
|
|
|
|
|
|
|
// maxStackSize is the maximum buffer size to allocate for stack traces.
|
|
|
|
const maxStackSize = 1 << 26 // 64MB
|
|
|
|
|
2018-06-20 20:30:39 +00:00
|
|
|
// Stacks returns goroutine stacks, like panic.
|
|
|
|
func Stacks(all bool) []byte {
|
2018-04-27 17:37:02 +00:00
|
|
|
var trace []byte
|
|
|
|
for s := defaultStackSize; s <= maxStackSize; s *= 4 {
|
|
|
|
trace = make([]byte, s)
|
|
|
|
nbytes := runtime.Stack(trace, all)
|
|
|
|
if nbytes == s {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
return trace[:nbytes]
|
|
|
|
}
|
|
|
|
trace = append(trace, []byte("\n\n...<too large, truncated>")...)
|
|
|
|
return trace
|
|
|
|
}
|
|
|
|
|
|
|
|
// Traceback logs the given message and dumps a stacktrace of the current
|
|
|
|
// goroutine.
|
|
|
|
//
|
|
|
|
// This will be print a traceback, tb, as Warningf(format+":\n%s", v..., tb).
|
|
|
|
func Traceback(format string, v ...interface{}) {
|
2018-06-20 20:30:39 +00:00
|
|
|
v = append(v, Stacks(false))
|
2018-04-27 17:37:02 +00:00
|
|
|
Warningf(format+":\n%s", v...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// TracebackAll logs the given message and dumps a stacktrace of all goroutines.
|
|
|
|
//
|
|
|
|
// This will be print a traceback, tb, as Warningf(format+":\n%s", v..., tb).
|
|
|
|
func TracebackAll(format string, v ...interface{}) {
|
2018-06-20 20:30:39 +00:00
|
|
|
v = append(v, Stacks(true))
|
2018-04-27 17:37:02 +00:00
|
|
|
Warningf(format+":\n%s", v...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// IsLogging returns whether the global logger is logging.
|
|
|
|
func IsLogging(level Level) bool {
|
|
|
|
return Log().IsLogging(level)
|
|
|
|
}
|
|
|
|
|
|
|
|
// CopyStandardLogTo redirects the stdlib log package global output to the global
|
|
|
|
// logger for the specified level.
|
|
|
|
func CopyStandardLogTo(l Level) error {
|
|
|
|
var f func(string, ...interface{})
|
|
|
|
|
|
|
|
switch l {
|
|
|
|
case Debug:
|
|
|
|
f = Debugf
|
|
|
|
case Info:
|
|
|
|
f = Infof
|
|
|
|
case Warning:
|
|
|
|
f = Warningf
|
|
|
|
default:
|
|
|
|
return fmt.Errorf("Unknown log level %v", l)
|
|
|
|
}
|
|
|
|
|
|
|
|
stdlog.SetOutput(linewriter.NewWriter(func(p []byte) {
|
|
|
|
// We must not retain p, but log formatting is not required to
|
|
|
|
// be synchronous (though the in-package implementations are),
|
|
|
|
// so we must make a copy.
|
|
|
|
b := make([]byte, len(p))
|
|
|
|
copy(b, p)
|
|
|
|
|
|
|
|
f("%s", b)
|
|
|
|
}))
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
// Store the initial value for the log.
|
|
|
|
log.Store(&BasicLogger{Level: Info, Emitter: GoogleEmitter{&Writer{Next: os.Stderr}}})
|
|
|
|
}
|