github.com/benz9527/xboot@v0.0.0-20240504061247-c23f15593274/timer/stats.go (about) 1 package timer 2 3 import ( 4 "context" 5 "fmt" 6 "strconv" 7 "sync/atomic" 8 9 "github.com/samber/lo" 10 "go.opentelemetry.io/otel" 11 "go.opentelemetry.io/otel/attribute" 12 "go.opentelemetry.io/otel/metric" 13 14 "github.com/benz9527/xboot/lib/hrtime" 15 ) 16 17 const ( 18 TimingWheelStatsName = "xboot/xtw" 19 ) 20 21 type xTimingWheelsStats struct { 22 ctx context.Context 23 minTickMs int64 24 clock hrtime.Clock 25 jobExecutedCount atomic.Int64 26 jobHighLatencyCount atomic.Int64 27 slotActiveCount atomic.Int64 28 jobAliveCounter metric.Int64UpDownCounter 29 jobTickAccuracy metric.Float64ObservableGauge 30 jobLatencies metric.Int64Histogram 31 jobExecuteDurations metric.Int64Histogram 32 jobExecutedCounter metric.Int64Counter 33 jobCancelledCounter metric.Int64Counter 34 slotCounter metric.Int64Counter 35 slotActiveCounter metric.Int64ObservableUpDownCounter 36 } 37 38 func (stats *xTimingWheelsStats) RecordJobAliveCount(count int64) { 39 if stats == nil { 40 return 41 } 42 stats.jobAliveCounter.Add(stats.ctx, count) 43 } 44 45 func (stats *xTimingWheelsStats) UpdateSlotActiveCount(count int64) { 46 if stats == nil { 47 return 48 } 49 stats.slotActiveCount.Swap(count) 50 } 51 52 func (stats *xTimingWheelsStats) RecordSlotCount(count int64) { 53 if stats == nil { 54 return 55 } 56 stats.slotCounter.Add(stats.ctx, count) 57 } 58 59 func (stats *xTimingWheelsStats) IncreaseJobExecutedCount() { 60 if stats == nil { 61 return 62 } 63 stats.jobExecutedCounter.Add(stats.ctx, 1) 64 stats.jobExecutedCount.Add(1) 65 } 66 67 func (stats *xTimingWheelsStats) IncreaseJobCancelledCount() { 68 if stats == nil { 69 return 70 } 71 stats.jobCancelledCounter.Add(stats.ctx, 1) 72 } 73 74 func (stats *xTimingWheelsStats) RecordJobLatency(latencyMs int64) { 75 if stats == nil { 76 return 77 } 78 as := attribute.NewSet( 79 attribute.String("xtw.job.latency.ms", strconv.FormatInt(latencyMs, 10)), 80 ) 81 stats.jobLatencies.Record(stats.ctx, 1, metric.WithAttributeSet(as)) 82 if latencyMs > stats.minTickMs || latencyMs < -stats.minTickMs { 83 stats.jobHighLatencyCount.Add(1) 84 } 85 } 86 87 func (stats *xTimingWheelsStats) RecordJobExecuteDuration(durationMs int64) { 88 if stats == nil { 89 return 90 } 91 as := attribute.NewSet( 92 attribute.String("xtw.job.execute.duration.ms", strconv.FormatInt(durationMs, 10)), 93 ) 94 stats.jobExecuteDurations.Record(stats.ctx, durationMs, metric.WithAttributeSet(as)) 95 } 96 97 func newTimingWheelStats(ref *xTimingWheelsOption) *xTimingWheelsStats { 98 meterName := fmt.Sprintf("%s/%s", TimingWheelStatsName, ref.getName()) 99 tickMs := ref.getBasicTickMilliseconds() 100 stats := &xTimingWheelsStats{ 101 ctx: context.Background(), 102 minTickMs: tickMs, 103 clock: ref.getClock(), 104 jobAliveCounter: lo.Must[metric.Int64UpDownCounter](otel.Meter(meterName). 105 Int64UpDownCounter( 106 "xtw.job.count", 107 metric.WithDescription("The number of jobs in the timing wheel."), 108 ), 109 ), 110 jobLatencies: lo.Must[metric.Int64Histogram](otel.Meter(meterName). 111 Int64Histogram( 112 "xtw.job.latency", 113 metric.WithDescription("The latency of the timing wheel job. In milliseconds."), 114 metric.WithUnit("ms"), 115 ), 116 ), 117 jobExecuteDurations: lo.Must[metric.Int64Histogram](otel.Meter(meterName). 118 Int64Histogram( 119 "xtw.job.execute.duration", 120 metric.WithDescription("The duration of the timing wheel job execution. In milliseconds."), 121 metric.WithUnit("ms"), 122 ), 123 ), 124 jobExecutedCounter: lo.Must[metric.Int64Counter](otel.Meter(meterName). 125 Int64Counter( 126 "xtw.job.executed.count", 127 metric.WithDescription("The number of jobs executed by the timing wheel."), 128 ), 129 ), 130 jobCancelledCounter: lo.Must[metric.Int64Counter](otel.Meter(meterName). 131 Int64Counter( 132 "xtw.job.cancelled.count", 133 metric.WithDescription("The number of jobs cancelled by the timing wheel."), 134 ), 135 ), 136 slotCounter: lo.Must[metric.Int64Counter](otel.Meter(meterName). 137 Int64Counter( 138 "xtw.slot.count", 139 metric.WithDescription("The number of slots belongs to the timing wheel."), 140 ), 141 ), 142 } 143 stats.jobTickAccuracy = lo.Must[metric.Float64ObservableGauge](otel.Meter(meterName). 144 Float64ObservableGauge( 145 "xtw.job.tick.accuracy", 146 metric.WithDescription(fmt.Sprintf("The accuracy of the timing wheel tick [-%d,%d] ms.", tickMs, tickMs)), 147 metric.WithFloat64Callback(func(ctx context.Context, ob metric.Float64Observer) error { 148 accuracy := 0.00 149 if stats.jobExecutedCount.Load() > 0 { 150 accuracy = float64(stats.jobExecutedCount.Load()-stats.jobHighLatencyCount.Load()) / 151 float64(stats.jobExecutedCount.Load()) 152 } 153 ob.Observe(accuracy) 154 return nil 155 }), 156 metric.WithUnit("%"), 157 ), 158 ) 159 stats.slotActiveCounter = lo.Must[metric.Int64ObservableUpDownCounter](otel.Meter(meterName). 160 Int64ObservableUpDownCounter( 161 "xtw.slot.active.count", 162 metric.WithDescription("The number of slots in active (expired) belongs to the timing wheel."), 163 metric.WithInt64Callback(func(ctx context.Context, ob metric.Int64Observer) error { 164 slots := stats.slotActiveCount.Load() 165 ob.Observe(slots) 166 return nil 167 }), 168 ), 169 ) 170 return stats 171 }