Files
telemetry-client-py/src/mosaicstack_telemetry/config.py

92 lines
3.1 KiB
Python

"""Telemetry client configuration."""
from __future__ import annotations
import os
import re
from dataclasses import dataclass, field
_HEX_64_RE = re.compile(r"^[0-9a-fA-F]{64}$")
_UUID_RE = re.compile(
r"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
)
@dataclass
class TelemetryConfig:
"""Configuration for the telemetry client.
Values can be provided directly or loaded from environment variables:
- MOSAIC_TELEMETRY_ENABLED -> enabled
- MOSAIC_TELEMETRY_SERVER_URL -> server_url
- MOSAIC_TELEMETRY_API_KEY -> api_key
- MOSAIC_TELEMETRY_INSTANCE_ID -> instance_id
"""
server_url: str = ""
api_key: str = ""
instance_id: str = ""
enabled: bool = True
submit_interval_seconds: float = 300.0
max_queue_size: int = 1000
batch_size: int = 100
request_timeout_seconds: float = 10.0
prediction_cache_ttl_seconds: float = 21600.0
dry_run: bool = False
max_retries: int = 3
user_agent: str = field(default="mosaicstack-telemetry-python/0.1.0")
def __post_init__(self) -> None:
"""Load environment variable overrides and validate."""
env_enabled = os.environ.get("MOSAIC_TELEMETRY_ENABLED")
if env_enabled is not None:
self.enabled = env_enabled.lower() in ("1", "true", "yes")
env_url = os.environ.get("MOSAIC_TELEMETRY_SERVER_URL")
if env_url and not self.server_url:
self.server_url = env_url
env_key = os.environ.get("MOSAIC_TELEMETRY_API_KEY")
if env_key and not self.api_key:
self.api_key = env_key
env_instance = os.environ.get("MOSAIC_TELEMETRY_INSTANCE_ID")
if env_instance and not self.instance_id:
self.instance_id = env_instance
# Strip trailing slashes from server_url
self.server_url = self.server_url.rstrip("/")
def validate(self) -> list[str]:
"""Validate configuration and return list of errors (empty if valid)."""
errors: list[str] = []
if not self.server_url:
errors.append("server_url is required")
elif not self.server_url.startswith(("http://", "https://")):
errors.append("server_url must start with http:// or https://")
if not self.api_key:
errors.append("api_key is required")
elif not _HEX_64_RE.match(self.api_key):
errors.append("api_key must be a 64-character hex string")
if not self.instance_id:
errors.append("instance_id is required")
elif not _UUID_RE.match(self.instance_id):
errors.append("instance_id must be a valid UUID string")
if self.submit_interval_seconds <= 0:
errors.append("submit_interval_seconds must be positive")
if self.max_queue_size <= 0:
errors.append("max_queue_size must be positive")
if self.batch_size <= 0 or self.batch_size > 100:
errors.append("batch_size must be between 1 and 100")
if self.request_timeout_seconds <= 0:
errors.append("request_timeout_seconds must be positive")
return errors