def __init__(self, value): if isinstance(value, Dsn): self.__dict__ = dict(value.__dict__) return parts = urlparse.urlsplit(text_type(value)) if parts.scheme not in (u"http", u"https"): raise BadDsn("Unsupported scheme %r" % parts.scheme) self.scheme = parts.scheme self.host = parts.hostname self.port = parts.port if self.port is None: self.port = self.scheme == "https" and 443 or 80 self.public_key = parts.username if not self.public_key: raise BadDsn("Missing public key") self.secret_key = parts.password path = parts.path.rsplit("/", 1) try: self.project_id = text_type(int(path.pop())) except (ValueError, TypeError): raise BadDsn("Invalid project in DSN (%r)" % (parts.path or "")[1:]) self.path = "/".join(path) + "/"
def fetch_git_sha(path, head=None): """ >>> fetch_git_sha(os.path.dirname(__file__)) Taken from https://git.io/JITmC """ if not head: head_path = os.path.join(path, '.git', 'HEAD') if not os.path.exists(head_path): raise InvalidGitRepository( 'Cannot identify HEAD for git repository at %s' % (path, )) with open(head_path, 'r') as fp: head = text_type(fp.read()).strip() if head.startswith('ref: '): head = head[5:] revision_file = os.path.join(path, '.git', *head.split('/')) else: return head else: revision_file = os.path.join(path, '.git', 'refs', 'heads', head) if not os.path.exists(revision_file): if not os.path.exists(os.path.join(path, '.git')): raise InvalidGitRepository( '%s does not seem to be the root of a git repository' % (path, )) # Check for our .git/packed-refs' file since a `git gc` may have run # https://git-scm.com/book/en/v2/Git-Internals-Maintenance-and-Data-Recovery packed_file = os.path.join(path, '.git', 'packed-refs') if os.path.exists(packed_file): with open(packed_file) as fh: for line in fh: line = line.rstrip() if line and line[:1] not in ('#', '^'): try: revision, ref = line.split(' ', 1) except ValueError: continue if ref == head: return text_type(revision) raise InvalidGitRepository( 'Unable to find ref to head "%s" in repository' % (head, )) with open(revision_file) as fh: return text_type(fh.read()).strip()
def test_transaction_events(capture_events, init_celery, celery_invocation, task_fails): celery = init_celery(traces_sample_rate=1.0) @celery.task(name="dummy_task") def dummy_task(x, y): return x / y # XXX: For some reason the first call does not get instrumented properly. celery_invocation(dummy_task, 1, 1) events = capture_events() with start_transaction(name="submission") as transaction: celery_invocation(dummy_task, 1, 0 if task_fails else 1) if task_fails: error_event = events.pop(0) assert error_event["contexts"]["trace"][ "trace_id"] == transaction.trace_id assert error_event["exception"]["values"][0][ "type"] == "ZeroDivisionError" execution_event, submission_event = events assert execution_event["transaction"] == "dummy_task" assert submission_event["transaction"] == "submission" assert execution_event["type"] == submission_event["type"] == "transaction" assert execution_event["contexts"]["trace"][ "trace_id"] == transaction.trace_id assert submission_event["contexts"]["trace"][ "trace_id"] == transaction.trace_id if task_fails: assert execution_event["contexts"]["trace"][ "status"] == "internal_error" else: assert execution_event["contexts"]["trace"]["status"] == "ok" assert execution_event["spans"] == [] assert submission_event["spans"] == [{ u"description": u"dummy_task", u"op": "celery.submit", u"parent_span_id": submission_event["contexts"]["trace"]["span_id"], u"same_process_as_parent": True, u"span_id": submission_event["spans"][0]["span_id"], u"start_timestamp": submission_event["spans"][0]["start_timestamp"], u"timestamp": submission_event["spans"][0]["timestamp"], u"trace_id": text_type(transaction.trace_id), }]
def test_transport_option(monkeypatch): dsn = "https://[email protected]/123" dsn2 = "https://[email protected]/124" assert str(Client(dsn=dsn).dsn) == dsn assert Client().dsn is None monkeypatch.setenv("SENTRY_DSN", dsn) transport = Transport({"dsn": dsn2}) assert text_type(transport.parsed_dsn) == dsn2 assert str(Client(transport=transport).dsn) == dsn
def _prepare_event(self, event, hint, scope): if event.get("timestamp") is None: event["timestamp"] = datetime.utcnow() if scope is not None: event = scope.apply_to_event(event, hint) if event is None: return if ( self.options["attach_stacktrace"] and "exception" not in event and "stacktrace" not in event and "threads" not in event ): with capture_internal_exceptions(): event["threads"] = [ { "stacktrace": current_stacktrace(), "crashed": False, "current": True, } ] for key in "release", "environment", "server_name", "dist": if event.get(key) is None and self.options[key] is not None: event[key] = text_type(self.options[key]).strip() if event.get("sdk") is None: sdk_info = dict(SDK_INFO) sdk_info["integrations"] = sorted(self.integrations.keys()) event["sdk"] = sdk_info if event.get("platform") is None: event["platform"] = "python" event = handle_in_app( event, self.options["in_app_exclude"], self.options["in_app_include"] ) before_send = self.options["before_send"] if before_send is not None: with capture_internal_exceptions(): new_event = before_send(event, hint) if new_event is None: logger.info("before send dropped event (%s)", event) event = new_event # Postprocess the event in the very end so that annotated types do # generally not surface in before_send if event is not None: strip_event_mut(event) event = flatten_metadata(event) event = convert_types(event) return event
def _annotate(**meta): # type: (**Any) -> None while len(meta_stack) <= len(path): try: segment = path[len(meta_stack) - 1] node = meta_stack[-1].setdefault(text_type(segment), {}) except IndexError: node = {} meta_stack.append(node) meta_stack[-1].setdefault("", {}).update(meta)
def sanitize(self, item, value): if value is None: return if not item: # key can be a NoneType return value # Just in case we have bytes here, we want to make them into text # properly without failing so we can perform our check. if isinstance(item, bytes): item = item.decode('utf-8', 'replace') else: item = text_type(item) item = item.lower() for key in self.sanitize_keys: if key in item: # store mask as a fixed length for security return self.MASK return value
def convert_types(obj): # type: (Any) -> Any if obj is None: return None if obj is CYCLE_MARKER: return u"<cyclic>" if isinstance(obj, datetime): return text_type(obj.strftime("%Y-%m-%dT%H:%M:%SZ")) if isinstance(obj, Mapping): return {k: convert_types(v) for k, v in obj.items()} if isinstance(obj, Sequence) and not isinstance(obj, (text_type, bytes)): return [convert_types(v) for v in obj] if isinstance(obj, AnnotatedValue): return AnnotatedValue(convert_types(obj.value), obj.metadata) if not isinstance(obj, string_types + number_types): return safe_repr(obj) if isinstance(obj, bytes): return obj.decode("utf-8", "replace") return obj
def _serialize_node_impl(self, obj, max_depth, max_breadth): # type: (Any, Optional[int], Optional[int]) -> Any if max_depth is None and max_breadth is None and self.meta_node.is_databag( ): max_depth = self.meta_node._depth + MAX_DATABAG_DEPTH max_breadth = self.meta_node._depth + MAX_DATABAG_BREADTH if max_depth is None: remaining_depth = None else: remaining_depth = max_depth - self.meta_node._depth obj = _flatten_annotated(obj, self.meta_node) if remaining_depth is not None and remaining_depth <= 0: self.meta_node.annotate(rem=[["!limit", "x"]]) if self.meta_node.is_databag(): return _flatten_annotated(strip_string(safe_repr(obj)), self.meta_node) return None if self.meta_node.is_databag(): hints = {"memo": self.memo, "remaining_depth": remaining_depth} for processor in global_repr_processors: with capture_internal_exceptions(): result = processor(obj, hints) if result is not NotImplemented: return _flatten_annotated(result, self.meta_node) if isinstance(obj, Mapping): # Create temporary list here to avoid calling too much code that # might mutate our dictionary while we're still iterating over it. items = [] for i, (k, v) in enumerate(iteritems(obj)): if max_breadth is not None and i >= max_breadth: self.meta_node.annotate(len=max_breadth) break items.append((k, v)) rv_dict = {} # type: Dict[Any, Any] for k, v in items: k = text_type(k) with self.enter(k): v = self._serialize_node(v, max_depth=max_depth, max_breadth=max_breadth) if v is not None: rv_dict[k] = v return rv_dict elif isinstance(obj, Sequence) and not isinstance(obj, string_types): rv_list = [] # type: List[Any] for i, v in enumerate(obj): if max_breadth is not None and i >= max_breadth: self.meta_node.annotate(len=max_breadth) break with self.enter(i): rv_list.append( self._serialize_node(v, max_depth=max_depth, max_breadth=max_breadth)) return rv_list if self.meta_node.should_repr_strings(): obj = safe_repr(obj) else: if obj is None or isinstance(obj, (bool, number_types)): return obj if isinstance(obj, datetime): return text_type(obj.strftime("%Y-%m-%dT%H:%M:%SZ")) if isinstance(obj, bytes): obj = obj.decode("utf-8", "replace") if not isinstance(obj, string_types): obj = safe_repr(obj) return _flatten_annotated(strip_string(obj), self.meta_node)
def _prepare_event( self, event, # type: Event hint, # type: Optional[Hint] scope, # type: Optional[Scope] ): # type: (...) -> Optional[Event] if event.get("timestamp") is None: event["timestamp"] = datetime.utcnow() hint = dict(hint or ()) # type: Hint if scope is not None: event_ = scope.apply_to_event(event, hint) if event_ is None: return None event = event_ if (self.options["attach_stacktrace"] and "exception" not in event and "stacktrace" not in event and "threads" not in event): with capture_internal_exceptions(): event["threads"] = { "values": [{ "stacktrace": current_stacktrace(self.options["with_locals"]), "crashed": False, "current": True, }] } for key in "release", "environment", "server_name", "dist": if event.get(key) is None and self.options[ key] is not None: # type: ignore event[key] = text_type( self.options[key]).strip() # type: ignore if event.get("sdk") is None: sdk_info = dict(SDK_INFO) sdk_info["integrations"] = sorted(self.integrations.keys()) event["sdk"] = sdk_info if event.get("platform") is None: event["platform"] = "python" event = handle_in_app(event, self.options["in_app_exclude"], self.options["in_app_include"]) # Postprocess the event here so that annotated types do # generally not surface in before_send if event is not None: event = Serializer().serialize_event(event) before_send = self.options["before_send"] if before_send is not None: new_event = None with capture_internal_exceptions(): new_event = before_send(event, hint or {}) if new_event is None: logger.info("before send dropped event (%s)", event) event = new_event # type: ignore return event
def _serialize_node_impl( obj, is_databag, should_repr_strings, remaining_depth, remaining_breadth ): # type: (Any, Optional[bool], Optional[bool], Optional[int], Optional[int]) -> Any if should_repr_strings is None: should_repr_strings = _should_repr_strings() if is_databag is None: is_databag = _is_databag() if is_databag and remaining_depth is None: remaining_depth = MAX_DATABAG_DEPTH if is_databag and remaining_breadth is None: remaining_breadth = MAX_DATABAG_BREADTH obj = _flatten_annotated(obj) if remaining_depth is not None and remaining_depth <= 0: _annotate(rem=[["!limit", "x"]]) if is_databag: return _flatten_annotated(strip_string(safe_repr(obj))) return None if is_databag and global_repr_processors: hints = {"memo": memo, "remaining_depth": remaining_depth} for processor in global_repr_processors: result = processor(obj, hints) if result is not NotImplemented: return _flatten_annotated(result) if obj is None or isinstance(obj, (bool, number_types)): return obj if not should_repr_strings else safe_repr(obj) elif isinstance(obj, datetime): return ( text_type(obj.strftime("%Y-%m-%dT%H:%M:%S.%fZ")) if not should_repr_strings else safe_repr(obj) ) elif isinstance(obj, Mapping): # Create temporary copy here to avoid calling too much code that # might mutate our dictionary while we're still iterating over it. obj = dict(iteritems(obj)) rv_dict = {} i = 0 for k, v in iteritems(obj): if remaining_breadth is not None and i >= remaining_breadth: _annotate(len=len(obj)) break str_k = text_type(k) v = _serialize_node( v, segment=str_k, should_repr_strings=should_repr_strings, is_databag=is_databag, remaining_depth=remaining_depth - 1 if remaining_depth is not None else None, remaining_breadth=remaining_breadth, ) if v is not None: rv_dict[str_k] = v i += 1 return rv_dict elif not isinstance(obj, serializable_str_types) and isinstance(obj, Sequence): rv_list = [] for i, v in enumerate(obj): if remaining_breadth is not None and i >= remaining_breadth: _annotate(len=len(obj)) break rv_list.append( _serialize_node( v, segment=i, should_repr_strings=should_repr_strings, is_databag=is_databag, remaining_depth=remaining_depth - 1 if remaining_depth is not None else None, remaining_breadth=remaining_breadth, ) ) return rv_list if should_repr_strings: obj = safe_repr(obj) else: if isinstance(obj, bytes): obj = obj.decode("utf-8", "replace") if not isinstance(obj, string_types): obj = safe_repr(obj) return _flatten_annotated(strip_string(obj))
def _serialize_node_impl(obj, is_databag, should_repr_strings, remaining_depth, remaining_breadth): # type: (Any, Optional[bool], Optional[bool], Optional[int], Optional[int]) -> Any if should_repr_strings is None: should_repr_strings = _should_repr_strings() if is_databag is None: is_databag = _is_databag() if is_databag and remaining_depth is None: remaining_depth = MAX_DATABAG_DEPTH if is_databag and remaining_breadth is None: remaining_breadth = MAX_DATABAG_BREADTH obj = _flatten_annotated(obj) if remaining_depth is not None and remaining_depth <= 0: _annotate(rem=[["!limit", "x"]]) if is_databag: return _flatten_annotated(strip_string(safe_repr(obj))) return None if is_databag and global_repr_processors: hints = {"memo": memo, "remaining_depth": remaining_depth} for processor in global_repr_processors: result = processor(obj, hints) if result is not NotImplemented: return _flatten_annotated(result) if obj is None or isinstance(obj, (bool, number_types)): if should_repr_strings or (isinstance(obj, float) and (math.isinf(obj) or math.isnan(obj))): return safe_repr(obj) else: return obj elif isinstance(obj, datetime): return (text_type(format_timestamp(obj)) if not should_repr_strings else safe_repr(obj)) elif isinstance(obj, Mapping): # Create temporary copy here to avoid calling too much code that # might mutate our dictionary while we're still iterating over it. obj = dict(iteritems(obj)) rv_dict = {} # type: Dict[str, Any] i = 0 for k, v in iteritems(obj): if remaining_breadth is not None and i >= remaining_breadth: _annotate(len=len(obj)) break str_k = text_type(k) v = _serialize_node( v, segment=str_k, should_repr_strings=should_repr_strings, is_databag=is_databag, remaining_depth=remaining_depth - 1 if remaining_depth is not None else None, remaining_breadth=remaining_breadth, ) rv_dict[str_k] = v i += 1 return rv_dict elif not isinstance(obj, serializable_str_types) and isinstance( obj, (Set, Sequence)): rv_list = [] for i, v in enumerate(obj): if remaining_breadth is not None and i >= remaining_breadth: _annotate(len=len(obj)) break rv_list.append( _serialize_node( v, segment=i, should_repr_strings=should_repr_strings, is_databag=is_databag, remaining_depth=remaining_depth - 1 if remaining_depth is not None else None, remaining_breadth=remaining_breadth, )) return rv_list if should_repr_strings: obj = safe_repr(obj) else: if isinstance(obj, bytes): obj = obj.decode("utf-8", "replace") if not isinstance(obj, string_types): obj = safe_repr(obj) # Allow span descriptions to be longer than other strings. # # For database auto-instrumented spans, the description contains # potentially long SQL queries that are most useful when not truncated. # Because arbitrarily large events may be discarded by the server as a # protection mechanism, we dynamically limit the description length # later in _truncate_span_descriptions. if (smart_transaction_trimming and len(path) == 3 and path[0] == "spans" and path[-1] == "description"): span_description_bytes.append(len(obj)) return obj return _flatten_annotated(strip_string(obj))
def safe_str(value): # type: (Any) -> str try: return text_type(value) except Exception: return safe_repr(value)
def _serialize_node_impl( obj, max_depth, max_breadth, is_databag, should_repr_strings ): # type: (Any, Optional[int], Optional[int], Optional[bool], Optional[bool]) -> Any if not should_repr_strings: should_repr_strings = ( _startswith_path( ("exception", "values", None, "stacktrace", "frames", None, "vars") ) or _startswith_path( ("threads", "values", None, "stacktrace", "frames", None, "vars") ) or _startswith_path(("stacktrace", "frames", None, "vars")) ) if obj is None or isinstance(obj, (bool, number_types)): return obj if not should_repr_strings else safe_repr(obj) if isinstance(obj, datetime): return ( text_type(obj.strftime("%Y-%m-%dT%H:%M:%S.%fZ")) if not should_repr_strings else safe_repr(obj) ) if not is_databag: is_databag = ( should_repr_strings or _startswith_path(("request", "data")) or _startswith_path(("breadcrumbs", None)) or _startswith_path(("extra",)) ) cur_depth = len(path) if max_depth is None and max_breadth is None and is_databag: max_depth = cur_depth + MAX_DATABAG_DEPTH max_breadth = cur_depth + MAX_DATABAG_BREADTH if max_depth is None: remaining_depth = None else: remaining_depth = max_depth - cur_depth obj = _flatten_annotated(obj) if remaining_depth is not None and remaining_depth <= 0: _annotate(rem=[["!limit", "x"]]) if is_databag: return _flatten_annotated(strip_string(safe_repr(obj))) return None if global_repr_processors and is_databag: hints = {"memo": memo, "remaining_depth": remaining_depth} for processor in global_repr_processors: result = processor(obj, hints) if result is not NotImplemented: return _flatten_annotated(result) if isinstance(obj, Mapping): # Create temporary copy here to avoid calling too much code that # might mutate our dictionary while we're still iterating over it. if max_breadth is not None and len(obj) >= max_breadth: rv_dict = dict(itertools.islice(iteritems(obj), None, max_breadth)) _annotate(len=len(obj)) else: if type(obj) is dict: rv_dict = dict(obj) else: rv_dict = dict(iteritems(obj)) for k in list(rv_dict): str_k = text_type(k) v = _serialize_node( rv_dict.pop(k), max_depth=max_depth, max_breadth=max_breadth, segment=str_k, should_repr_strings=should_repr_strings, is_databag=is_databag, ) if v is not None: rv_dict[str_k] = v return rv_dict elif not isinstance(obj, string_types) and isinstance(obj, Sequence): if max_breadth is not None and len(obj) >= max_breadth: rv_list = list(obj)[:max_breadth] _annotate(len=len(obj)) else: rv_list = list(obj) for i in range(len(rv_list)): rv_list[i] = _serialize_node( rv_list[i], max_depth=max_depth, max_breadth=max_breadth, segment=i, should_repr_strings=should_repr_strings, is_databag=is_databag, ) return rv_list if should_repr_strings: obj = safe_repr(obj) else: if isinstance(obj, bytes): obj = obj.decode("utf-8", "replace") if not isinstance(obj, string_types): obj = safe_repr(obj) return _flatten_annotated(strip_string(obj))
def safe_str(value): try: return text_type(value) except Exception: return safe_repr(value)
def to_string(value): try: return text_type(value) except UnicodeDecodeError: return repr(value)[1:-1]
def _prepare_event( self, event, # type: Event hint, # type: Hint scope, # type: Optional[Scope] ): # type: (...) -> Optional[Event] if event.get("timestamp") is None: event["timestamp"] = datetime.utcnow() if scope is not None: is_transaction = event.get("type") == "transaction" event_ = scope.apply_to_event(event, hint) # one of the event/error processors returned None if event_ is None: if self.transport: self.transport.record_lost_event( "event_processor", data_category=("transaction" if is_transaction else "error"), ) return None event = event_ if ( self.options["attach_stacktrace"] and "exception" not in event and "stacktrace" not in event and "threads" not in event ): with capture_internal_exceptions(): event["threads"] = { "values": [ { "stacktrace": current_stacktrace( self.options["with_locals"], self.options["with_source_context"], ), "crashed": False, "current": True, } ] } for key in "release", "environment", "server_name", "dist": if event.get(key) is None and self.options[key] is not None: event[key] = text_type(self.options[key]).strip() if event.get("sdk") is None: sdk_info = dict(SDK_INFO) sdk_info["integrations"] = sorted(self.integrations.keys()) event["sdk"] = sdk_info if event.get("platform") is None: event["platform"] = "python" event = handle_in_app( event, self.options["in_app_exclude"], self.options["in_app_include"] ) # Postprocess the event here so that annotated types do # generally not surface in before_send if event is not None: event = serialize( event, smart_transaction_trimming=self.options["_experiments"].get( "smart_transaction_trimming" ), ) before_send = self.options["before_send"] if before_send is not None and event.get("type") != "transaction": new_event = None with capture_internal_exceptions(): new_event = before_send(event, hint or {}) if new_event is None: logger.info("before send dropped event (%s)", event) if self.transport: self.transport.record_lost_event( "before_send", data_category="error" ) event = new_event # type: ignore return event