from __future__ import annotations import time from dataclasses import dataclass from typing import Any, Optional import requests from .telemetry import SDK_NAME, configure_telemetry from .version import __version__ DEFAULT_BASE_URL = "https://blog.starryskymeow.top" class StarrySdkError(Exception): """Base exception for this demo SDK.""" class StarryNotFoundError(StarrySdkError): """Raised when the upstream service returns HTTP 404.""" def __init__(self, *, path: str, url: str) -> None: self.path = path self.url = url self.status_code = 404 super().__init__(f"Resource not found: path={path!r}, url={url!r}") @dataclass(frozen=True) class _CallContext: path: str url: str sdk_interface: str = "get" http_method: str = "GET" def _normalize_path(path: str) -> str: if path is None: raise ValueError("path must be a string, got None") return str(path).lstrip("/") def _metric_path(path: str) -> str: # Keep metrics low-cardinality: drop query strings and collapse an empty path to "/". normalized = _normalize_path(path).split("?", 1)[0] return f"/{normalized}" if normalized else "/" def _build_url(base_url: str, path: str) -> str: normalized = _normalize_path(path) if normalized: return f"{base_url.rstrip('/')}/{normalized}" return f"{base_url.rstrip('/')}/" class StarryClient: """Demo client SDK. `get(path)` requests `https://blog.starryskymeow.top/{path}` and returns `str`. HTTP 404 is converted to `StarryNotFoundError` and logged through OpenTelemetry. """ def __init__( self, *, base_url: str = DEFAULT_BASE_URL, timeout_seconds: float = 10.0, sdk_version: str = __version__, service_name: Optional[str] = None, ) -> None: self.base_url = base_url.rstrip("/") self.timeout_seconds = timeout_seconds self.sdk_version = sdk_version self._telemetry = configure_telemetry(service_name=service_name, sdk_version=sdk_version) def get(self, path: str = "") -> str: normalized_path = _normalize_path(path) url = _build_url(self.base_url, normalized_path) context = _CallContext(path=normalized_path, url=url) attrs: dict[str, Any] = { "sdk_name": SDK_NAME, "sdk_version": self.sdk_version, "sdk_interface": context.sdk_interface, "http_method": context.http_method, "url_path": _metric_path(normalized_path), "outcome": "unknown", "http_status_code": 0, } start = time.perf_counter() try: response = requests.get(url, timeout=self.timeout_seconds) attrs["http_status_code"] = response.status_code if response.status_code == 404: attrs["outcome"] = "error" attrs["error_type"] = "StarryNotFoundError" raise StarryNotFoundError(path=normalized_path, url=url) response.raise_for_status() attrs["outcome"] = "success" return response.text except Exception as exc: attrs["outcome"] = "error" attrs.setdefault("error_type", exc.__class__.__name__) self._telemetry.logger.exception( "Starry SDK request error", extra={ "sdk_name": SDK_NAME, "sdk_version": self.sdk_version, "sdk_interface": context.sdk_interface, "http_method": context.http_method, "http_status_code": attrs.get("http_status_code", 0), "url_path": attrs["url_path"], "error_type": attrs["error_type"], }, ) raise finally: duration_ms = (time.perf_counter() - start) * 1000.0 self._telemetry.request_counter.add(1, attributes=attrs) self._telemetry.duration_histogram.record(duration_ms, attributes=attrs) if attrs.get("outcome") == "error": self._telemetry.error_counter.add(1, attributes=attrs)