o
    <i9_                     @   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mZ dd
lmZmZ ddlm Z m!Z! e"e#Z$z
ddl%m&Z& dZ'W n e(y~   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ConnectionState	CSCReason	CSCResultGeoFailoverReasonPubSubDirectionget_pool_name)MetricGroup
OTelConfig)deprecated_argsdeprecated_function)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$   Z/root/parts/websockify/install/lib/python3.10/site-packages/redis/observability/metrics.pyr   0   s
    r   c                   @   s4  e Zd ZdZdZdZdedefddZdld
dZ	dlddZ
dlddZdlddZdlddZdlddZdlddZ														dmd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	*dnd+ed,ed-edd	fd.d/Zed0d1d2d3edd	fd4d5Zd3edd	fd6d7Zd+edd	fd8d9Z d:ed; d<e!dd	fd=d>Z"d+ed<e!dd	fd?d@Z#e$dAgdBdCdD																		dodEed<e!dee dee dFee dAee dee dee dee dee dGee dd	fdHdIZ%				dpdJee& dee dd	fdKdLZ'dMed!edNedd	fdOdPZ(d+edd	fdQdRZ)				dpdSe*dTee dUee dd	fdVdWZ+e$dXgdYdCdD						dqdZe!d[ee d\ee dXee dd	f
d]d^Z,		drd_ee- dd	fd`daZ.		drdbed'ee/ dd	fdcddZ0deedd	fdfdgZ1e2de!fdhdiZ3defdjdkZ4d	S )s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 | _tj| jjv r| 	  tj
| jjv r&|   tj| jjv r1|   tj| jjv r<|   tj| jjv rG|   tj| jjv rR|   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_builderr   
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__R   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   s`   | j jddd| jjd| _| j jdddd| _| j jd	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rB   rC   rD   #explicit_bucket_boundaries_advisoryz'redis.client.connection.relaxed_timeoutz{relaxation}z@Counts up for relaxed timeout, counts down for unrelaxed timeoutrA   zredis.client.connection.handoffz	{handoff}zIConnections that have been handed off (e.g., after a MOVING notification)Nzdb.client.connection.count{connection}z4Number of connections currently in the pool by state)r'   create_histogramr(   buckets_connection_create_timeconnection_create_timecreate_up_down_counterconnection_relaxed_timeoutrE   connection_handoffconnection_countconnection_count_updownrI   r$   r$   r%   r2      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.rA   zdb.client.connection.wait_timerK   z/Time to obtain an open connection from the poolrL   zredis.client.connection.closedrN   z"Total number of closed connectionsN)r'   rE   connection_timeoutsrO   r(   buckets_connection_wait_timeconnection_wait_timeconnection_closedrI   r$   r$   r%   r4      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.durationrK   zCommand execution durationrL   N)r'   rO   r(   buckets_operation_durationoperation_durationrI   r$   r$   r%   r0         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 messagesrA   N)r'   rE   pubsub_messagesrI   r$   r$   r%   r6      s
   z*RedisMetricsCollector._init_pubsub_metricsc                 C   r[   )z(Initialize Streaming metric instruments.zredis.client.stream.lagrK   zkEnd-to-end lag per message, showing how stale are the messages when the application starts processing them.rL   N)r'   rO   r(   "buckets_stream_processing_duration
stream_lagrI   r$   r$   r%   r8      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 cacherA   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'   rE   csc_requestscsc_evictionscsc_network_savedrI   r$   r$   r%   r:      rJ   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)
        rF   Nrf   rg   )rh   ri   rk   )rj   rl      
attributes)hasattrr+   build_base_attributesupdatebuild_operation_attributesbuild_error_attributesrF   add)	r=   rf   rg   rh   ri   rj   rk   rl   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
        rG   Nrm   )rh   ri   rn   ro   )rq   r+   rr   rs   rt   r   rG   rv   )r=   rf   rg   rh   ri   ry   rw   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
        rH   N)r{   r|   r}   rn   ro   )rq   r+   build_geo_failover_attributesrH   rv   )r=   r{   r|   r}   rw   r$   r$   r%   record_geo_failoverC  s   
z)RedisMetricsCollector.record_geo_failoverrn   	pool_nameconnection_statecounterc                 C   s2   t | dsdS | jj||d}| jj||d dS )a  
        Record a connection count change for a single state.

        Args:
            pool_name: Connection pool name
            connection_state: State to update (IDLE or USED)
            counter: Number to add (positive) or subtract (negative)
        rV   N)r   r   ro   )rq   r+   build_connection_attributesrV   rv   )r=   r   r   r   rw   r$   r$   r%   record_connection_count]  s   
z-RedisMetricsCollector.record_connection_countz{Connection count is now tracked via record_connection_count(). This functionality will be removed in the next major versionz7.4.0)r}   versioncallbackc                 C   s.   t j| jjvr	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 counts
        Nz%db.client.connection.count.deprecatedrN   zThe number of connections that are currently in state described by the state attribute (deprecated - use db.client.connection.count instead)rB   rC   rD   	callbacks)r   r1   r(   r-   r'   create_observable_gaugerU   r=   r   r$   r$   r%   init_connection_countt  s   z+RedisMetricsCollector.init_connection_countc                 C   s4   t j| jjvr| jsdS | jjddd|gd| _dS )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   r9   r(   r-   	csc_itemsr'   r   r   r$   r$   r%   init_csc_items  s   
z$RedisMetricsCollector.init_csc_itemsc                 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
        rW   Nr   rn   ro   )rq   r+   r   rW   rv   r=   r   rw   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
        rQ   Nr   ro   )rq   r+   r   r   rQ   record)r=   r   r   rw   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
        rY   Nr   ro   )rq   r+   r   rY   r   )r=   r   r   rw   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_warnr}   r   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
        r]   N)rf   rg   r   )r   rh   ri   rk   r   rj   ro   )
rq   r(   should_track_commandr+   rr   rs   rt   ru   r]   r   )r=   r   r   rf   rg   r   r   rj   rh   ri   rk   r   rw   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
        rZ   Nr   rn   ro   )	rq   r+   r   valuer   rs   ru   rZ   rv   )r=   r   rj   rw   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)
        rS   Nr   rn   ro   )rq   r+   r   r   rS   rv   )r=   r   ry   r   rw   r$   r$   r%   !record_connection_relaxed_timeout7  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
        rT   Nr   rn   ro   )rq   r+   r   rT   rv   r   r$   r$   r%   record_connection_handoffL     

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   rn   ro   )rq   r+   build_pubsub_message_attributesr_   rv   )r=   r   r   r   rw   r$   r$   r%   record_pubsub_message^  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
        ra   N)r   r   ro   )rq   r+   build_streaming_attributesra   r   )r=   r   r   r   r   rw   r$   r$   r%   record_streaming_lagx  s   
z*RedisMetricsCollector.record_streaming_lagresultc                 C   r   )z}
        Record a Client Side Caching (CSC) request.

        Args:
            result: CSC result ('hit' or 'miss')
        rc   N)r   rn   ro   )rq   r+   build_csc_attributesrc   rv   )r=   r   rw   r$   r$   r%   record_csc_request  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
        rd   N)r}   ro   )rq   r+   r   rd   rv   )r=   r   r}   rw   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
        re   Nro   )rq   r+   r   re   rv   )r=   r   rw   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(   rI   r$   r$   r%   __repr__  s   zRedisMetricsCollector.__repr__)r?   N)NNNNNNN)rn   )	NNNNNNNNN)NN)NNN)N)5r   r   r   r    
METER_NAMEMETER_VERSIONr   r   r>   r.   r2   r4   r0   r6   r8   r:   r   strint	Exceptionboolrx   rz   r   r   r   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&   B   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   r   redis.observability.configr   r   redis.utilsr   r   	getLoggerr   r;   opentelemetry.metricsr   r)   r*   Counter	HistogramUpDownCounterr   r&   r$   r$   r$   r%   <module>   s4    ,
