Skip to content

Commit 9995059

Browse files
committed
change: Enhance telemetry logging module and feature coverage
1 parent 0c85185 commit 9995059

File tree

4 files changed

+98
-79
lines changed

4 files changed

+98
-79
lines changed

src/sagemaker/remote_function/client.py

+3
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
from sagemaker.utils import name_from_base, base_from_name
4141
from sagemaker.remote_function.spark_config import SparkConfig
4242
from sagemaker.remote_function.custom_file_filter import CustomFileFilter
43+
from sagemaker.telemetry.telemetry_logging import _telemetry_emitter
44+
from sagemaker.telemetry.constants import Feature
4345

4446
_API_CALL_LIMIT = {
4547
"SubmittingIntervalInSecs": 1,
@@ -57,6 +59,7 @@
5759
logger = logging_config.get_logger()
5860

5961

62+
@_telemetry_emitter(feature=Feature.REMOTE_FUNCTION, func_name="remote_function.remote")
6063
def remote(
6164
_func=None,
6265
*,

src/sagemaker/telemetry/constants.py

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class Feature(Enum):
2424

2525
SDK_DEFAULTS = 1
2626
LOCAL_MODE = 2
27+
REMOTE_FUNCTION = 3
2728

2829
def __str__(self): # pylint: disable=E0307
2930
"""Return the feature name."""

src/sagemaker/telemetry/telemetry_logging.py

+91-75
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@
1717
import sys
1818
from time import perf_counter
1919
from typing import List
20+
import functools
21+
import requests
2022

23+
from sagemaker.session import Session
2124
from sagemaker.utils import resolve_value_from_config
2225
from sagemaker.config.config_schema import TELEMETRY_OPT_OUT_PATH
2326
from sagemaker.telemetry.constants import (
@@ -47,6 +50,7 @@
4750
FEATURE_TO_CODE = {
4851
str(Feature.SDK_DEFAULTS): 1,
4952
str(Feature.LOCAL_MODE): 2,
53+
str(Feature.REMOTE_FUNCTION): 3,
5054
}
5155

5256
STATUS_TO_CODE = {
@@ -59,86 +63,101 @@ def _telemetry_emitter(feature: str, func_name: str):
5963
"""Decorator to emit telemetry logs for SageMaker Python SDK functions"""
6064

6165
def decorator(func):
62-
def wrapper(self, *args, **kwargs):
63-
logger.info(TELEMETRY_OPT_OUT_MESSAGING)
64-
response = None
65-
caught_ex = None
66-
studio_app_type = process_studio_metadata_file()
67-
68-
# Check if telemetry is opted out
69-
telemetry_opt_out_flag = resolve_value_from_config(
70-
direct_input=None,
71-
config_path=TELEMETRY_OPT_OUT_PATH,
72-
default_value=False,
73-
sagemaker_session=self.sagemaker_session,
74-
)
75-
logger.debug("TelemetryOptOut flag is set to: %s", telemetry_opt_out_flag)
76-
77-
# Construct the feature list to track feature combinations
78-
feature_list: List[int] = [FEATURE_TO_CODE[str(feature)]]
79-
if self.sagemaker_session:
80-
if self.sagemaker_session.sagemaker_config and feature != Feature.SDK_DEFAULTS:
66+
@functools.wraps(func)
67+
def wrapper(*args, **kwargs):
68+
sagemaker_session = None
69+
if len(args) > 0 and hasattr(args[0], "sagemaker_session"):
70+
# Get the sagemaker_session from the instance method args
71+
sagemaker_session = args[0].sagemaker_session
72+
elif feature == Feature.REMOTE_FUNCTION:
73+
# Get the sagemaker_session from the function keyword arguments for remote function
74+
sagemaker_session = kwargs.get("sagemaker_session", Session())
75+
76+
if sagemaker_session:
77+
logger.debug("sagemaker_session found, preparing to emit telemetry...")
78+
logger.info(TELEMETRY_OPT_OUT_MESSAGING)
79+
response = None
80+
caught_ex = None
81+
studio_app_type = process_studio_metadata_file()
82+
83+
# Check if telemetry is opted out
84+
telemetry_opt_out_flag = resolve_value_from_config(
85+
direct_input=None,
86+
config_path=TELEMETRY_OPT_OUT_PATH,
87+
default_value=False,
88+
sagemaker_session=sagemaker_session,
89+
)
90+
logger.debug("TelemetryOptOut flag is set to: %s", telemetry_opt_out_flag)
91+
92+
# Construct the feature list to track feature combinations
93+
feature_list: List[int] = [FEATURE_TO_CODE[str(feature)]]
94+
95+
if sagemaker_session.sagemaker_config and feature != Feature.SDK_DEFAULTS:
8196
feature_list.append(FEATURE_TO_CODE[str(Feature.SDK_DEFAULTS)])
8297

83-
if self.sagemaker_session.local_mode and feature != Feature.LOCAL_MODE:
98+
if sagemaker_session.local_mode and feature != Feature.LOCAL_MODE:
8499
feature_list.append(FEATURE_TO_CODE[str(Feature.LOCAL_MODE)])
85100

86-
# Construct the extra info to track platform and environment usage metadata
87-
extra = (
88-
f"{func_name}"
89-
f"&x-sdkVersion={SDK_VERSION}"
90-
f"&x-env={PYTHON_VERSION}"
91-
f"&x-sys={OS_NAME_VERSION}"
92-
f"&x-platform={studio_app_type}"
93-
)
94-
95-
# Add endpoint ARN to the extra info if available
96-
if self.sagemaker_session and self.sagemaker_session.endpoint_arn:
97-
extra += f"&x-endpointArn={self.sagemaker_session.endpoint_arn}"
98-
99-
start_timer = perf_counter()
100-
try:
101-
# Call the original function
102-
response = func(self, *args, **kwargs)
103-
stop_timer = perf_counter()
104-
elapsed = stop_timer - start_timer
105-
extra += f"&x-latency={round(elapsed, 2)}"
106-
if not telemetry_opt_out_flag:
107-
_send_telemetry_request(
108-
STATUS_TO_CODE[str(Status.SUCCESS)],
109-
feature_list,
110-
self.sagemaker_session,
111-
None,
112-
None,
113-
extra,
114-
)
115-
except Exception as e: # pylint: disable=W0703
116-
stop_timer = perf_counter()
117-
elapsed = stop_timer - start_timer
118-
extra += f"&x-latency={round(elapsed, 2)}"
119-
if not telemetry_opt_out_flag:
120-
_send_telemetry_request(
121-
STATUS_TO_CODE[str(Status.FAILURE)],
122-
feature_list,
123-
self.sagemaker_session,
124-
str(e),
125-
e.__class__.__name__,
126-
extra,
127-
)
128-
caught_ex = e
129-
finally:
130-
if caught_ex:
131-
raise caught_ex
132-
return response # pylint: disable=W0150
101+
# Construct the extra info to track platform and environment usage metadata
102+
extra = (
103+
f"{func_name}"
104+
f"&x-sdkVersion={SDK_VERSION}"
105+
f"&x-env={PYTHON_VERSION}"
106+
f"&x-sys={OS_NAME_VERSION}"
107+
f"&x-platform={studio_app_type}"
108+
)
109+
110+
# Add endpoint ARN to the extra info if available
111+
if sagemaker_session.endpoint_arn:
112+
extra += f"&x-endpointArn={sagemaker_session.endpoint_arn}"
113+
114+
start_timer = perf_counter()
115+
try:
116+
# Call the original function
117+
response = func(*args, **kwargs)
118+
stop_timer = perf_counter()
119+
elapsed = stop_timer - start_timer
120+
extra += f"&x-latency={round(elapsed, 2)}"
121+
if not telemetry_opt_out_flag:
122+
_send_telemetry_request(
123+
STATUS_TO_CODE[str(Status.SUCCESS)],
124+
feature_list,
125+
sagemaker_session,
126+
None,
127+
None,
128+
extra,
129+
)
130+
except Exception as e: # pylint: disable=W0703
131+
stop_timer = perf_counter()
132+
elapsed = stop_timer - start_timer
133+
extra += f"&x-latency={round(elapsed, 2)}"
134+
if not telemetry_opt_out_flag:
135+
_send_telemetry_request(
136+
STATUS_TO_CODE[str(Status.FAILURE)],
137+
feature_list,
138+
sagemaker_session,
139+
str(e),
140+
e.__class__.__name__,
141+
extra,
142+
)
143+
caught_ex = e
144+
finally:
145+
if caught_ex:
146+
raise caught_ex
147+
return response # pylint: disable=W0150
148+
else:
149+
logger.debug(
150+
"Unable to send telemetry for function %s. "
151+
"sagemaker_session is not provided or not valid.",
152+
func_name,
153+
)
154+
return func(*args, **kwargs)
133155

134156
return wrapper
135157

136158
return decorator
137159

138160

139-
from sagemaker.session import Session # noqa: E402 pylint: disable=C0413
140-
141-
142161
def _send_telemetry_request(
143162
status: int,
144163
feature_list: List[int],
@@ -165,9 +184,9 @@ def _send_telemetry_request(
165184
# Send the telemetry request
166185
logger.debug("Sending telemetry request to [%s]", url)
167186
_requests_helper(url, 2)
168-
logger.debug("SageMaker Python SDK telemetry successfully emitted!")
187+
logger.debug("SageMaker Python SDK telemetry successfully emitted.")
169188
except Exception: # pylint: disable=W0703
170-
logger.debug("SageMaker Python SDK telemetry not emitted!!")
189+
logger.debug("SageMaker Python SDK telemetry not emitted!")
171190

172191

173192
def _construct_url(
@@ -196,9 +215,6 @@ def _construct_url(
196215
return base_url
197216

198217

199-
import requests # noqa: E402 pylint: disable=C0413,C0411
200-
201-
202218
def _requests_helper(url, timeout):
203219
"""Make a GET request to the given URL"""
204220

tests/unit/sagemaker/remote_function/test_client.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import os
1616
import threading
1717
import time
18+
import inspect
1819

1920
import pytest
2021
from mock import MagicMock, patch, Mock, ANY, call
@@ -1498,7 +1499,6 @@ def test_consistency_between_remote_and_step_decorator():
14981499
from sagemaker.workflow.function_step import step
14991500

15001501
remote_args_to_ignore = [
1501-
"_remote",
15021502
"include_local_workdir",
15031503
"custom_file_filter",
15041504
"s3_kms_key",
@@ -1508,7 +1508,7 @@ def test_consistency_between_remote_and_step_decorator():
15081508

15091509
step_args_to_ignore = ["_step", "name", "display_name", "description", "retry_policies"]
15101510

1511-
remote_decorator_args = remote.__code__.co_varnames
1511+
remote_decorator_args = inspect.signature(remote).parameters.keys()
15121512
common_remote_decorator_args = set(remote_args_to_ignore) ^ set(remote_decorator_args)
15131513

15141514
step_decorator_args = step.__code__.co_varnames
@@ -1522,8 +1522,7 @@ def test_consistency_between_remote_and_executor():
15221522
executor_arg_list.remove("self")
15231523
executor_arg_list.remove("max_parallel_jobs")
15241524

1525-
remote_args_list = list(remote.__code__.co_varnames)
1526-
remote_args_list.remove("_remote")
1525+
remote_args_list = list(inspect.signature(remote).parameters.keys())
15271526
remote_args_list.remove("_func")
15281527

15291528
assert executor_arg_list == remote_args_list

0 commit comments

Comments
 (0)