o
    i/                     @   s   U d Z ddlZddlmZ ddlmZ eeZz
ddl	m
Z
 dZW n ey/   dZdZ
Y nw dZed ed	< G d
d dZG dd dZdaee ed< defddZdddZdS )a  
OpenTelemetry provider management for redis-py.

This module handles initialization and lifecycle management of OTel SDK components
including MeterProvider, TracerProvider (future), and LoggerProvider (future).

Uses a singleton pattern - initialize once globally, all Redis clients use it automatically.

Redis-py uses the global MeterProvider set by your application. Set it up before
initializing observability:

    from opentelemetry import metrics
    from opentelemetry.sdk.metrics import MeterProvider

    provider = MeterProvider(...)
    metrics.set_meter_provider(provider)

    # Then initialize redis-py observability
    otel = get_observability_instance()
    otel.init(OTelConfig(enable_metrics=True))
    N)Optional)
OTelConfig)MeterProviderTFOTelProviderManager_global_provider_managerc                   @   sv   e Zd ZdZdefddZdee fddZdd	e	de
fd
dZdd	e	de
fddZdd Zdd ZdefddZdS )r   a  
    Manages OpenTelemetry SDK providers and their lifecycle.

    This class handles:
    - Getting the global MeterProvider set by the application
    - Configuring histogram bucket boundaries via Views
    - Graceful shutdown

    Args:
        config: OTel configuration object
    configc                 C   s   || _ d | _d S N)r   _meter_providerselfr    r   Q/home/ubuntu/.local/lib/python3.10/site-packages/redis/observability/providers.py__init__8   s   
zOTelProviderManager.__init__returnc                 C   sz   | j  sdS zddlm} ddlm} W n ty    tdw | jdu r:| | _t	| j|r5t
dtd | jS )a;  
        Get the global MeterProvider set by the application.

        Returns:
            MeterProvider instance or None if metrics are disabled

        Raises:
            ImportError: If OpenTelemetry is not installed
            RuntimeError: If metrics are enabled but no global MeterProvider is set
        Nr   )metrics)NoOpMeterProviderzOpenTelemetry is not installed. Install it with:
  pip install opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp-proto-httpa  Metrics are enabled but no global MeterProvider is configured.

Set up OpenTelemetry before initializing redis-py observability:

  from opentelemetry import metrics
  from opentelemetry.sdk.metrics import MeterProvider
  from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader
  from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter

  # Create exporter
  exporter = OTLPMetricExporter(
      endpoint='http://localhost:4318/v1/metrics'
  )

  # Create reader
  reader = PeriodicExportingMetricReader(
      exporter=exporter,
      export_interval_millis=10000
  )

  # Create and set global provider
  provider = MeterProvider(metric_readers=[reader])
  metrics.set_meter_provider(provider)

  # Now initialize redis-py observability
  from redis.observability import get_observability_instance, OTelConfig
  otel = get_observability_instance()
  otel.init(OTelConfig(enable_metrics=True))
z+Using global MeterProvider from application)r   
is_enabledopentelemetryr   opentelemetry.metricsr   ImportErrorr	   get_meter_provider
isinstanceRuntimeErrorloggerinfo)r   r   r   r   r   r   r   <   s$   



z&OTelProviderManager.get_meter_provider0u  timeout_millisc                 C   s   t d | j|dS )ai  
        Shutdown observability and flush any pending metrics.

        Note: We don't shutdown the global MeterProvider since it's owned by the application.
        We only force flush pending metrics.

        Args:
            timeout_millis: Maximum time to wait for flush

        Returns:
            True if flush was successful, False otherwise
        zIFlushing metrics before shutdown (not shutting down global MeterProvider)r   )r   debugforce_flushr   r   r   r   r   shutdown}   s   zOTelProviderManager.shutdownc              
   C   s~   | j du rdS t| j dstd dS ztd | j j|d W dS  ty> } ztd|  W Y d}~dS d}~ww )	z
        Force flush any pending metrics from the global MeterProvider.

        Args:
            timeout_millis: Maximum time to wait for flush

        Returns:
            True if flush was successful, False otherwise
        NTr   z4MeterProvider does not support force_flush, skippingz0Force flushing metrics from global MeterProviderr   zError flushing metrics: F)r	   hasattrr   r   r   	Exceptionerror)r   r   er   r   r   r      s   



zOTelProviderManager.force_flushc                 C   s   | S )zContext manager entry.r   r   r   r   r   	__enter__   s   zOTelProviderManager.__enter__c                 C   s   |    dS )z)Context manager exit - shutdown provider.N)r!   )r   	_exc_type_exc_val_exc_tbr   r   r   __exit__   s   zOTelProviderManager.__exit__c                 C   s   d| j  dS )NzOTelProviderManager(config=))r   r&   r   r   r   __repr__   s   zOTelProviderManager.__repr__Nr   )__name__
__module____qualname____doc__r   r   r   r   r   intboolr!   r   r'   r+   strr-   r   r   r   r   r   +   s    Ac                   @   sr   e Zd ZdZdd Zdedd fddZdefdd	Zde	e
 fd
dZddedefddZddedefddZdS )ObservabilityInstancea  
    Singleton instance for managing OpenTelemetry observability.

    This class follows the singleton pattern similar to Glide's GetOtelInstance().
    Use GetObservabilityInstance() to get the singleton instance, then call init()
    to initialize observability.

    Example:
        >>> from redis.observability.config import OTelConfig
        >>>
        >>> # Get singleton instance
        >>> otel = get_observability_instance()
        >>>
        >>> # Initialize once at app startup
        >>> otel.init(OTelConfig())
        >>>
        >>> # All Redis clients now automatically collect metrics
        >>> import redis
        >>> r = redis.Redis(host='localhost', port=6379)
        >>> r.set('key', 'value')  # Metrics collected automatically
    c                 C   s
   d | _ d S r   _provider_managerr&   r   r   r   r      s   
zObservabilityInstance.__init__r   r   c                 C   s6   | j durtd | j   t|| _ td | S )ax  
        Initialize OpenTelemetry observability globally for all Redis clients.

        This should be called once at application startup. After initialization,
        all Redis clients will automatically collect and export metrics without
        needing any additional configuration.

        Safe to call multiple times - will shutdown previous instance before
        initializing a new one.

        Args:
            config: OTel configuration object

        Returns:
            Self for method chaining

        Example:
            >>> otel = get_observability_instance()
            >>> otel.init(OTelConfig())
        NzCObservability already initialized. Shutting down previous instance.zObservability initialized)r8   r   warningr!   r   r   r
   r   r   r   init   s   



zObservabilityInstance.initc                 C   s   | j duo
| j j S )a4  
        Check if observability is enabled.

        Returns:
            True if observability is initialized and metrics are enabled

        Example:
            >>> otel = get_observability_instance()
            >>> if otel.is_enabled():
            ...     print("Metrics are being collected")
        N)r8   r   r   r&   r   r   r   r      s   

z ObservabilityInstance.is_enabledc                 C   s   | j S )az  
        Get the provider manager instance.

        Returns:
            The provider manager, or None if not initialized

        Example:
            >>> otel = get_observability_instance()
            >>> manager = otel.get_provider_manager()
            >>> if manager is not None:
            ...     print(f"Observability enabled: {manager.config.is_enabled()}")
        r7   r&   r   r   r   get_provider_manager  s   z*ObservabilityInstance.get_provider_managerr   r   c                 C   s8   | j du rtd dS | j |}d| _ td |S )a  
        Shutdown observability and flush any pending metrics.

        This should be called at application shutdown to ensure all metrics
        are exported before the application exits.

        Args:
            timeout_millis: Maximum time to wait for shutdown

        Returns:
            True if shutdown was successful

        Example:
            >>> otel = get_observability_instance()
            >>> # At application shutdown
            >>> otel.shutdown()
        Nz2Observability not initialized, nothing to shutdownTzObservability shutdown)r8   r   r   r!   r   )r   r   successr   r   r   r!     s   


zObservabilityInstance.shutdownc                 C   s$   | j du rtd dS | j |S )a3  
        Force flush all pending metrics immediately.

        Useful for testing or when you want to ensure metrics are exported
        before a specific point in your application.

        Args:
            timeout_millis: Maximum time to wait for flush

        Returns:
            True if flush was successful

        Example:
            >>> otel = get_observability_instance()
            >>> # Execute some Redis commands
            >>> r.set('key', 'value')
            >>> # Force flush metrics immediately
            >>> otel.force_flush()
        Nz/Observability not initialized, nothing to flushT)r8   r   r   r   r    r   r   r   r   /  s   

z!ObservabilityInstance.force_flushNr.   )r/   r0   r1   r2   r   r   r:   r4   r   r   r   r;   r3   r!   r   r   r   r   r   r6      s    !r6   _observability_instancer   c                   C   s   t du rt a t S )a&  
    Get the global observability singleton instance.

    This is the Pythonic way to get the singleton instance.

    Returns:
        The global ObservabilityInstance singleton

    Example:
        >>>
        >>> otel = get_observability_instance()
        >>> otel.init(OTelConfig())
    N)r=   r6   r   r   r   r   get_observability_instanceN  s   r>   c                   C   s   t durt   da dS dS )a2  
    Reset the global observability singleton instance.

    This is primarily used for testing and benchmarking to ensure
    a clean state between test runs.

    Warning:
        This will shutdown any active provider manager and reset
        the global state. Use with caution in production code.
    N)r=   r!   r   r   r   r   reset_observability_instanced  s   r?   )r   N)r2   loggingtypingr   redis.observability.configr   	getLoggerr/   r   opentelemetry.sdk.metricsr   OTEL_AVAILABLEr   r   __annotations__r   r6   r=   r>   r?   r   r   r   r   <module>   s(    
  