Skip to content

Instantly share code, notes, and snippets.

@wolever
Created November 19, 2014 21:28
Show Gist options
  • Save wolever/1354837c0ddd14776400 to your computer and use it in GitHub Desktop.
Save wolever/1354837c0ddd14776400 to your computer and use it in GitHub Desktop.
A simple example of how I do logging in an API client
"""
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