Skip to content

Commit b72ff94

Browse files
author
Stephen Cefali
authored
logging(apis): better logging for api failures (#56432)
This PR ensures we pass in the URL an the integration id into failure log messages
1 parent 17d7b08 commit b72ff94

File tree

3 files changed

+23
-16
lines changed

3 files changed

+23
-16
lines changed

src/sentry/integrations/slack/client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ def track_response_data(
8484
span: Span | None = None,
8585
error: Optional[str] = None,
8686
resp: Optional[Response] = None,
87+
extra: Optional[Mapping[str, str]] = None,
8788
) -> None:
8889
# if no span was passed, create a dummy to which to add data to avoid having to wrap every
8990
# span call in `if span`
@@ -121,6 +122,7 @@ def track_response_data(
121122
)
122123

123124
extra = {
125+
**(extra or {}),
124126
self.integration_type: self.name,
125127
"status_string": str(code),
126128
"error": str(error)[:256] if error else None,

src/sentry/shared_integrations/client/base.py

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,11 @@ def _request(
243243
if span and existing_transaction:
244244
span.set_data("existing_transaction", existing_transaction)
245245

246+
extra = {"url": full_url}
247+
# It shouldn't be possible for integration_type to be null.
248+
if self.integration_type:
249+
extra[self.integration_type] = self.name
250+
246251
try:
247252
with build_session() as session:
248253
finalized_request = self.finalize_request(_prepared_request)
@@ -266,30 +271,27 @@ def _request(
266271
return resp
267272
resp.raise_for_status()
268273
except RestrictedIPAddress as e:
269-
self.track_response_data("restricted_ip_address", span, e)
274+
self.track_response_data("restricted_ip_address", span, e, extra=extra)
270275
self.record_error(e)
271276
raise ApiHostError.from_exception(e) from e
272277
except ConnectionError as e:
273-
self.track_response_data("connection_error", span, e)
278+
self.track_response_data("connection_error", span, e, extra=extra)
274279
self.record_error(e)
275280
raise ApiHostError.from_exception(e) from e
276281
except Timeout as e:
277-
self.track_response_data("timeout", span, e)
282+
self.track_response_data("timeout", span, e, extra=extra)
278283
self.record_error(e)
279284
raise ApiTimeoutError.from_exception(e) from e
280285
except HTTPError as e:
281286
error_resp = e.response
282287
if error_resp is None:
283-
self.track_response_data("unknown", span, e)
288+
self.track_response_data("unknown", span, e, extra=extra)
284289

285-
# It shouldn't be possible for integration_type to be null.
286-
extra = {"url": full_url}
287-
if self.integration_type:
288-
extra[self.integration_type] = self.name
289290
self.logger.exception("request.error", extra=extra)
290291
self.record_error(e)
291292
raise ApiError("Internal Error", url=full_url) from e
292-
self.track_response_data(error_resp.status_code, span, e)
293+
294+
self.track_response_data(error_resp.status_code, span, e, extra=extra)
293295
self.record_error(e)
294296
raise ApiError.from_response(error_resp, url=full_url) from e
295297

@@ -301,19 +303,19 @@ def _request(
301303
# which is a ChunkedEncodingError caused by a ProtocolError caused by a ConnectionResetError.
302304
# Rather than worrying about what the other layers might be, we just stringify to detect this.
303305
if "ConnectionResetError" in str(e):
304-
self.track_response_data("connection_reset_error", span, e)
306+
self.track_response_data("connection_reset_error", span, e, extra=extra)
305307
self.record_error(e)
306308
raise ApiConnectionResetError("Connection reset by peer", url=full_url) from e
307309
# The same thing can happen with an InvalidChunkLength exception, which is a subclass of HTTPError
308310
if "InvalidChunkLength" in str(e):
309-
self.track_response_data("invalid_chunk_length", span, e)
311+
self.track_response_data("invalid_chunk_length", span, e, extra=extra)
310312
self.record_error(e)
311313
raise ApiError("Connection broken: invalid chunk length", url=full_url) from e
312314

313315
# If it's not something we recognize, let the caller deal with it
314316
raise e
315317

316-
self.track_response_data(resp.status_code, span, None, resp)
318+
self.track_response_data(resp.status_code, span, None, resp, extra=extra)
317319

318320
self.record_response(resp)
319321

src/sentry/shared_integrations/track_response.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from __future__ import annotations
22

33
import logging
4+
from typing import Mapping, Optional
45

56
from django.utils.functional import cached_property
67
from rest_framework.response import Response
@@ -41,6 +42,7 @@ def track_response_data(
4142
span: Span | None = None,
4243
error: Exception | None = None,
4344
resp: Response | None = None,
45+
extra: Optional[Mapping[str, str]] = None,
4446
) -> None:
4547
# if no span was passed, create a dummy to which to add data to avoid having to wrap every
4648
# span call in `if span`
@@ -57,12 +59,13 @@ def track_response_data(
5759
except ValueError:
5860
span.set_status(str(code))
5961

60-
extra = {
62+
log_params = {
63+
**(extra or {}),
6164
"status_string": str(code),
6265
"error": str(error)[:256] if error else None,
6366
}
6467
if self.integration_type:
65-
extra[self.integration_type] = self.name
68+
log_params[self.integration_type] = self.name
6669

67-
extra.update(getattr(self, "logging_context", None) or {})
68-
self.logger.info(f"{self.integration_type}.http_response", extra=extra)
70+
log_params.update(getattr(self, "logging_context", None) or {})
71+
self.logger.info(f"{self.integration_type}.http_response", extra=log_params)

0 commit comments

Comments
 (0)