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  }