aboutsummaryrefslogtreecommitdiff
path: root/.venv/lib/python3.12/site-packages/azure/ai/ml/_telemetry
diff options
context:
space:
mode:
authorS. Solomon Darnell2025-03-28 21:52:21 -0500
committerS. Solomon Darnell2025-03-28 21:52:21 -0500
commit4a52a71956a8d46fcb7294ac71734504bb09bcc2 (patch)
treeee3dc5af3b6313e921cd920906356f5d4febc4ed /.venv/lib/python3.12/site-packages/azure/ai/ml/_telemetry
parentcc961e04ba734dd72309fb548a2f97d67d578813 (diff)
downloadgn-ai-master.tar.gz
two version of R2R are hereHEADmaster
Diffstat (limited to '.venv/lib/python3.12/site-packages/azure/ai/ml/_telemetry')
-rw-r--r--.venv/lib/python3.12/site-packages/azure/ai/ml/_telemetry/__init__.py17
-rw-r--r--.venv/lib/python3.12/site-packages/azure/ai/ml/_telemetry/_customtraceback.py183
-rw-r--r--.venv/lib/python3.12/site-packages/azure/ai/ml/_telemetry/activity.py379
-rw-r--r--.venv/lib/python3.12/site-packages/azure/ai/ml/_telemetry/logging_handler.py150
4 files changed, 729 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/azure/ai/ml/_telemetry/__init__.py b/.venv/lib/python3.12/site-packages/azure/ai/ml/_telemetry/__init__.py
new file mode 100644
index 00000000..2bfa3fd6
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/azure/ai/ml/_telemetry/__init__.py
@@ -0,0 +1,17 @@
+# ---------------------------------------------------------
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# ---------------------------------------------------------
+
+__path__ = __import__("pkgutil").extend_path(__path__, __name__)
+
+from .activity import ActivityType, log_activity, monitor_with_activity, monitor_with_telemetry_mixin
+from .logging_handler import AML_INTERNAL_LOGGER_NAMESPACE, configure_appinsights_logging
+
+__all__ = [
+ "monitor_with_activity",
+ "monitor_with_telemetry_mixin",
+ "log_activity",
+ "ActivityType",
+ "configure_appinsights_logging",
+ "AML_INTERNAL_LOGGER_NAMESPACE",
+]
diff --git a/.venv/lib/python3.12/site-packages/azure/ai/ml/_telemetry/_customtraceback.py b/.venv/lib/python3.12/site-packages/azure/ai/ml/_telemetry/_customtraceback.py
new file mode 100644
index 00000000..f8b41393
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/azure/ai/ml/_telemetry/_customtraceback.py
@@ -0,0 +1,183 @@
+# ---------------------------------------------------------
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# ---------------------------------------------------------
+
+# pylint: disable=protected-access
+
+"""A file for custom traceback for removing file path from the trace for compliance need."""
+
+import os
+import sys
+import traceback
+import types
+from typing import Any, Generator, List, Optional, Type
+
+
+class _CustomStackSummary(traceback.StackSummary):
+ """Subclass of StackSummary."""
+
+ def format(self) -> List[str]:
+ """Format the stack ready for printing.
+
+ :return: A list of strings ready for printing.
+ * Each string in the resulting list corresponds to a single frame from the stack.
+ * Each string ends in a newline; the strings may contain internal newlines as well, for those items with
+ source text lines.
+ :rtype: List[str]
+ """
+ result = []
+ for frame in self:
+ row = [' File "{}", line {}, in {}\n'.format(os.path.basename(frame.filename), frame.lineno, frame.name)]
+ if frame.line:
+ row.append(" {}\n".format(frame.line.strip()))
+ if frame.locals:
+ for name, value in sorted(frame.locals.items()):
+ row.append(" {name} = {value}\n".format(name=name, value=value))
+ result.append("".join(row))
+ return result
+
+
+# pylint: disable=too-many-instance-attributes
+class _CustomTracebackException(traceback.TracebackException):
+ # pylint: disable=super-init-not-called
+ def __init__(
+ self, exc_type, exc_value, exc_traceback, *, limit=None, lookup_lines=True, capture_locals=False, _seen=None
+ ):
+ if _seen is None:
+ _seen = set()
+ _seen.add(exc_value)
+ # Gracefully handle (the way Python 2.4 and earlier did) the case of
+ # being called with no type or value (None, None, None).
+ if exc_value and exc_value.__cause__ is not None and exc_value.__cause__ not in _seen:
+ cause = _CustomTracebackException(
+ type(exc_value.__cause__),
+ exc_value.__cause__,
+ exc_value.__cause__.__traceback__,
+ limit=limit,
+ lookup_lines=False,
+ capture_locals=capture_locals,
+ _seen=_seen,
+ )
+ else:
+ cause = None
+ if exc_value and exc_value.__context__ is not None and exc_value.__context__ not in _seen:
+ context = _CustomTracebackException(
+ type(exc_value.__context__),
+ exc_value.__context__,
+ exc_value.__context__.__traceback__,
+ limit=limit,
+ lookup_lines=False,
+ capture_locals=capture_locals,
+ _seen=_seen,
+ )
+ else:
+ context = None
+ self.exc_traceback = exc_traceback
+ self.__cause__ = cause
+ self.__context__ = context
+ self.__suppress_context__ = exc_value.__suppress_context__ if exc_value else False
+ # TODO: locals.
+ self.stack = _CustomStackSummary.extract(
+ traceback.walk_tb(exc_traceback),
+ limit=limit,
+ lookup_lines=lookup_lines,
+ capture_locals=capture_locals,
+ )
+ self.exc_type = exc_type
+ # Capture now to permit freeing resources: only complication is in the
+ # unofficial API _format_final_exc_line
+ self._str = traceback._some_str(exc_value)
+ if exc_type and issubclass(exc_type, SyntaxError):
+ # Handle SyntaxError's specially
+ self.filename = exc_value.filename
+ self.lineno = str(exc_value.lineno)
+ self.text = exc_value.text
+ self.offset = exc_value.offset
+ self.msg = exc_value.msg
+ if lookup_lines:
+ self._load_lines()
+
+ def format_exception_only(self) -> Generator:
+ """Format the exception part of the traceback.
+
+ :return: An iterable of strings, each ending in a newline. Normally, the generator emits a single
+ string; however, for SyntaxError exceptions, it emits several lines that (when printed) display
+ detailed information about where the syntax error occurred. The message indicating which exception occurred
+ is always the last string in the output.
+ :rtype: Iterable[str]
+ """
+ if self.exc_type is None:
+ yield traceback._format_final_exc_line(None, self._str)
+ return
+
+ stype = self.exc_type.__qualname__
+ smod = self.exc_type.__module__
+ if smod not in ("__main__", "builtins"):
+ stype = smod + "." + stype
+
+ if not issubclass(self.exc_type, SyntaxError):
+ yield traceback._format_final_exc_line(stype, self._str) # type: ignore[attr-defined]
+ return
+
+ # It was a syntax error; show exactly where the problem was found.
+ filename = os.path.basename(self.filename) or "<string>"
+ lineno = str(self.lineno) or "?"
+ yield ' File "{}", line {}\n'.format(filename, lineno)
+
+ badline = self.text
+ offset = self.offset
+ if badline is not None:
+ yield " {}\n".format(badline.strip())
+ if offset is not None:
+ caretspace: Any = badline.rstrip("\n")
+ offset = min(len(caretspace), offset) - 1
+ caretspace = caretspace[:offset].lstrip()
+ # non-space whitespace (likes tabs) must be kept for alignment
+ caretspace = ((c.isspace() and c or " ") for c in caretspace)
+ yield " {}^\n".format("".join(caretspace))
+ msg = self.msg or "<no detail available>"
+ yield "{}: {}\n".format(stype, msg)
+
+
+def format_exc(limit: Optional[int] = None, chain: bool = True) -> str:
+ """Like print_exc() but return a string.
+
+ :param limit: None to include all frames or the number of frames to include.
+ :type limit: Optional[int]
+ :param chain: Whether to format __cause__ and __context__. Defaults to True
+ :type chain: bool
+ :return: The formatted exception string
+ :rtype: str
+ """
+ return "".join(format_exception(*sys.exc_info(), limit=limit, chain=chain)) # type: ignore[misc]
+
+
+def format_exception( # pylint: disable=unused-argument
+ etype: Type[BaseException],
+ value: BaseException,
+ tb: types.TracebackType,
+ limit: Optional[int] = None,
+ chain: bool = True,
+) -> List[str]:
+ """Format a stack trace and the exception information.
+
+ The arguments have the same meaning as the corresponding arguments to print_exception().
+
+ :param etype: The type of the exception
+ :type etype: Type[BaseException]
+ :param value: The exception
+ :type value: BaseException
+ :param tb: The exception traceback
+ :type tb: types.TracebackType
+ :param limit: None to include all frames or the number of frames to include.
+ :type limit: Optional[int]
+ :param chain: Whether to format __cause__ and __context__. Defaults to True
+ :type chain: bool
+ :return: A list of strings, each ending in a newline and some containing internal newlines.
+ When these lines are concatenated and printed, exactly the same text is printed as does print_exception().
+ :rtype: List[str]
+ """
+ # format_exception has ignored etype for some time, and code such as cgitb
+ # passes in bogus values as a result. For compatibility with such code we
+ # ignore it here (rather than in the new TracebackException API).
+ return list(_CustomTracebackException(type(value), value, tb, limit=limit).format(chain=chain))
diff --git a/.venv/lib/python3.12/site-packages/azure/ai/ml/_telemetry/activity.py b/.venv/lib/python3.12/site-packages/azure/ai/ml/_telemetry/activity.py
new file mode 100644
index 00000000..169fa5ef
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/azure/ai/ml/_telemetry/activity.py
@@ -0,0 +1,379 @@
+# ---------------------------------------------------------
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# ---------------------------------------------------------
+
+# pylint: disable=protected-access
+
+"""Defines functionality for collecting telemetry about code.
+
+An activity is a logical block of code that consumers want to monitor.
+To monitor an activity, either wrap the logical block of code with the
+``log_activity()`` method or use the ``@monitor_with_activity``
+decorator.
+"""
+import contextlib
+import functools
+import inspect
+import logging
+import os
+import uuid
+from datetime import datetime
+from typing import Dict, Tuple
+from uuid import uuid4
+
+from marshmallow import ValidationError
+
+from azure.ai.ml._utils._logger_utils import OpsLogger
+from azure.ai.ml._utils.utils import _is_user_error_from_exception_type, _is_user_error_from_status_code, _str_to_bool
+from azure.ai.ml.exceptions import ErrorCategory, MlException
+from azure.core.exceptions import HttpResponseError
+
+# Get environment variable IS_IN_CI_PIPELINE to decide whether it's in CI test
+IS_IN_CI_PIPELINE = _str_to_bool(os.environ.get("IS_IN_CI_PIPELINE", "False"))
+
+ACTIVITY_SPAN = "activity_span"
+
+
+class ActivityType(object):
+ """The type of activity (code) monitored.
+
+ The default type is "PublicAPI".
+ """
+
+ PUBLICAPI = "PublicApi" # incoming public API call (default)
+ INTERNALCALL = "InternalCall" # internal (function) call
+ CLIENTPROXY = "ClientProxy" # an outgoing service API call
+
+
+class ActivityCompletionStatus(object):
+ """The activity (code) completion status, success, or failure."""
+
+ SUCCESS = "Success"
+ FAILURE = "Failure"
+
+
+class ActivityLoggerAdapter(logging.LoggerAdapter):
+ """An adapter for loggers to keep activity contextual information in
+ logging output.
+
+ :param logger: The activity logger adapter.
+ :type logger: logging.LoggerAdapter
+ :param activity_info: The info to write to the logger.
+ :type activity_info: str
+ """
+
+ def __init__(self, logger: logging.Logger, activity_info: Dict):
+ """Initialize a new instance of the class.
+
+ :param logger: The activity logger.
+ :type logger: logging.Logger
+ :param activity_info: The info to write to the logger.
+ :type activity_info: Dict
+ """
+ self._activity_info = activity_info
+ super(ActivityLoggerAdapter, self).__init__(logger, None) # type: ignore[arg-type]
+
+ @property
+ def activity_info(self) -> Dict:
+ """Return current activity info.
+
+ :return: The info to write to the logger
+ :rtype: Dict
+ """
+ return self._activity_info
+
+ def process(self, msg: str, kwargs: Dict) -> Tuple[str, Dict]: # type: ignore[override]
+ """Process the log message.
+
+ :param msg: The log message.
+ :type msg: str
+ :param kwargs: The arguments with properties.
+ :type kwargs: dict
+ :return: A tuple of the processed msg and kwargs
+ :rtype: Tuple[str, Dict]
+ """
+ if "extra" not in kwargs:
+ kwargs["extra"] = {}
+
+ kwargs["extra"].update(self._activity_info)
+
+ return msg, kwargs
+
+
+def error_preprocess(activityLogger, exception):
+ """Try to update activityLogger if exception is HttpResponseError or
+ builtin error (such as KeyError, TypeError)
+
+ For HttpResponseError, will log exception message and classify error category according to http response status
+ code if have, otherwise classify as Unknown. For builtin error, will log exception message and classify error
+ category as Unknown.
+
+ :param activityLogger: The logger adapter.
+ :type activityLogger: ActivityLoggerAdapter
+ :param exception: The raised exception to be preprocessed.
+ :type exception: BaseException
+ :return: The provided exception
+ :rtype: Exception
+ """
+
+ if isinstance(exception, HttpResponseError):
+ activityLogger.activity_info["errorMessage"] = exception.message
+ if exception.response:
+ http_status_code = exception.response.status_code
+ error_category = (
+ ErrorCategory.USER_ERROR
+ if _is_user_error_from_status_code(http_status_code)
+ else ErrorCategory.SYSTEM_ERROR
+ )
+ else:
+ error_category = ErrorCategory.UNKNOWN
+ activityLogger.activity_info["errorCategory"] = error_category
+ if exception.inner_exception:
+ activityLogger.activity_info["innerException"] = type(exception.inner_exception).__name__
+ elif isinstance(exception, MlException):
+ # If exception is MlException, it will have error_category, message and target attributes and will log those
+ # information in log_activity, no need more actions here.
+ pass
+ elif isinstance(exception, ValidationError):
+ # Validation error should be user error
+ activityLogger.activity_info["errorMessage"] = str(exception)
+ activityLogger.activity_info["errorCategory"] = ErrorCategory.USER_ERROR
+ else:
+ activityLogger.activity_info["errorMessage"] = "Got error {0}: '{1}' while calling {2}".format(
+ exception.__class__.__name__,
+ exception,
+ activityLogger.activity_info["activity_name"],
+ )
+ if _is_user_error_from_exception_type(exception) or _is_user_error_from_exception_type(exception.__cause__):
+ activityLogger.activity_info["errorCategory"] = ErrorCategory.USER_ERROR
+ else:
+ # Todo: should check KeyError, TypeError, ValueError caused by user before request or raise in code directly
+ activityLogger.activity_info["errorCategory"] = ErrorCategory.UNKNOWN
+ return exception
+
+
+@contextlib.contextmanager
+def log_activity(
+ logger,
+ activity_name,
+ activity_type=ActivityType.INTERNALCALL,
+ custom_dimensions=None,
+):
+ """Log an activity.
+
+ An activity is a logical block of code that consumers want to monitor.
+ To monitor, wrap the logical block of code with the ``log_activity()`` method. As an alternative, you can
+ also use the ``@monitor_with_activity`` decorator.
+
+ :param logger: The logger adapter.
+ :type logger: logging.LoggerAdapter
+ :param activity_name: The name of the activity. The name should be unique per the wrapped logical code block.
+ :type activity_name: str
+ :param activity_type: One of PUBLICAPI, INTERNALCALL, or CLIENTPROXY which represent an incoming API call,
+ an internal (function) call, or an outgoing API call. If not specified, INTERNALCALL is used.
+ :type activity_type: str
+ :param custom_dimensions: The custom properties of the activity.
+ :type custom_dimensions: dict
+ :return: An activity logger
+ :rtype: Iterable[ActivityLoggerAdapter]
+ """
+ activity_info = {
+ "activity_id": str(uuid.uuid4()),
+ "activity_name": activity_name,
+ "activity_type": activity_type,
+ }
+ custom_dimensions = custom_dimensions or {}
+ custom_dimensions.update({"client_request_id": str(uuid4())})
+ activity_info.update(custom_dimensions)
+
+ start_time = datetime.utcnow()
+ completion_status = ActivityCompletionStatus.SUCCESS
+
+ message = "ActivityStarted, {}".format(activity_name)
+ activityLogger = ActivityLoggerAdapter(logger, activity_info) # type: ignore[arg-type]
+ activityLogger.info(message)
+ exception = None
+
+ try:
+ yield activityLogger
+ except BaseException as e:
+ exception = error_preprocess(activityLogger, e)
+ completion_status = ActivityCompletionStatus.FAILURE
+ # All the system and unknown errors except for NotImplementedError will be wrapped with a new exception.
+ if IS_IN_CI_PIPELINE and not isinstance(e, NotImplementedError):
+ if (
+ isinstance(exception, MlException)
+ and exception.error_category # pylint: disable=no-member
+ in [ErrorCategory.SYSTEM_ERROR, ErrorCategory.UNKNOWN]
+ ) or (
+ "errorCategory" in activityLogger.activity_info
+ and activityLogger.activity_info["errorCategory"] # type: ignore[index]
+ in [ErrorCategory.SYSTEM_ERROR, ErrorCategory.UNKNOWN]
+ ):
+ # pylint: disable=W0719
+ raise Exception("Got InternalSDKError", e) from e
+ raise
+ raise
+ finally:
+ try:
+ end_time = datetime.utcnow()
+ duration_ms = round((end_time - start_time).total_seconds() * 1000, 2)
+
+ activityLogger.activity_info["completionStatus"] = completion_status # type: ignore[index]
+ activityLogger.activity_info["durationMs"] = duration_ms # type: ignore[index]
+ message = "ActivityCompleted: Activity={}, HowEnded={}, Duration={} [ms]".format(
+ activity_name, completion_status, duration_ms
+ )
+ if exception:
+ activityLogger.activity_info["exception"] = type(exception).__name__ # type: ignore[index]
+ if isinstance(exception, MlException):
+ activityLogger.activity_info[ # type: ignore[index]
+ "errorMessage"
+ ] = exception.no_personal_data_message # pylint: disable=no-member
+ # pylint: disable=no-member
+ activityLogger.activity_info["errorTarget"] = exception.target # type: ignore[index]
+ activityLogger.activity_info[ # type: ignore[index]
+ "errorCategory"
+ ] = exception.error_category # pylint: disable=no-member
+ if exception.inner_exception: # pylint: disable=no-member
+ # pylint: disable=no-member
+ activityLogger.activity_info["innerException"] = type( # type: ignore[index]
+ exception.inner_exception
+ ).__name__
+ message += ", Exception={}".format(activityLogger.activity_info["exception"])
+ message += ", ErrorCategory={}".format(activityLogger.activity_info["errorCategory"])
+ message += ", ErrorMessage={}".format(activityLogger.activity_info["errorMessage"])
+
+ activityLogger.error(message)
+ else:
+ activityLogger.info(message)
+ except Exception: # pylint: disable=W0718
+ pass
+
+
+# pylint: disable-next=docstring-missing-rtype
+def monitor_with_activity(
+ logger,
+ activity_name,
+ activity_type=ActivityType.INTERNALCALL,
+ custom_dimensions=None,
+):
+ """Add a wrapper for monitoring an activity (code).
+
+ An activity is a logical block of code that consumers want to monitor.
+ To monitor, use the ``@monitor_with_activity`` decorator. As an alternative, you can also wrap the
+ logical block of code with the ``log_activity()`` method.
+
+ :param logger: The operations logging class, containing loggers and tracer for the package and module
+ :type logger: ~azure.ai.ml._utils._logger_utils.OpsLogger
+ :param activity_name: The name of the activity. The name should be unique per the wrapped logical code block.
+ :type activity_name: str
+ :param activity_type: One of PUBLICAPI, INTERNALCALL, or CLIENTPROXY which represent an incoming API call,
+ an internal (function) call, or an outgoing API call. If not specified, INTERNALCALL is used.
+ :type activity_type: str
+ :param custom_dimensions: The custom properties of the activity.
+ :type custom_dimensions: dict
+ :return:
+ """
+
+ def monitor(f):
+ @functools.wraps(f)
+ def wrapper(*args, **kwargs):
+ tracer = logger.package_tracer if isinstance(logger, OpsLogger) else None
+ if tracer:
+ with tracer.start_as_current_span(ACTIVITY_SPAN):
+ with log_activity(
+ logger.package_logger, activity_name or f.__name__, activity_type, custom_dimensions
+ ):
+ return f(*args, **kwargs)
+ elif hasattr(logger, "package_logger"):
+ with log_activity(logger.package_logger, activity_name or f.__name__, activity_type, custom_dimensions):
+ return f(*args, **kwargs)
+ else:
+ with log_activity(logger, activity_name or f.__name__, activity_type, custom_dimensions):
+ return f(*args, **kwargs)
+
+ return wrapper
+
+ return monitor
+
+
+# pylint: disable-next=docstring-missing-rtype
+def monitor_with_telemetry_mixin(
+ logger,
+ activity_name,
+ activity_type=ActivityType.INTERNALCALL,
+ custom_dimensions=None,
+ extra_keys=None,
+):
+ """Add a wrapper for monitoring an activity (code) with telemetry mixin.
+
+ An activity is a logical block of code that consumers want to monitor.
+ Object telemetry values will be added into custom dimensions if activity parameter or return object is
+ an instance of TelemetryMixin, which means log dimension will include: 1. telemetry collected from
+ parameter object. 2. custom dimensions passed in. 3.(optional) if no telemetry found in parameter,
+ will collect from return value.
+ To monitor, use the ``@monitor_with_telemetry_mixin`` decorator.
+
+ :param logger: The operations logging class, containing loggers and tracer for the package and module
+ :type logger: logging.LoggerAdapter
+ :param activity_name: The name of the activity. The name should be unique per the wrapped logical code block.
+ :type activity_name: str
+ :param activity_type: One of PUBLICAPI, INTERNALCALL, or CLIENTPROXY which represent an incoming API call,
+ an internal (function) call, or an outgoing API call. If not specified, INTERNALCALL is used.
+ :type activity_type: str
+ :param custom_dimensions: The custom properties of the activity.
+ :type custom_dimensions: dict
+ :param extra_keys: Extra keys from parameter to be tracked. The key in extra_keys will always be added into
+ dimensions, the value of it will be str(obj) if it exists in parameters, or 'None' if not.
+ :type extra_keys: list[str]
+ :return:
+ """
+
+ logger = logger.package_logger if isinstance(logger, OpsLogger) else logger
+
+ def monitor(f):
+ def _collect_from_parameters(f, args, kwargs, extra_keys):
+ dimensions = {}
+ named_args = dict(zip(inspect.signature(f).parameters.keys(), args))
+ parameters = {**named_args, **kwargs}
+ from azure.ai.ml.entities._mixins import TelemetryMixin
+
+ # extract mixin dimensions from arguments
+ for key, obj in parameters.items():
+ try:
+ if isinstance(obj, TelemetryMixin):
+ dimensions.update(obj._get_telemetry_values())
+ elif extra_keys and key in extra_keys:
+ dimensions[key] = str(obj)
+ except Exception: # pylint: disable=W0718
+ pass
+ # add left keys with None
+ if extra_keys:
+ for key in extra_keys:
+ if key not in parameters:
+ dimensions[key] = "None"
+ return dimensions
+
+ def _collect_from_return_value(value):
+ from azure.ai.ml.entities._mixins import TelemetryMixin
+
+ try:
+ return value._get_telemetry_values() if isinstance(value, TelemetryMixin) else {}
+ except Exception: # pylint: disable=W0718
+ return {}
+
+ @functools.wraps(f)
+ def wrapper(*args, **kwargs):
+ parameter_dimensions = _collect_from_parameters(f, args, kwargs, extra_keys)
+ dimensions = {**parameter_dimensions, **(custom_dimensions or {})}
+ with log_activity(logger, activity_name or f.__name__, activity_type, dimensions) as activityLogger:
+ return_value = f(*args, **kwargs)
+ if not parameter_dimensions:
+ # collect from return if no dimensions from parameter
+ activityLogger.activity_info.update(_collect_from_return_value(return_value))
+ return return_value
+
+ return wrapper
+
+ return monitor
diff --git a/.venv/lib/python3.12/site-packages/azure/ai/ml/_telemetry/logging_handler.py b/.venv/lib/python3.12/site-packages/azure/ai/ml/_telemetry/logging_handler.py
new file mode 100644
index 00000000..cbbbb481
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/azure/ai/ml/_telemetry/logging_handler.py
@@ -0,0 +1,150 @@
+# ---------------------------------------------------------
+# Copyright (c) Microsoft Corporation. All rights reserved.
+# ---------------------------------------------------------
+
+"""Contains functionality for sending telemetry to Application Insights via OpenCensus Azure Monitor Exporter."""
+
+import logging
+import platform
+from typing import Any
+
+from azure.ai.ml._user_agent import USER_AGENT
+
+# Disable internal azure monitor openTelemetry logs
+AZURE_MONITOR_OPENTELEMETRY_LOGGER_NAMESPACE = "azure.monitor.opentelemetry"
+logging.getLogger(AZURE_MONITOR_OPENTELEMETRY_LOGGER_NAMESPACE).addHandler(logging.NullHandler())
+
+AML_INTERNAL_LOGGER_NAMESPACE = "azure.ai.ml._telemetry"
+CONNECTION_STRING = (
+ "InstrumentationKey=71b954a8-6b7d-43f5-986c-3d3a6605d803;"
+ "IngestionEndpoint=https://westus2-0.in.applicationinsights.azure.com/;"
+ "LiveEndpoint=https://westus2.livediagnostics.monitor.azure.com/;"
+ "ApplicationId=82daf08e-6a78-455f-9ce1-9396a8b5128b"
+)
+
+test_subscriptions = [
+ "b17253fa-f327-42d6-9686-f3e553e24763",
+ "test_subscription",
+ "6560575d-fa06-4e7d-95fb-f962e74efd7a",
+ "b17253fa-f327-42d6-9686-f3e553e2452",
+ "74eccef0-4b8d-4f83-b5f9-fa100d155b22",
+ "4faaaf21-663f-4391-96fd-47197c630979",
+ "00000000-0000-0000-0000-000000000",
+]
+
+
+class CustomDimensionsFilter(logging.Filter):
+ """Add application-wide properties to log record"""
+
+ def __init__(self, custom_dimensions=None): # pylint: disable=super-init-not-called
+ self.custom_dimensions = custom_dimensions or {}
+
+ def filter(self, record: dict) -> bool: # type: ignore[override]
+ """Adds the default custom_dimensions into the current log record. Does not
+ otherwise filter any records
+
+ :param record: The record
+ :type record: dict
+ :return: True
+ :rtype: bool
+ """
+
+ custom_dimensions = self.custom_dimensions.copy()
+ if isinstance(custom_dimensions, dict):
+ record.__dict__.update(custom_dimensions)
+ return True
+
+
+def in_jupyter_notebook() -> bool:
+ """Checks if user is using a Jupyter Notebook. This is necessary because logging is not allowed in
+ non-Jupyter contexts.
+
+ Adapted from https://stackoverflow.com/a/22424821
+
+ :return: Whether is running in a Jupyter Notebook
+ :rtype: bool
+ """
+ try: # cspell:ignore ipython
+ from IPython import get_ipython
+
+ if "IPKernelApp" not in get_ipython().config:
+ return False
+ except ImportError:
+ return False
+ except AttributeError:
+ return False
+ return True
+
+
+def setup_azure_monitor(connection_string=None) -> None:
+ """
+ Set up Azure Monitor distro.
+
+ This function sets up Azure Monitor using the provided connection string and specified logger name.
+
+ :param connection_string: The Application Insights connection string.
+ :type connection_string: str
+ :return: None
+ """
+ # Dynamically import the azure.monitor.opentelemetry module to avoid dependency issues later on CLI
+ from azure.monitor.opentelemetry import configure_azure_monitor
+
+ configure_azure_monitor(
+ connection_string=connection_string,
+ logger_name=AML_INTERNAL_LOGGER_NAMESPACE,
+ )
+
+
+# cspell:ignore overriden
+def configure_appinsights_logging(
+ user_agent,
+ connection_string=None,
+ enable_telemetry=True,
+ **kwargs: Any,
+) -> None:
+ """Set the Opentelemetry logging distro for specified logger and connection string to send info to AppInsights.
+
+ :param user_agent: Information about the user's browser.
+ :type user_agent: Dict[str, str]
+ :param connection_string: The Application Insights connection string.
+ :type connection_string: str
+ :param enable_telemetry: Whether to enable telemetry. Will be overriden to False if not in a Jupyter Notebook.
+ :type enable_telemetry: bool
+ :return: None
+ """
+ try:
+ if connection_string is None:
+ connection_string = CONNECTION_STRING
+
+ logger = logging.getLogger(AML_INTERNAL_LOGGER_NAMESPACE)
+ logger.setLevel(logging.INFO)
+ logger.propagate = False
+
+ if (
+ not in_jupyter_notebook()
+ or not enable_telemetry
+ or not user_agent
+ or not user_agent.lower() == USER_AGENT.lower()
+ ):
+ # Disable logging for this logger, all the child loggers will inherit this setting
+ logger.addHandler(logging.NullHandler())
+ return
+
+ if kwargs:
+ if "properties" in kwargs and "subscription_id" in kwargs.get("properties"): # type: ignore[operator]
+ if kwargs.get("properties")["subscription_id"] in test_subscriptions: # type: ignore[index]
+ logger.addHandler(logging.NullHandler())
+ return
+
+ custom_properties = {"PythonVersion": platform.python_version()}
+ custom_properties.update({"user_agent": user_agent})
+ if "properties" in kwargs:
+ custom_properties.update(kwargs.pop("properties"))
+
+ logger.addFilter(CustomDimensionsFilter(custom_properties))
+
+ setup_azure_monitor(connection_string)
+
+ except Exception: # pylint: disable=W0718
+ # ignore any exceptions, telemetry collection errors shouldn't block an operation
+ return