Add logging to webhooks

Fixes #5798
This commit is contained in:
Rapptz 2020-09-09 20:46:16 -04:00
parent a1a4a4fd7f
commit 425bb809ed

View File

@ -24,6 +24,7 @@ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE. DEALINGS IN THE SOFTWARE.
""" """
import logging
import asyncio import asyncio
import json import json
import time import time
@ -46,6 +47,8 @@ __all__ = (
'Webhook', 'Webhook',
) )
log = logging.getLogger(__name__)
class WebhookAdapter: class WebhookAdapter:
"""Base class for all webhook adapters. """Base class for all webhook adapters.
@ -182,7 +185,7 @@ class AsyncWebhookAdapter(WebhookAdapter):
if payload: if payload:
headers['Content-Type'] = 'application/json' headers['Content-Type'] = 'application/json'
data = utils.to_json(payload) data = utils.to_json(payload)
if reason: if reason:
headers['X-Audit-Log-Reason'] = _uriquote(reason, safe='/ ') headers['X-Audit-Log-Reason'] = _uriquote(reason, safe='/ ')
@ -194,11 +197,14 @@ class AsyncWebhookAdapter(WebhookAdapter):
else: else:
data.add_field(key, value) data.add_field(key, value)
base_url = url.replace(self._request_url, '/') or '/'
_id = self._webhook_id
for tries in range(5): for tries in range(5):
for file in files: for file in files:
file.reset(seek=tries) file.reset(seek=tries)
async with self.session.request(verb, url, headers=headers, data=data) as r: async with self.session.request(verb, url, headers=headers, data=data) as r:
log.debug('Webhook ID %s with %s %s has returned status code %s', _id, verb, base_url, r.status)
# Coerce empty strings to return None for hygiene purposes # Coerce empty strings to return None for hygiene purposes
response = (await r.text(encoding='utf-8')) or None response = (await r.text(encoding='utf-8')) or None
if r.headers['Content-Type'] == 'application/json': if r.headers['Content-Type'] == 'application/json':
@ -208,6 +214,7 @@ class AsyncWebhookAdapter(WebhookAdapter):
remaining = r.headers.get('X-Ratelimit-Remaining') remaining = r.headers.get('X-Ratelimit-Remaining')
if remaining == '0' and r.status != 429: if remaining == '0' and r.status != 429:
delta = utils._parse_ratelimit_header(r) delta = utils._parse_ratelimit_header(r)
log.debug('Webhook ID %s has been pre-emptively rate limited, waiting %.2f seconds', _id, delta)
await asyncio.sleep(delta) await asyncio.sleep(delta)
if 300 > r.status >= 200: if 300 > r.status >= 200:
@ -216,6 +223,7 @@ class AsyncWebhookAdapter(WebhookAdapter):
# we are being rate limited # we are being rate limited
if r.status == 429: if r.status == 429:
retry_after = response['retry_after'] / 1000.0 retry_after = response['retry_after'] / 1000.0
log.warning('Webhook ID %s is rate limited. Retrying in %.2f seconds', _id, retry_after)
await asyncio.sleep(retry_after) await asyncio.sleep(retry_after)
continue continue
@ -271,13 +279,15 @@ class RequestsWebhookAdapter(WebhookAdapter):
if payload: if payload:
headers['Content-Type'] = 'application/json' headers['Content-Type'] = 'application/json'
data = utils.to_json(payload) data = utils.to_json(payload)
if reason: if reason:
headers['X-Audit-Log-Reason'] = _uriquote(reason, safe='/ ') headers['X-Audit-Log-Reason'] = _uriquote(reason, safe='/ ')
if multipart is not None: if multipart is not None:
data = {'payload_json': multipart.pop('payload_json')} data = {'payload_json': multipart.pop('payload_json')}
base_url = url.replace(self._request_url, '/') or '/'
_id = self._webhook_id
for tries in range(5): for tries in range(5):
for file in files: for file in files:
file.reset(seek=tries) file.reset(seek=tries)
@ -290,6 +300,7 @@ class RequestsWebhookAdapter(WebhookAdapter):
# compatibility with aiohttp # compatibility with aiohttp
r.status = r.status_code r.status = r.status_code
log.debug('Webhook ID %s with %s %s has returned status code %s', _id, verb, base_url, r.status)
if r.headers['Content-Type'] == 'application/json': if r.headers['Content-Type'] == 'application/json':
response = json.loads(response) response = json.loads(response)
@ -297,6 +308,7 @@ class RequestsWebhookAdapter(WebhookAdapter):
remaining = r.headers.get('X-Ratelimit-Remaining') remaining = r.headers.get('X-Ratelimit-Remaining')
if remaining == '0' and r.status != 429 and self.sleep: if remaining == '0' and r.status != 429 and self.sleep:
delta = utils._parse_ratelimit_header(r) delta = utils._parse_ratelimit_header(r)
log.debug('Webhook ID %s has been pre-emptively rate limited, waiting %.2f seconds', _id, delta)
time.sleep(delta) time.sleep(delta)
if 300 > r.status >= 200: if 300 > r.status >= 200:
@ -306,6 +318,7 @@ class RequestsWebhookAdapter(WebhookAdapter):
if r.status == 429: if r.status == 429:
if self.sleep: if self.sleep:
retry_after = response['retry_after'] / 1000.0 retry_after = response['retry_after'] / 1000.0
log.warning('Webhook ID %s is rate limited. Retrying in %.2f seconds', _id, retry_after)
time.sleep(retry_after) time.sleep(retry_after)
continue continue
else: else:
@ -409,22 +422,22 @@ class Webhook(Hashable):
webhook.send('Hello World', username='Foo') webhook.send('Hello World', username='Foo')
.. container:: operations .. container:: operations
.. describe:: x == y .. describe:: x == y
Checks if two webhooks are equal. Checks if two webhooks are equal.
.. describe:: x != y .. describe:: x != y
Checks if two webhooks are not equal. Checks if two webhooks are not equal.
.. describe:: hash(x) .. describe:: hash(x)
Returns the webhooks's hash. Returns the webhooks's hash.
.. versionchanged:: 1.4 .. versionchanged:: 1.4
Webhooks are now comparable and hashable. Webhooks are now comparable and hashable.
Attributes Attributes
------------ ------------
id: :class:`int` id: :class:`int`