You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
182 lines
6.4 KiB
182 lines
6.4 KiB
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}"
|