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 }