Created
November 19, 2014 21:28
-
-
Save wolever/1354837c0ddd14776400 to your computer and use it in GitHub Desktop.
A simple example of how I do logging in an API client
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
When I integrate with a 3rd party API, I write a thin wrapper around it to | |
normalize any eccentricities and provide a well-defined Python-level API. | |
""" | |
log = logging.getLogger("acme.api") | |
class AcmeAPIClient(object): | |
def get_widgets(self): | |
""" | |
GET acme.com/api/widgets | |
HTTP 200 Ok | |
{ | |
"widgets": [<widget>, <widget>], | |
"ok": True, | |
} | |
""" | |
return self._request("GET", "widgets")["widgets"] | |
def _request(self, method, suffix, data=None, expected_errors=None): | |
expected_errors = set(expected_errors or []) | |
data = data or {} | |
data["api_key"] = self.api_key | |
url = self.base_url + suffix | |
start_time = time.time() | |
exc = None | |
try: | |
r = self._backend.request(method, url, params=data) | |
resp_text = r.text | |
resp_status_code = r.status_code | |
except Exception as e: | |
exc = e | |
resp_text = "" | |
resp_status_code = -1 | |
duration = time.time() - start_time | |
try: | |
resp_obj = json.loads(resp_text) | |
except Exception as e: | |
resp_obj = { | |
"error": { | |
"message": "error decoding response: %r" %(e, ), | |
"code": -42, | |
} | |
} | |
error = ( | |
{"message": repr(exc), "code": -43} if exc else | |
resp_obj["error"] if "error" in resp_obj else | |
None | |
) | |
try: | |
params_str = urllib.urlencode(data) | |
except Exception as e: | |
params_str = repr(data) | |
error_is_expected = error and error.get("code") in expected_errors | |
logger = ( | |
log.info if not error or error_is_expected else | |
log.error | |
) | |
logger( | |
"%s %s?%s -> %s %s%s", method, url, params_str, resp_status_code, | |
resp_text[:100], error or "", | |
extra=OrderedDict([ | |
("method", method), | |
("url", url), | |
("params", data), | |
("duration", duration), | |
("status_code", resp_status_code), | |
("response_text", resp_text), | |
] + | |
([("error", error)] if error else []) + | |
([("error_expected", error_is_expected)] if error_is_expected else []) | |
), | |
) | |
if error: | |
if error_is_expected: | |
return { | |
"error": error, | |
"error_is_expected": error_is_expected, | |
} | |
raise AcmeAPIError( | |
error.get("message") or "<unknown>", | |
error.get("code"), | |
) | |
return resp_obj |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment