Files
2026-07-13 13:22:34 +08:00

124 lines
4.2 KiB
Python

import functools
import inspect
import logging
import time
from typing import Any, Callable, ParamSpec, TypeVar
from mlflow.environment_variables import MLFLOW_EXPERIMENT_ID
from mlflow.telemetry.client import get_telemetry_client
from mlflow.telemetry.events import Event
from mlflow.telemetry.schemas import Record, Status
from mlflow.telemetry.utils import _log_error, is_telemetry_disabled
P = ParamSpec("P")
R = TypeVar("R")
_logger = logging.getLogger(__name__)
def record_usage_event(event: type[Event]) -> Callable[[Callable[P, R]], Callable[P, R]]:
def decorator(func: Callable[P, R]) -> Callable[P, R]:
@functools.wraps(func)
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
if is_telemetry_disabled() or _is_telemetry_disabled_for_event(event):
return func(*args, **kwargs)
success = True
result = None
start_time = time.time()
try:
result = func(*args, **kwargs)
return result # noqa: RET504
except Exception:
success = False
raise
finally:
try:
duration_ms = int((time.time() - start_time) * 1000)
_add_telemetry_record(func, args, kwargs, success, duration_ms, event, result)
except Exception as e:
_log_error(f"Failed to record telemetry event {event.name}: {e}")
return wrapper
return decorator
def _add_telemetry_record(
func: Callable[..., Any],
args: tuple[Any, ...],
kwargs: dict[str, Any],
success: bool,
duration_ms: int,
event: type[Event],
result: Any,
) -> None:
try:
if client := get_telemetry_client():
signature = inspect.signature(func)
bound_args = signature.bind(*args, **kwargs)
bound_args.apply_defaults()
arguments = dict(bound_args.arguments)
record_params = event.parse(arguments) or {}
if hasattr(event, "parse_result"):
record_params.update(event.parse_result(result))
if experiment_id := MLFLOW_EXPERIMENT_ID.get():
record_params["mlflow_experiment_id"] = experiment_id
record = Record(
event_name=event.name,
timestamp_ns=time.time_ns(),
params=record_params or None,
status=Status.SUCCESS if success else Status.FAILURE,
duration_ms=duration_ms,
)
client.add_record(record)
except Exception as e:
_log_error(f"Failed to generate telemetry record for event {event.name}: {e}")
def _record_event(
event: type[Event],
params: dict[str, Any],
*,
success: bool = True,
duration_ms: int = 0,
) -> None:
try:
if _is_telemetry_disabled_for_event(event):
return
if client := get_telemetry_client():
record_params = params.copy()
if experiment_id := MLFLOW_EXPERIMENT_ID.get():
record_params["mlflow_experiment_id"] = experiment_id
client.add_record(
Record(
event_name=event.name,
params=record_params or None,
timestamp_ns=time.time_ns(),
status=Status.SUCCESS if success else Status.FAILURE,
duration_ms=duration_ms,
)
)
except Exception as e:
_log_error(f"Failed to record telemetry event {event.name}: {e}")
def _is_telemetry_disabled_for_event(event: type[Event]) -> bool:
try:
if client := get_telemetry_client():
if client.config:
return event.name in client.config.disable_events
# when config is not fetched yet, we assume telemetry is enabled and
# append records. After fetching the config, we check the telemetry
# status and drop the records if disabled.
else:
return False
# telemetry is disabled
else:
return True
except Exception as e:
_log_error(f"Failed to check telemetry status for event {event.name}: {e}")
return True