vitess.io/vitess@v0.16.2/go/vt/logutil/logger.go (about)

     1  /*
     2  Copyright 2019 The Vitess Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package logutil
    18  
    19  import (
    20  	"bytes"
    21  	"fmt"
    22  	"io"
    23  	"runtime"
    24  	"strings"
    25  	"sync"
    26  	"time"
    27  
    28  	logutilpb "vitess.io/vitess/go/vt/proto/logutil"
    29  )
    30  
    31  // Logger defines the interface to use for our logging interface.
    32  // All methods should be thread safe (i.e. multiple go routines can
    33  // call these methods simultaneously).
    34  type Logger interface {
    35  	// Infof logs at INFO level. A newline is appended if missing.
    36  	Infof(format string, v ...any)
    37  	// Warningf logs at WARNING level. A newline is appended if missing.
    38  	Warningf(format string, v ...any)
    39  	// Errorf logs at ERROR level. A newline is appended if missing.
    40  	Errorf(format string, v ...any)
    41  	// Errorf2 logs an error with stack traces at ERROR level. A newline is appended if missing.
    42  	Errorf2(e error, message string, v ...any)
    43  
    44  	Error(e error)
    45  	// Printf will just display information on stdout when possible.
    46  	// No newline is appended.
    47  	Printf(format string, v ...any)
    48  
    49  	// InfoDepth allows call frame depth to be adjusted when logging to INFO.
    50  	InfoDepth(depth int, s string)
    51  	// WarningDepth allows call frame depth to be adjusted when logging to WARNING.
    52  	WarningDepth(depth int, s string)
    53  	// ErrorDepth allows call frame depth to be adjusted when logging to ERROR.
    54  	ErrorDepth(depth int, s string)
    55  }
    56  
    57  // EventToBuffer formats an individual Event into a buffer, without the
    58  // final '\n'
    59  func EventToBuffer(event *logutilpb.Event, buf *bytes.Buffer) {
    60  	// Avoid Fprintf, for speed. The format is so simple that we
    61  	// can do it quickly by hand.  It's worth about 3X. Fprintf is hard.
    62  
    63  	// Lmmdd hh:mm:ss.uuuuuu file:line]
    64  	switch event.Level {
    65  	case logutilpb.Level_INFO:
    66  		buf.WriteByte('I')
    67  	case logutilpb.Level_WARNING:
    68  		buf.WriteByte('W')
    69  	case logutilpb.Level_ERROR:
    70  		buf.WriteByte('E')
    71  	case logutilpb.Level_CONSOLE:
    72  		buf.WriteString(event.Value)
    73  		return
    74  	}
    75  
    76  	t := ProtoToTime(event.Time)
    77  	_, month, day := t.Date()
    78  	hour, minute, second := t.Clock()
    79  	twoDigits(buf, int(month))
    80  	twoDigits(buf, day)
    81  	buf.WriteByte(' ')
    82  	twoDigits(buf, hour)
    83  	buf.WriteByte(':')
    84  	twoDigits(buf, minute)
    85  	buf.WriteByte(':')
    86  	twoDigits(buf, second)
    87  	buf.WriteByte('.')
    88  	nDigits(buf, 6, t.Nanosecond()/1000, '0')
    89  	buf.WriteByte(' ')
    90  	buf.WriteString(event.File)
    91  	buf.WriteByte(':')
    92  	someDigits(buf, event.Line)
    93  	buf.WriteByte(']')
    94  	buf.WriteByte(' ')
    95  	buf.WriteString(event.Value)
    96  }
    97  
    98  // EventString returns the line in one string
    99  func EventString(event *logutilpb.Event) string {
   100  	buf := new(bytes.Buffer)
   101  	EventToBuffer(event, buf)
   102  	return buf.String()
   103  }
   104  
   105  // LogEvent sends an event to a Logger, using the level specified in the event.
   106  // The event struct is converted to a string with EventString().
   107  func LogEvent(logger Logger, event *logutilpb.Event) {
   108  	switch event.Level {
   109  	case logutilpb.Level_INFO:
   110  		logger.InfoDepth(1, EventString(event))
   111  	case logutilpb.Level_WARNING:
   112  		logger.WarningDepth(1, EventString(event))
   113  	case logutilpb.Level_ERROR:
   114  		logger.ErrorDepth(1, EventString(event))
   115  	case logutilpb.Level_CONSOLE:
   116  		// Note we can't just pass the string, because it might contain '%'.
   117  		logger.Printf("%s", EventString(event))
   118  	}
   119  }
   120  
   121  // CallbackLogger is a logger that sends the logging event to a callback
   122  // for consumption.
   123  type CallbackLogger struct {
   124  	f func(*logutilpb.Event)
   125  }
   126  
   127  // NewCallbackLogger returns a new logger to the given callback.
   128  // Note this and the other objects using this object should either
   129  // all use pointer receivers, or non-pointer receivers.
   130  // (that is ChannelLogger and MemoryLogger). That way they can share the
   131  // 'depth' parameter freely. In this code now, they all use pointer receivers.
   132  func NewCallbackLogger(f func(*logutilpb.Event)) *CallbackLogger {
   133  	return &CallbackLogger{f}
   134  }
   135  
   136  // InfoDepth is part of the Logger interface.
   137  func (cl *CallbackLogger) InfoDepth(depth int, s string) {
   138  	file, line := fileAndLine(2 + depth)
   139  	cl.f(&logutilpb.Event{
   140  		Time:  TimeToProto(time.Now()),
   141  		Level: logutilpb.Level_INFO,
   142  		File:  file,
   143  		Line:  line,
   144  		Value: s,
   145  	})
   146  }
   147  
   148  // WarningDepth is part of the Logger interface
   149  func (cl *CallbackLogger) WarningDepth(depth int, s string) {
   150  	file, line := fileAndLine(2 + depth)
   151  	cl.f(&logutilpb.Event{
   152  		Time:  TimeToProto(time.Now()),
   153  		Level: logutilpb.Level_WARNING,
   154  		File:  file,
   155  		Line:  line,
   156  		Value: s,
   157  	})
   158  }
   159  
   160  // ErrorDepth is part of the Logger interface
   161  func (cl *CallbackLogger) ErrorDepth(depth int, s string) {
   162  	file, line := fileAndLine(2 + depth)
   163  	cl.f(&logutilpb.Event{
   164  		Time:  TimeToProto(time.Now()),
   165  		Level: logutilpb.Level_ERROR,
   166  		File:  file,
   167  		Line:  line,
   168  		Value: s,
   169  	})
   170  }
   171  
   172  // Infof is part of the Logger interface.
   173  func (cl *CallbackLogger) Infof(format string, v ...any) {
   174  	cl.InfoDepth(1, fmt.Sprintf(format, v...))
   175  }
   176  
   177  // Warningf is part of the Logger interface.
   178  func (cl *CallbackLogger) Warningf(format string, v ...any) {
   179  	cl.WarningDepth(1, fmt.Sprintf(format, v...))
   180  }
   181  
   182  // Errorf is part of the Logger interface.
   183  func (cl *CallbackLogger) Errorf(format string, v ...any) {
   184  	cl.ErrorDepth(1, fmt.Sprintf(format, v...))
   185  }
   186  
   187  // Errorf2 is part of the Logger interface
   188  func (cl *CallbackLogger) Errorf2(err error, format string, v ...any) {
   189  	cl.ErrorDepth(1, fmt.Sprintf(format+": %+v", append(v, err)))
   190  }
   191  
   192  // Error is part of the Logger interface
   193  func (cl *CallbackLogger) Error(err error) {
   194  	cl.ErrorDepth(1, fmt.Sprintf("%+v", err))
   195  }
   196  
   197  // Printf is part of the Logger interface.
   198  func (cl *CallbackLogger) Printf(format string, v ...any) {
   199  	file, line := fileAndLine(2)
   200  	cl.f(&logutilpb.Event{
   201  		Time:  TimeToProto(time.Now()),
   202  		Level: logutilpb.Level_CONSOLE,
   203  		File:  file,
   204  		Line:  line,
   205  		Value: fmt.Sprintf(format, v...),
   206  	})
   207  }
   208  
   209  // ChannelLogger is a Logger that sends the logging events through a channel for
   210  // consumption.
   211  type ChannelLogger struct {
   212  	CallbackLogger
   213  	C chan *logutilpb.Event
   214  }
   215  
   216  // NewChannelLogger returns a CallbackLogger which will write the data
   217  // on a channel
   218  func NewChannelLogger(size int) *ChannelLogger {
   219  	c := make(chan *logutilpb.Event, size)
   220  	return &ChannelLogger{
   221  		CallbackLogger: CallbackLogger{
   222  			f: func(e *logutilpb.Event) {
   223  				c <- e
   224  			},
   225  		},
   226  		C: c,
   227  	}
   228  }
   229  
   230  // MemoryLogger keeps the logging events in memory.
   231  // All protected by a mutex.
   232  type MemoryLogger struct {
   233  	CallbackLogger
   234  
   235  	// mu protects the Events
   236  	mu     sync.Mutex
   237  	Events []*logutilpb.Event
   238  }
   239  
   240  // NewMemoryLogger returns a new MemoryLogger
   241  func NewMemoryLogger() *MemoryLogger {
   242  	ml := &MemoryLogger{}
   243  	ml.CallbackLogger.f = func(e *logutilpb.Event) {
   244  		ml.mu.Lock()
   245  		defer ml.mu.Unlock()
   246  		ml.Events = append(ml.Events, e)
   247  	}
   248  	return ml
   249  }
   250  
   251  // String returns all the lines in one String, separated by '\n'
   252  func (ml *MemoryLogger) String() string {
   253  	buf := new(bytes.Buffer)
   254  	ml.mu.Lock()
   255  	defer ml.mu.Unlock()
   256  	for _, event := range ml.Events {
   257  		EventToBuffer(event, buf)
   258  		buf.WriteByte('\n')
   259  	}
   260  	return buf.String()
   261  }
   262  
   263  // Clear clears the logs.
   264  func (ml *MemoryLogger) Clear() {
   265  	ml.mu.Lock()
   266  	ml.Events = nil
   267  	ml.mu.Unlock()
   268  }
   269  
   270  // LoggerWriter is an adapter that implements the io.Writer interface.
   271  type LoggerWriter struct {
   272  	logger Logger
   273  }
   274  
   275  // NewLoggerWriter returns an io.Writer on top of the logger
   276  func NewLoggerWriter(logger Logger) io.Writer {
   277  	return LoggerWriter{
   278  		logger: logger,
   279  	}
   280  }
   281  
   282  // Write implements io.Writer
   283  func (lw LoggerWriter) Write(p []byte) (n int, err error) {
   284  	if len(p) == 0 {
   285  		return 0, nil
   286  	}
   287  	lw.logger.Printf("%v", string(p))
   288  	return len(p), nil
   289  }
   290  
   291  // TeeLogger is a Logger that sends its logs to two underlying logger
   292  type TeeLogger struct {
   293  	One, Two Logger
   294  }
   295  
   296  // NewTeeLogger returns a logger that sends its logs to both loggers
   297  func NewTeeLogger(one, two Logger) *TeeLogger {
   298  	return &TeeLogger{
   299  		One: one,
   300  		Two: two,
   301  	}
   302  }
   303  
   304  // InfoDepth is part of the Logger interface
   305  func (tl *TeeLogger) InfoDepth(depth int, s string) {
   306  	tl.One.InfoDepth(1+depth, s)
   307  	tl.Two.InfoDepth(1+depth, s)
   308  }
   309  
   310  // WarningDepth is part of the Logger interface
   311  func (tl *TeeLogger) WarningDepth(depth int, s string) {
   312  	tl.One.WarningDepth(1+depth, s)
   313  	tl.Two.WarningDepth(1+depth, s)
   314  }
   315  
   316  // ErrorDepth is part of the Logger interface
   317  func (tl *TeeLogger) ErrorDepth(depth int, s string) {
   318  	tl.One.ErrorDepth(1+depth, s)
   319  	tl.Two.ErrorDepth(1+depth, s)
   320  }
   321  
   322  // Infof is part of the Logger interface
   323  func (tl *TeeLogger) Infof(format string, v ...any) {
   324  	tl.InfoDepth(1, fmt.Sprintf(format, v...))
   325  }
   326  
   327  // Warningf is part of the Logger interface
   328  func (tl *TeeLogger) Warningf(format string, v ...any) {
   329  	tl.WarningDepth(1, fmt.Sprintf(format, v...))
   330  }
   331  
   332  // Errorf is part of the Logger interface
   333  func (tl *TeeLogger) Errorf(format string, v ...any) {
   334  	tl.ErrorDepth(1, fmt.Sprintf(format, v...))
   335  }
   336  
   337  // Errorf2 is part of the Logger interface
   338  func (tl *TeeLogger) Errorf2(err error, format string, v ...any) {
   339  	tl.ErrorDepth(1, fmt.Sprintf(format+": %+v", append(v, err)))
   340  }
   341  
   342  // Error is part of the Logger interface
   343  func (tl *TeeLogger) Error(err error) {
   344  	tl.ErrorDepth(1, fmt.Sprintf("%+v", err))
   345  }
   346  
   347  // Printf is part of the Logger interface
   348  func (tl *TeeLogger) Printf(format string, v ...any) {
   349  	tl.One.Printf(format, v...)
   350  	tl.Two.Printf(format, v...)
   351  }
   352  
   353  // array for fast int -> string conversion
   354  const digits = "0123456789"
   355  
   356  // twoDigits adds a zero-prefixed two-digit integer to buf
   357  func twoDigits(buf *bytes.Buffer, value int) {
   358  	buf.WriteByte(digits[value/10])
   359  	buf.WriteByte(digits[value%10])
   360  }
   361  
   362  // nDigits adds an n-digit integer d to buf
   363  // padding with pad on the left.
   364  // It assumes d >= 0.
   365  func nDigits(buf *bytes.Buffer, n, d int, pad byte) {
   366  	tmp := make([]byte, n)
   367  	j := n - 1
   368  	for ; j >= 0 && d > 0; j-- {
   369  		tmp[j] = digits[d%10]
   370  		d /= 10
   371  	}
   372  	for ; j >= 0; j-- {
   373  		tmp[j] = pad
   374  	}
   375  	buf.Write(tmp)
   376  }
   377  
   378  // someDigits adds a zero-prefixed variable-width integer to buf
   379  func someDigits(buf *bytes.Buffer, d int64) {
   380  	// Print into the top, then copy down.
   381  	tmp := make([]byte, 10)
   382  	j := 10
   383  	for {
   384  		j--
   385  		tmp[j] = digits[d%10]
   386  		d /= 10
   387  		if d == 0 {
   388  			break
   389  		}
   390  	}
   391  	buf.Write(tmp[j:])
   392  }
   393  
   394  // fileAndLine returns the caller's file and line 2 levels above
   395  func fileAndLine(depth int) (string, int64) {
   396  	_, file, line, ok := runtime.Caller(depth)
   397  	if !ok {
   398  		return "???", 1
   399  	}
   400  
   401  	slash := strings.LastIndex(file, "/")
   402  	if slash >= 0 {
   403  		file = file[slash+1:]
   404  	}
   405  	return file, int64(line)
   406  }