@ -228,6 +228,53 @@ func RateLimitedFnWithClock(logf Logf, f time.Duration, burst int, maxCache int,
}
}
}
}
// SlowLoggerWithClock is a logger that applies rate limits similar to
// RateLimitedFnWithClock, but instead of dropping logs will sleep until they
// can be written. This should only be used for debug logs, and not in a hot path.
//
// The provided context, if canceled, will cause all logs to be dropped and
// prevent any sleeps.
func SlowLoggerWithClock ( ctx context . Context , logf Logf , f time . Duration , burst int , timeNow func ( ) time . Time ) Logf {
var (
mu sync . Mutex
tb = newTokenBucket ( f , burst , timeNow ( ) )
)
return func ( format string , args ... any ) {
if ctx . Err ( ) != nil {
return
}
// Hold the mutex for the entire length of the check + log
// since our token bucket isn't concurrency-safe.
mu . Lock ( )
defer mu . Unlock ( )
tb . AdvanceTo ( timeNow ( ) )
// If we can get a token, then do that and return.
if tb . Get ( ) {
logf ( format , args ... )
return
}
// Otherwise, sleep for 2x the duration so that we don't
// immediately sleep again on the next call.
tmr := time . NewTimer ( 2 * f )
defer tmr . Stop ( )
select {
case curr := <- tmr . C :
tb . AdvanceTo ( curr )
case <- ctx . Done ( ) :
return
}
if ! tb . Get ( ) {
log . Printf ( "[unexpected] error rate-limiting in SlowLoggerWithClock" )
return
}
logf ( format , args ... )
}
}
// LogOnChange logs a given line only if line != lastLine, or if maxInterval has passed
// LogOnChange logs a given line only if line != lastLine, or if maxInterval has passed
// since the last time this identical line was logged.
// since the last time this identical line was logged.
func LogOnChange ( logf Logf , maxInterval time . Duration , timeNow func ( ) time . Time ) Logf {
func LogOnChange ( logf Logf , maxInterval time . Duration , timeNow func ( ) time . Time ) Logf {