@@ -37,12 +37,14 @@ public class MetricsReporter {
3737 @ Value ("${simple.metrics.dumpRate:PT5S}" )
3838 private Duration dumpRate ;
3939
40- private final Map <String , Timer > commandLatencyTimers = new ConcurrentHashMap <>();
40+ private final Map <CommandKey , Timer > commandLatencyTimers = new ConcurrentHashMap <>();
4141
4242 private final Timer commandLatencyTotalTimer ;
4343
4444 private final Map <String , Counter > commandErrorCounters = new ConcurrentHashMap <>();
4545
46+ private final Map <CommandKey , Counter > commandTotalCounter = new ConcurrentHashMap <>();
47+
4648 private final Counter commandErrorTotalCounter ;
4749
4850 private final Timer connectionSuccessTimer ;
@@ -51,6 +53,12 @@ public class MetricsReporter {
5153
5254 private final Map <ConnectionKey , Counter > reconnectAttemptCounter = new ConcurrentHashMap <>();
5355
56+ private final Map <ReconnectAttemptKey , Counter > redisConnectionsTotal = new ConcurrentHashMap <>();
57+
58+ private final Map <ConnectionKey , Counter > redisConnectionsDrops = new ConcurrentHashMap <>();
59+
60+ private final Map <PubSubOpKey , Counter > pubSubOperationCounter = new ConcurrentHashMap <>();
61+
5462 private final Counter reconnectAttemptTotalCounter ;
5563
5664 private final Map <ConnectionKey , Counter > reconnectFailureCounter = new ConcurrentHashMap <>();
@@ -87,11 +95,18 @@ Timer.Sample startCommandTimer() {
8795 return Timer .start (meterRegistry );
8896 }
8997
90- void recordCommandLatency (String commandName , Timer .Sample sample ) {
91- Timer timer = commandLatencyTimers .computeIfAbsent (commandName , this ::createCommandLatencyTimer );
98+ public record CommandKey (String commandName , OperationStatus status ) {
99+
100+ }
101+
102+ void recordCommandLatency (CommandKey commandKey , Timer .Sample sample ) {
103+ Timer timer = commandLatencyTimers .computeIfAbsent (commandKey , this ::createCommandLatencyTimer );
92104 long timeNs = sample .stop (timer );
93105
94106 commandLatencyTotalTimer .record (Duration .ofNanos (timeNs ));
107+
108+ Counter counter = commandTotalCounter .computeIfAbsent (commandKey , this ::createCommandTotalCounter );
109+ counter .increment ();
95110 }
96111
97112 void incrementCommandError (String commandName ) {
@@ -101,8 +116,8 @@ void incrementCommandError(String commandName) {
101116
102117 public void recordWorkloadExecutionDuration (Timer .Sample sample , String workloadName , BaseWorkload .Status status ) {
103118 Timer workloadDuration = Timer .builder ("redis.workload.execution.duration" )
104- .description ("Time taken to complete a workload" ).tag ("workload" , workloadName ). tag ( "status" , status . toString ())
105- .register (meterRegistry );
119+ .description ("Time taken to complete a workload" ).tag ("workload" , workloadName )
120+ .tag ( "status" , status . name (). toLowerCase ()). register (meterRegistry );
106121 sample .stop (workloadDuration );
107122 }
108123
@@ -119,21 +134,50 @@ public void incrementReconnectAttempt(ConnectionKey connectionKey) {
119134 reconnectAttemptTotalCounter .increment ();
120135 }
121136
137+ public void incrementRedisConnectionAttempt (ReconnectAttemptKey reconnectAttempt ) {
138+ redisConnectionsTotal .computeIfAbsent (reconnectAttempt , this ::createRedisConnectionsTotal ).increment ();
139+ }
140+
141+ public void incrementRedisConnectionDrops (ConnectionKey connectionKey ) {
142+ redisConnectionsDrops .computeIfAbsent (connectionKey , this ::createRedisConnectionsDrops ).increment ();
143+ }
144+
122145 public void incrementReconnectFailure (ConnectionKey connectionKey ) {
123146 reconnectFailureCounter .computeIfAbsent (connectionKey , this ::createReconnectFailedAttempCounter ).increment ();
124147 reconnectFailureTotalCounter .increment ();
125148 }
126149
127- private Timer createCommandLatencyTimer (String commandName ) {
128- return Timer .builder ("redis.command.latency" ).description (
150+ public enum PubSubOperation {
151+ SUBSCRIBE , UNSUBSCRIBE , PUBLISH , RECEIVE
152+ }
153+
154+ public record PubSubOpKey (String channel , PubSubOperation operation , String subsriberId , OperationStatus status ) {
155+
156+ }
157+
158+ public void incrementPubSubOperation (PubSubOpKey pubSubOpKey ) {
159+ pubSubOperationCounter .computeIfAbsent (pubSubOpKey , this ::createPubSubOperationCounter ).increment ();
160+ }
161+
162+ private Counter createPubSubOperationCounter (PubSubOpKey pubSubOpKey ) {
163+ return Counter .builder ("redis.pubsub.operations.total" )
164+ .description ("Total number of Redis pub/sub operations (publish and receive)" )
165+ .tag ("channel" , pubSubOpKey .channel ).tag ("operation_type" , pubSubOpKey .operation .name ().toLowerCase ())
166+ .tag ("subscriber_id" , pubSubOpKey .subsriberId ).tag ("status" , pubSubOpKey .status .name ().toLowerCase ())
167+ .register (meterRegistry );
168+ }
169+
170+ private Timer createCommandLatencyTimer (CommandKey commandKey ) {
171+ return Timer .builder ("redis.operation.duration" ).description (
129172 "Measures the execution time of Redis commands from API invocation until command completion per command" )
130- .tag ("command" , commandName ).register (meterRegistry );
173+ .tag ("command" , commandKey .commandName ).tag ("status" , commandKey .status .name ().toLowerCase ())
174+ .publishPercentileHistogram (true ).publishPercentiles (0.5 , 0.95 , 0.99 ).register (meterRegistry );
131175 }
132176
133177 private Timer createCommandLatencyTotalTimer () {
134- return Timer .builder ("redis.command .total.latency " )
178+ return Timer .builder ("redis.operation .total.duration " )
135179 .description ("Measures the execution time of Redis commands from API invocation until command completion" )
136- .register (meterRegistry );
180+ .publishPercentileHistogram ( true ). publishPercentiles ( 0.5 , 0.95 , 0.99 ). register (meterRegistry );
137181 }
138182
139183 private Counter createCommandErrorCounter (String commandName ) {
@@ -142,12 +186,39 @@ private Counter createCommandErrorCounter(String commandName) {
142186 .tag ("command" , commandName ).register (meterRegistry );
143187 }
144188
189+ private Counter createCommandTotalCounter (CommandKey commandKey ) {
190+ return Counter .builder ("redis.operations.total" )
191+ .description ("Counts the number of total Redis command API calls completed successfully or with an error" )
192+ .tag ("command" , commandKey .commandName ).tag ("status" , commandKey .status .name ().toLowerCase ())
193+ .register (meterRegistry );
194+ }
195+
145196 private Counter createCommandErrorTotalCounter () {
146197 return Counter .builder ("redis.command.total.errors" )
147198 .description ("Counts the number of failed Redis command API calls that completed with an exception" )
148199 .register (meterRegistry );
149200 }
150201
202+ public record ReconnectAttemptKey (ConnectionKey connectionKey , OperationStatus status ) {
203+ }
204+
205+ private Counter createRedisConnectionsTotal (ReconnectAttemptKey reconnectAttempt ) {
206+ ConnectionKey connectionKey = reconnectAttempt .connectionKey ;
207+ OperationStatus status = reconnectAttempt .status ;
208+
209+ return Counter .builder ("redis.connections.total" )
210+ .description ("Counts the number of Redis reconnect attempts per connection" )
211+ .tag ("status" , status .name ().toLowerCase ()).tag ("epid" , connectionKey .getEpId ())
212+ .tag ("local" , connectionKey .getLocalAddress ().toString ())
213+ .tag ("remote" , connectionKey .getRemoteAddress ().toString ()).register (meterRegistry );
214+ }
215+
216+ private Counter createRedisConnectionsDrops (ConnectionKey connectionKey ) {
217+ return Counter .builder ("redis.connection.drops.total" ).description ("Counts the number of disconnects" )
218+ .tag ("epid" , connectionKey .getEpId ()).tag ("local" , connectionKey .getLocalAddress ().toString ())
219+ .tag ("remote" , connectionKey .getRemoteAddress ().toString ()).register (meterRegistry );
220+ }
221+
151222 private Counter createReconnectAttemptCounter (ConnectionKey connectionKey ) {
152223 return Counter .builder ("lettuce.reconnect.attempts" )
153224 .description ("Counts the number of Redis reconnect attempts per connection" )
@@ -214,4 +285,12 @@ public void shutdown() {
214285 }
215286 }
216287
288+ public static CommandKey cmdKeyOk (String commandName ) {
289+ return new CommandKey (commandName , OperationStatus .SUCCESS );
290+ }
291+
292+ public static CommandKey cmdKeyError (String commandName , Throwable throwable ) {
293+ return new CommandKey (commandName , OperationStatus .ERROR );
294+ }
295+
217296}
0 commit comments