github.com/asynkron/protoactor-go@v0.0.0-20240308120642-ef91a6abee75/metrics/actor_metrics.go (about) 1 // Copyright (C) 2017 - 2022 Asynkron.se <http://www.asynkron.se> 2 3 package metrics 4 5 import ( 6 "fmt" 7 "log/slog" 8 "sync" 9 10 "go.opentelemetry.io/otel" 11 "go.opentelemetry.io/otel/metric" 12 ) 13 14 const LibName string = "protoactor" 15 16 type ActorMetrics struct { 17 // Mutual Exclusion Primitive to use with ActorMailboxLength 18 mu *sync.Mutex 19 20 // MetricsID 21 ID string 22 23 // Actors 24 ActorFailureCount metric.Int64Counter 25 ActorMailboxLength metric.Int64ObservableGauge 26 ActorMessageReceiveHistogram metric.Float64Histogram 27 ActorRestartedCount metric.Int64Counter 28 ActorSpawnCount metric.Int64Counter 29 ActorStoppedCount metric.Int64Counter 30 31 // Deadletters 32 DeadLetterCount metric.Int64Counter 33 34 // Futures 35 FuturesStartedCount metric.Int64Counter 36 FuturesCompletedCount metric.Int64Counter 37 FuturesTimedOutCount metric.Int64Counter 38 39 // Threadpool 40 ThreadPoolLatency metric.Int64Histogram 41 } 42 43 // NewActorMetrics creates a new ActorMetrics value and returns a pointer to it 44 func NewActorMetrics(logger *slog.Logger) *ActorMetrics { 45 instruments := newInstruments(logger) 46 return instruments 47 } 48 49 // newInstruments will create instruments using a meter from 50 // the given provider p 51 func newInstruments(logger *slog.Logger) *ActorMetrics { 52 meter := otel.Meter(LibName) 53 instruments := ActorMetrics{mu: &sync.Mutex{}} 54 55 var err error 56 57 if instruments.ActorFailureCount, err = meter.Int64Counter( 58 "protoactor_actor_failure_count", 59 metric.WithDescription("Number of actor failures"), 60 metric.WithUnit("1"), 61 ); err != nil { 62 err = fmt.Errorf("failed to create ActorFailureCount instrument, %w", err) 63 logger.Error(err.Error(), slog.Any("error", err)) 64 } 65 66 if instruments.ActorMessageReceiveHistogram, err = meter.Float64Histogram( 67 "protoactor_actor_message_receive_duration_seconds", 68 metric.WithDescription("Actor's messages received duration in seconds"), 69 ); err != nil { 70 err = fmt.Errorf("failed to create ActorMessageReceiveHistogram instrument, %w", err) 71 logger.Error(err.Error(), slog.Any("error", err)) 72 } 73 74 if instruments.ActorRestartedCount, err = meter.Int64Counter( 75 "protoactor_actor_restarted_count", 76 metric.WithDescription("Number of actors restarts"), 77 metric.WithUnit("1"), 78 ); err != nil { 79 err = fmt.Errorf("failed to create ActorRestartedCount instrument, %w", err) 80 logger.Error(err.Error(), slog.Any("error", err)) 81 } 82 83 if instruments.ActorStoppedCount, err = meter.Int64Counter( 84 "protoactor_actor_stopped_count", 85 metric.WithDescription("Number of actors stopped"), 86 metric.WithUnit("1"), 87 ); err != nil { 88 err = fmt.Errorf("failed to create ActorStoppedCount instrument, %w", err) 89 logger.Error(err.Error(), slog.Any("error", err)) 90 } 91 92 if instruments.ActorSpawnCount, err = meter.Int64Counter( 93 "protoactor_actor_spawn_count", 94 metric.WithDescription("Number of actors spawn"), 95 metric.WithUnit("1"), 96 ); err != nil { 97 err = fmt.Errorf("failed to create ActorSpawnCount instrument, %w", err) 98 logger.Error(err.Error(), slog.Any("error", err)) 99 } 100 101 if instruments.DeadLetterCount, err = meter.Int64Counter( 102 "protoactor_deadletter_count", 103 metric.WithDescription("Number of deadletters"), 104 metric.WithUnit("1"), 105 ); err != nil { 106 err = fmt.Errorf("failed to create DeadLetterCount instrument, %w", err) 107 logger.Error(err.Error(), slog.Any("error", err)) 108 } 109 110 if instruments.FuturesCompletedCount, err = meter.Int64Counter( 111 "protoactor_futures_completed_count", 112 metric.WithDescription("Number of futures completed"), 113 metric.WithUnit("1"), 114 ); err != nil { 115 err = fmt.Errorf("failed to create FuturesCompletedCount instrument, %w", err) 116 logger.Error(err.Error(), slog.Any("error", err)) 117 } 118 119 if instruments.FuturesStartedCount, err = meter.Int64Counter( 120 "protoactor_futures_started_count", 121 metric.WithDescription("Number of futures started"), 122 metric.WithUnit("1"), 123 ); err != nil { 124 err = fmt.Errorf("failed to create FuturesStartedCount instrument, %w", err) 125 logger.Error(err.Error(), slog.Any("error", err)) 126 } 127 128 if instruments.FuturesTimedOutCount, err = meter.Int64Counter( 129 "protoactor_futures_timed_out_count", 130 metric.WithDescription("Number of futures timed out"), 131 metric.WithUnit("1"), 132 ); err != nil { 133 err = fmt.Errorf("failed to create FuturesTimedOutCount instrument, %w", err) 134 logger.Error(err.Error(), slog.Any("error", err)) 135 } 136 137 if instruments.ThreadPoolLatency, err = meter.Int64Histogram( 138 "protoactor_thread_pool_latency_duration_seconds", 139 metric.WithDescription("History of latency in second"), 140 metric.WithUnit("ms"), 141 ); err != nil { 142 err = fmt.Errorf("failed to create ThreadPoolLatency instrument, %w", err) 143 logger.Error(err.Error(), slog.Any("error", err)) 144 } 145 146 return &instruments 147 } 148 149 // SetActorMailboxLengthGauge makes sure access to ActorMailboxLength is sequenced 150 func (am *ActorMetrics) SetActorMailboxLengthGauge(gauge metric.Int64ObservableGauge) { 151 // lock our mutex 152 am.mu.Lock() 153 defer am.mu.Unlock() 154 155 am.ActorMailboxLength = gauge 156 }