Skip to content

Commit dab840a

Browse files
joamagclaude
andcommitted
feat: add custom logging with TRACE support for API classes
Adds _ensure_logger() to API module with TRACE-aware format selection and improves HTTP error handling with reason propagation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 65406b2 commit dab840a

3 files changed

Lines changed: 61 additions & 3 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111

12-
*
12+
* TRACE-aware logging format in API module's `_ensure_logger()`
13+
* Custom logging support for API classes with configurable level and format
1314

1415
### Changed
1516

src/appier/api.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,19 @@
4545
import hashlib
4646
import logging
4747

48+
from . import log
4849
from . import base
4950
from . import http
51+
from . import config
5052
from . import legacy
5153
from . import observer
5254
from . import exceptions
5355
from . import structures
5456

57+
_LOGGERS = {}
58+
""" Dictionary of loggers that have already been initialized,
59+
used to ensure singleton-based initialization of named loggers """
60+
5561

5662
class API(observer.Observable):
5763
"""
@@ -319,9 +325,53 @@ def handle_error(self, error):
319325
def logger(self):
320326
if self.owner:
321327
return self.owner.logger
328+
elif hasattr(self, "_log_name") and self._log_name:
329+
return self._ensure_logger(self._log_name)
322330
else:
323331
return logging.getLogger()
324332

333+
@classmethod
334+
def _ensure_logger(cls, name):
335+
# verifies if the logger already exists in the global
336+
# loggers map and returns it immediately if so
337+
if name in _LOGGERS:
338+
return _LOGGERS[name]
339+
340+
# retrieves the logging level and format from the
341+
# current configuration defaulting to sane values
342+
level_s = config.conf("LEVEL", None)
343+
format = config.conf("LOGGING_FORMAT", None)
344+
345+
# resolves the logging level using the application's
346+
# level method falling back to the INFO level
347+
level = base.APP._level(level_s) if base.APP else None
348+
level = level or logging.INFO
349+
350+
# resolves the logging format taking into account if
351+
# the level is TRACE to use the verbose format
352+
is_trace = level <= log.TRACE
353+
format = format or (
354+
log.LOGGING_FORMAT_TRACE if is_trace else log.LOGGING_FORMAT
355+
)
356+
357+
# creates the logger with the provided name and sets
358+
# the resolved logging level in it
359+
logger = logging.getLogger(name)
360+
logger.setLevel(level)
361+
362+
# creates the stream handler setting the level and the
363+
# formatter using the thread aware formatter
364+
handler = logging.StreamHandler()
365+
handler.setLevel(level)
366+
handler.setFormatter(log.ThreadFormatter(format))
367+
368+
# adds the handler to the logger and stores it in the
369+
# global loggers map for later retrieval
370+
logger.addHandler(handler)
371+
372+
_LOGGERS[name] = logger
373+
return logger
374+
325375

326376
class OAuthAPI(API):
327377
"""

src/appier/http.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,8 @@ def _method(method, *args, **kwargs):
278278
result = method(*args, **kwargs)
279279
except legacy.HTTPError as error:
280280
code = error.getcode()
281-
raise exceptions.HTTPError(error, code)
281+
reason = error.reason if hasattr(error, "reason") else None
282+
raise exceptions.HTTPError(error, code=code, message=reason)
282283

283284
return result
284285

@@ -875,7 +876,13 @@ def _resolve_netius(url, method, headers, data, silent, timeout, **kwargs):
875876
code = response.getcode()
876877
is_error = _is_error(code)
877878
if is_error:
878-
raise legacy.HTTPError(url, code, "HTTP retrieval problem", None, response)
879+
raise legacy.HTTPError(
880+
url,
881+
code,
882+
"HTTP retrieval problem: %s" % error if error else "HTTP retrieval problem",
883+
None,
884+
response,
885+
)
879886

880887
# returns the final response object to the upper layers, this object
881888
# may be used freely under the compatibility interface it provides

0 commit comments

Comments
 (0)