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. 98 : Eventf(ctx context.Context, format string, args ...interface{}) 99 : // IsTracingEnabled returns true if tracing is enabled. It can be used as an 100 : // optimization to avoid calling Eventf (which will be a noop when tracing 101 : // is not enabled) to avoid the overhead of boxing the args. 102 : IsTracingEnabled(ctx context.Context) bool 103 : } 104 : 105 : // LoggerWithNoopTracer wraps a logger and does no tracing. 106 : type LoggerWithNoopTracer struct { 107 : Logger 108 : } 109 : 110 : var _ LoggerAndTracer = &LoggerWithNoopTracer{} 111 : 112 : // Eventf implements LoggerAndTracer. 113 0 : func (*LoggerWithNoopTracer) Eventf(ctx context.Context, format string, args ...interface{}) { 114 0 : if invariants.Enabled && ctx == nil { 115 0 : panic("Eventf context is nil") 116 : } 117 : } 118 : 119 : // IsTracingEnabled implements LoggerAndTracer. 120 1 : func (*LoggerWithNoopTracer) IsTracingEnabled(ctx context.Context) bool { 121 1 : if invariants.Enabled && ctx == nil { 122 0 : panic("IsTracingEnabled ctx is nil") 123 : } 124 1 : return false 125 : } 126 : 127 : // NoopLoggerAndTracer does no logging and tracing. Remember that struct{} is 128 : // special cased in Go and does not incur an allocation when it backs the 129 : // interface LoggerAndTracer. 130 : type NoopLoggerAndTracer struct{} 131 : 132 : var _ LoggerAndTracer = NoopLoggerAndTracer{} 133 : 134 : // Infof implements LoggerAndTracer. 135 0 : func (l NoopLoggerAndTracer) Infof(format string, args ...interface{}) {} 136 : 137 : // Errorf implements LoggerAndTracer. 138 0 : func (l NoopLoggerAndTracer) Errorf(format string, args ...interface{}) {} 139 : 140 : // Fatalf implements LoggerAndTracer. 141 0 : func (l NoopLoggerAndTracer) Fatalf(format string, args ...interface{}) {} 142 : 143 : // Eventf implements LoggerAndTracer. 144 0 : func (l NoopLoggerAndTracer) Eventf(ctx context.Context, format string, args ...interface{}) { 145 0 : if invariants.Enabled && ctx == nil { 146 0 : panic("Eventf context is nil") 147 : } 148 : } 149 : 150 : // IsTracingEnabled implements LoggerAndTracer. 151 1 : func (l NoopLoggerAndTracer) IsTracingEnabled(ctx context.Context) bool { 152 1 : if invariants.Enabled && ctx == nil { 153 0 : panic("IsTracingEnabled ctx is nil") 154 : } 155 1 : return false 156 : }