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