11#if NET6_0_OR_GREATER
22
3+ using System ;
34using System . Collections . Generic ;
5+ using System . Diagnostics ;
46using System . Diagnostics . Metrics ;
57
68namespace StackExchange . Redis ;
79
810internal sealed class RedisMetrics
911{
12+ private static readonly double s_tickFrequency = ( double ) TimeSpan . TicksPerSecond / Stopwatch . Frequency ;
13+ // cache these boxed boolean values so we don't allocate on each usage.
14+ private static readonly object s_trueBox = true ;
15+ private static readonly object s_falseBox = false ;
16+
1017 private readonly Meter _meter ;
1118 private readonly Counter < long > _operationCount ;
12- private readonly Counter < long > _completedAsynchronously ;
13- private readonly Counter < long > _completedSynchronously ;
14- private readonly Counter < long > _failedAsynchronously ;
15- private readonly Counter < long > _failedSynchronously ;
19+ private readonly Histogram < double > _messageDuration ;
1620 private readonly Counter < long > _nonPreferredEndpointCount ;
1721
1822 public static readonly RedisMetrics Instance = new RedisMetrics ( ) ;
@@ -22,67 +26,53 @@ private RedisMetrics()
2226 _meter = new Meter ( "StackExchange.Redis" ) ;
2327
2428 _operationCount = _meter . CreateCounter < long > (
25- "redis- operation- count" ,
29+ "db. redis. operation. count" ,
2630 description : "The number of operations performed." ) ;
2731
28- _completedAsynchronously = _meter . CreateCounter < long > (
29- "redis-completed-asynchronously" ,
30- description : "The number of operations that have been completed asynchronously." ) ;
31-
32- _completedSynchronously = _meter . CreateCounter < long > (
33- "redis-completed-synchronously" ,
34- description : "The number of operations that have been completed synchronously." ) ;
35-
36- _failedAsynchronously = _meter . CreateCounter < long > (
37- "redis-failed-asynchronously" ,
38- description : "The number of operations that failed to complete asynchronously." ) ;
39-
40- _failedSynchronously = _meter . CreateCounter < long > (
41- "redis-failed-synchronously" ,
42- description : "The number of operations that failed to complete synchronously." ) ;
32+ _messageDuration = _meter . CreateHistogram < double > (
33+ "db.redis.duration" ,
34+ unit : "s" ,
35+ description : "Measures the duration of outbound message requests." ) ;
4336
4437 _nonPreferredEndpointCount = _meter . CreateCounter < long > (
45- "redis-non-preferred-endpoint- count" ,
38+ "db. redis.non_preferred_endpoint. count" ,
4639 description : "Indicates the total number of messages dispatched to a non-preferred endpoint, for example sent to a primary when the caller stated a preference of replica." ) ;
4740 }
4841
4942 public void IncrementOperationCount ( string endpoint )
5043 {
51- if ( _operationCount . Enabled )
52- {
53- _operationCount . Add ( 1 ,
54- new KeyValuePair < string , object ? > ( "endpoint" , endpoint ) ) ;
55- }
44+ _operationCount . Add ( 1 ,
45+ new KeyValuePair < string , object ? > ( "endpoint" , endpoint ) ) ;
5646 }
5747
58- public void OnMessageComplete ( IResultBox ? result )
48+ public void OnMessageComplete ( Message message , IResultBox ? result )
5949 {
60- if ( result is not null &&
61- ( _completedAsynchronously . Enabled ||
62- _completedSynchronously . Enabled ||
63- _failedAsynchronously . Enabled ||
64- _failedSynchronously . Enabled ) )
50+ // The caller ensures we can don't record on the same resultBox from two threads.
51+ // 'result' can be null if this method is called for the same message more than once.
52+ if ( result is not null && _messageDuration . Enabled )
6553 {
66- Counter < long > counter = ( result . IsFaulted , result . IsAsync ) switch
54+ // Stopwatch.GetElapsedTime is only available in net7.0+
55+ // https://github.com/dotnet/runtime/blob/ae068fec6ede58d2a5b343c5ac41c9ca8715fa47/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Stopwatch.cs#L129-L137
56+ var now = Stopwatch . GetTimestamp ( ) ;
57+ var duration = new TimeSpan ( ( long ) ( ( now - message . CreatedTimestamp ) * s_tickFrequency ) ) ;
58+
59+ var tags = new TagList
6760 {
68- ( false , true ) => _completedAsynchronously ,
69- ( false , false ) => _completedSynchronously ,
70- ( true , true ) => _failedAsynchronously ,
71- ( true , false ) => _failedSynchronously ,
61+ { "db.redis.async" , result . IsAsync ? s_trueBox : s_falseBox } ,
62+ { "db.redis.faulted" , result . IsFaulted ? s_trueBox : s_falseBox }
63+ // TODO: can we pass endpoint here?
64+ // should we log the Db?
65+ // { "db.redis.database_index", message.Db },
7266 } ;
7367
74- // TODO: can we pass endpoint here?
75- counter . Add ( 1 ) ;
68+ _messageDuration . Record ( duration . TotalSeconds , tags ) ;
7669 }
7770 }
7871
7972 public void IncrementNonPreferredEndpointCount ( string endpoint )
8073 {
81- if ( _nonPreferredEndpointCount . Enabled )
82- {
83- _nonPreferredEndpointCount . Add ( 1 ,
84- new KeyValuePair < string , object ? > ( "endpoint" , endpoint ) ) ;
85- }
74+ _nonPreferredEndpointCount . Add ( 1 ,
75+ new KeyValuePair < string , object ? > ( "endpoint" , endpoint ) ) ;
8676 }
8777}
8878
0 commit comments