github.com/haraldrudell/parl@v0.4.176/slow-detector.go (about) 1 /* 2 © 2023–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/) 3 ISC License 4 */ 5 6 package parl 7 8 import ( 9 "time" 10 11 "github.com/haraldrudell/parl/pruntime" 12 "github.com/haraldrudell/parl/ptime" 13 ) 14 15 const ( 16 newFrames = 1 17 ensureFrames = 1 18 ) 19 20 // SlowDetector measures latency via Start-Stop invocations and prints 21 // max latency values above threshold to stderr. Thread-safe 22 type SlowDetector struct { 23 sd SlowDetectorCore 24 printf PrintfFunc 25 label string 26 } 27 28 type SlowInvocation interface { 29 Stop(value ...time.Time) 30 Interval(label string, t ...time.Time) 31 } 32 33 // NewSlowDetector returns a Start-Stop variable detecting slowness 34 // - label is a name for the measured activity, default the code location of caller 35 // - slowTyp is most commonly SlowDefault using a shared thread 36 // - default printf is parl.Log, ie. ouput to stderr 37 // - first optional duration is minimum latency to report, default 100 ms 38 // if first optional duration is 0, all max-slowness invocations are printed 39 // - second optional duration is reporting period of non-return, default 1 minute 40 // - output is to stderr 41 func NewSlowDetector(label string, slowTyp slowType, printf PrintfFunc, goGen GoGen, threshold ...time.Duration) (slowDetector *SlowDetector) { 42 if label == "" { 43 label = pruntime.NewCodeLocation(newFrames).Short() 44 } 45 if printf == nil { 46 printf = Log 47 } 48 sd := SlowDetector{ 49 label: label, 50 printf: printf, 51 } 52 sd.sd = *NewSlowDetectorCore(sd.callback, slowTyp, goGen, threshold...) 53 return &sd 54 } 55 56 func (sd *SlowDetector) IsValid() (isValid bool) { 57 if sd == nil { 58 return 59 } 60 isValid = sd.printf != nil 61 return 62 } 63 64 func (sd *SlowDetector) Start0() (slowInvocation SlowInvocation) { 65 return sd.sd.Start(pruntime.NewCodeLocation(ensureFrames).Short()) 66 } 67 68 func (sd *SlowDetector) Start(label string, value ...time.Time) (slowInvocation SlowInvocation) { 69 if label == "" { 70 label = pruntime.NewCodeLocation(ensureFrames).Short() 71 } 72 return sd.sd.Start(label, value...) 73 } 74 75 func (sd *SlowDetector) Values() (last, average, max time.Duration, hasValue bool) { 76 return sd.sd.Values() 77 } 78 79 // last-duration / average duration / max duration 80 func (sd *SlowDetector) Status0() (s string) { 81 last, average, max, hasValue := sd.sd.Values() 82 if !hasValue { 83 return "-/-/-" 84 } 85 s = ptime.Duration(last) + "/" + 86 ptime.Duration(average) + "/" + 87 ptime.Duration(max) 88 return 89 } 90 91 func (sd *SlowDetector) Status() (s string) { 92 return sd.label + ": " + sd.Status0() 93 } 94 95 func (sd *SlowDetector) callback(sdi *SlowDetectorInvocation, didReturn bool, duration time.Duration) { 96 97 var inProgressStr string 98 if !didReturn { 99 inProgressStr = " in progress…" 100 } 101 102 var threadIDStr string 103 if threadID := sdi.ThreadID(); threadID.IsValid() { 104 threadIDStr = " threadID: " + threadID.String() 105 } 106 107 var intervalStr = sdi.Intervals() 108 109 sd.printf("Slowness: %s %s duration: %s%s%s%s", 110 sd.label, sdi.Label(), 111 ptime.Duration(duration), 112 intervalStr, 113 threadIDStr, 114 inProgressStr, 115 ) 116 }