Line data Source code
1 : // Copyright 2023 The LevelDB-Go and Pebble Authors. All rights reserved. Use 2 : // of this source code is governed by a BSD-style license that can be found in 3 : // the LICENSE file. 4 : 5 : package base 6 : 7 : import ( 8 : "bytes" 9 : "context" 10 : "fmt" 11 : "log" 12 : "os" 13 : "sync" 14 : 15 : "github.com/cockroachdb/pebble/internal/invariants" 16 : ) 17 : 18 : // Logger defines an interface for writing log messages. 19 : type Logger interface { 20 : Infof(format string, args ...interface{}) 21 : Errorf(format string, args ...interface{}) 22 : Fatalf(format string, args ...interface{}) 23 : } 24 : type defaultLogger struct{} 25 : 26 : // DefaultLogger logs to the Go stdlib logs. 27 : var DefaultLogger defaultLogger 28 : 29 : var _ Logger = DefaultLogger 30 : 31 : // Infof implements the Logger.Infof interface. 32 1 : func (defaultLogger) Infof(format string, args ...interface{}) { 33 1 : _ = log.Output(2, fmt.Sprintf(format, args...)) 34 1 : } 35 : 36 : // Errorf implements the Logger.Errorf interface. 37 0 : func (defaultLogger) Errorf(format string, args ...interface{}) { 38 0 : _ = log.Output(2, fmt.Sprintf(format, args...)) 39 0 : } 40 : 41 : // Fatalf implements the Logger.Fatalf interface. 42 0 : func (defaultLogger) Fatalf(format string, args ...interface{}) { 43 0 : _ = log.Output(2, fmt.Sprintf(format, args...)) 44 0 : os.Exit(1) 45 0 : } 46 : 47 : // InMemLogger implements Logger using an in-memory buffer (used for testing). 48 : // The buffer can be read via String() and cleared via Reset(). 49 : type InMemLogger struct { 50 : mu struct { 51 : sync.Mutex 52 : buf bytes.Buffer 53 : } 54 : } 55 : 56 : var _ Logger = (*InMemLogger)(nil) 57 : 58 : // Reset clears the internal buffer. 59 1 : func (b *InMemLogger) Reset() { 60 1 : b.mu.Lock() 61 1 : defer b.mu.Unlock() 62 1 : b.mu.buf.Reset() 63 1 : } 64 : 65 : // String returns the current internal buffer. 66 1 : func (b *InMemLogger) String() string { 67 1 : b.mu.Lock() 68 1 : defer b.mu.Unlock() 69 1 : return b.mu.buf.String() 70 1 : } 71 : 72 : // Infof is part of the Logger interface. 73 1 : func (b *InMemLogger) Infof(format string, args ...interface{}) { 74 1 : s := fmt.Sprintf(format, args...) 75 1 : b.mu.Lock() 76 1 : defer b.mu.Unlock() 77 1 : b.mu.buf.Write([]byte(s)) 78 1 : if n := len(s); n == 0 || s[n-1] != '\n' { 79 1 : b.mu.buf.Write([]byte("\n")) 80 1 : } 81 : } 82 : 83 : // Errorf is part of the Logger interface. 84 0 : func (b *InMemLogger) Errorf(format string, args ...interface{}) { 85 0 : b.Infof(format, args...) 86 0 : } 87 : 88 : // Fatalf is part of the Logger interface. 89 1 : func (b *InMemLogger) Fatalf(format string, args ...interface{}) { 90 1 : b.Infof("FATAL: "+format, args...) 91 1 : } 92 : 93 : // LoggerAndTracer defines an interface for logging and tracing. 94 : type LoggerAndTracer interface { 95 : Logger 96 : // Eventf formats and emits a tracing log, if tracing is enabled in the 97 : // current context. It can also emit to a regular log, if expensive 98 : // logging is enabled. 99 : Eventf(ctx context.Context, format string, args ...interface{}) 100 : // IsTracingEnabled returns true if tracing is enabled for this context, 101 : // or expensive logging is enabled. It can be used as an optimization to 102 : // avoid calling Eventf (which will be a noop when tracing or expensive 103 : // logging is not enabled) to avoid the overhead of boxing the args. 104 : IsTracingEnabled(ctx context.Context) bool 105 : } 106 : 107 : // LoggerWithNoopTracer wraps a logger and does no tracing. 108 : type LoggerWithNoopTracer struct { 109 : Logger 110 : } 111 : 112 : var _ LoggerAndTracer = &LoggerWithNoopTracer{} 113 : 114 : // Eventf implements LoggerAndTracer. 115 0 : func (*LoggerWithNoopTracer) Eventf(ctx context.Context, format string, args ...interface{}) { 116 0 : if invariants.Enabled && ctx == nil { 117 0 : panic("Eventf context is nil") 118 : } 119 : } 120 : 121 : // IsTracingEnabled implements LoggerAndTracer. 122 1 : func (*LoggerWithNoopTracer) IsTracingEnabled(ctx context.Context) bool { 123 1 : if invariants.Enabled && ctx == nil { 124 0 : panic("IsTracingEnabled ctx is nil") 125 : } 126 1 : return false 127 : } 128 : 129 : // NoopLoggerAndTracer does no logging and tracing. Remember that struct{} is 130 : // special cased in Go and does not incur an allocation when it backs the 131 : // interface LoggerAndTracer. 132 : type NoopLoggerAndTracer struct{} 133 : 134 : var _ LoggerAndTracer = NoopLoggerAndTracer{} 135 : 136 : // Infof implements LoggerAndTracer. 137 0 : func (l NoopLoggerAndTracer) Infof(format string, args ...interface{}) {} 138 : 139 : // Errorf implements LoggerAndTracer. 140 0 : func (l NoopLoggerAndTracer) Errorf(format string, args ...interface{}) {} 141 : 142 : // Fatalf implements LoggerAndTracer. 143 0 : func (l NoopLoggerAndTracer) Fatalf(format string, args ...interface{}) {} 144 : 145 : // Eventf implements LoggerAndTracer. 146 0 : func (l NoopLoggerAndTracer) Eventf(ctx context.Context, format string, args ...interface{}) { 147 0 : if invariants.Enabled && ctx == nil { 148 0 : panic("Eventf context is nil") 149 : } 150 : } 151 : 152 : // IsTracingEnabled implements LoggerAndTracer. 153 1 : func (l NoopLoggerAndTracer) IsTracingEnabled(ctx context.Context) bool { 154 1 : if invariants.Enabled && ctx == nil { 155 0 : panic("IsTracingEnabled ctx is nil") 156 : } 157 1 : return false 158 : }