| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 |
- from enum import IntFlag, auto
- from typing import List, Optional, Sequence
- """
- OpenTelemetry configuration for redis-py.
- This module handles configuration for OTel observability features,
- including parsing environment variables and validating settings.
- """
- class MetricGroup(IntFlag):
- """Metric groups that can be enabled/disabled."""
- RESILIENCY = auto()
- CONNECTION_BASIC = auto()
- CONNECTION_ADVANCED = auto()
- COMMAND = auto()
- CSC = auto()
- STREAMING = auto()
- PUBSUB = auto()
- class TelemetryOption(IntFlag):
- """Telemetry options to export."""
- METRICS = auto()
- def default_operation_duration_buckets() -> Sequence[float]:
- return [
- 0.0001,
- 0.00025,
- 0.0005,
- 0.001,
- 0.0025,
- 0.005,
- 0.01,
- 0.025,
- 0.05,
- 0.1,
- 0.25,
- 0.5,
- 1,
- 2.5,
- ]
- def default_histogram_buckets() -> Sequence[float]:
- return [0.0001, 0.0005, 0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 5, 10]
- class OTelConfig:
- """
- Configuration for OpenTelemetry observability in redis-py.
- This class manages all OTel-related settings including metrics, traces (future),
- and logs (future). Configuration can be provided via constructor parameters or
- environment variables (OTEL_* spec).
- Constructor parameters take precedence over environment variables.
- Args:
- enabled_telemetry: Enabled telemetry options to export (default: metrics). Traces and logs will be added
- in future phases.
- metric_groups: Group of metrics that should be exported.
- include_commands: Explicit allowlist of commands to track
- exclude_commands: Blocklist of commands to track
- hide_pubsub_channel_names: If True, hide PubSub channel names in metrics (default: False)
- hide_stream_names: If True, hide stream names in streaming metrics (default: False)
- Note:
- 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
- from opentelemetry.sdk.metrics._internal.view import View
- from opentelemetry.sdk.metrics._internal.aggregation import ExplicitBucketHistogramAggregation
- # Configure histogram bucket boundaries via Views
- views = [
- View(
- instrument_name="db.client.operation.duration",
- aggregation=ExplicitBucketHistogramAggregation(
- boundaries=[0.0001, 0.00025, 0.0005, 0.001, 0.0025, 0.005,
- 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5]
- ),
- ),
- # Add more views for other histograms...
- ]
- provider = MeterProvider(views=views, metric_readers=[reader])
- metrics.set_meter_provider(provider)
- # Then initialize redis-py observability
- from redis.observability import get_observability_instance, OTelConfig
- otel = get_observability_instance()
- otel.init(OTelConfig())
- """
- DEFAULT_TELEMETRY = TelemetryOption.METRICS
- DEFAULT_METRIC_GROUPS = MetricGroup.CONNECTION_BASIC | MetricGroup.RESILIENCY
- def __init__(
- self,
- # Core enablement
- enabled_telemetry: Optional[List[TelemetryOption]] = None,
- # Metrics-specific
- metric_groups: Optional[List[MetricGroup]] = None,
- # Redis-specific telemetry controls
- include_commands: Optional[List[str]] = None,
- exclude_commands: Optional[List[str]] = None,
- # Privacy controls
- hide_pubsub_channel_names: bool = False,
- hide_stream_names: bool = False,
- # Bucket sizes
- buckets_operation_duration: Sequence[
- float
- ] = default_operation_duration_buckets(),
- buckets_stream_processing_duration: Sequence[
- float
- ] = default_histogram_buckets(),
- buckets_connection_create_time: Sequence[float] = default_histogram_buckets(),
- buckets_connection_wait_time: Sequence[float] = default_histogram_buckets(),
- ):
- # Core enablement
- if enabled_telemetry is None:
- self.enabled_telemetry = self.DEFAULT_TELEMETRY
- else:
- self.enabled_telemetry = TelemetryOption(0)
- for option in enabled_telemetry:
- self.enabled_telemetry |= option
- # Enable default metrics if None given
- if metric_groups is None:
- self.metric_groups = self.DEFAULT_METRIC_GROUPS
- else:
- self.metric_groups = MetricGroup(0)
- for metric_group in metric_groups:
- self.metric_groups |= metric_group
- # Redis-specific controls
- self.include_commands = set(include_commands) if include_commands else None
- self.exclude_commands = set(exclude_commands) if exclude_commands else set()
- # Privacy controls for hiding sensitive names in metrics
- self.hide_pubsub_channel_names = hide_pubsub_channel_names
- self.hide_stream_names = hide_stream_names
- # Bucket sizes
- self.buckets_operation_duration = buckets_operation_duration
- self.buckets_stream_processing_duration = buckets_stream_processing_duration
- self.buckets_connection_create_time = buckets_connection_create_time
- self.buckets_connection_wait_time = buckets_connection_wait_time
- def is_enabled(self) -> bool:
- """Check if any observability feature is enabled."""
- return bool(self.enabled_telemetry)
- def should_track_command(self, command_name: str) -> bool:
- """
- Determine if a command should be tracked based on include/exclude lists.
- Args:
- command_name: The Redis command name (e.g., 'GET', 'SET')
- Returns:
- True if the command should be tracked, False otherwise
- """
- command_upper = command_name.upper()
- # If include list is specified, only track commands in the list
- if self.include_commands is not None:
- return command_upper in self.include_commands
- # Otherwise, track all commands except those in exclude list
- return command_upper not in self.exclude_commands
- def __repr__(self) -> str:
- return f"OTelConfig(enabled_telemetry={self.enabled_telemetry}"
|