utils.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. from sentry_sdk.consts import SPANDATA
  2. from sentry_sdk.integrations.redis.consts import (
  3. _COMMANDS_INCLUDING_SENSITIVE_DATA,
  4. _MAX_NUM_ARGS,
  5. _MAX_NUM_COMMANDS,
  6. _MULTI_KEY_COMMANDS,
  7. _SINGLE_KEY_COMMANDS,
  8. )
  9. from sentry_sdk.scope import should_send_default_pii
  10. from sentry_sdk.utils import SENSITIVE_DATA_SUBSTITUTE
  11. from typing import TYPE_CHECKING
  12. if TYPE_CHECKING:
  13. from typing import Any, Optional, Sequence
  14. from sentry_sdk.tracing import Span
  15. def _get_safe_command(name: str, args: "Sequence[Any]") -> str:
  16. command_parts = [name]
  17. name_low = name.lower()
  18. send_default_pii = should_send_default_pii()
  19. for i, arg in enumerate(args):
  20. if i > _MAX_NUM_ARGS:
  21. break
  22. if name_low in _COMMANDS_INCLUDING_SENSITIVE_DATA:
  23. command_parts.append(SENSITIVE_DATA_SUBSTITUTE)
  24. continue
  25. arg_is_the_key = i == 0
  26. if arg_is_the_key:
  27. command_parts.append(repr(arg))
  28. else:
  29. if send_default_pii:
  30. command_parts.append(repr(arg))
  31. else:
  32. command_parts.append(SENSITIVE_DATA_SUBSTITUTE)
  33. command = " ".join(command_parts)
  34. return command
  35. def _safe_decode(key: "Any") -> str:
  36. if isinstance(key, bytes):
  37. try:
  38. return key.decode()
  39. except UnicodeDecodeError:
  40. return ""
  41. return str(key)
  42. def _key_as_string(key: "Any") -> str:
  43. if isinstance(key, (dict, list, tuple)):
  44. key = ", ".join(_safe_decode(x) for x in key)
  45. elif isinstance(key, bytes):
  46. key = _safe_decode(key)
  47. elif key is None:
  48. key = ""
  49. else:
  50. key = str(key)
  51. return key
  52. def _get_safe_key(
  53. method_name: str,
  54. args: "Optional[tuple[Any, ...]]",
  55. kwargs: "Optional[dict[str, Any]]",
  56. ) -> "Optional[tuple[str, ...]]":
  57. """
  58. Gets the key (or keys) from the given method_name.
  59. The method_name could be a redis command or a django caching command
  60. """
  61. key = None
  62. if args is not None and method_name.lower() in _MULTI_KEY_COMMANDS:
  63. # for example redis "mget"
  64. key = tuple(args)
  65. elif args is not None and len(args) >= 1:
  66. # for example django "set_many/get_many" or redis "get"
  67. if isinstance(args[0], (dict, list, tuple)):
  68. key = tuple(args[0])
  69. else:
  70. key = (args[0],)
  71. elif kwargs is not None and "key" in kwargs:
  72. # this is a legacy case for older versions of Django
  73. if isinstance(kwargs["key"], (list, tuple)):
  74. if len(kwargs["key"]) > 0:
  75. key = tuple(kwargs["key"])
  76. else:
  77. if kwargs["key"] is not None:
  78. key = (kwargs["key"],)
  79. return key
  80. def _parse_rediscluster_command(command: "Any") -> "Sequence[Any]":
  81. return command.args
  82. def _set_pipeline_data(
  83. span: "Span",
  84. is_cluster: bool,
  85. get_command_args_fn: "Any",
  86. is_transaction: bool,
  87. commands_seq: "Sequence[Any]",
  88. ) -> None:
  89. span.set_tag("redis.is_cluster", is_cluster)
  90. span.set_tag("redis.transaction", is_transaction)
  91. commands = []
  92. for i, arg in enumerate(commands_seq):
  93. if i >= _MAX_NUM_COMMANDS:
  94. break
  95. command = get_command_args_fn(arg)
  96. commands.append(_get_safe_command(command[0], command[1:]))
  97. span.set_data(
  98. "redis.commands",
  99. {
  100. "count": len(commands_seq),
  101. "first_ten": commands,
  102. },
  103. )
  104. def _set_client_data(span: "Span", is_cluster: bool, name: str, *args: "Any") -> None:
  105. span.set_tag("redis.is_cluster", is_cluster)
  106. if name:
  107. span.set_tag("redis.command", name)
  108. span.set_tag(SPANDATA.DB_OPERATION, name)
  109. if name and args:
  110. name_low = name.lower()
  111. if (name_low in _SINGLE_KEY_COMMANDS) or (
  112. name_low in _MULTI_KEY_COMMANDS and len(args) == 1
  113. ):
  114. span.set_tag("redis.key", args[0])