Skip to content

Commit 74b8644

Browse files
author
Ruban Hussain
committed
change: SDK Defaults - switch from config printing to logging
1 parent e2624af commit 74b8644

File tree

4 files changed

+205
-117
lines changed

4 files changed

+205
-117
lines changed

src/sagemaker/config/config.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
from __future__ import absolute_import
2020

2121
import pathlib
22-
import logging
2322
import os
2423
from typing import List
2524
import boto3
@@ -29,8 +28,9 @@
2928
from botocore.utils import merge_dicts
3029
from six.moves.urllib.parse import urlparse
3130
from sagemaker.config.config_schema import SAGEMAKER_PYTHON_SDK_CONFIG_SCHEMA
31+
from sagemaker.config.config_utils import get_sagemaker_config_logger
3232

33-
logger = logging.getLogger("sagemaker")
33+
logger = get_sagemaker_config_logger()
3434

3535
_APP_NAME = "sagemaker"
3636
# The default config file location of the Administrator provided config file. This path can be
@@ -122,6 +122,9 @@ def load_sagemaker_config(additional_config_paths: List[str] = None, s3_resource
122122
if config_from_file:
123123
validate_sagemaker_config(config_from_file)
124124
merge_dicts(merged_config, config_from_file)
125+
logger.info("Fetched defaults config from location: %s", file_path)
126+
else:
127+
logger.debug("Fetched defaults config from location: %s, but it was empty", file_path)
125128
return merged_config
126129

127130

@@ -148,7 +151,7 @@ def _load_config_from_file(file_path: str) -> dict:
148151
f"Unable to load the config file from the location: {file_path}"
149152
f"Provide a valid file path"
150153
)
151-
logger.debug("Fetching config file from the path: %s", file_path)
154+
logger.debug("Fetching defaults config from location: %s", file_path)
152155
return yaml.safe_load(open(inferred_file_path, "r"))
153156

154157

@@ -164,7 +167,7 @@ def _load_config_from_s3(s3_uri, s3_resource_for_config) -> dict:
164167
)
165168
s3_resource_for_config = boto_session.resource("s3", region_name=boto_region_name)
166169

167-
logger.debug("Fetching config file from the S3 URI: %s", s3_uri)
170+
logger.debug("Fetching defaults config from location: %s", s3_uri)
168171
inferred_s3_uri = _get_inferred_s3_uri(s3_uri, s3_resource_for_config)
169172
parsed_url = urlparse(inferred_s3_uri)
170173
bucket, key_prefix = parsed_url.netloc, parsed_url.path.lstrip("/")

src/sagemaker/config/config_utils.py

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License"). You
4+
# may not use this file except in compliance with the License. A copy of
5+
# the License is located at
6+
#
7+
# http://aws.amazon.com/apache2.0/
8+
#
9+
# or in the "license" file accompanying this file. This file is
10+
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11+
# ANY KIND, either express or implied. See the License for the specific
12+
# language governing permissions and limitations under the License.
13+
"""This file contains util functions for the sagemaker Defaults Config.
14+
15+
These utils may be used inside or outside the config module.
16+
"""
17+
from __future__ import absolute_import
18+
19+
import logging
20+
import sys
21+
22+
23+
def get_sagemaker_config_logger():
24+
"""Return a logger with the name 'sagemaker.config'
25+
26+
If the logger to be returned has no level or handlers set, this will get level and handler
27+
attributes. (So if the SDK user has setup loggers in a certain way, that setup will not be
28+
changed by this function.) It is safe to make repeat calls to this function.
29+
"""
30+
sagemaker_config_logger = logging.getLogger("sagemaker.config")
31+
sagemaker_logger = logging.getLogger("sagemaker")
32+
33+
if sagemaker_config_logger.level == logging.NOTSET:
34+
sagemaker_config_logger.setLevel(logging.INFO)
35+
36+
# check sagemaker_logger here as well, so that if handlers were set for the parent logger
37+
# already, we dont change behavior for the child logger
38+
if len(sagemaker_config_logger.handlers) == 0 and len(sagemaker_logger.handlers) == 0:
39+
# use sys.stdout so logs dont show up with a red background in a notebook
40+
handler = logging.StreamHandler(sys.stdout)
41+
42+
formatter = logging.Formatter("%(name)s %(levelname)-4s - %(message)s")
43+
handler.setFormatter(formatter)
44+
sagemaker_config_logger.addHandler(handler)
45+
46+
# if a handler is being added, we dont want the root handler to also process the same events
47+
sagemaker_config_logger.propagate = False
48+
49+
return sagemaker_config_logger
50+
51+
52+
def _log_sagemaker_config_single_substitution(source_value, config_value, config_key_path: str):
53+
"""Informs the SDK user whether a config value was present and automatically substituted
54+
55+
Args:
56+
source_value: The value that will be used if it exists. Usually, this is user-provided
57+
input to a Class or to a session.py method, or None if no input was provided.
58+
config_value: The value fetched from sagemaker_config. If it exists, this is the value that
59+
will be used if direct_input is None.
60+
config_key_path: A string denoting the path of keys that point to the config value in the
61+
sagemaker_config.
62+
63+
Returns:
64+
None. Logs information to the "sagemaker.config" logger.
65+
"""
66+
logger = get_sagemaker_config_logger()
67+
68+
if config_value is not None:
69+
70+
if source_value is None:
71+
# Sagemaker Config value is going to be used
72+
logger.info(
73+
"Applied value\n config key = %s\n config value that will be used = %s",
74+
config_key_path,
75+
config_value,
76+
)
77+
elif source_value is not None and config_value != source_value:
78+
# Sagemaker Config had a value defined that is NOT going to be used
79+
# and the config value has not already been applied earlier
80+
logger.debug(
81+
(
82+
"Skipped value\n"
83+
" config key = %s\n"
84+
" config value = %s\n"
85+
" source value that will be used = %s",
86+
),
87+
config_key_path,
88+
config_value,
89+
source_value,
90+
)
91+
elif source_value is not None and config_value == source_value:
92+
# Sagemaker Config had a value defined that is NOT going to be used here.
93+
# Either (1) the config value was already fetched and applied earlier, or
94+
# (2) the user happened to pass in the same value.
95+
# Logged as a debug statement because most users do not need to know this.
96+
logger.debug(
97+
(
98+
"Skipped value\n"
99+
" config key = %s\n"
100+
" config value = %s\n"
101+
" source value that will be used = %s"
102+
),
103+
config_key_path,
104+
config_value,
105+
source_value,
106+
)
107+
else:
108+
logger.debug("Skipped value because no value defined\n config key = %s", config_key_path)
109+
# There is no print statement needed if nothing was specified in the config and nothing is
110+
# being automatically applied
111+
112+
113+
def _log_sagemaker_config_merge(
114+
source_value=None,
115+
config_value=None,
116+
merged_source_and_config_value=None,
117+
config_key_path: str = None,
118+
):
119+
"""Informs the SDK user whether a config value was present and automatically substituted
120+
121+
Args:
122+
source_value: The dict or object that would be used if no default values existed. Usually,
123+
this is user-provided input to a Class or to a session.py method, or None if no input
124+
was provided.
125+
config_value: The dict or object fetched from sagemaker_config. If it exists, this is the
126+
value that will be used if source_value is None.
127+
merged_source_and_config_value: The value that results from the merging of source_value and
128+
original_config_value. This will be the value used.
129+
config_key_path: A string denoting the path of keys that point to the config value in the
130+
sagemaker_config.
131+
132+
Returns:
133+
None. Logs information to the "sagemaker.config" logger.
134+
"""
135+
logger = get_sagemaker_config_logger()
136+
137+
if config_value:
138+
139+
if source_value == merged_source_and_config_value:
140+
# Sagemaker Config had a value defined that is NOT going to be used here.
141+
# Either (1) the config value was already fetched and applied earlier, or
142+
# (2) the user happened to pass in the same values.
143+
# Logged as a debug statement because most users do not need to know this.
144+
logger.debug(
145+
(
146+
"Skipped values\n"
147+
" config key = %s\n"
148+
" config value = %s\n"
149+
" source value that will be used = %s"
150+
),
151+
config_key_path,
152+
config_value,
153+
merged_source_and_config_value,
154+
)
155+
else:
156+
logger.info(
157+
(
158+
"Applied values\n"
159+
" config key = %s\n"
160+
" config value = %s\n"
161+
" source value = %s\n"
162+
" combined value that will be used = %s"
163+
),
164+
config_key_path,
165+
config_value,
166+
source_value,
167+
merged_source_and_config_value,
168+
)
169+
else:
170+
logger.debug("Skipped value because no value defined\n config key = %s", config_key_path)
171+
# There is no print statement needed if nothing was specified in the config and nothing is
172+
# being automatically applied

src/sagemaker/session.py

Lines changed: 6 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@
9595
SESSION_DEFAULT_S3_BUCKET_PATH,
9696
SESSION_DEFAULT_S3_OBJECT_KEY_PREFIX_PATH,
9797
)
98+
from sagemaker.config.config_utils import _log_sagemaker_config_merge
9899
from sagemaker.deprecations import deprecated_class
99100
from sagemaker.inputs import ShuffleConfig, TrainingInput, BatchDataCaptureConfig
100101
from sagemaker.user_agent import prepend_user_agent
@@ -606,47 +607,6 @@ def _create_s3_bucket_if_it_does_not_exist(self, bucket_name, region):
606607
else:
607608
raise
608609

609-
def _print_message_on_sagemaker_config_usage(
610-
self, direct_input, config_value, config_path: str
611-
):
612-
"""Informs the SDK user whether a config value was present and automatically substituted
613-
614-
Args:
615-
direct_input: the value that would be used if no sagemaker_config or default values
616-
existed. Usually this will be user-provided input to a Class or to a
617-
session.py method, or None if no input was provided.
618-
config_value: the value fetched from sagemaker_config. This is usually the value that
619-
will be used if direct_input is None.
620-
config_path: a string denoting the path of keys that point to the config value in the
621-
sagemaker_config
622-
623-
Returns:
624-
No output (just prints information)
625-
"""
626-
627-
if config_value is not None:
628-
629-
if direct_input is not None and config_value != direct_input:
630-
# Sagemaker Config had a value defined that is NOT going to be used
631-
# and the config value has not already been applied earlier
632-
print(
633-
"[Sagemaker Config - skipped value]\n",
634-
"config key = {}\n".format(config_path),
635-
"config value = {}\n".format(config_value),
636-
"specified value that will be used = {}\n".format(direct_input),
637-
)
638-
639-
elif direct_input is None:
640-
# Sagemaker Config value is going to be used
641-
print(
642-
"[Sagemaker Config - applied value]\n",
643-
"config key = {}\n".format(config_path),
644-
"config value that will be used = {}\n".format(config_value),
645-
)
646-
647-
# There is no print statement needed if nothing was specified in the config and nothing is
648-
# being automatically applied
649-
650610
def _append_sagemaker_config_tags(self, tags: list, config_path_to_tags: str):
651611
"""Appends tags specified in the sagemaker_config to the given list of tags.
652612
@@ -677,12 +637,11 @@ def _append_sagemaker_config_tags(self, tags: list, config_path_to_tags: str):
677637
# user-provided tags.
678638
all_tags.append(config_tag)
679639

680-
print(
681-
"[Sagemaker Config - applied value]\n",
682-
"config key = {}\n".format(config_path_to_tags),
683-
"config value = {}\n".format(config_tags),
684-
"source value = {}\n".format(tags),
685-
"combined value that will be used = {}\n".format(all_tags),
640+
_log_sagemaker_config_merge(
641+
source_value=tags,
642+
config_value=config_tags,
643+
merged_source_and_config_value=all_tags,
644+
config_key_path=config_path_to_tags,
686645
)
687646

688647
return all_tags

0 commit comments

Comments
 (0)