{"id":7449,"library":"netflix-spectator-py","title":"Netflix Spectator Python Client","description":"netflix-spectator-py is a thin-client library for reporting metrics from Python applications to SpectatorD and the Netflix Atlas Timeseries Database. It is currently at version 1.1.2 and maintains an active release cadence, providing continuous bug fixes and feature enhancements.","status":"active","version":"1.1.2","language":"en","source_language":"en","source_url":"https://github.com/Netflix/spectator-py","tags":["metrics","observability","netflix","atlas","spectator","monitoring","python3"],"install":[{"cmd":"pip install netflix-spectator-py","lang":"bash","label":"Install latest version"}],"dependencies":[],"imports":[{"symbol":"Registry","correct":"from spectator import Registry"},{"symbol":"Config","correct":"from spectator import Config"},{"note":"While GlobalRegistry is still supported for legacy reasons, direct import from 'spectator' is the correct modern path. It's a hold-over from the thick-client and stateless in this thin-client version.","wrong":"from spectator.main import GlobalRegistry","symbol":"GlobalRegistry","correct":"from spectator import GlobalRegistry"},{"symbol":"StopWatch","correct":"from spectator import StopWatch"}],"quickstart":{"code":"from spectator import Registry, Config\nimport time\nimport os\n\n# Configure with optional extra common tags (e.g., for environment, service)\nconfig = Config(extra_common_tags={'env': os.environ.get('SPECTATOR_ENV', 'dev')})\nregistry = Registry(config)\n\n# Create and interact with a counter\nrequest_counter = registry.counter('my_service.request_count')\nrequest_counter.increment()\nprint(f\"Request Count: {request_counter.get()}\")\n\n# Create and interact with a timer\nprocessing_timer = registry.timer('my_service.processing_latency')\nwith processing_timer.start():\n    time.sleep(0.1) # Simulate work\nprint(f\"Processing Latency recorded: {processing_timer.get_count()} events\")\n\n# Create and interact with a gauge (manual update)\ncurrent_items = registry.gauge('my_service.current_items')\ncurrent_items.set(5)\nprint(f\"Current Items: {current_items.get()}\")\n\n# Note: Metrics are typically sent to a SpectatorD agent via UDP by default. Ensure SpectatorD is running.","lang":"python","description":"Initializes a `Registry` with an optional `Config` for common tags, then demonstrates basic usage of `Counter`, `Timer`, and `Gauge` to report metrics. By default, metrics are sent via UDP to a local SpectatorD agent."},"warnings":[{"fix":"Initialize `Registry` within each child process's main execution block, or use a multiprocessing-safe method for managing the registry instance, such as passing metrics back to a single reporting process.","message":"When using `netflix-spectator-py` in a multiprocessing environment, especially with 'fork' or 'forkserver' start methods (Linux default), you must create a new `Registry` instance *after* each child process has started. Failing to do so can lead to deadlocks or incorrect metric reporting due to shared socket descriptors and background threads.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Pre-aggregate metrics within your application and report them at a cadence closer to SpectatorD's publish interval (e.g., every 5 seconds).","message":"For high-volume metric reporting (tens of thousands to millions of operations per second), direct per-operation metric updates can lead to high CPU usage for both your application and SpectatorD. This client sends a UDP packet for each update.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Ensure all tag keys and values are explicitly cast to strings before being passed to `with_tag()` or `with_tags()` methods. Monitor `spectator.meter.meter_id` logger at `WARNING` or `DEBUG` level for validation issues.","message":"All tag keys and values for `MeterId` objects must be strings. If non-string values are provided, the `MeterId` class will validate them, potentially dropping or changing invalid values and reporting a warning log. This can lead to unexpected metric dimensions or missing data.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Replace `from spectator import GlobalRegistry` and `GlobalRegistry.counter(...)` with `from spectator import Registry, Config` and `registry = Registry(Config(...)); registry.counter(...)`.","message":"The `GlobalRegistry` is a legacy concept inherited from a prior thick-client version. While still functional and supported for backward compatibility, new code should prefer instantiating `Registry` objects directly with an explicit `Config`.","severity":"deprecated","affected_versions":"<=1.1.2"}],"env_vars":null,"last_verified":"2026-04-16T00:00:00.000Z","next_check":"2026-07-15T00:00:00.000Z","problems":[{"fix":"For explicit `Registry` instances, ensure `.stop()` is called (e.g., in a `finally` block or application shutdown hook). For `GlobalRegistry` in tests or short scripts, consider `spectator.GlobalRegistry.stop()` explicitly, or switch to an explicit `Registry` instance for better lifecycle management.","cause":"The SpectatorD client opens a UDP or Unix domain socket to send metrics. If a `Registry` (especially `GlobalRegistry` in short-lived scripts) is not explicitly shut down or if the script exits abruptly, the socket may not be closed properly.","error":"ResourceWarning: unclosed socket <socket.socket fd=X, family=AddressFamily.AF_INET, type=SocketKind.SOCK_DGRAM, proto=0, laddr=('0.0.0.0', 0)>"},{"fix":"Install the library using pip: `pip install netflix-spectator-py`. Ensure you are running your script with the Python interpreter where the library was installed.","cause":"The `netflix-spectator-py` library is not installed or the Python environment is not correctly configured.","error":"ModuleNotFoundError: No module named 'spectator'"},{"fix":"Always cast tag keys and values to strings: `tags={'status': str(status_code)}` or `id.with_tag('value', str(my_int_value))`.","cause":"Spectator requires all tag keys and values to be strings. Passing integers, booleans, or other non-string types directly as tag values will cause type errors or silent tag dropping after validation.","error":"TypeError: 'int' object is not subscriptable (or similar errors with non-string tags)"}]}