Skip to content

Commit 9815aa0

Browse files
Refactor logging (#53)
* Refactor logging * Configure root logger to original logging handler format if no root logger has been defined at the application-level. * Fix indendation. * Add note to README about default logging handlers. --------- Co-authored-by: jayckaiser <jayckaiser@gmail.com>
1 parent 87721c1 commit 9815aa0

7 files changed

Lines changed: 67 additions & 50 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ If either `client_key` or `client_secret` are empty, a session with the ODS will
6161

6262

6363
All code examples in this document use verbose-logging to more-explicitly show interactions with the API.
64-
It is recommended to set `verbose=True` while working interactively with the API.
64+
It is recommended to set `verbose=True` while working interactively with the API. If logging handlers have not been configured before enabling verbose-logging, default package handlers will be used.
6565

6666
```python
6767
>>> from edfi_api_client import EdFiClient

edfi_api_client/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,6 @@
1+
import logging
2+
13
from edfi_api_client.edfi_client import EdFiClient
24
from edfi_api_client.util import camel_to_snake, snake_to_camel, url_join
5+
6+
logging.getLogger("edfi_api_client").addHandler(logging.NullHandler())

edfi_api_client/edfi_client.py

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,10 @@
99
from edfi_api_client.session import EdFiSession
1010
from edfi_api_client.token_cache import BaseTokenCache
1111

12-
1312
import logging
14-
logging.basicConfig(
15-
level="WARNING",
16-
format='[%(asctime)s] %(levelname)s: %(message)s',
17-
datefmt='%Y-%m-%d %H:%M:%S',
18-
)
13+
14+
logger = logging.getLogger(__name__)
15+
1916

2017
class EdFiClient:
2118
"""
@@ -49,9 +46,17 @@ def __init__(self,
4946
token_cache: Optional[BaseTokenCache] = None,
5047
**kwargs
5148
):
52-
# Update logger first
49+
# Update package logger level when verbose mode is enabled
5350
if verbose:
54-
logging.getLogger().setLevel(logging.INFO)
51+
logging.getLogger("edfi_api_client").setLevel(logging.INFO)
52+
53+
# If no logger is enabled at the application level, enable it here.
54+
if not logging.getLogger().handlers:
55+
logging.basicConfig(
56+
level=logging.INFO,
57+
format='[%(asctime)s] %(levelname)s: %(message)s',
58+
datefmt='%Y-%m-%d %H:%M:%S',
59+
)
5560

5661
self.base_url = base_url
5762
self.client_key = client_key
@@ -188,7 +193,7 @@ def _set_swagger(self, component: str):
188193
:return:
189194
"""
190195
if self.swaggers.get(component) is None:
191-
logging.info(f"[Get {component.title()} Swagger] Retrieving Swagger into memory...")
196+
logger.info(f"[Get {component.title()} Swagger] Retrieving Swagger into memory...")
192197
self.get_swagger(component)
193198

194199

@@ -296,7 +301,7 @@ def get_token_info(self) -> dict:
296301
https://edfi.atlassian.net/wiki/spaces/ODSAPIS3V520/pages/25100511/Authorization
297302
"""
298303
token_info_url = util.url_join(self.base_url, "oauth/token_info")
299-
logging.info(f"[Get Token Info] Endpoint: {token_info_url}")
304+
logger.info(f"[Get Token Info] Endpoint: {token_info_url}")
300305

301306
token_response = self.session.post_response(
302307
token_info_url,
@@ -314,7 +319,7 @@ def get_newest_change_version(self) -> int:
314319
:return:
315320
"""
316321
change_version_url = util.url_join(self.base_url, 'changeQueries/v1', self.instance_locator, 'availableChangeVersions')
317-
logging.info(f"[Get Newest Change Version] Endpoint: {change_version_url}")
322+
logger.info(f"[Get Newest Change Version] Endpoint: {change_version_url}")
318323

319324
res = self.session.get_response(change_version_url)
320325
if not res.ok:

edfi_api_client/edfi_endpoint.py

Lines changed: 29 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
if TYPE_CHECKING:
1212
from edfi_api_client.edfi_client import EdFiClient
1313

14+
logger = logging.getLogger(__name__)
15+
1416

1517
class EdFiEndpoint:
1618
"""
@@ -75,7 +77,7 @@ def _parse_names(client: 'EdFiClient', namespace: str, name: str) -> Tuple[str,
7577
elif len(name) == 2:
7678
return name
7779
else:
78-
logging.error("Arguments `namespace` and `name` must be passed explicitly, or as a `(namespace, name)` tuple.")
80+
logger.error("Arguments `namespace` and `name` must be passed explicitly, or as a `(namespace, name)` tuple.")
7981

8082
@property
8183
def raw(self) -> str:
@@ -109,7 +111,7 @@ def ping(self, *, params: Optional[dict] = None, **kwargs) -> requests.Response:
109111
110112
:return:
111113
"""
112-
logging.info(f"[Ping {self.component}] Endpoint: {self.url}")
114+
logger.info(f"[Ping {self.component}] Endpoint: {self.url}")
113115

114116
# Override init params if passed
115117
params = EdFiParams(params or self.params).copy()
@@ -129,14 +131,14 @@ def get(self, limit: Optional[int] = None, *, params: Optional[dict] = None, **k
129131
130132
:return:
131133
"""
132-
logging.info(f"[Get {self.component}] Endpoint: {self.url}")
134+
logger.info(f"[Get {self.component}] Endpoint: {self.url}")
133135

134136
# Override init params if passed
135137
params = EdFiParams(params or self.params).copy()
136138
if limit: # Override limit if passed
137139
params['limit'] = limit
138140

139-
logging.info(f"[Get {self.component}] Parameters: {params}")
141+
logger.info(f"[Get {self.component}] Parameters: {params}")
140142
return self.client.session.get_response(self.url, params=params, **kwargs).json()
141143

142144

@@ -202,42 +204,42 @@ def get_pages(self,
202204

203205
### Prepare pagination variables, depending on type of pagination being used
204206
if step_change_version and reverse_paging:
205-
logging.info(f"[Paged Get {self.component}] Pagination Method: Change Version Stepping with Reverse-Offset Pagination")
207+
logger.info(f"[Paged Get {self.component}] Pagination Method: Change Version Stepping with Reverse-Offset Pagination")
206208
paged_params.init_page_by_change_version_step(change_version_step_size)
207209
total_count = self.get_total_count(params=paged_params, **kwargs)
208210
paged_params.init_reverse_page_by_offset(total_count, page_size)
209211

210212
elif step_change_version:
211-
logging.info(f"[Paged Get {self.component}] Pagination Method: Change Version Stepping")
213+
logger.info(f"[Paged Get {self.component}] Pagination Method: Change Version Stepping")
212214
paged_params.init_page_by_offset(page_size)
213215
paged_params.init_page_by_change_version_step(change_version_step_size)
214216

215217
else:
216-
logging.info(f"[Paged Get {self.component}] Pagination Method: Offset Pagination")
218+
logger.info(f"[Paged Get {self.component}] Pagination Method: Offset Pagination")
217219
paged_params.init_page_by_offset(page_size)
218220

219221
# Begin pagination-loop
220222
while True:
221223

222224
### GET from the API and yield the resulting JSON payload
223225
paged_rows = self.get(params=paged_params, **kwargs)
224-
logging.info(f"[Get {self.component}] Retrieved {len(paged_rows)} rows.")
226+
logger.info(f"[Get {self.component}] Retrieved {len(paged_rows)} rows.")
225227
yield paged_rows
226228

227229
### Paginate, depending on the method specified in arguments
228230
# Reverse offset pagination is only applicable during change-version stepping.
229231
if step_change_version and reverse_paging:
230-
logging.info(f"[Paged Get {self.component}] @ Reverse-paginating offset...")
232+
logger.info(f"[Paged Get {self.component}] @ Reverse-paginating offset...")
231233
try:
232234
paged_params.reverse_page_by_offset()
233235
except StopIteration:
234-
logging.info(f"[Paged Get {self.component}] @ Reverse-paginated into negatives. Stepping change version...")
236+
logger.info(f"[Paged Get {self.component}] @ Reverse-paginated into negatives. Stepping change version...")
235237
try:
236238
paged_params.page_by_change_version_step() # This raises a StopIteration if max change version is exceeded.
237239
total_count = self.get_total_count(params=paged_params, **kwargs)
238240
paged_params.init_reverse_page_by_offset(total_count, page_size)
239241
except StopIteration:
240-
logging.info(f"[Paged Get {self.component}] @ Change version exceeded max. Ending pagination.")
242+
logger.info(f"[Paged Get {self.component}] @ Change version exceeded max. Ending pagination.")
241243
break
242244

243245
else:
@@ -246,18 +248,18 @@ def get_pages(self,
246248

247249
if step_change_version:
248250
try:
249-
logging.info(f"[Paged Get {self.component}] @ Stepping change version...")
251+
logger.info(f"[Paged Get {self.component}] @ Stepping change version...")
250252
paged_params.page_by_change_version_step() # This raises a StopIteration if max change version is exceeded.
251253
except StopIteration:
252-
logging.info(f"[Paged Get {self.component}] @ Change version exceeded max. Ending pagination.")
254+
logger.info(f"[Paged Get {self.component}] @ Change version exceeded max. Ending pagination.")
253255
break
254256
else:
255-
logging.info(f"[Paged Get {self.component}] @ Retrieved zero rows. Ending pagination.")
257+
logger.info(f"[Paged Get {self.component}] @ Retrieved zero rows. Ending pagination.")
256258
break
257259

258260
# Otherwise, paginate offset.
259261
else:
260-
logging.info(f"@ Paginating offset...")
262+
logger.info(f"@ Paginating offset...")
261263
paged_params.page_by_offset()
262264

263265

@@ -269,19 +271,19 @@ def get_total_count(self, *, params: Optional[dict] = None, **kwargs) -> int:
269271
270272
:return:
271273
"""
272-
logging.info(f"[Get Total Count {self.component}] Endpoint: {self.url}")
274+
logger.info(f"[Get Total Count {self.component}] Endpoint: {self.url}")
273275

274276
# Override init params if passed
275277
params = EdFiParams(params or self.params).copy()
276278
params['totalCount'] = True
277279
params['limit'] = 0
278280

279-
logging.info(f"[Get Total Count {self.component}] Parameters: {params}")
281+
logger.info(f"[Get Total Count {self.component}] Parameters: {params}")
280282
res = self.client.session.get_response(self.url, params, **kwargs)
281283
return int(res.headers.get('Total-Count'))
282284

283285
def total_count(self, *args, **kwargs) -> int:
284-
logging.warning("`EdFiEndpoint.total_count()` is deprecated. Use `EdFiEndpoint.get_total_count()` instead.")
286+
logger.warning("`EdFiEndpoint.total_count()` is deprecated. Use `EdFiEndpoint.get_total_count()` instead.")
285287
return self.get_total_count(*args, **kwargs)
286288

287289

@@ -331,7 +333,7 @@ def __init__(self, *args, **kwargs):
331333
super().__init__(*args, **kwargs)
332334

333335
if self.get_deletes:
334-
logging.warning("Descriptors do not have /deletes endpoints. Argument `get_deletes` has been ignored.")
336+
logger.warning("Descriptors do not have /deletes endpoints. Argument `get_deletes` has been ignored.")
335337

336338

337339
class EdFiComposite(EdFiEndpoint):
@@ -356,9 +358,9 @@ def __init__(self,
356358
super().__init__(*args, **kwargs)
357359

358360
if self.get_deletes:
359-
logging.warning("Composites do not have /deletes endpoints. Argument `get_deletes` has been ignored.")
361+
logger.warning("Composites do not have /deletes endpoints. Argument `get_deletes` has been ignored.")
360362
if self.get_key_changes:
361-
logging.warning("Composites do not have /keyChanges endpoints. Argument `get_key_changes` has been ignored.")
363+
logger.warning("Composites do not have /keyChanges endpoints. Argument `get_key_changes` has been ignored.")
362364

363365

364366
def __repr__(self):
@@ -414,10 +416,10 @@ def get_pages(self, *, params: Optional[dict] = None, page_size: int = 100, **kw
414416
:return:
415417
"""
416418
if kwargs.get('step_change_version'):
417-
logging.warning("Change versions are not implemented in composites! Change version stepping arguments are ignored.")
419+
logger.warning("Change versions are not implemented in composites! Change version stepping arguments are ignored.")
418420

419-
logging.info(f"[Paged Get {self.component}] Endpoint: {self.url}")
420-
logging.info(f"[Paged Get {self.component}] Pagination Method: Offset Pagination")
421+
logger.info(f"[Paged Get {self.component}] Endpoint: {self.url}")
422+
logger.info(f"[Paged Get {self.component}] Pagination Method: Offset Pagination")
421423

422424
# Reset pagination parameters
423425
paged_params = EdFiParams(params or self.params).copy()
@@ -431,13 +433,13 @@ def get_pages(self, *, params: Optional[dict] = None, page_size: int = 100, **kw
431433

432434
# If rows have been returned, there may be more to ingest.
433435
if res.json():
434-
logging.info(f"[Paged Get {self.component}] Retrieved {len(res.json())} rows.")
436+
logger.info(f"[Paged Get {self.component}] Retrieved {len(res.json())} rows.")
435437
yield res.json()
436438

437-
logging.info(f" @ Paginating offset...")
439+
logger.info(f" @ Paginating offset...")
438440
paged_params.page_by_offset(page_size)
439441

440442
# If no rows are returned, end pagination.
441443
else:
442-
logging.info(f"[Paged Get {self.component}] @ Retrieved zero rows. Ending pagination.")
444+
logger.info(f"[Paged Get {self.component}] @ Retrieved zero rows. Ending pagination.")
443445
break

edfi_api_client/edfi_params.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
from edfi_api_client import util
77

8+
logger = logging.getLogger(__name__)
9+
810

911
class EdFiParams(dict):
1012
"""
@@ -69,17 +71,17 @@ def __get_duplicates(list_: List[str]):
6971
cc_kwargs = [util.snake_to_camel(key) for key in _kwargs.keys()]
7072

7173
for key in __get_duplicates(cc_params):
72-
logging.warning(f"Duplicate key `{key}` found in `params`! The last will be used.")
74+
logger.warning(f"Duplicate key `{key}` found in `params`! The last will be used.")
7375

7476
for key in __get_duplicates(cc_kwargs):
75-
logging.warning(f"Duplicate key `{key}` found in `kwargs`! The last will be used.")
77+
logger.warning(f"Duplicate key `{key}` found in `kwargs`! The last will be used.")
7678

7779

7880
# Make sure the user does not pass in duplicates between params and kwargs.
7981
cc_kwargs_params = list(set(cc_params)) + list(set(cc_kwargs))
8082

8183
for key in __get_duplicates(cc_kwargs_params):
82-
logging.warning(f"Duplicate key `{key}` found between `params` and `kwargs`! The kwarg will be used.")
84+
logger.warning(f"Duplicate key `{key}` found between `params` and `kwargs`! The kwarg will be used.")
8385

8486
# Populate the final parameters.
8587
final_params = {}
@@ -104,7 +106,7 @@ def init_page_by_offset(self, page_size: int):
104106
self.page_size = page_size
105107

106108
if 'limit' in self or 'offset' in self:
107-
logging.warning("The previously-defined limit and offset will be reset for paging.")
109+
logger.warning("The previously-defined limit and offset will be reset for paging.")
108110

109111
self['limit'] = self.page_size
110112
self['offset'] = 0

edfi_api_client/session.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
if TYPE_CHECKING:
1616
from edfi_api_client.edfi_params import EdFiParams
1717

18+
logger = logging.getLogger(__name__)
19+
1820

1921
class EdFiSession:
2022
"""
@@ -125,7 +127,7 @@ def authenticate(self) -> dict:
125127
# Only re-authenticate when necessary.
126128
if self.authenticated_at:
127129
if self.refresh_at < int(time.time()):
128-
logging.info("Session authentication is expired. Attempting reconnection...")
130+
logger.info("Session authentication is expired. Attempting reconnection...")
129131
else:
130132
return self.auth_headers
131133

@@ -137,7 +139,7 @@ def authenticate(self) -> dict:
137139
self._last_auth_payload = auth_payload
138140

139141
self._access_token = auth_payload.get('access_token', '')
140-
logging.info(f'Using token starting with {self._access_token[:5]}')
142+
logger.info(f'Using token starting with {self._access_token[:5]}')
141143

142144
# Update headers
143145
self.auth_headers.update({
@@ -165,7 +167,7 @@ def _load_or_update_token_from_cache(self, force_write_lock: bool = False):
165167
return self._load_or_update_token_from_cache(force_write_lock=True)
166168

167169
else:
168-
logging.info('Token cache is stale; attempting to get write lock')
170+
logger.info('Token cache is stale; attempting to get write lock')
169171

170172
with self.token_cache.get_write_lock():
171173
auth_payload = None
@@ -261,7 +263,7 @@ def wrapped(self,
261263
except RequestsWarning as retry_warning:
262264
# If an API call fails, it may be due to rate-limiting.
263265
sleep_secs = min((2 ** n_tries) * 2, max_wait)
264-
logging.warning(f"{retry_warning} Sleeping for {sleep_secs} seconds before retry number {n_tries + 1}...")
266+
logger.warning(f"{retry_warning} Sleeping for {sleep_secs} seconds before retry number {n_tries + 1}...")
265267
self.safe_sleep(sleep_secs)
266268

267269
# This block is reached only if max_retries has been reached.

edfi_api_client/token_cache.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
if TYPE_CHECKING:
1313
from edfi_api_client.session import EdFiSession
1414

15+
logger = logging.getLogger(__name__)
16+
1517

1618
class TokenCacheError(Exception):
1719
pass
@@ -128,7 +130,7 @@ def get_last_modified(self) -> int:
128130
def load(self) -> dict:
129131
"""Loads value from cache"""
130132
try:
131-
logging.info(f'Loading cache from {self.cache_path}')
133+
logger.info(f'Loading cache from {self.cache_path}')
132134
with open(self.cache_path, 'r') as fp:
133135
value = json.loads(fp.read())
134136
except json.JSONDecodeError:
@@ -141,7 +143,7 @@ def load(self) -> dict:
141143
def update(self, value: dict):
142144
"""Updates cache with new value"""
143145
with open(self.cache_path, 'w') as fp:
144-
logging.info(f'Writing cache to {self.cache_path}')
146+
logger.info(f'Writing cache to {self.cache_path}')
145147
fp.write(json.dumps(value))
146148

147149
@contextlib.contextmanager
@@ -161,7 +163,7 @@ def get_write_lock(self):
161163
lockfile_age = time.time() - os.path.getmtime(self.lockfile_path)
162164
if lockfile_age > self.write_lock_staleness_threshold:
163165
# assume another client died while holding the lock
164-
logging.info(f'Lockfile at {self.lockfile_path} touched more than {self.write_lock_staleness_threshold}s ago. Removing lockfile.')
166+
logger.info(f'Lockfile at {self.lockfile_path} touched more than {self.write_lock_staleness_threshold}s ago. Removing lockfile.')
165167
os.remove(self.lockfile_path)
166168

167169
with open(self.lockfile_path, 'x') as f:

0 commit comments

Comments
 (0)