vitess.io/vitess@v0.16.2/go/vt/logutil/throttled.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 "fmt" 21 "sync" 22 "time" 23 24 "vitess.io/vitess/go/vt/log" 25 ) 26 27 // ThrottledLogger will allow logging of messages but won't spam the 28 // logs. 29 type ThrottledLogger struct { 30 // set at construction 31 name string 32 maxInterval time.Duration 33 34 // mu protects the following members 35 mu sync.Mutex 36 lastlogTime time.Time 37 skippedCount int 38 } 39 40 // NewThrottledLogger will create a ThrottledLogger with the given 41 // name and throttling interval. 42 func NewThrottledLogger(name string, maxInterval time.Duration) *ThrottledLogger { 43 return &ThrottledLogger{ 44 name: name, 45 maxInterval: maxInterval, 46 } 47 } 48 49 type logFunc func(int, ...any) 50 51 var ( 52 infoDepth = log.InfoDepth 53 warningDepth = log.WarningDepth 54 errorDepth = log.ErrorDepth 55 ) 56 57 func (tl *ThrottledLogger) log(logF logFunc, format string, v ...any) { 58 now := time.Now() 59 60 tl.mu.Lock() 61 defer tl.mu.Unlock() 62 logWaitTime := tl.maxInterval - (now.Sub(tl.lastlogTime)) 63 if logWaitTime < 0 { 64 tl.lastlogTime = now 65 logF(2, fmt.Sprintf(tl.name+": "+format, v...)) 66 return 67 } 68 // If this is the first message to be skipped, start a goroutine 69 // to log and reset skippedCount 70 if tl.skippedCount == 0 { 71 go func(d time.Duration) { 72 time.Sleep(d) 73 tl.mu.Lock() 74 defer tl.mu.Unlock() 75 // Because of the go func(), we lose the stack trace, 76 // so we just use the current line for this. 77 logF(0, fmt.Sprintf("%v: skipped %v log messages", tl.name, tl.skippedCount)) 78 tl.skippedCount = 0 79 }(logWaitTime) 80 } 81 tl.skippedCount++ 82 } 83 84 // Infof logs an info if not throttled. 85 func (tl *ThrottledLogger) Infof(format string, v ...any) { 86 tl.log(infoDepth, format, v...) 87 } 88 89 // Warningf logs a warning if not throttled. 90 func (tl *ThrottledLogger) Warningf(format string, v ...any) { 91 tl.log(warningDepth, format, v...) 92 } 93 94 // Errorf logs an error if not throttled. 95 func (tl *ThrottledLogger) Errorf(format string, v ...any) { 96 tl.log(errorDepth, format, v...) 97 }