Skip to content

Commit f8d1701

Browse files
bmehta001Copilot
andcommitted
Address simple telemetry review comments
Tighten singleton locking, simplify CI/cache helpers, clarify per-process telemetry behavior, and update privacy wording for the default host metadata. Files changed: - docs/Privacy.md - olive/telemetry/telemetry.py - olive/telemetry/library/telemetry_logger.py Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 96b37a7 commit f8d1701

3 files changed

Lines changed: 35 additions & 33 deletions

File tree

docs/Privacy.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,4 @@ In addition, Olive may collect additional telemetry data such as:
1313
- Performance data
1414
- Exception information
1515

16-
Collection of this additional telemetry can be disabled by adding the `--disable_telemetry` flag to any Olive CLI command, or by setting the `OLIVE_DISABLE_TELEMETRY` environment variable to `1` before running. In CI/CD environments (e.g., GitHub Actions, Azure Pipelines, Jenkins), Olive suppresses the general heartbeat/action/error events and only emits the `OliveRecipe` event. The `OliveRecipe` event may include recipe metadata such as pass types, explicitly configured target settings, the host system type and any explicitly configured host accelerator settings, whether a custom package config was provided, a redacted snapshot of custom package-config overrides, and a redacted snapshot of explicitly supplied config overrides. Outside CI/CD environments, if telemetry is enabled but cannot be sent to Microsoft, it will be stored locally and sent when a connection is available. You can override the default cache location by setting the `OLIVE_TELEMETRY_CACHE_DIR` environment variable to a valid directory path.
16+
Collection of this additional telemetry can be disabled by adding the `--disable_telemetry` flag to any Olive CLI command, or by setting the `OLIVE_DISABLE_TELEMETRY` environment variable to `1` before running. In CI/CD environments (e.g., GitHub Actions, Azure Pipelines, Jenkins), Olive suppresses the general heartbeat/action/error events and only emits the `OliveRecipe` event. The `OliveRecipe` event may include recipe metadata such as pass types, explicitly configured target settings, the host system type (including the default `LocalSystem` host) and any explicitly configured host accelerator settings, whether a custom package config was provided, a redacted snapshot of custom package-config overrides, and a redacted snapshot of explicitly supplied config overrides. Outside CI/CD environments, if telemetry is enabled but cannot be sent to Microsoft, it will be stored locally and sent when a connection is available. You can override the default cache location by setting the `OLIVE_TELEMETRY_CACHE_DIR` environment variable to a valid directory path.

olive/telemetry/library/telemetry_logger.py

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ class TelemetryLogger:
2929

3030
_instance: Optional["TelemetryLogger"] = None
3131
_default_logger: Optional["TelemetryLogger"] = None
32-
_singleton_lock = threading.RLock()
32+
_instance_lock = threading.RLock()
33+
_default_logger_lock = threading.RLock()
3334
_logger: Optional[logging.Logger] = None
3435
_logger_exporter: Optional[OneCollectorLogExporter] = None
3536
_logger_provider: Optional[LoggerProvider] = None
@@ -41,10 +42,11 @@ def __new__(cls, options: Optional[OneCollectorExporterOptions] = None):
4142
options: Exporter options (only used on first instantiation)
4243
4344
"""
44-
with cls._singleton_lock:
45-
if cls._instance is None:
46-
cls._instance = super().__new__(cls)
47-
cls._instance._initialize(options)
45+
if cls._instance is None:
46+
with cls._instance_lock:
47+
if cls._instance is None:
48+
cls._instance = super().__new__(cls)
49+
cls._instance._initialize(options)
4850

4951
return cls._instance
5052

@@ -160,23 +162,25 @@ def get_default_logger(
160162
TelemetryLogger instance
161163
162164
"""
163-
with cls._singleton_lock:
164-
if cls._default_logger is None:
165-
options = None
166-
if connection_string:
167-
options = OneCollectorExporterOptions(
168-
connection_string=connection_string, service_name=service_name
169-
)
170-
cls._default_logger = cls(options=options)
165+
if cls._default_logger is None:
166+
with cls._default_logger_lock:
167+
if cls._default_logger is None:
168+
options = None
169+
if connection_string:
170+
options = OneCollectorExporterOptions(
171+
connection_string=connection_string, service_name=service_name
172+
)
173+
cls._default_logger = cls(options=options)
171174

172175
return cls._default_logger
173176

174177
@classmethod
175178
def shutdown_default_logger(cls) -> None:
176179
"""Shutdown the default telemetry logger."""
177-
if cls._default_logger:
178-
cls._default_logger.shutdown()
179-
cls._default_logger = None
180+
with cls._default_logger_lock:
181+
if cls._default_logger:
182+
cls._default_logger.shutdown()
183+
cls._default_logger = None
180184

181185

182186
def get_telemetry_logger(

olive/telemetry/telemetry.py

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -219,9 +219,11 @@ def on_payload_transmitted(self, args: "PayloadTransmittedCallbackArgs") -> None
219219
finally:
220220
with self._condition:
221221
self._callbacks_item_count += args.item_count
222+
# Wake threads waiting for flush/shutdown callback accounting.
222223
self._condition.notify_all()
223224

224225
def wait_for_callbacks(self, timeout_sec: float, during_flush: bool = False) -> bool:
226+
"""Wait until callbacks have caught up with logged telemetry items."""
225227
deadline = time.time() + timeout_sec
226228
with self._condition:
227229
while True:
@@ -278,11 +280,9 @@ def cache_path(self) -> Optional[Path]:
278280
279281
"""
280282
telemetry_cache_dir = None
281-
telemetry_cache_dir_override = os.environ.get("OLIVE_TELEMETRY_CACHE_DIR")
283+
telemetry_cache_dir_override = (os.environ.get("OLIVE_TELEMETRY_CACHE_DIR") or "").strip()
282284
if telemetry_cache_dir_override:
283-
telemetry_cache_dir_override = telemetry_cache_dir_override.strip()
284-
if telemetry_cache_dir_override:
285-
telemetry_cache_dir = Path(telemetry_cache_dir_override).expanduser()
285+
telemetry_cache_dir = Path(telemetry_cache_dir_override).expanduser()
286286
if not telemetry_cache_dir:
287287
telemetry_cache_dir = get_telemetry_base_dir() / "cache"
288288
return telemetry_cache_dir / self._cache_file_name
@@ -442,7 +442,9 @@ def is_flushing(self) -> bool:
442442
class Telemetry:
443443
"""Wrapper that wires environment configuration into the library logger.
444444
445-
This is a singleton class - all instances share the same state.
445+
This is a per-process singleton class - all instances in a process share the same state.
446+
Separate processes get separate in-memory singleton instances and coordinate only through
447+
the shared telemetry cache file lock.
446448
Use Telemetry() to get the singleton instance.
447449
"""
448450

@@ -451,11 +453,12 @@ class Telemetry:
451453

452454
def __new__(cls):
453455
"""Create or return the singleton instance."""
454-
with cls._lock:
455-
if cls._instance is None:
456-
instance = super().__new__(cls)
457-
instance._initialized = False
458-
cls._instance = instance
456+
if cls._instance is None:
457+
with cls._lock:
458+
if cls._instance is None:
459+
instance = super().__new__(cls)
460+
instance._initialized = False
461+
cls._instance = instance
459462
return cls._instance
460463

461464
def __init__(self):
@@ -472,7 +475,7 @@ def __init__(self):
472475
self._logger = self._create_logger()
473476
event_source.disable()
474477

475-
is_ci = self._is_ci_environment()
478+
is_ci = is_ci_environment()
476479
self._recipe_only_ci_telemetry = is_ci
477480
if not is_ci:
478481
self._cache_handler = TelemetryCacheHandler(self)
@@ -485,11 +488,6 @@ def __init__(self):
485488
# Fail silently — telemetry must never crash the host application
486489
self._initialized = True
487490

488-
@staticmethod
489-
def _is_ci_environment() -> bool:
490-
"""Detect CI/CD environments by checking well-known environment variables."""
491-
return is_ci_environment()
492-
493491
def _create_logger(self) -> Optional[TelemetryLogger]:
494492
try:
495493
return get_telemetry_logger(base64.b64decode(CONNECTION_STRING).decode(), service_name=APP_NAME)

0 commit comments

Comments
 (0)