o
    iX                     @   s  d Z ddlZddlZddlmZ ddlmZmZmZm	Z	 er6ddl
mZ ddlmZ ddlmZ ddlmZ dd	lmZmZmZmZmZmZmZmZ dd
lmZmZ ddlmZ e e!Z"z
ddl#m$Z$ dZ%W n e&yz   dZ%dZ'dZ(dZ$dZ)Y nw G dd deZ*G dd dZ+dS )z
OpenTelemetry metrics collector for redis-py.

This module defines and manages all metric instruments according to
OTel semantic conventions for database clients.
    N)Enum)TYPE_CHECKINGCallableOptionalUnion)ConnectionPool)AsyncDatabase)ConnectionPoolInterface)SyncDatabase)$REDIS_CLIENT_CONNECTION_CLOSE_REASON$REDIS_CLIENT_CONNECTION_NOTIFICATIONAttributeBuilder	CSCReason	CSCResultGeoFailoverReasonPubSubDirectionget_pool_name)MetricGroup
OTelConfig)deprecated_args)MeterTFc                   @   s   e Zd ZdZdZdZdZdS )CloseReasona  
    Enum representing the reason why a Redis client connection was closed.

    Values:
        APPLICATION_CLOSE: The connection was closed intentionally by the application
            (for example, during normal shutdown or explicit cleanup).
        ERROR: The connection was closed due to an unexpected error
            (for example, network failure or protocol error).
        HEALTHCHECK_FAILED: The connection was closed because a health check
            or liveness check for the connection failed.
    application_closeerrorhealthcheck_failedN)__name__
__module____qualname____doc__APPLICATION_CLOSEERRORHEALTHCHECK_FAILED r"   r"   O/home/ubuntu/.local/lib/python3.10/site-packages/redis/observability/metrics.pyr   /   s
    r   c                   @   s
  e Zd ZdZdZdZdedefddZddd
dZ	ddddZ
ddddZddddZddddZddddZddddZ														dedee dee dee dee dee dee dee fdd Zdedededed!ef
d"d#Zd$ed% d&ed% d'efd(d)Zd*edd	fd+d,Zd*edd	fd-d.Zd/edd	fd0d1Zd2ed3 d4edd	fd5d6Zd/ed4edd	fd7d8Z e!d9gd:d;d<																		dfd=ed4edee dee d>ee d9ee dee dee dee dee d?ee dd	fd@dAZ"				dgdBee# dee dd	fdCdDZ$dEed!edFedd	fdGdHZ%d/edd	fdIdJZ&				dgdKe'dLee dMee dd	fdNdOZ(e!dPgdQd;d<						dhdRedSee dTee dPee dd	f
dUdVZ)		didWee* dd	fdXdYZ+		didZed'ee, dd	fd[d\Z-d]edd	fd^d_Z.e/defd`daZ0defdbdcZ1d	S )jRedisMetricsCollectorap  
    Collects and records OpenTelemetry metrics for Redis operations.

    This class manages all metric instruments and provides methods to record
    various Redis operations including connection pool events, command execution,
    and cluster-specific operations.

    Args:
        meter: OpenTelemetry Meter instance
        config: OTel configuration object
    zredis-pyz1.0.0meterconfigc                 C   s   t std|| _|| _t | _d | _tj| jj	v r| 
  tj| jj	v r)|   tj| jj	v r4|   tj| jj	v r?|   tj| jj	v rJ|   tj| jj	v rU|   tj| jj	v r`|   td d S )NzROpenTelemetry API is not installed. Install it with: pip install opentelemetry-apiz!RedisMetricsCollector initialized)OTEL_AVAILABLEImportErrorr%   r&   r   attr_builderconnection_countr   
RESILIENCYmetric_groups_init_resiliency_metricsCOMMAND_init_command_metricsCONNECTION_BASIC_init_connection_basic_metricsCONNECTION_ADVANCED!_init_connection_advanced_metricsPUBSUB_init_pubsub_metrics	STREAMING_init_streaming_metricsCSC_init_csc_metricsloggerinfo)selfr%   r&   r"   r"   r#   __init__Q   s.   zRedisMetricsCollector.__init__returnNc                 C   @   | j jdddd| _| j jdddd| _| j jdd	d
d| _dS )zInitialize resiliency metrics.zredis.client.errorsz{error}z`A counter of all errors (both returned to the user and handled internally in the client library)nameunitdescriptionz&redis.client.maintenance.notificationsz{notification}z,Tracks server-side maintenance notificationsz"redis.client.geofailover.failoversz{geofailover}z6Total count of failovers happened using MultiDbClient.N)r%   create_counterclient_errorsmaintenance_notificationsgeo_failoversr<   r"   r"   r#   r-   v      z.RedisMetricsCollector._init_resiliency_metricsc                 C   sF   | j jddd| jjd| _| j jdddd| _| j jd	d
dd| _dS )z$Initialize basic connection metrics.z db.client.connection.create_timeszTime to create a new connectionrA   rB   rC   #explicit_bucket_boundaries_advisoryz'redis.client.connection.relaxed_timeoutz{relaxation}z@Counts up for relaxed timeout, counts down for unrelaxed timeoutr@   zredis.client.connection.handoffz	{handoff}zIConnections that have been handed off (e.g., after a MOVING notification)N)	r%   create_histogramr&   buckets_connection_create_timeconnection_create_timecreate_up_down_counterconnection_relaxed_timeoutrD   connection_handoffrH   r"   r"   r#   r1      s    z4RedisMetricsCollector._init_connection_basic_metricsc                 C   sF   | j jdddd| _| j jddd| jjd| _| j jd	d
dd| _dS )z'Initialize advanced connection metrics.zdb.client.connection.timeoutsz	{timeout}zaThe number of connection timeouts that have occurred trying to obtain a connection from the pool.r@   zdb.client.connection.wait_timerJ   z/Time to obtain an open connection from the poolrK   zredis.client.connection.closed{connection}z"Total number of closed connectionsN)r%   rD   connection_timeoutsrM   r&   buckets_connection_wait_timeconnection_wait_timeconnection_closedrH   r"   r"   r#   r3      s    z7RedisMetricsCollector._init_connection_advanced_metricsc                 C      | j jddd| jjd| _dS )z0Initialize command execution metric instruments.zdb.client.operation.durationrJ   zCommand execution durationrK   N)r%   rM   r&   buckets_operation_durationoperation_durationrH   r"   r"   r#   r/         z+RedisMetricsCollector._init_command_metricsc                 C   s   | j jdddd| _dS )z%Initialize PubSub metric instruments.zredis.client.pubsub.messagesz	{message}z&Tracks published and received messagesr@   N)r%   rD   pubsub_messagesrH   r"   r"   r#   r5      s
   z*RedisMetricsCollector._init_pubsub_metricsc                 C   rX   )z(Initialize Streaming metric instruments.zredis.client.stream.lagrJ   zkEnd-to-end lag per message, showing how stale are the messages when the application starts processing them.rK   N)r%   rM   r&   "buckets_stream_processing_duration
stream_lagrH   r"   r"   r#   r7      r[   z-RedisMetricsCollector._init_streaming_metricsc                 C   r?   )z8Initialize Client Side Caching (CSC) metric instruments.zredis.client.csc.requestsz	{request}z)The total number of requests to the cacher@   zredis.client.csc.evictionsz
{eviction}z#The total number of cache evictionszredis.client.csc.network_savedByz,The total number of bytes saved by using CSCN)r%   rD   csc_requestscsc_evictionscsc_network_savedrH   r"   r"   r#   r9      rI   z'RedisMetricsCollector._init_csc_metricsserver_addressserver_portnetwork_peer_addressnetwork_peer_port
error_typeretry_attemptsis_internalc           	      C   s`   t | dsdS | jj||d}|| jj|||d || jj||d | jjd|d dS )a  
        Record error count

        Args:
            server_address: Server address
            server_port: Server port
            network_peer_address: Network peer address
            network_peer_port: Network peer port
            error_type: Error type
            retry_attempts: Retry attempts
            is_internal: Whether the error is internal (e.g., timeout, network error)
        rE   Nrc   rd   )re   rf   rh   )rg   ri      
attributes)hasattrr)   build_base_attributesupdatebuild_operation_attributesbuild_error_attributesrE   add)	r<   rc   rd   re   rf   rg   rh   ri   attrsr"   r"   r#   record_error_count   s(   
z(RedisMetricsCollector.record_error_countmaint_notificationc                 C   sP   t | dsdS | jj||d}|| jj||d ||t< | jjd|d dS )a7  
        Record maintenance notification count

        Args:
            server_address: Server address
            server_port: Server port
            network_peer_address: Network peer address
            network_peer_port: Network peer port
            maint_notification: Maintenance notification
        rF   Nrj   )re   rf   rk   rl   )rn   r)   ro   rp   rq   r   rF   rs   )r<   rc   rd   re   rf   rv   rt   r"   r"   r#   record_maint_notification_count  s   
z5RedisMetricsCollector.record_maint_notification_count	fail_from)r
   r   fail_toreasonc                 C   s0   t | dsdS | jj|||d}| jjd|dS )z
        Record geo failover

        Args:
            fail_from: Database failed from
            fail_to: Database failed to
            reason: Reason for the failover
        rG   N)rx   ry   rz   rk   rl   )rn   r)   build_geo_failover_attributesrG   rs   )r<   rx   ry   rz   rt   r"   r"   r#   record_geo_failover6  s   
z)RedisMetricsCollector.record_geo_failovercallbackc                 C   4   t j| jjvr| jsdS | jjddd|gd| _dS )z
        Initialize observable gauge for connection count metric.

        Args:
            callback: Callback function to retrieve connection count
        Nzdb.client.connection.countrS   z!Number of connections in the poolrA   rB   rC   	callbacks)r   r0   r&   r,   r*   r%   create_observable_gauger<   r}   r"   r"   r#   init_connection_countP  s   z+RedisMetricsCollector.init_connection_countc                 C   r~   )z
        Initialize observable gauge for CSC items metric.

        Args:
            callback: Callback function to retrieve CSC items count
        Nzredis.client.csc.itemsz{item}z5The total number of cached responses currently storedr   )r   r8   r&   r,   	csc_itemsr%   r   r   r"   r"   r#   init_csc_itemsg  s   
z$RedisMetricsCollector.init_csc_items	pool_namec                 C   0   t | dsdS | jj|d}| jjd|d dS )zo
        Record a connection timeout event.

        Args:
            pool_name: Connection pool name
        rT   Nr   rk   rl   )rn   r)   build_connection_attributesrT   rs   r<   r   rt   r"   r"   r#   record_connection_timeout{  s   
z/RedisMetricsCollector.record_connection_timeoutconnection_pool)r	   r   duration_secondsc                 C   s4   t | dsdS | jjt|d}| jj||d dS )z
        Record time taken to create a new connection.

        Args:
            connection_pool: Connection pool implementation
            duration_seconds: Creation time in seconds
        rO   Nr   rl   )rn   r)   r   r   rO   record)r<   r   r   rt   r"   r"   r#   record_connection_create_time  s   
z3RedisMetricsCollector.record_connection_create_timec                 C   s0   t | dsdS | jj|d}| jj||d dS )z
        Record time taken to obtain a connection from the pool.

        Args:
            pool_name: Connection pool name
            duration_seconds: Wait time in seconds
        rV   Nr   rl   )rn   r)   r   rV   r   )r<   r   r   rt   r"   r"   r#   record_connection_wait_time     
z1RedisMetricsCollector.record_connection_wait_time
batch_sizezXThe batch_size argument is no longer used and will be removed in the next major version.z7.2.1)args_to_warnrz   versioncommand_namedb_namespaceis_blockingc              	   C   st   t | dsdS | j|sdS | jj|||d}|| jj|||	|
|d || jj|d | jj	||d dS )a  
        Record command execution duration.

        Args:
            command_name: Redis command name (e.g., 'GET', 'SET', 'MULTI')
            duration_seconds: Execution time in seconds
            server_address: Redis server address
            server_port: Redis server port
            db_namespace: Redis database index
            batch_size: Number of commands in batch (for pipelines/transactions)
            error_type: Error type if operation failed
            network_peer_address: Resolved peer address
            network_peer_port: Peer port number
            retry_attempts: Number of retry attempts made
            is_blocking: Whether the operation is a blocking command
        rZ   N)rc   rd   r   )r   re   rf   rh   r   rg   rl   )
rn   r&   should_track_commandr)   ro   rp   rq   rr   rZ   r   )r<   r   r   rc   rd   r   r   rg   re   rf   rh   r   rt   r"   r"   r#   record_operation_duration  s0   
#
z/RedisMetricsCollector.record_operation_durationclose_reasonc                 C   sN   t | dsdS | j }|r|j|t< || jj|d | jjd|d dS )z
        Record a connection closed event.

        Args:
            close_reason: Reason for closing (e.g. 'error', 'application_close')
            error_type: Error type if closed due to error
        rW   Nr   rk   rl   )	rn   r)   r   valuer   rp   rr   rW   rs   )r<   r   rg   rt   r"   r"   r#   record_connection_closed  s   


z.RedisMetricsCollector.record_connection_closedconnection_namerelaxedc                 C   s@   t | dsdS | jj|d}||t< | jj|rdnd|d dS )a
  
        Record a connection timeout relaxation event.

        Args:
            connection_name: Connection name
            maint_notification: Maintenance notification type
            relaxed: True to count up (relaxed), False to count down (unrelaxed)
        rQ   N)r   rk   rl   )rn   r)   r   r   rQ   rs   )r<   r   rv   r   rt   r"   r"   r#   !record_connection_relaxed_timeout  s   
z7RedisMetricsCollector.record_connection_relaxed_timeoutc                 C   r   )z
        Record a connection handoff event (e.g., after MOVING notification).

        Args:
            pool_name: Connection pool name
        rR   Nr   rk   rl   )rn   r)   r   rR   rs   r   r"   r"   r#   record_connection_handoff$     

z/RedisMetricsCollector.record_connection_handoff	directionchannelshardedc                 C   s4   t | dsdS | jj|||d}| jjd|d dS )z
        Record a PubSub message (published or received).

        Args:
            direction: Message direction ('publish' or 'receive')
            channel: Pub/Sub channel name
            sharded: True if sharded Pub/Sub channel
        r\   N)r   r   r   rk   rl   )rn   r)   build_pubsub_message_attributesr\   rs   )r<   r   r   r   rt   r"   r"   r#   record_pubsub_message6  s   
z+RedisMetricsCollector.record_pubsub_messageconsumer_namez[The consumer_name argument is no longer used and will be removed in the next major version.lag_secondsstream_nameconsumer_groupc                 C   s2   t | dsdS | jj||d}| jj||d dS )z
        Record the lag of a streaming message.

        Args:
            lag_seconds: Lag in seconds
            stream_name: Stream name
            consumer_group: Consumer group name
            consumer_name: Consumer name
        r^   N)r   r   rl   )rn   r)   build_streaming_attributesr^   r   )r<   r   r   r   r   rt   r"   r"   r#   record_streaming_lagP  s   
z*RedisMetricsCollector.record_streaming_lagresultc                 C   r   )z}
        Record a Client Side Caching (CSC) request.

        Args:
            result: CSC result ('hit' or 'miss')
        r`   N)r   rk   rl   )rn   r)   build_csc_attributesr`   rs   )r<   r   rt   r"   r"   r#   record_csc_requestp  r   z(RedisMetricsCollector.record_csc_requestcountc                 C   s0   t | dsdS | jj|d}| jj||d dS )z
        Record a Client Side Caching (CSC) eviction.

        Args:
            count: Number of evictions
            reason: Reason for eviction
        ra   N)rz   rl   )rn   r)   r   ra   rs   )r<   r   rz   rt   r"   r"   r#   record_csc_eviction  r   z)RedisMetricsCollector.record_csc_evictionbytes_savedc                 C   s,   t | dsdS | j }| jj||d dS )z
        Record the number of bytes saved by using Client Side Caching (CSC).

        Args:
            bytes_saved: Number of bytes saved
        rb   Nrl   )rn   r)   r   rb   rs   )r<   r   rt   r"   r"   r#   record_csc_network_saved  s   


z.RedisMetricsCollector.record_csc_network_savedc                   C   s   t  S )z
        Get monotonic time for duration measurements.

        Returns:
            Current monotonic time in seconds
        )time	monotonicr"   r"   r"   r#   monotonic_time  s   z$RedisMetricsCollector.monotonic_timec                 C   s   d| j  d| j dS )NzRedisMetricsCollector(meter=z	, config=))r%   r&   rH   r"   r"   r#   __repr__  s   zRedisMetricsCollector.__repr__)r>   N)NNNNNNN)	NNNNNNNNN)NN)NNN)N)2r   r   r   r   
METER_NAMEMETER_VERSIONr   r   r=   r-   r1   r3   r/   r5   r7   r9   r   strint	Exceptionboolru   rw   r   r   r|   r   r   r   r   floatr   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   staticmethodr   r   r"   r"   r"   r#   r$   A   s~   
%



	

	
.
$




		
?






	r$   ),r   loggingr   enumr   typingr   r   r   r   redis.asyncio.connectionr   redis.asyncio.multidb.databaser   redis.connectionr	   redis.multidb.databaser
   redis.observability.attributesr   r   r   r   r   r   r   r   redis.observability.configr   r   redis.utilsr   	getLoggerr   r:   opentelemetry.metricsr   r'   r(   Counter	HistogramUpDownCounterr   r$   r"   r"   r"   r#   <module>   s4    (

