socket.py 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. import socket
  2. import sentry_sdk
  3. from sentry_sdk._types import MYPY
  4. from sentry_sdk.consts import OP
  5. from sentry_sdk.integrations import Integration
  6. if MYPY:
  7. from socket import AddressFamily, SocketKind
  8. from typing import Tuple, Optional, Union, List
  9. __all__ = ["SocketIntegration"]
  10. class SocketIntegration(Integration):
  11. identifier = "socket"
  12. origin = f"auto.socket.{identifier}"
  13. @staticmethod
  14. def setup_once() -> None:
  15. """
  16. patches two of the most used functions of socket: create_connection and getaddrinfo(dns resolver)
  17. """
  18. _patch_create_connection()
  19. _patch_getaddrinfo()
  20. def _get_span_description(
  21. host: "Union[bytes, str, None]", port: "Union[bytes, str, int, None]"
  22. ) -> str:
  23. try:
  24. host = host.decode() # type: ignore
  25. except (UnicodeDecodeError, AttributeError):
  26. pass
  27. try:
  28. port = port.decode() # type: ignore
  29. except (UnicodeDecodeError, AttributeError):
  30. pass
  31. description = "%s:%s" % (host, port) # type: ignore
  32. return description
  33. def _patch_create_connection() -> None:
  34. real_create_connection = socket.create_connection
  35. def create_connection(
  36. address: "Tuple[Optional[str], int]",
  37. timeout: "Optional[float]" = socket._GLOBAL_DEFAULT_TIMEOUT, # type: ignore
  38. source_address: "Optional[Tuple[Union[bytearray, bytes, str], int]]" = None,
  39. ) -> "socket.socket":
  40. integration = sentry_sdk.get_client().get_integration(SocketIntegration)
  41. if integration is None:
  42. return real_create_connection(address, timeout, source_address)
  43. with sentry_sdk.start_span(
  44. op=OP.SOCKET_CONNECTION,
  45. name=_get_span_description(address[0], address[1]),
  46. origin=SocketIntegration.origin,
  47. ) as span:
  48. span.set_data("address", address)
  49. span.set_data("timeout", timeout)
  50. span.set_data("source_address", source_address)
  51. return real_create_connection(
  52. address=address, timeout=timeout, source_address=source_address
  53. )
  54. socket.create_connection = create_connection # type: ignore
  55. def _patch_getaddrinfo() -> None:
  56. real_getaddrinfo = socket.getaddrinfo
  57. def getaddrinfo(
  58. host: "Union[bytes, str, None]",
  59. port: "Union[bytes, str, int, None]",
  60. family: int = 0,
  61. type: int = 0,
  62. proto: int = 0,
  63. flags: int = 0,
  64. ) -> "List[Tuple[AddressFamily, SocketKind, int, str, Union[Tuple[str, int], Tuple[str, int, int, int], Tuple[int, bytes]]]]":
  65. integration = sentry_sdk.get_client().get_integration(SocketIntegration)
  66. if integration is None:
  67. return real_getaddrinfo(host, port, family, type, proto, flags)
  68. with sentry_sdk.start_span(
  69. op=OP.SOCKET_DNS,
  70. name=_get_span_description(host, port),
  71. origin=SocketIntegration.origin,
  72. ) as span:
  73. span.set_data("host", host)
  74. span.set_data("port", port)
  75. return real_getaddrinfo(host, port, family, type, proto, flags)
  76. socket.getaddrinfo = getaddrinfo